2016-02-02 23 views
6

numaralı telefonu arayarak şu anda bir makine öğrenme projesi üzerinde çalışıyorum - bir veri matrisi Z ve bir vektör rho - verilen değer ve eğimini hesaplamak zorundayım rho'da logistic loss function. Hesaplama, temel taşma-vektör çarpımını ve log/exp işlemlerini içerir, sayısal taşmayı önlemek için bir numara (bu previous post sayılı belgede açıklanmıştır).Python'da matris-vektör çarpma ve üssüasyonun hızlandırılması, muhtemelen C/C++

Şu anda bunu aşağıda gösterildiği gibi Python'da NumPy kullanarak yapıyorum (başvuru olarak bu kod 0,2s'de çalışır). Bu iyi çalışıyor olsa da, kodumda birden çok kez işlevi çağırdığımdan hızlandırmak istiyorum (ve projemde yer alan hesaplamanın% 90'ından fazlasını temsil ediyor).

Paralel olmadan bu kodun çalışma zamanını iyileştirmenin herhangi bir yolunu arıyorum (örn. Sadece 1 CPU). Python'da herkese açık olan bir paketi kullanmaktan veya C veya C++ 'yı aradığımdan (bu durumun çalışma zamanlarını büyüklük sırasına göre iyileştirdiğini duyduğumdan) mutluyum. Veri matrisi Z'un ön işlemlerinin yapılması da Tamam olur. Daha iyi hesaplaması için yararlanılabilir Bazı şeyler vektör rho (girişler = 0 yaklaşık% 50 ile), genellikle seyrek ve kadar fazla satır


(çoğu durumda n_cols <= 100 olarak) sütunlardan daha genelde bulunmamasıdır BLAS ailesinin kitaplıkları, en iyi performans için zaten çoktan ayarlanmış durumdadır.
import time 
import numpy as np 

np.__config__.show() #make sure BLAS/LAPACK is being used 
np.random.seed(seed = 0) 

#initialize data matrix X and label vector Y 
n_rows, n_cols = 1e6, 100 
X = np.random.random(size=(n_rows, n_cols)) 
Y = np.random.randint(low=0, high=2, size=(n_rows, 1)) 
Y[Y==0] = -1 
Z = X*Y # all operations are carried out on Z 

def compute_logistic_loss_value_and_slope(rho, Z): 
    #compute the value and slope of the logistic loss function in a way that is numerically stable 
    #loss_value: (1 x 1) scalar = 1/n_rows * sum(log(1 .+ exp(-Z*rho)) 
    #loss_slope: (n_cols x 1) vector = 1/n_rows * sum(-Z*rho ./ (1+exp(-Z*rho)) 
    #see also: https://stackoverflow.com/questions/20085768/ 

    scores = Z.dot(rho) 
    pos_idx = scores > 0 
    exp_scores_pos = np.exp(-scores[pos_idx]) 
    exp_scores_neg = np.exp(scores[~pos_idx]) 

    #compute loss value 
    loss_value = np.empty_like(scores) 
    loss_value[pos_idx] = np.log(1.0 + exp_scores_pos) 
    loss_value[~pos_idx] = -scores[~pos_idx] + np.log(1.0 + exp_scores_neg) 
    loss_value = loss_value.mean() 

    #compute loss slope 
    phi_slope = np.empty_like(scores) 
    phi_slope[pos_idx] = 1.0/(1.0 + exp_scores_pos) 
    phi_slope[~pos_idx] = exp_scores_neg/(1.0 + exp_scores_neg) 
    loss_slope = Z.T.dot(phi_slope - 1.0)/Z.shape[0] 

    return loss_value, loss_slope 


#initialize a vector of integers where more than half of the entries = 0 
rho_test = np.random.randint(low=-10, high=10, size=(n_cols, 1)) 
set_to_zero = np.random.choice(range(0,n_cols), size =(np.floor(n_cols/2), 1), replace=False) 
rho_test[set_to_zero] = 0.0 

start_time = time.time() 
loss_value, loss_slope = compute_logistic_loss_value_and_slope(rho_test, Z) 
print "total runtime = %1.5f seconds" % (time.time() - start_time) 
+3

1'den fazla CPU'yu neden hariç tutuyorsunuz? Her ne kadar Python VM temelde tek bir iş parçacığı olsa da, verileri daha çok iş parçacığı dostu bir veri yapısına kopyaladıktan sonra bir C uzantısından POSIX iş parçacığı çağırabilirsiniz.Birden fazla CPU kullanmamanın başka nedenleri olabilir, ancak C. – rts1

+1

@rts'e kaçarsanız bu kısıtlama ile sınırlı değilsiniz. Bu durumda, 'compute_logistic_loss_function' işlevini çağıran kod aslında paralelleştirilmiş olduğundan, 1 CPU'yla sınırlandırmam gerekiyor ... Bu yüzden işlev çağrıldığında yalnızca 1 CPU kullanılabilir. –

+1

Büyük 'n' için çalışma zamanı,' Z' ile aynı boyuta yayın yapan loss_slope = Z * (phi_slope - 1.0) 'ile baskın görünüyor. Sıra üzeri satırları aldığınız için, bunu bir nokta ürün olarak yeniden yazabilirim. ZTdot (phi_slope) .T/Z.shape [0] ', bu da benim için 4'lük bir hız artışı sağlar. makinesi. –

cevap

1

Öyleyse, bazı C/C++ kodlarına bağlanma çabaları size herhangi bir fayda sağlamayacaktır. Bununla birlikte, bazı CPU'lara özel olarak ayarlanmış olanlar da dahil olmak üzere, bunlardan sadece birkaçı olduğundan, çeşitli BLAS uygulamalarını deneyebilirsiniz.

benim akla gelen diğer bir şey theano gibi bir kütüphane kullanmaktır (veya Google'ın tensorflow) tüm hesaplama grafiği (yukarıda Fonksiyonunuzda operasyonların tümü) temsil eden ve buna küresel optimizasyonları uygulamak yapabiliyor. Daha sonra bu grafikten C++ (ve ayrıca basit bir anahtan da GPU kodu çevirerek) üzerinden CPU kodu üretebilir. Ayrıca sizin için sembolik türevleri otomatik olarak hesaplayabilir. Makine öğrenimi problemleri için theano kullandım ve öğrenmesi en kolay olanı olmasa da, bunun için gerçekten harika bir kütüphane.

(bir yorum için çok uzun olduğu için ben cevap olarak bu mesaj)

Düzenleme: Aslında Theano bu gitmek vardı ama sonuç aslında yaklaşık 2x olduğunu

CPU üzerinde daha yavaş, nedenini aşağıya bakın. Numpy oldukça optimize edilmiştir

import theano 

def make_graph(rho, Z): 
    scores = theano.tensor.dot(Z, rho) 

    # this is very inefficient... it calculates everything twice and 
    # then picks one of them depending on scores being positive or not. 
    # not sure how to express this in theano in a more efficient way 
    pos = theano.tensor.log(1 + theano.tensor.exp(-scores)) 
    neg = theano.tensor.log(scores + theano.tensor.exp(scores)) 
    loss_value = theano.tensor.switch(scores > 0, pos, neg) 
    loss_value = loss_value.mean() 

    # however computing the derivative is a real joy now: 
    loss_slope = theano.tensor.grad(loss_value, rho) 

    return loss_value, loss_slope 

sym_rho = theano.tensor.col('rho') 
sym_Z = theano.tensor.matrix('Z') 
sym_loss_value, sym_loss_slope = make_graph(sym_rho, sym_Z) 

compute_logistic_loss_value_and_slope = theano.function(
     inputs=[sym_rho, sym_Z], 
     outputs=[sym_loss_value, sym_loss_slope] 
     ) 

# use function compute_logistic_loss_value_and_slope() as in original code 
0

(bu orijinal yayından kodu ile komple sadece kısmi bir koddur): Zaten buradan göndeririz, belki de daha iyi bir şeyler yapmak başkası için bir başlangıç ​​noktası. Yapabileceğiniz en iyi şey, aynı büyüklükteki veriyi rastgele (başlangıçta 0'a değil) başlatılan ve kendi ölçütlerinizi yapan diğer kitaplıkları denemektir.

Denemek isterseniz, BLAS'ı deneyebilirsiniz. Ayrıca, eigen numaralı telefonu denemelisiniz, kişisel olarak uygulamalardan birinde daha hızlı buldum.