Skip to content

Latest commit

 

History

History
178 lines (125 loc) · 9.83 KB

sparse-matrix-representation-python.md

File metadata and controls

178 lines (125 loc) · 9.83 KB

Python 中的稀疏矩阵表示

原文:www.kdnuggets.com/2020/05/sparse-matrix-representation-python.html

大多数机器学习从业者习惯于在将数据输入机器学习算法之前采用矩阵表示法。矩阵是理想的形式,通常行表示数据集实例,列表示特征。

稀疏矩阵是大多数元素为零的矩阵。这与密集矩阵相对,后者的区分特征你现在可能已经能自己弄明白了。


我们的前三大课程推荐

1. Google 网络安全证书 - 快速通道进入网络安全职业生涯。

2. Google 数据分析专业证书 - 提升你的数据分析技能

3. Google IT 支持专业证书 - 支持你的组织 IT


图示

图片来源:TU 柏林

我们的数据通常是密集的,每个实例的特征列都被填满。如果我们使用有限数量的列来全面描述某些事物,一般来说,给定数据点的描述值需要充分发挥作用,以提供有意义的表示:一个人、一张图片、一朵鸢尾花、房价、潜在的信用风险。

但有些类型的数据在其表示中并不需要如此冗长。想想关系。我们可能需要捕捉大量潜在事物的关系状态,但在这些事物的交集处,我们可能只需记录“是的,有关系”或“不是,没有关系”。

这个人是否购买了那个物品?那句话中是否包含这个词?在任何给定的句子中可能出现很多潜在的词,但实际出现的词却不多。同样,虽然市场上可能有很多销售的物品,但任何一个人实际上购买的物品却不多。

这是稀疏矩阵在机器学习中发挥作用的一种方式。想象一下列代表销售项,行代表购物者。在每个给定项没有被给定购物者购买的交集处,将有一个“无”(空)表示,比如 0. 只有在给定购物者购买了给定项的交集处才需要有“是”表示,比如 1. 对于给定句子中的单词出现情况也是如此。你可以看到为什么这样的矩阵会包含很多零,即它们会是稀疏的。

稀疏矩阵的一个问题是它们可能会对内存造成很大的压力。假设你采用标准的方法来表示一个 2x2 矩阵,那么每个空的表示都需要在内存中分配,尽管没有捕捉到有用的信息。这种内存压力还会持续到永久存储中。在标准的矩阵表示方法中,我们被迫记录事物的缺失,而不仅仅是存在。

但等等,一定还有更好的方法!

确实有。稀疏矩阵不必以标准矩阵形式表示。有很多方法可以缓解这种标准形式对计算系统的压力,而恰好在流行的 Python 机器学习工具包 Scikit-learn 中,一些算法接受这些稀疏表示作为输入。熟悉这些方法可以节省你的时间、麻烦和精力。

我们如何更好地表示这些稀疏矩阵?我们需要一种方式来跟踪零值不在的位置。怎样用一个两列的表格,其中一列跟踪row,col(行,列)位置的非零项,另一列记录对应的值?请记住,稀疏矩阵不必仅仅包含零和一;只要大多数元素是零,无论非零元素是什么,矩阵就是稀疏的。

我们还需要确定创建稀疏矩阵的顺序——是逐行处理,每遇到一个非零元素就存储它,还是逐列处理?如果决定逐行处理,恭喜你,你刚刚创建了一个压缩稀疏矩阵。如果逐列处理,你现在拥有的是一个压缩稀疏矩阵。方便的是,Scipy 支持这两种方法。

让我们来看一下如何创建这些矩阵。首先,我们在 Numpy 中创建一个简单的矩阵。

import numpy as np
from scipy import sparse

X = np.random.uniform(size=(6, 6))
print(X)
[[0.79904211 0.76621075 0.57847599 0.72606798 0.10008544 0.90838851]
 [0.45504345 0.10275931 0.0191763  0.09037216 0.14604688 0.02899529]
 [0.92234613 0.78231698 0.43204239 0.61663291 0.78899765 0.44088739]
 [0.50422356 0.72221353 0.57838041 0.30222171 0.25263237 0.55913311]
 [0.191842   0.07242766 0.17230918 0.31543582 0.90043157 0.8787012 ]
 [0.71360049 0.45130523 0.46687117 0.96148004 0.56777938 0.40780967]]

然后我们需要将大多数矩阵元素置为零,使其变为稀疏矩阵。

X[X < 0.7] = 0
print(X)
[[0.79904211 0.76621075 0\.         0.72606798 0\.         0.90838851]
 [0\.         0\.         0\.         0\.         0\.         0\.        ]
 [0.92234613 0.78231698 0\.         0\.         0.78899765 0\.        ]
 [0\.         0.72221353 0\.         0\.         0\.         0\.        ]
 [0\.         0\.         0\.         0\.         0.90043157 0.8787012 ]
 [0.71360049 0\.         0\.         0.96148004 0\.         0\.        ]]

现在我们将标准矩阵 X 存储为压缩稀疏行矩阵。为此,元素按照从左到右的顺序逐行遍历,并在遇到时输入到这个压缩矩阵表示中。

X_csr = sparse.csr_matrix(X)
print(X_csr)
 (0, 0)	0.799042106215471
  (0, 1)	0.7662107548809229
  (0, 3)	0.7260679774297479
  (0, 5)	0.9083885095042665
  (2, 0)	0.9223461264672205
  (2, 1)	0.7823169848589594
  (2, 4)	0.7889976504606654
  (3, 1)	0.7222135307432606
  (4, 4)	0.9004315651436953
  (4, 5)	0.8787011979799789
  (5, 0)	0.7136004887949333
  (5, 3)	0.9614800409505844

瞧!

那么压缩稀疏矩阵怎么样呢?在其创建过程中,元素按照从上到下的顺序逐列遍历,并在遇到时输入到压缩表示中。

X_csc = sparse.csc_matrix(X)
print(X_csc)
 (0, 0)	0.799042106215471
  (2, 0)	0.9223461264672205
  (5, 0)	0.7136004887949333
  (0, 1)	0.7662107548809229
  (2, 1)	0.7823169848589594
  (3, 1)	0.7222135307432606
  (0, 3)	0.7260679774297479
  (5, 3)	0.9614800409505844
  (2, 4)	0.7889976504606654
  (4, 4)	0.9004315651436953
  (0, 5)	0.9083885095042665
  (4, 5)	0.8787011979799789

注意结果稀疏矩阵表示之间的差异,特别是相同元素值的位置差异。

如前所述,许多 Scikit-learn 算法接受形状为 [num_samples, num_features]scipy.sparse 矩阵作为 Numpy 数组的替代,因此目前没有迫切需要将它们转换回标准的 Numpy 表示形式。也可能存在内存限制阻止这种转换(回想一下,这是采用这种方法的主要原因之一)。但为了演示,以下是如何将稀疏的 Scipy 矩阵表示转换回 Numpy 多维数组。

print(X_csr.toarray())
[[0.79904211 0.76621075 0\.         0.72606798 0\.         0.90838851]
 [0\.         0\.         0\.         0\.         0\.         0\.        ]
 [0.92234613 0.78231698 0\.         0\.         0.78899765 0\.        ]
 [0\.         0.72221353 0\.         0\.         0\.         0\.        ]
 [0\.         0\.         0\.         0\.         0.90043157 0.8787012 ]
 [0.71360049 0\.         0\.         0.96148004 0\.         0\.        ]]

那么两种表示方式之间的内存需求差异呢?让我们再次通过这个过程,从一个更大的标准 Numpy 矩阵开始,然后计算每种表示方式所使用的内存(以字节为单位)。

import numpy as np
from scipy import sparse

X = np.random.uniform(size=(10000, 10000))
X[X < 0.7] = 0
X_csr = sparse.csr_matrix(X)

print(f"Size in bytes of original matrix: {X.nbytes}")
print(f"Size in bytes of compressed sparse row matrix: {X_csr.data.nbytes + X_csr.indptr.nbytes + X_csr.indices.nbytes}")
Size in bytes of original matrix: 800000000
Size in bytes of compressed sparse row matrix: 360065312

在这里你可以看到,压缩矩阵形式相比标准 Numpy 表示方式在内存上的显著节省,大约 360 兆字节对比 800 兆字节。这节省了 440 兆字节,并且几乎没有时间开销,因为格式之间的转换经过高度优化。显然,这些稀疏的 SciPy 矩阵也可以直接创建,从而节省了中间内存消耗的步骤。

现在想象一下你正在处理一个庞大的数据集,并考虑通过正确使用稀疏矩阵格式所能享受的内存节省(以及相关的存储和处理时间)。

Matthew Mayo (@mattmayo13) 是数据科学家和 KDnuggets 的总编辑,KDnuggets 是开创性的在线数据科学和机器学习资源。他的兴趣包括自然语言处理、算法设计与优化、无监督学习、神经网络以及自动化机器学习方法。Matthew 拥有计算机科学硕士学位和数据挖掘研究生文凭。他的联系方式是 editor1 at kdnuggets[dot]com。

相关话题更多内容