机器学习稀疏矩阵简介(附Python代码)

HappinessSourceL 2019-06-26

对于一个矩阵而言,若数值为零的元素远远多于非零元素的个数,且非零元素分布没有规律时,这样的矩阵被称作稀疏矩阵;与之相反,若非零元素数目占据绝大多数时,这样的矩阵被称作稠密矩阵。

稀疏矩阵在工程应用中经常被使用,尤其是在通信编码和机器学习中。若编码矩阵或特征表达矩阵是稀疏矩阵时,其计算速度会大大提升。对于机器学习而言,稀疏矩阵应用非常广,比如在数据特征表示、自然语言处理等领域。

用稀疏表示和工作在计算上代价很高,需要专门处理稀疏矩阵的表示和操作等,但是这些操作可以大幅提升性能。

在本教程中,读者可以学习稀疏矩阵的基本概念、存在的问题以及如何在Python中使用它。

机器学习稀疏矩阵简介(附Python代码)

稀疏矩阵

稀疏矩阵是由大部分为零的矩阵组成的矩阵,这是和稠密矩阵有所区别的主要特点。

如果它的许多元素为零,则矩阵是稀疏的。对稀疏性感兴趣的原因是利用好这一特性能够大幅降低计算量,并且在实践中发现很多大型矩阵问题也是稀疏的。

矩阵的稀疏性可以用一个分数来量化,即矩阵中零元素的个数除以矩阵中元素的总数。

sparsity = count zero elements / total elements

下面是一个小的3x6的稀疏矩阵例子

1, 0, 0, 1, 0, 0
A = (0, 0, 2, 0, 0, 1)
     0, 0, 0, 2, 0, 0

上面这个矩阵中总共有18个元素,其中有13个元素为0,则该矩阵的稀疏分数为0.722或72%左右。

稀疏存在的问题

稀疏矩阵会导致空间和时间复杂度方面的问题。

空间复杂度

大型矩阵需要大量的内存来存储,我们希望使用的一些大型矩阵是稀疏的。

实际上,大多数大型矩阵都是稀疏的,几乎所有的条目都是零

一个例子是大型矩阵太大以至于不能存储在内存中,这个矩阵就是链接矩阵,它表示的从一个网站到另一个网站的链接。一个较小的稀疏矩阵例子可能是一本书中针对所有已知单词或术语出现矩阵。这两种情况所包含的矩阵都是稀疏的,其零值比非零数据值多,将这些矩阵表示为稠密矩阵的问题是需要内存,并且在矩阵中必须分配32位或64位零值。这显然是对内存资源的一种浪费,因为这些零值不包含任何信息。

时间复杂度

假设一个非常大型的稀疏矩阵可以存储在内存中,之后将在这个矩阵上执行一些操作。简单来说,若矩阵主要包含的是零值,即没有多少数据,那么对这个矩阵执行操作可能需要花费很长的时间,其中执行的大部分计算将涉及零值相加或相乘。

在这样的问题上使用线性代数的方法是浪费的,因为大多数O(N^3)的算术运算致力于求解方程组或矩阵求逆涉及的零操作数。

矩阵运算的时间复杂度随着矩阵大小增加而增加。对于机器学习而言,即使是最简单的方法也可能需要对每一行、每一列甚至整个矩阵进行许多操作运算,这会导致执行时间会变得很长,上述问题会变得更加复杂。

机器学习中的稀疏矩阵

稀疏矩阵在机器学习应用中经常出现。本节将讨论一些常见的示例,以便读者对其有个直观的了解,并深入的理解稀疏性问题。

数据

稀疏矩阵一般出现在一些特定类型的数据中,比如常见的记录活动发生的次数等。

这里有三个例子:

  • 用户是否在电影目录中观看过电影;
  • 用户是否购买产品目录中的产品;
  • 歌曲目录中收听歌曲的次数;

数据准备

稀疏矩阵出现在用于编写数据的编码方案中。三个常见的例子如下:

  1. 独热编码,用于将分类数据表示为稀疏二元向量;
  2. 计数编码,用于表示文档词汇表中单词的频率;
  3. TF-IDF编码,用于表示词汇表中词频逆文档频数;

研究领域

机器学习中一些研究领域必须开发专门的方法来直接解决稀疏性问题,这是因为输入数据几乎总是稀疏的。以下是三个例子:

1.处理文本文档的自然语言处理;

2.用于处理目录中的产品使用的推荐系统;

3.处理包含大量黑色像素图像时的计算机视觉问题;

若语言模型中有100000个单词,那么特征向量的长度为100000,但对于简短的电子邮件消息而言,几乎所有的特征计数为零。

使用稀疏矩阵

表示和使用稀疏矩阵的解决方案是使用替代的数据结构来表示稀疏矩阵。零元素值可以被忽略,只有稀疏矩阵中的非零元素值需要被存储或使用。有多种数据结构能有效地构造稀疏矩阵,下面列出三个常见示例:

1.字典:一个字典使用行和列索引映射出一个值;

2.列表的列表:矩阵的每一行都以列表形式存储,每个子列表包含列的索引和其值;

3.坐标列表:元组列表存储在包含行索引、列索引和其值的每个元组中;

还有一些更适合执行有效操作的数据结构,比如以下两个常见示例:

  1. CSR(Compressed Sparse Row):稀疏矩阵用非零值的三个一维数组、行的范围和列索引表示;
  2. CSC(Compressed Sparse Column):与CSR方法相同,只是列索引在行索引之前被压缩并首先被读取;

Python中的稀疏矩阵

SciPy使用多个数据结构为创建稀疏矩阵提供了工具,以及将稠密矩阵转化为稀疏矩阵的工具。许多在Numpy数组上运行的线性代数Numpy和SciPy函数可以在SciPy稀疏数组上操作。此外,使用Numpy数据结构的机器学习库也可以在Scipy稀疏数组上操作,例如,用于机器学习的scikit-learning和用于深度学习的Keras。

通过调用scr_matrix()函数,可以使用CSR表示将存储在Numpy数组中的稠密矩阵转换为稀疏矩阵。在下面的例子中,定义一个3x6稀疏矩阵作为一个密集数组,并将其转换为CSR稀疏表示,然后通过调用todense()函数将其转换回密集数组。

# dense to sparse
from numpy import array
from scipy.sparse import csr_matrix
# create dense matrix
A = array([[1, 0, 0, 1, 0, 0], [0, 0, 2, 0, 0, 1], [0, 0, 0, 2, 0, 0]])
print(A)
# convert to sparse matrix (CSR method)
S = csr_matrix(A)
print(S)
# reconstruct dense matrix
B = S.todense()
print(B)

运行该示例后,首先打印出定义的密集数组,然后打印出CSR表示,最后打印出重建的密集矩阵。

[[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不提供函数来计算矩阵的稀疏性。不过,可以通过首先找到矩阵的密度并从中减去相关值来轻松地计算出来。Numpy数组中的非零元素的数量可以由count_nonzero()函数给出,数组中的元素总个数可以由数组的size属性给出。因此,可以将数组稀疏度计算为:

sparsity = 1.0 - count_nonzero(A) / A.size

下面的示例演示如何计算数组的稀疏度:

# calculate sparsity
from numpy import array
from numpy import count_nonzero
# create dense matrix
A = array([[1, 0, 0, 1, 0, 0], [0, 0, 2, 0, 0, 1], [0, 0, 0, 2, 0, 0]])
print(A)
# calculate sparsity
sparsity = 1.0 - count_nonzero(A) / A.size
print(sparsity)

运行示例后,首先打印定义的稀疏矩阵,然后是矩阵的稀疏度。

[[1 0 0 1 0 0]
 [0 0 2 0 0 1]
 [0 0 0 2 0 0]]

0.7222222222222222

文章标题《A Gentle Introduction to Sparse Matrices for Machine Learning》,作者:Jason Brownlee,译者:海棠。

文章详细的内容请查看原文

相关推荐