Jak uzyskać szybszy Kod niż numpy.kropka do mnożenia macierzy?
Tutaj mnożenie macierzy za pomocą hdf5 używam hdf5 (pytables) do mnożenia dużych macierzy, ale byłem zaskoczony, ponieważ używając hdf5 działa jeszcze szybciej niż używając zwykłej numpy.dot i przechowuj macierze w pamięci RAM, jaka jest przyczyna takiego zachowania?
I może jest jakaś szybsza funkcja do mnożenia macierzy w Pythonie, ponieważ nadal używam numpy.kropka do mnożenia macierzy małych bloków.
Oto jakiś kod:
Załóżmy, że macierze mogą zmieścić się w pamięci RAM: test na matryca 10*1000 x 1000.
Using default numpy (I think no BLAS lib). Zwykłe tablice numpy są w pamięci RAM: czas 9.48
Jeśli A, B w pamięci RAM, C na dysku: czas 1.48
Jeśli A, B, C na dysku: czas 372.25
Jeśli używam numpy z MKL wyniki są: 0.15, 0.45, 43.5.
Wyniki wyglądają rozsądnie, ale nadal nie rozumiem, dlaczego w 1. przypadku mnożenie bloków jest szybsze (gdy przechowujemy A, B w pamięci RAM).
n_row=1000
n_col=1000
n_batch=10
def test_plain_numpy():
A=np.random.rand(n_row,n_col)# float by default?
B=np.random.rand(n_col,n_row)
t0= time.time()
res= np.dot(A,B)
print (time.time()-t0)
#A,B in RAM, C on disk
def test_hdf5_ram():
rows = n_row
cols = n_col
batches = n_batch
#using numpy array
A=np.random.rand(n_row,n_col)
B=np.random.rand(n_col,n_row)
#settings for all hdf5 files
atom = tables.Float32Atom() #if store uint8 less memory?
filters = tables.Filters(complevel=9, complib='blosc') # tune parameters
Nchunk = 128 # ?
chunkshape = (Nchunk, Nchunk)
chunk_multiple = 1
block_size = chunk_multiple * Nchunk
#using hdf5
fileName_C = 'CArray_C.h5'
shape = (A.shape[0], B.shape[1])
h5f_C = tables.open_file(fileName_C, 'w')
C = h5f_C.create_carray(h5f_C.root, 'CArray', atom, shape, chunkshape=chunkshape, filters=filters)
sz= block_size
t0= time.time()
for i in range(0, A.shape[0], sz):
for j in range(0, B.shape[1], sz):
for k in range(0, A.shape[1], sz):
C[i:i+sz,j:j+sz] += np.dot(A[i:i+sz,k:k+sz],B[k:k+sz,j:j+sz])
print (time.time()-t0)
h5f_C.close()
def test_hdf5_disk():
rows = n_row
cols = n_col
batches = n_batch
#settings for all hdf5 files
atom = tables.Float32Atom() #if store uint8 less memory?
filters = tables.Filters(complevel=9, complib='blosc') # tune parameters
Nchunk = 128 # ?
chunkshape = (Nchunk, Nchunk)
chunk_multiple = 1
block_size = chunk_multiple * Nchunk
fileName_A = 'carray_A.h5'
shape_A = (n_row*n_batch, n_col) # predefined size
h5f_A = tables.open_file(fileName_A, 'w')
A = h5f_A.create_carray(h5f_A.root, 'CArray', atom, shape_A, chunkshape=chunkshape, filters=filters)
for i in range(batches):
data = np.random.rand(n_row, n_col)
A[i*n_row:(i+1)*n_row]= data[:]
rows = n_col
cols = n_row
batches = n_batch
fileName_B = 'carray_B.h5'
shape_B = (rows, cols*batches) # predefined size
h5f_B = tables.open_file(fileName_B, 'w')
B = h5f_B.create_carray(h5f_B.root, 'CArray', atom, shape_B, chunkshape=chunkshape, filters=filters)
sz= rows/batches
for i in range(batches):
data = np.random.rand(sz, cols*batches)
B[i*sz:(i+1)*sz]= data[:]
fileName_C = 'CArray_C.h5'
shape = (A.shape[0], B.shape[1])
h5f_C = tables.open_file(fileName_C, 'w')
C = h5f_C.create_carray(h5f_C.root, 'CArray', atom, shape, chunkshape=chunkshape, filters=filters)
sz= block_size
t0= time.time()
for i in range(0, A.shape[0], sz):
for j in range(0, B.shape[1], sz):
for k in range(0, A.shape[1], sz):
C[i:i+sz,j:j+sz] += np.dot(A[i:i+sz,k:k+sz],B[k:k+sz,j:j+sz])
print (time.time()-t0)
h5f_A.close()
h5f_B.close()
h5f_C.close()
1 answers
np.dot
wysyłamy do BLAS Kiedy
- NumPy został skompilowany do użycia BLAS, W tym celu należy skontaktować się z Działem obsługi klienta.]}
- Twoje dane mają jeden z typów dtyp
float32
,float64
,complex32
lubcomplex64
, oraz - dane są odpowiednio wyrównane w pamięci.
W przeciwnym razie, domyślnie używa własnej, powolnej procedury mnożenia macierzy.
Sprawdzanie połączenia BLAS jest opisane tutaj . Krótko mówiąc, sprawdź, czy istnieje plik _dotblas.so
lub podobny w Twojej instalacji NumPy. Kiedy jest, sprawdź, z którą biblioteką BLAS jest połączona; referencyjne BLAS jest powolne, ATLAS jest szybki, openblas i wersje specyficzne dla dostawcy, takie jak Intel MKL są jeszcze szybsze. Uważaj na wielowątkowe implementacje BLAS, ponieważ nie grają ładnie z multiprocessing
Pythona.
Następnie sprawdź wyrównanie danych, sprawdzając flags
tablic. W wersjach NumPy przed 1.7.2 oba argumenty np.dot
powinny być C-rozkaz. W NumPy > = 1.7.2 nie ma to już większego znaczenia, ponieważ wprowadzono specjalne Przypadki dla tablic Fortran.
>>> X = np.random.randn(10, 4)
>>> Y = np.random.randn(7, 4).T
>>> X.flags
C_CONTIGUOUS : True
F_CONTIGUOUS : False
OWNDATA : True
WRITEABLE : True
ALIGNED : True
UPDATEIFCOPY : False
>>> Y.flags
C_CONTIGUOUS : False
F_CONTIGUOUS : True
OWNDATA : False
WRITEABLE : True
ALIGNED : True
UPDATEIFCOPY : False
Jeśli Twoja NumPy nie jest powiązana z BLAS, albo (łatwo) zainstaluj ją ponownie, albo (ciężko) użyj funkcji BLAS gemm
(uogólnione mnożenie macierzy) z SciPy:
>>> from scipy.linalg import get_blas_funcs
>>> gemm = get_blas_funcs("gemm", [X, Y])
>>> np.all(gemm(1, X, Y) == np.dot(X, Y))
True
Wygląda to na łatwe, ale prawie nie sprawdza błędów, więc musisz naprawdę wiedzieć, co robisz.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:13