【问题标题】:efficient way for writing to list of lists made from matrix写入由矩阵组成的列表列表的有效方法
【发布时间】:2021-09-09 00:38:20
【问题描述】:

我正在尝试使用以下代码从另一个填充稀疏 lil 矩阵的一部分:

adj_mat = sp.dok_matrix((self.n_users + self.m_items, self.n_users + self.m_items), dtype=np.float32)
adj_mat = adj_mat.tolil()
R = self.UserItemNet.tolil()

当我尝试用此代码填充时:

adj_mat[:self.n_users, self.n_users:] = R
adj_mat[self.n_users:, :self.n_users] = R.T

由于超出 RAM 内存 (240Gi),我的进程被杀死。 我的数据集很大:

adj_mat:

<1374194x1374194 sparse matrix of type '<class 'numpy.float32'>'
with 0 stored elements in List of Lists format>

R:

<940696x433498 sparse matrix of type '<class 'numpy.float64'>'
with 24053124 stored elements in List of Lists format>

self.n_users = 940696

有没有更有效的方法来填充这样的列表?

最好的问候

【问题讨论】:

  • 我们必须研究它是如何从一个lil 复制到另一个的。太糟糕了,与内存错误消息相反,这是一个杀戮。很高兴知道它走了多远。第一个副本成功了吗? R.T 有问题吗?是不是在第二个副本。不幸的是,这不是我想在我的电脑上测试的东西——我不喜欢挂起进程。而且我的内存要小得多。
  • 实际上,第一行杀死了该进程。我不知道它走了多远,但我试图在我的一半数据上做到这一点——它也失败了。我只有 1/10 的数据通过了该代码。
  • List of list 是一种极其低效的数据存储方式。永远不要以任何理由使用它。改用首席运营官。
  • 挖掘lil __setitem__ 代码,它最终使用__set_arrayXarray_sparse,但这会产生“# Fall back to densifying x”。换句话说,它不会尝试找出如何将R 的非零值复制到adj_mat,而是执行R.toarray() 并进行“常规”分配。所以致密R 可能是导致内存错误的原因。
  • @CJR,我怀疑他正在使用lil,因为它应该最适合索引分配。 coo 不做索引。我不确定lil 格式在内存使用方面是否比coo 更糟糕。对于大多数矩阵,csr 优于 coo 是众所周知的。但在这里我认为问题是默认的setitem 路由。

标签: python matrix memory scipy sparse-matrix


【解决方案1】:

这是构造复合矩阵的bmat 方法(假设我已经推断出正确的布局):

制作一个矩阵。 bmat 将结合 coo 属性,所以让我们开始吧:

In [389]: R = sparse.coo_matrix([[0,1],[2,0],[0,0],[3,4]])
In [390]: R
Out[390]: 
<4x2 sparse matrix of type '<class 'numpy.int64'>'
    with 4 stored elements in COOrdinate format>
In [391]: R.A
Out[391]: 
array([[0, 1],
       [2, 0],
       [0, 0],
       [3, 4]])

并定义“空白”填充矩阵:

In [392]: Z1 = sparse.coo_matrix((4,4),dtype=int)
In [393]: Z2 = sparse.coo_matrix((2,2),dtype=int)

现在加入他们:

In [394]: M = sparse.bmat([[Z1,R],[R.T,Z2]])
In [395]: M
Out[395]: 
<6x6 sparse matrix of type '<class 'numpy.int64'>'
    with 8 stored elements in COOrdinate format>
In [396]: M.A
Out[396]: 
array([[0, 0, 0, 0, 0, 1],
       [0, 0, 0, 0, 2, 0],
       [0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 3, 4],
       [0, 2, 0, 3, 0, 0],
       [1, 0, 0, 4, 0, 0]])

这将避免默认分配明显所做的密集化。

block_diag 使用另一个对角线:

In [398]: sparse.block_diag([R,R.T])
Out[398]: 
<6x6 sparse matrix of type '<class 'numpy.int64'>'
    with 8 stored elements in COOrdinate format>
In [399]: _.A
Out[399]: 
array([[0, 1, 0, 0, 0, 0],
       [2, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0],
       [3, 4, 0, 0, 0, 0],
       [0, 0, 0, 2, 0, 3],
       [0, 0, 1, 0, 0, 4]])

如果您想编写自己的版本,这个block_diag 代码将是一个很好的模型。 v1.6 发行说明声称它比以前的版本更有效(我相信它通过bmat 工作)。

分配效率

针对@CJR 的cmets 关于lil 内存效率低下的问题,我研究了一些替代方案。

制作一个大的coo矩阵:

In [10]: M=sparse.random(10000,10000, .2, 'coo')

转换为lil 比转换为csr 慢:

In [11]: timeit M.tocsr()
1.43 s ± 1.11 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [12]: timeit M.tolil()
3.69 s ± 10.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

那么块分配如何比较? (使用比 OP 小得多的块):

In [13]: Ml=M.tolil(); Mr=M.tocsr()

In [14]: timeit Ml[:100,:100]=np.eye(100)
1.07 ms ± 341 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [15]: timeit Mr[:100,:100]=np.eye(100)
/usr/local/lib/python3.8/dist-packages/scipy/sparse/_index.py:125: SparseEfficiencyWarning: Changing the sparsity structure of a csr_matrix is expensive. lil_matrix is more efficient.
  self._set_arrayXarray(i, j, x)
14.1 ms ± 144 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

csr 赋值要慢一些,而coo 赋值甚至不起作用。

In [16]: timeit M[:100,:100]=np.eye(100)
Traceback (most recent call last):
  ....
TypeError: 'coo_matrix' object does not support item assignment

因此,如果您必须阻止分配,lil 是一个不错的选择,前提是块不是太大。但是通过bmat 直接从块构造矩阵更好。正如lil 文档所说,如果您要构建大型矩阵,请使用coo

【讨论】:

  • 我已经用 bmat 制作了它,它可以工作!感谢您的帮助。
猜你喜欢
  • 2017-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-10
  • 2017-05-09
  • 1970-01-01
  • 2013-05-08
  • 1970-01-01
相关资源
最近更新 更多