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()
Author: Community, 2013-11-07

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 lub complex64, 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.

 38
Author: Fred Foo,
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