Sto cercando di implementare una funzione in NumPy/Scipy per calcolare Jensen-Shannon divergence tra un singolo vettore (di allenamento) e un gran numero di altri vettori (di osservazione). I vettori di osservazione sono memorizzati in un numero molto grande (500.000x65536) Scipy sparse matrix (una matrice densa non si adatta alla memoria).Aggiunta di una matrice molto ripetitiva a una sparsa in numpy/scipy?
Come parte dell'algoritmo ho bisogno di calcolare T + O i per ogni osservazione vettore O i, dove T è il vettore di formazione. Non ero in grado di trovare un modo per farlo usando le solite regole di broadcasting di NumPy, dal momento che le matrici sparse non sembrano supportare quelle (se T è lasciato come un array denso, Scipy prova a rendere prima la matrice sparsa densa, che esegue memoria insufficiente, se faccio T in una matrice sparsa, T + O i non riesce perché le forme sono incoerenti).
Attualmente mi sto prendendo il passo grossolanamente inefficiente delle piastrelle del vettore di formazione in una 500,000x65536 matrice sparsa:
training = sp.csr_matrix(training.astype(np.float32))
tindptr = np.arange(0, len(training.indices)*observations.shape[0]+1, len(training.indices), dtype=np.int32)
tindices = np.tile(training.indices, observations.shape[0])
tdata = np.tile(training.data, observations.shape[0])
mtraining = sp.csr_matrix((tdata, tindices, tindptr), shape=observations.shape)
Ma questo richiede una grande quantità di memoria (circa 6 GB), quando è solo la memorizzazione ~ 1500 elementi "reali". È anche piuttosto lento da costruire.
Ho cercato di essere intelligente utilizzando stride_tricks per rendere i membri dell'indptr e dei dati della matrice CSR non utilizzare memoria aggiuntiva sui dati ripetuti.
training = sp.csr_matrix(training)
mtraining = sp.csr_matrix(observations.shape,dtype=np.int32)
tdata = training.data
vdata = np.lib.stride_tricks.as_strided(tdata, (mtraining.shape[0], tdata.size), (0, tdata.itemsize))
indices = training.indices
vindices = np.lib.stride_tricks.as_strided(indices, (mtraining.shape[0], indices.size), (0, indices.itemsize))
mtraining.indptr = np.arange(0, len(indices)*mtraining.shape[0]+1, len(indices), dtype=np.int32)
mtraining.data = vdata
mtraining.indices = vindices
Ma questo non funziona perché i panorami strided mtraining.data e mtraining.indices sono la forma sbagliata (e secondo this answer non c'è alcun modo per rendere la forma a destra). Cercare di renderli flat con l'iteratore .flat fallisce perché non sembra abbastanza come un array (ad esempio non ha un membro dtype) e l'uso del metodo flatten() finisce per fare una copia.
C'è un modo per ottenere questo risultato?
Se si desidera generare tutte le somme in una sola volta, allora si sta andando ad avere bisogno del 6 GB di stoccaggio in ogni caso, quindi non c'è davvero nulla da vincere ritardandolo. Assicurati di fare il summing sul posto, con '+ ='! A proposito, la tua implementazione della piastrellatura è molto intelligente ed efficiente, non penso che tu possa ottenere qualcosa di meglio: ho provato ad alimentare 'csr_matrix' una vista del vettore rimodellata con' as_strided' per avere 500000 righe, e ci è voluto molto più tempo del tuo approccio, penso che internamente la matrice venga copiata, rompendo la magia. Il tuo secondo approccio, come si nota, non può essere fatto con numpy. – Jaime
Le matrici CSR non possono essere modificate sul posto, sfortunatamente (+ = solleva NotImplemented). Quindi immagino di essere bloccato con l'utilizzo di 3 volte più memoria di cui io (in teoria) ho bisogno, il che è doloroso dato che mi sto avvicinando ai limiti del mio (generoso) 32GB. –