【问题标题】:Using a sparse matrix versus numpy array使用稀疏矩阵与 numpy 数组
【发布时间】:2016-08-26 11:27:06
【问题描述】:

我在 Python 中创建了一些带有字数的 numpy 数组:行是文档,列是单词 X 的计数。如果我有很多零计数,人们建议在进一步处理这些时使用稀疏矩阵,例如在分类器中。然而,当将一个 numpy 数组与一个稀疏矩阵输入 Scikit logistic regression classifier 时,它似乎并没有太大的区别。所以我想知道三件事:

  • Wikipedia

    稀疏矩阵是大多数元素为零的矩阵

    这是确定何时使用稀疏矩阵的合适方法吗 格式 - 一旦 > 50 % 的值为零?或者它使 以防万一有用吗?

  • 稀疏矩阵对像我这样的任务的性能有多大帮助, 尤其是与 numpy 数组或标准列表相比?
  • 到目前为止,我将数据收集到一个 numpy 数组中,然后转换为 Scipy 中的 csr_matrix。这是正确的方法吗?我不能 弄清楚如何从头开始构建一个稀疏矩阵,然后 可能是不可能的。

非常感谢任何帮助!

【问题讨论】:

    标签: python numpy matrix scipy scikit-learn


    【解决方案1】:

    scipy 稀疏矩阵包和 MATLAB 中的类似包基于从线性代数问题发展而来的思想,例如求解大型稀疏线性方程(例如有限差分和有限元实现)。因此,矩阵乘积(numpy 数组的dot 乘积)和方程求解器之类的东西都得到了很好的开发。

    我的粗略经验是,稀疏csr 矩阵乘积必须具有 1% 的稀疏度才能比等效的密集dot 运算更快——换句话说,每 99 个零有一个非零值。 (但请参阅下面的测试)

    但人们也尝试使用稀疏矩阵来节省内存。但请记住,这样的矩阵必须存储 3 个值数组(至少在 coo 格式中)。所以稀疏度必须小于 1/3 才能开始节省内存。显然,如果您首先构建密集数组并从中创建稀疏数组,则不会节省内存。

    scipy 包实现了许多稀疏格式。 coo 格式最容易理解和构建。根据文档构建一个并查看其.data.row.col 属性(3 个一维数组)。

    csrcsc 通常是从 coo 格式构建的,并稍微压缩数据,使它们更难理解。但它们具有大部分数学功能。

    也可以索引csr 格式,尽管通常这比等效的密集矩阵/数组情况要慢。改变值(尤其是从 0 到非零)、连接、增量增长等其他操作也较慢。

    lil(列表列表)也很容易理解,最适合增量构建。 dok 实际上是一个字典子类。

    关键点是稀疏矩阵仅限于 2d,并且在许多方面表现得像 np.matrix 类(尽管它不是子类)。

    使用scikit-learnsparse 搜索其他问题可能是找出使用这些矩阵的优缺点的最佳方式。我已经回答了许多问题,但我比“学习”方面更了解“稀疏”方面。我认为它们很有用,但我觉得合身并不总是最好的。任何自定义都在learn 方面。到目前为止,sparse 包尚未针对此应用程序进行优化。


    我刚刚尝试了一些矩阵乘积测试,使用sparse.random 方法创建具有指定稀疏度的稀疏矩阵。稀疏矩阵乘法的表现比我预期的要好。

    In [251]: M=sparse.random(1000,1000,.5)
    
    In [252]: timeit M1=M*M
    1 loops, best of 3: 2.78 s per loop
    
    In [253]: timeit Ma=M.toarray(); M2=Ma.dot(Ma)
    1 loops, best of 3: 4.28 s per loop
    

    这是一个尺寸问题;对于较小的矩阵,密集的dot 更快

    In [255]: M=sparse.random(100,100,.5)
    
    In [256]: timeit M1=M*M
    100 loops, best of 3: 3.24 ms per loop
    
    In [257]: timeit Ma=M.toarray(); M2=Ma.dot(Ma)
    1000 loops, best of 3: 1.44 ms per loop
    

    但是比较索引

    In [268]: timeit M.tocsr()[500,500]
    10 loops, best of 3: 86.4 ms per loop
    
    In [269]: timeit Ma[500,500]
    1000000 loops, best of 3: 318 ns per loop
    
    In [270]: timeit Ma=M.toarray();Ma[500,500]
    10 loops, best of 3: 23.6 ms per loop
    

    【讨论】:

    • In [257]: timeit Ma=M.toarray(); M2=Ma.dot(Ma) 这行不考虑将稀疏矩阵转换为密集矩阵的时间吗?
    • @NicholasZhi,是的,toarray 是时间的一部分。如果您认为Ma.dot 部分很重要,可以很容易地对其进行计时。
    【解决方案2】:

    @hpaulj 你的时间错了,考虑到这一点,你的结果很慢,因为将 sparse.random 映射到 numpy 数组(它的速度很慢):

    M=sparse.random(1000,1000,.5)
    Ma=M.toarray()
    
    %timeit -n 25 M1=M*M
    352 ms ± 1.18 ms per loop (mean ± std. dev. of 7 runs, 25 loops each)
    
    %timeit -n 25 M2=Ma.dot(Ma)
    13.5 ms ± 2.17 ms per loop (mean ± std. dev. of 7 runs, 25 loops each)
    

    要接近 numpy,我们需要拥有

    M=sparse.random(1000,1000,.03)
    
    %timeit -n 25 M1=M*M
    10.7 ms ± 119 µs per loop (mean ± std. dev. of 7 runs, 25 loops each)
    
    %timeit -n 25 M2=Ma.dot(Ma)
    11.4 ms ± 564 µs per loop (mean ± std. dev. of 7 runs, 25 loops each)
    
    
    

    【讨论】:

    • 第二次,Ma 的值与 M 不同,对吧?只是确认一下,我认为理论上它不应该改变 numpy.dot 的计时结果。
    【解决方案3】:

    稀疏矩阵是大多数元素为零的矩阵 这是确定何时使用稀疏矩阵格式的适当方法 - 只要 > 50 % 的值为零?还是以防万一有意义?

    没有一般规则。这完全取决于您以后的确切用法。您必须根据稀疏矩阵计算模型的复杂度,然后才能找到“最佳位置”。这将取决于样本数量和维度。一般来说,它通常归结为以下形式的矩阵乘法

    X' W
    

    其中 X 是数据矩阵 N x d,W 是某个权重矩阵 d x K。因此,“密集”乘法需要 NdK 时间,而稀疏的,假设您的平均每行稀疏度为 p 是 NpdK。因此,如果您的稀疏度为 50%,则可以预期运行速度快近 2 倍。较难的部分是估计稀疏访问的开销,而不是高度优化的基于密集的访问。

    稀疏矩阵在像我这样的任务中对性能有多大帮助,尤其是与 numpy 数组或标准列表相比?

    对于 LR 的特定情况,这可能比密集格式快几倍,但为了观察差异,您需要大量高维 (>100) 的数据 (>1000)。

    到目前为止,我将数据收集到一个 numpy 数组中,然后在 Scipy 中转换为 csr_matrix。这是正确的方法吗?我不知道如何从头开始构建稀疏矩阵,这可能是不可能的。

    不,这不是一个好方法。您可以“从头开始”构建它,例如首先构建一个字典然后对其进行转换等。有很多方法可以构建稀疏矩阵,而无需首先构建密集矩阵。

    【讨论】:

    • 作为补充说明,scipy 文档 patrick 链接到实际上在底部有一些示例,说明如何从头开始构建稀疏矩阵。
    • 我知道您不应该发布“谢谢”cmets,但这是一个很棒的答案和非常有用的评论。谢谢大家。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-20
    • 2021-11-25
    • 2013-09-24
    • 2016-11-24
    • 1970-01-01
    • 2018-05-30
    相关资源
    最近更新 更多