Python dask核外矩阵乘法调度

Python dask核外矩阵乘法调度,python,matrix-multiplication,h5py,dask,Python,Matrix Multiplication,H5py,Dask,我试图计算一个10000*800000的矩阵X的矩阵积Y=XX^T。矩阵X以h5py文件的形式存储在磁盘上。结果Y应该是存储在同一h5py文件中的10000*10000矩阵。这是一个可复制的示例代码 import dask.array as da from blaze import into into("h5py:///tmp/dummy::/X", da.ones((10**4, 8*10**5), chunks=(10**4,10**4))) x = into(da.Array, "h5p

我试图计算一个10000*800000的矩阵X的矩阵积Y=XX^T。矩阵X以h5py文件的形式存储在磁盘上。结果Y应该是存储在同一h5py文件中的10000*10000矩阵。这是一个可复制的示例代码

import dask.array as da
from blaze import into

into("h5py:///tmp/dummy::/X", da.ones((10**4, 8*10**5), chunks=(10**4,10**4)))
x = into(da.Array, "h5py:///tmp/dummy::/X", chunks=(10**4,10**4)))
y = x.dot(x.T)
into("h5py:///tmp/dummy::/Y", y)
我希望这个计算能够顺利进行,因为每个(10000*10000)块都应该被单独转置,然后是一个点积,然后求和到最终结果。但是,运行此计算会填满我的RAM和交换内存,直到进程最终被终止

以下是使用点图绘制的计算图示例:

根据sheduling文件,http://dask.pydata.org/en/latest/scheduling-policy.html 我希望在单独计算后,将上张量的中间结果一个接一个地汇总到最后的汇总结果中。这将释放这些tensordot中间结果的内存,这样我们就不会面临内存错误

玩一个较小的玩具示例:

from dask.diagnostics import Profiler, CacheProfiler, ResourceProfiler

# Experiment on a (1,0000 * 5,000) matrix X split into 500 chunks of size (1,000 * 10)
x = into(da.Array, "h5py:///tmp/dummy::/X", chunks=(10**3,10)))[:10**3,5000]
y = x.T.dot(x)
with Profiler() as prof, CacheProfiler() as cprof, ResourceProfiler() as rprof:
    into("h5py:///tmp/dummy::/X", y)
rprof.visualize()
我得到以下显示:

其中,绿色条表示求和运算,黄色和紫色条分别表示get_数组和tensordot运算。这似乎表明求和操作在求和之前会等待所有中间tensordot操作的执行。这也解释了我的进程内存不足并被杀死的原因

因此,我的问题是:

  • 这是求和运算的正常行为吗
  • 有没有办法强迫它先计算中间值 中间的tensordot乘积被计算并保存在内存中
  • 如果没有,是否有不涉及溢出到磁盘的解决方法

非常感谢任何帮助

一般来说,在小空间中执行密集矩阵乘法是困难的。这是因为每个中间块将被多个输出块使用

根据sheduling文档http://dask.pydata.org/en/latest/scheduling-policy.html,我希望在单独计算后,将上张量的中间结果一个接一个地汇总到最后的汇总结果中

您所显示的图形有许多和函数的输入。Dask将等待所有这些输入完成后再运行sum函数。任务调度器不知道sum是关联的,可以逐段运行。语义信息的缺乏是使用Dask这样的通用任务调度系统而不是专用的线性代数库所付出的代价。如果你的目标是尽可能高效地执行密集线性代数,那么你可能想去别处看看;这是一块覆盖良好的田地

因此,如果Dask按照正确的顺序进行(通常应该这样做),那么您的内存需求至少是
8e5*1e4*dtype.itemsize

您可以尝试以下方法:

  • 沿非收缩维度减小chunksize
  • 使用0.14.1之后的Dask版本(0.14.2应在2017年5月5日之前发布),在该版本中,我们在图中明确地将这些大额调用分解为许多较小的调用
  • 使用分布式调度程序,它可以更有效地处理向磁盘写入数据的操作

    from dask.distributed import Client
    client = Client(processes=False)  # create a local cluster in this process
    

  • 非常感谢@MRocklin!关于选项3,我遇到了中提到的问题。Hdf5序列化阵列似乎无法与分布式调度程序配合使用。对它进行修复将允许我使用分布式调度器的实时诊断/监视工具,这在这种情况下非常有用。另外,您知道pydata/python生态系统中有专门的ooc线性代数库吗?我最初开始使用dask是因为它的核心外线性代数特性与Numpy API相结合。