当前位置: 首页 > 面试题库 >

如何获得比numpy.dot更快的代码进行矩阵乘法?

曹理
2023-03-14
问题内容

在这里,使用hdf5进行矩阵乘法时,我使用hdf5(pytables)进行大型矩阵乘法,但令我惊讶的是,使用hdf5比使用普通numpy.dot更快,并且将矩阵存储在RAM中,这种行为的原因是什么?

也许在python中有一些更快的矩阵乘法功能,因为我仍然使用numpy.dot进行小块矩阵乘法。

这是一些代码:

假设矩阵可以容纳在RAM中:在10 * 1000 x 1000的矩阵上进行测试。

使用默认的numpy(我认为没有BLAS lib)。普通的numpy数组在RAM中:时间9.48

如果RAM中的A,B,磁盘上的C:时间1.48

如果磁盘上有A,B,C:时间372.25

如果我将numpy与MKL一起使用,则结果为:0.15、0.45、43.5。

结果看起来合理,但我仍然不明白为什么在第一种情况下块乘法更快(当我们将A,B存储在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()

问题答案:

np.dot何时分派到BLAS

  • NumPy已被编译为使用BLAS,
  • 在运行时可以使用BLAS实现,
  • 你的数据有dtypes之一float32float64complex32complex64
  • 数据在内存中适当对齐。

否则,它默认使用自己的慢速矩阵乘法例程。

这里介绍了检查BLAS链接的方法。简而言之,请检查_dotblas.so您的NumPy安装中是否有文件或类似文件。如果存在,请检查链接到哪个BLAS库;参考BLAS较慢,ATLAS较快,OpenBLAS和特定于供应商的版本(例如Intel
MKL)甚至更快。当心多线程BLAS实现,因为它们与Python的配合不好multiprocessing

接下来,通过检查flags数组的来检查数据对齐方式。在1.7.2之前的NumPy版本中,to的两个参数都np.dot应按C顺序排列。在NumPy>
= 1.7.2中,这不再重要,因为已经引入了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

如果您的NumPy没有与BLAS链接,请(轻松)重新安装它,或(硬)使用gemmSciPy的BLAS (通用矩阵乘法)功能:

>>> 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

这看起来很容易,但是几乎不会进行任何错误检查,因此您必须真正知道自己在做什么。



 类似资料:
  • C++:15秒(源) Python:6分13秒(来源) C++:45分钟(源) 蟒蛇:10小时后被杀死(来源) 为什么Strassen矩阵乘法比标准矩阵乘法慢得多? null null null

  • 在课堂上,我必须为稀疏矩阵编写自己的线性方程求解器。我可以自由地使用任何类型的数据结构为稀疏矩阵,我必须实现几个解决方案,包括共轭梯度。 谢了!

  • 问题内容: 我有两个矩阵 我想得到元素乘积,等于 我试过了 和 但两者都给出结果 这是矩阵乘积,而不是元素乘积。如何使用内置函数获得按元素分类的产品(又名Hadamard产品)? 问题答案: 对于对象的元素乘法,可以使用: 结果 但是,您应该真正使用而不是。对象与常规ndarray具有各种可怕的不兼容性。使用ndarrays时,您可以仅使用元素级乘法: 如果您使用的是Python 3.5+,则您甚

  • 我想问的是,是否有可能用按位运算大大改进整数矩阵乘法。矩阵很小,元素是小的非负整数(小表示最多20个)。 为了保持我们的注意力集中,让我们非常具体,假设我有两个3x3矩阵,有整数条目0<=x<15。 下面这个朴素的C++实现被执行了一百万次,使用linux来衡量,它的性能大约为1秒。 备注: 矩阵不一定是稀疏的。 类似Strassen的注释在这里没有帮助。 让我们尽量不使用间接观察,即在这个具体问

  • 主要内容:逐元素矩阵乘法,矩阵乘积运算,矩阵点积矩阵乘法是将两个矩阵作为输入值,并将 A 矩阵的行与 B 矩阵的列对应位置相乘再相加,从而生成一个新矩阵,如下图所示: 注意:必须确保第一个矩阵中的行数等于第二个矩阵中的列数,否则不能进行矩阵乘法运算。 图1:矩阵乘法 矩阵乘法运算被称为向量化操作,向量化的主要目的是减少使用的 for 循环次数或者根本不使用。这样做的目的是为了加速程序的计算。 下面介绍 NumPy 提供的三种矩阵乘法,从而进一步