Attualmente sto lavorando a un progetto di apprendimento automatico in cui - data una matrice di dati Z
e un vettore rho
- Devo calcolare il valore e la pendenza di il logistic loss function allo rho
. Il calcolo comporta operazioni di moltiplicazione di matrice vettore-matrice e operazioni log/exp, con un trucco per evitare l'overflow numerico (descritto in questo previous post).Accelerazione della moltiplicazione e dell'esponenziazione di matrice vettoriale in Python, possibilmente chiamando C/C++
Attualmente sto facendo questo in Python usando NumPy come mostrato di seguito (come riferimento, questo codice viene eseguito in 0.2 s). Anche se questo funziona bene, vorrei accelerarlo poiché chiamo la funzione più volte nel mio codice (e rappresenta oltre il 90% del calcolo coinvolto nel mio progetto).
Sto cercando un modo per migliorare il runtime di questo codice senza parallelizzazione (vale a dire solo 1 CPU). Sono felice di utilizzare qualsiasi pacchetto disponibile pubblicamente in Python, o di chiamare C o C++ (poiché ho sentito che questo migliora i tempi di esecuzione di un ordine di grandezza). Anche la pre-elaborazione della matrice di dati Z
andrebbe bene. Alcune cose che potrebbero essere sfruttati per meglio calcolo sono che il vettore rho
è generalmente scarsa (con circa il 50% immissioni = 0) e ci sono solitamente molto più righe di colonne (nella maggior parte dei casi n_cols <= 100
)
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)
Perché stai escludendo più di 1 CPU? Sebbene Python VM sia essenzialmente a thread singolo, è possibile richiamare i thread POSIX da un'estensione C dopo aver copiato i dati in una struttura dati più thread-friendly.Potrebbero esserci altri motivi per non utilizzare più CPU, ma non sei limitato da tale restrizione se scappi a C. – rts1
@rts Buona domanda. In questo caso, ho bisogno di limitarlo a 1 CPU poiché il codice che chiama 'compute_logistic_loss_function' è in realtà in parallelo ... Quindi solo 1 CPU sarà disponibile quando viene chiamata la funzione. –
Per grandi 'n' il runtime sembra essere dominato da' loss_slope = Z * (phi_slope - 1.0) ', che trasmette alla stessa dimensione di' Z'. Dato che stai prendendo la media sulle righe, potresti riscriverlo come un prodotto di punti usando 'ZTdot (phi_slope) .T/Z.shape [0]', che dà circa un fattore di 4 accelerazione sul mio macchina. –