【发布时间】:2013-02-20 18:43:32
【问题描述】:
我正在尝试在 NumPy/Scipy 中实现一个函数,以在单个(训练)向量和大量其他(观察)向量之间计算 Jensen-Shannon divergence。观察向量存储在一个非常大的 (500,000x65536) Scipy sparse matrix 中(密集矩阵不适合内存)。
作为算法的一部分,我需要为每个观察向量 Oi 计算 T+Oi,其中 T 是训练向量。我无法使用 NumPy 的常用广播规则找到一种方法,因为稀疏矩阵似乎不支持这些(如果 T 保留为密集数组,Scipy 会尝试首先使稀疏矩阵密集,它运行内存不足;如果我将 T 设为稀疏矩阵,则 T+Oi 会失败,因为形状不一致)。
目前,我正在将训练向量平铺成 500,000x65536 稀疏矩阵,这一步骤效率极低:
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)
但这会占用大量内存(大约 6GB),而它只存储约 1500 个“真实”元素。构建起来也很慢。
我试图通过使用 stride_tricks 使 CSR 矩阵的 indptr 和数据成员不会在重复数据上使用额外的内存来变得聪明。
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
但这不起作用,因为跨步视图 mtraining.data 和 mtraining.indices 是错误的形状(根据this answer,没有办法使它成为正确的形状)。尝试使用 .flat 迭代器使它们看起来平坦失败,因为它看起来不够像数组(例如,它没有 dtype 成员),并且使用 flatten() 方法最终会制作副本。
有什么办法可以解决吗?
【问题讨论】:
-
如果你想一次生成所有的总和,那么无论如何你都需要 6GB 的存储空间,所以延迟它真的没什么可取的。只需确保使用
+=就地求和!顺便说一句,您的平铺实现非常智能和高效,我认为您没有比这更好的了:我尝试给csr_matrix提供一个用as_strided重新整形的矢量视图以获得500000 行,并且它比您的方法花费的时间要长得多,我认为内部数组正在被复制,打破了魔力。正如您所指出的,您的第二种方法不能用 numpy 完成。 -
CSR 矩阵不能就地修改,不幸的是(+= 引发 NotImplemented)。所以我想我坚持使用(理论上)需要的 3 倍内存,这很痛苦,因为我接近我(慷慨的)32GB 的极限。
标签: python matrix numpy scipy sparse-matrix