包含大部分零值的矩阵称为稀疏矩阵,这与大部分值为非零值的矩阵(称为稠密矩阵)不同。
大型稀疏矩阵在通用和应用机器学习中都很常见,例如包含计数的数据、将类别映射到计数的编码,甚至在自然语言处理等整个机器学习子领域中。
将稀疏矩阵表示和处理得像稠密矩阵一样在计算上成本很高,通过使用专门处理矩阵稀疏性的表示和操作,可以显著提高性能。
在本教程中,您将了解稀疏矩阵、它们带来的问题以及如何在 Python 中直接处理它们。
完成本教程后,您将了解:
- 稀疏矩阵包含大部分零值,并且与稠密矩阵不同。
- 您可能在数据、数据准备和机器学习子领域中遇到的稀疏矩阵的众多领域。
- 有许多有效的方法可以存储和处理稀疏矩阵,SciPy 提供了可以直接使用的实现。
立即开始您的项目,获取我的新书《机器学习线性代数》,其中包含分步教程和所有示例的Python源代码文件。
让我们开始吧。

机器学习稀疏矩阵入门指南
照片来源:CAJC: in the Rockies,保留部分权利。
教程概述
本教程分为5个部分,它们是:
- 稀疏矩阵
- 稀疏性问题
- 机器学习中的稀疏矩阵
- 处理稀疏矩阵
- Python 中的稀疏矩阵
在机器学习线性代数方面需要帮助吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
稀疏矩阵
稀疏矩阵是由大部分零值组成的矩阵。
稀疏矩阵与大部分非零值的矩阵(称为稠密矩阵)不同。
如果一个矩阵的许多系数为零,则称该矩阵是稀疏的。人们对稀疏性的兴趣在于,利用稀疏性可以带来巨大的计算节省,并且实践中遇到的许多大型矩阵问题都是稀疏的。
— 第 1 页,《稀疏矩阵直接法》,第二版,2017 年。
矩阵的稀疏性可以通过分数来量化,该分数是矩阵中零值的数量除以矩阵中的总元素数量。
1 |
稀疏性 = 零元素计数 / 总元素数 |
下面是一个 3x6 小型稀疏矩阵的示例。
1 2 3 |
1, 0, 0, 1, 0, 0 A = (0, 0, 2, 0, 0, 1) 0, 0, 0, 2, 0, 0 |
该示例中矩阵有 18 个元素,其中 13 个是零值,因此该矩阵的稀疏性得分为 0.722,约为 72%。
稀疏性问题
稀疏矩阵在空间和时间复杂度方面可能引起问题。
空间复杂度
非常大的矩阵需要大量内存,而我们希望处理的一些非常大的矩阵是稀疏的。
在实践中,大多数大型矩阵都是稀疏的——几乎所有元素都是零。
— 第 465 页,《线性代数导论》,第五版,2016 年。
一个无法存储在内存中的非常大的矩阵的例子是链接矩阵,它显示了一个网站与其他网站之间的链接。
一个较小的稀疏矩阵的例子可能是单词或术语出现矩阵,其中包含一个书中单词与所有已知英语单词的对应关系。
在这两种情况下,矩阵都包含稀疏数据,零值远多于数据值。将这些稀疏矩阵表示为稠密矩阵的问题在于,内存需要为矩阵中的每个 32 位甚至 64 位零值分配。
这显然是在浪费内存资源,因为这些零值不包含任何信息。
时间复杂度
假设一个非常大的稀疏矩阵可以放入内存,我们将希望在该矩阵上执行操作。
简单来说,如果矩阵包含大部分零值,即没有数据,那么对该矩阵执行操作可能需要很长时间,因为大部分计算涉及到将零值相加或相乘。
对这类问题使用通用的线性代数方法是浪费的,因为求解方程组或求逆矩阵的 O(N^3) 算术运算大部分涉及零操作数。
— 第 75 页,《数值方法:科学计算的艺术》,第三版,2007 年。
这是一个矩阵操作时间复杂度增加的问题,该复杂度随矩阵大小的增大而增加。
当我们考虑到即使是简单的机器学习方法也可能需要在每一行、每一列甚至整个矩阵上进行多次操作时,这个问题就会加剧,导致执行时间大大延长。
机器学习中的稀疏矩阵
稀疏矩阵在应用机器学习中非常常见。
在本节中,我们将看一些常见示例,以促使您了解稀疏性问题。
数据
稀疏矩阵出现在某些特定类型的数据中,尤其是那些记录活动发生或计数的观测数据。
三个例子包括:
- 用户是否在电影目录中看过某部电影。
- 用户是否在产品目录中购买过某件产品。
- 用户在歌曲目录中收听某首歌曲的次数。
数据准备
稀疏矩阵出现在数据准备中使用的编码方案中。
三个常见示例包括
- 独热编码,用于将分类数据表示为稀疏二元向量。
- 计数编码,用于表示文档词汇表中单词的频率。
- TF-IDF 编码,用于表示词汇表中归一化的单词频率分数。
研究领域
机器学习中的一些研究领域必须开发专门的方法来直接解决稀疏性问题,因为输入数据几乎总是稀疏的。
三个例子包括:
- 自然语言处理,用于处理文本文档。
- 推荐系统,用于处理目录中的产品使用情况。
- 计算机视觉,用于处理包含大量黑色像素的图像。
如果语言模型中有 100,000 个单词,那么特征向量的长度就是 100,000,但对于一个简短的电子邮件消息,几乎所有特征的计数都为零。
— 第 866 页,《人工智能:一种现代方法》,第三版,2009 年。
处理稀疏矩阵
表示和处理稀疏矩阵的解决方案是使用替代数据结构来表示稀疏数据。
可以忽略零值,只需要存储或处理稀疏矩阵中的数据或非零值。
有多种数据结构可用于高效地构建稀疏矩阵;下面列出了三个常见示例。
- 键值对字典。使用字典,其中行和列索引映射到值。
- 列表的列表。矩阵的每一行都存储为一个列表,每个子列表包含列索引和值。
- 坐标列表。存储一个元组列表,每个元组包含行索引、列索引和值。
还有更适合执行高效操作的数据结构;下面列出了两个常用的示例。
- 压缩稀疏行 (CSR)。稀疏矩阵使用三个一维数组来表示非零值、行的范围和列索引。
- 压缩稀疏列 (CSC)。与压缩稀疏行方法相同,只是列索引被压缩并且在行索引之前读取。
压缩稀疏行(简称 CSR)常用于表示机器学习中的稀疏矩阵,因为它支持高效的访问和矩阵乘法。
Python 中的稀疏矩阵
SciPy 提供了使用多种数据结构创建稀疏矩阵的工具,以及将稠密矩阵转换为稀疏矩阵的工具。
许多 线性代数 NumPy 和 SciPy 函数可以透明地处理 SciPy 稀疏数组。此外,使用 NumPy 数据结构的机器学习库也可以透明地处理 SciPy 稀疏数组,例如用于通用机器学习的 scikit-learn 和用于深度学习的 Keras。
存储在 NumPy 数组中的稠密矩阵可以通过调用 `csr_matrix()` 函数转换为 CSR 表示的稀疏矩阵。
在下面的示例中,我们将一个 3x6 的稀疏矩阵定义为一个稠密数组,将其转换为 CSR 稀疏表示,然后通过调用 `todense()` 函数将其转换回稠密数组。
1 2 3 4 5 6 7 8 9 10 11 12 |
# 稠密转稀疏 from numpy import array from scipy.sparse import csr_matrix # 创建稠密矩阵 A = array([[1, 0, 0, 1, 0, 0], [0, 0, 2, 0, 0, 1], [0, 0, 0, 2, 0, 0]]) print(A) # 转换为稀疏矩阵(CSR 方法) S = csr_matrix(A) print(S) # 重构稠密矩阵 B = S.todense() print(B) |
运行示例后,首先打印定义的稠密数组,然后是 CSR 表示,最后是重构的稠密矩阵。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[[1 0 0 1 0 0] [0 0 2 0 0 1] [0 0 0 2 0 0]] (0, 0) 1 (0, 3) 1 (1, 2) 2 (1, 5) 1 (2, 3) 2 [[1 0 0 1 0 0] [0 0 2 0 0 1] [0 0 0 2 0 0]] |
NumPy 没有提供计算矩阵稀疏性的函数。
尽管如此,我们可以通过先找到矩阵的密度并将其从 1 中减去来轻松计算它。NumPy 数组中的非零元素数量可以通过 `count_nonzero()` 函数获得,数组中的总元素数量可以通过数组的 `size` 属性获得。因此,数组稀疏性可以计算为:
1 |
sparsity = 1.0 - count_nonzero(A) / A.size |
下面的示例演示了如何计算数组的稀疏性。
1 2 3 4 5 6 7 8 9 |
# 计算稀疏性 from numpy import array from numpy import count_nonzero # 创建稠密矩阵 A = array([[1, 0, 0, 1, 0, 0], [0, 0, 2, 0, 0, 1], [0, 0, 0, 2, 0, 0]]) print(A) # 计算稀疏性 sparsity = 1.0 - count_nonzero(A) / A.size print(sparsity) |
运行示例后,首先打印定义的稀疏矩阵,然后是矩阵的稀疏性。
1 2 3 4 5 |
[[1 0 0 1 0 0] [0 0 2 0 0 1] [0 0 0 2 0 0]] 0.7222222222222222 |
扩展
本节列出了一些您可能希望探索的扩展本教程的想法。
- 开发您自己的示例,用于将稠密数组转换为稀疏数组和计算稀疏性。
- 为 SciPy 支持的每种稀疏矩阵表示方法开发一个示例。
- 选择一种稀疏表示方法,并从头开始实现它。
如果您探索了这些扩展中的任何一个,我很想知道。
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
书籍
- 《线性代数导论》,第五版,2016 年。
- 第二章 2.7 稀疏线性系统,《数值方法:科学计算的艺术》,第三版,2007 年。
- 《人工智能:一种现代方法》,第三版,2009 年。
- 《稀疏矩阵直接法》,第二版,2017 年。
API
- 稀疏矩阵 (scipy.sparse) API
- scipy.sparse.csr_matrix() API
- numpy.count_nonzero() API
- numpy.ndarray.size API
文章
总结
在本教程中,您了解了稀疏矩阵、它们带来的问题以及如何在 Python 中直接处理它们。
具体来说,你学到了:
- 稀疏矩阵包含大部分零值,并且与稠密矩阵不同。
- 您可能在数据、数据准备和机器学习子领域中遇到的稀疏矩阵的众多领域。
- 有许多有效的方法可以存储和处理稀疏矩阵,SciPy 提供了可以直接使用的实现。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
工作很棒
谢谢!
谢谢,作为一名正在为计算机科学复习代数的成人学习者,这是最好的解释!
谢谢,很高兴听到这个!
Jason 先生,非常感谢。您的博客总是很棒。我从您宝贵的帖子中学到了很多。有没有一种方法可以生成例如 0-16 之间的 1000000 个随机数,并将它们分成特定大小的 mini_batch 来训练神经网络?
为什么?
对计算基础知识做得很好!
但是,请提供更多关于如何驱动逻辑结果或应用的“如何做”和计算。
例如什么?
CSR/CSC 的性能在性能方面受到生成索引的开销的严重限制。
块状 CSR/CSC 是更好的方法,尤其适用于 SIMD 机器,并且允许循环展开和向量化,与普通的 CSC/CSR 相比,可以极大地提高性能。
David,很棒的建议,谢谢。
Jason 和各位好,您在这些领域付出的努力真是太棒了。
您询问的例子是提供超出系统计算的指令,以及如何驱动上下文中的矩阵比较、乘法和其他可以直接生成结果的操作的逻辑。
我希望我们能继续讨论,并相信 Jason 是这里的一个优秀人选。
此致
抱歉,我没听懂。您能提供更多上下文吗?
Jason,
感谢这篇精彩的文章。下一个自然的问题是:我们能否在 scikit-learn 等 ML 库中使用高效的稀疏矩阵表示?
此致
我相信是的。
非常有帮助,再一次!由于我只有使用稠密 numpy 数组的经验,我不知道如何将 Keras 稀疏数组输入到机器学习 API。如果您能涵盖这一点,那就太好了。
我以为可以直接将稀疏数组提供给 Keras。您试过了吗?
我没试过。稀疏数组对我来说是全新的。我试图将 100k 个稀疏数组合并为一个 NumPy 数组,但出现了一个错误。我就是这样找到您的博客的(似乎我经常在这里找到解决方案🙂)。
看起来 NumPy 不支持稀疏数组;所以我不得不重写我的代码来支持稀疏数组,或者展开它们并将它们加载到 NumPy 数组中。
我不清楚稀疏数组是如何处理的。API 是先展开它们再处理,还是做了其他事情?
展开它们是个坏主意(还不如一开始就用稠密矩阵)。
我预计给定 API 会显式处理稀疏结构(通过 if 语句处理稀疏/稠密)。
谢谢您的精彩文章!我一直在想,既然稀疏性有缺点,那么 TF-IDF 模型该如何处理?
我在这里举了一个 TF-IDF 的例子
https://machinelearning.org.cn/prepare-text-data-machine-learning-scikit-learn/
先生,当我尝试将 CSR 稀疏矩阵转换为 numpy 数组时,出现“内存错误”。您能否告诉我如何解决将稀疏矩阵转换为 numpy 时的“内存错误”?
scores = cross_val_score(knn, x_train.toarray(), polarity_train, cv=10, scoring=’accuracy’)
我的 x_train 是 CSR 稀疏矩阵,形状为:(700, 5904)
“x_train.toarray()”导致我出现内存错误。请告诉我如何实现?
也许您有 bug,请发到 stackoverflow?
也许尝试使用更小的数据集?
也许尝试使用更多 RAM 的机器,例如 EC2 实例?
你好,
假设我们的 DataFrame 中既有数值数据也有文本数据。我们使用 tfIdf vector 将文本数据转换为稀疏矩阵。我能否将此稀疏矩阵添加到我的数值变量中并将其用作特征变量数据?
谢谢。
是的,您可以组合这些向量,或者使用支持两个输入的模型,一个用于文本,一个用于数值数据(例如神经网络)。
你好 Jason,
对稀疏性进行了很好的回顾!
除了能够通过 API 操作,您做得很好,也通过特定的代码示例来操作,我认为我们很乐意时不时地获得一些更直观的(或概念背后的深层思想),以便更容易地开发未来的概念,这是我对您博客的建议,如果您允许的话……
所以,说到稀疏性,一个“直观的”(也是强大的)想法可以用“向量空间”(即使是无穷维空间)来表达。向量可以表示为空间向量基的组合。例如,在 3D 欧几里得空间中,一个向量有 3 个坐标(每个 3D 空间维度一个)。
因此,稀疏性,在一个无穷空间中,我们(理想情况下)需要很多坐标(无穷大)来完全定义这个向量与该空间基的关系,它必须只用几个坐标(主要坐标)来概括,因为这个向量不依赖于其余的基(或者几乎不依赖)……
另一种看待它的方式是,稀疏性是向量投影到无穷(或大空间基)后降维的一个特征,因为它只依赖于其中几个,这显然简化了复杂性……
我们甚至可以(通过正则化权重、PCA 分析、寻找显著协方差(或独立变量)等)来强制忽略其他值很小的坐标(简化、减少、平滑……是描述这个问题的直观词语),与其他的(相对)主要坐标相比。仅仅为了保留主要坐标……
这是一个(有助于理解的)图片,用于介绍和解释实践稀疏性工具的原因……我希望它能帮助其他处于相同情况的人……
很棒的建议,谢谢 JG。
假设一个数据集使用一个特定的非零值(例如 100,而这个值在原始数据中没有出现)代替零。有没有办法在使用库之前进行处理,而不是在库调用前将它们替换为 0?
是的,使用 numpy 数组操作将所有值为 100 的项视为 0。
你好 Jason……
能否模拟 TF-IDF 矩阵?
您是什么意思,模拟?
您从数据中计算矩阵。
我想做一个模拟研究,我需要模拟一个 TFIDF 矩阵,使用某种分布作为其组件……例如……狄利克雷分布……
很有趣。我不确定我是否有教程可以帮助您。
你好 Jasno,
是否可以使用大型稀疏矩阵作为神经网络自编码器的输入?
您能举个例子吗?
也许吧。我没有示例,抱歉。
试试看。Keras 可能支持它。
你好,
谢谢分享。但是,我有一个关于您示例中显示的 CSR 格式的问题。
根据 CSR 格式的定义,它使用三个(一维)数组 (A, IA, JA) 以行形式存储一个 m × n 稀疏矩阵 M。但是,用户如何在您的示例中获得 (A, IA, JA)?谢谢。
https://en.wikipedia.org/wiki/Sparse_matrix
您可以在此处了解更多关于该函数的信息
https://docs.scipy.org.cn/doc/scipy/reference/generated/scipy.sparse.csr_matrix.html
Jason,这篇帖子和其他帖子一样很棒。
我正在处理一些奇怪的数据集,这些数据集似乎是由相当稀疏的图像(平均约 90%)组成的。您能否指出一种算法来确定稀疏矩阵中非零元素密度最高的区域?它不必是非零元素的连续子矩阵,我想象的是类似 k-means 但计算量不那么大的东西。谢谢!
有趣。我第一个想到的可能是对密集矩阵进行最大池化或平均池化?
嗨 Jason,感谢您的帖子。
我想知道如何在 Keras 或 TensorFlow 等框架中利用稀疏权重矩阵,我认为我从未见过对 CSR 矩阵的支持。我已经通过将模型的大部分权重设置为零成功地进行了剪枝。但令人困惑的是,我不知道如何从我的架构中删除它们,因为尽管它们是零,但内存占用仍然相同。您能否就此事提供一些见解?谢谢。
嗯,我认为许多框架确实支持稀疏矩阵。
据我记忆,我认为 sklearn 和 keras 都支持它们,也许我记错了?
您好,首先我要感谢您写了这篇清晰而精彩的文章。
请问,当您说
“表示和处理稀疏矩阵的解决方案是使用替代数据结构来表示稀疏数据。
可以忽略零值,只需要存储或处理稀疏矩阵中的数据或非零值。
有多种数据结构可以用来高效地构造稀疏矩阵;”
我想向您确认一下,当您说 CSR(压缩稀疏行)表示不存储任何零值在内存中,这是真的吗?
提前感谢您的回答
每种技术操作方式不同,但是的,这就是总体的想法。
我很难理解/概念化稀疏矩阵的索引指针。
有人能帮我用下面的例子来理解索引指针吗?
indptr = np.array([0, 2, 3, 6])
indices = np.array([0, 2, 2, 0, 1, 2])
data = np.array([1, 2, 3, 4, 5, 6])
csc_matrix((data, indices, indptr), shape=(3, 3)).toarray()
array([[1, 0, 4],
[0, 0, 5],
[2, 3, 6]])
抱歉,我无法深入研究您的示例。也许可以尝试在 stackoverflow 上发布?
非常感谢您的博文,它非常有用,我有一些问题想问
我构建了一个数据集,其中包含从各种源代码文件中提取的文本特征,
问题是我的数据集中的每个记录都有不同的列数,导致我的 Excel 表格中出现多个 NAN 值,所以我想将每一行重塑为特定数量的列,并想知道您对此有何看法?
这 realmente 取决于您数据的具体情况。也许可以尝试几种方法并比较结果?
通过搜索“稀疏矩阵到普通矩阵”,找到了这篇文章,您让矩阵变得易于理解且细节丰富。非常感谢!
我正在使用 R 中的 tm 包来执行 tdm 或 dtm(还不确定如何决定),并且网上很多示例都显示了其稀疏性:98%,然后继续使用 as.matrix()。
但是使用我从新闻网站抓取的数据,我的稀疏性是 62%,这让我怀疑下一步的 as.matrix() 是否有必要?
抱歉,我的问题似乎与您的文章讨论内容无关,但我希望我的问题是清晰的,我将非常感谢您的回复
谢谢,很高兴它帮到了你!
抱歉,我对那个包不熟悉。也许可以尝试在 stackoverflow 或 crossvalidated 上发布?
很好的解释。
谢谢!
Jason,您好,感谢您的博文。它们真的很有帮助。我有一个关于稀疏矩阵的问题。
我正在使用 TFIDF vectorizer,其 fit 函数的输出是一个稀疏矩阵。现在,基于 idf 分数,我只想选取前 1000 个特征,为此我必须访问稀疏矩阵的元素。
但是,我找不到一种有效的方法。我不想将整个矩阵转换为密集或 toarray。是否有其他方法可以访问稀疏矩阵的非零元素?
好问题。
我不太确定是否有立竿见影的有效方法。也许有一个 numpy 函数——试试检查 API?
感谢您的精彩文章。
根据您对稀疏矩阵和密集矩阵的定义,稀疏矩阵是包含大部分零值的矩阵,而密集矩阵是包含大部分非零值的矩阵,但在示例代码中,您创建了一个包含许多“零”值的密集矩阵!?(这不就是稀疏矩阵吗?)。您能澄清一下吗?
好问题。
矩阵可以是稀疏的或密集的(很多零,很少的零)。
并且我们可以使用密集表示或稀疏表示来表示矩阵。
对于密集矩阵使用密集表示,对于稀疏矩阵使用稀疏表示更有效,但这并非强制要求。
嗨,Jason,
首先,我想祝贺您的工作。您的研究和举措非常值得称赞。我读过您的一些文章,它们非常实用且信息丰富。现在,我想听听您的看法——您认为使用稀疏数据(特征向量有很多零)训练神经网络(MLP)是否困难?根据您的经验,您是否遇到过稀疏数据让网络难以学习的情况?
谢谢,
Euler
谢谢。
不怎么难。正则化会有帮助。
谢谢。我正在使用 L2 正则化,但这无助于解决稀疏数据的问题。将分布上限设为例如第 95 百分位数确实有帮助,因为它通过删除异常值减少了近似零值或非常小的值的数量。如果我不这样做,SGD 就无法工作/需要很长时间来训练并且表现不佳。我有一个想法,即采样 mini-batches,使其倾向于采样非零的观测值。这个链接——https://www.quora.com/Why-are-deep-neural-networks-so-bad-with-sparse-data 讨论了这个话题。
许多现代创新都旨在使模型在内部稀疏化,例如 relu、权重正则化、激活正则化等。
嗨,Jason,
感谢这篇帖子。
我在尝试使用 One Hot Encoding 为 2 个文本列字段训练模型时遇到了以下错误。希望您能给我一些建议来克服这个错误。提前感谢。
<8573×24 稀疏矩阵,类型为‘
具有 34134 个存储的元素,采用压缩稀疏行格式>
也许尝试将您的代码和错误发布到stackoverflow?
谢谢!!
不客气。
非常感谢您……
不客气。
https://magoosh.com/data-science/non-sparse-matrix-to-sparse-matrix/
似乎有人抄袭了您的博客 Jason
至少需要引用您的博客
听到这个我很遗憾。有些人真是不要脸。
Jason Brownlee 女士。我有一个问题。两个稀疏矩阵相乘会得到一个稀疏矩阵,对吗?
你好 Huyen… 我不认为这通常是结果。
https://medium.com/@glynnnnnnnnn/sparse-matrix-multiplication-c34ef43c16db
您好,我的数据大部分是零,正如您建议的那样。您认为在使用此类数据预测下一序列数据时,使用时间序列算法会出现问题吗?如果会,该怎么办?
谢谢。
最近我在 Python 和 Julia 中处理稀疏矩阵的经验,我必须说 Julia 是个野兽!简单、优雅且功能强大,至少在我测试过的线性代数方面是如此。
是的,Julia 还年轻但很有前途。也许几年后,我们可以看到它的生态系统更加成熟,拥有更多工具来做一些伟大的事情!