前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >稀疏矩阵的概念介绍

稀疏矩阵的概念介绍

作者头像
数据派THU
发布2022-03-31 13:40:16
1.6K0
发布2022-03-31 13:40:16
举报
文章被收录于专栏:数据派THU
代码语言:javascript
复制
来源:DeepHub IMBA本文约2700字,建议阅读9分钟本文为你介绍一种既能够保存信息,又节省内存的方案:我们称之为“稀疏矩阵”。

在机器学习中,如果我们的样本数量很大,在大多数情况下,首选解决方案是减少样本量、更改算法,或者通过添加更多内存来升级机器。这些方案不仅粗暴,而且可能并不总是可行的。由于大多数机器学习算法都期望数据集(例如常用的 DataFrame)是保存在内存中的对象(因为内存读取要比磁盘读取快不止一个量级),所以升级硬件这种解决方案基本上会被否定。所以科学家们找到的一种既能够保存信息,又节省内存的方案:我们称之为“稀疏矩阵”。

背景

Pandas的DataFrame 已经算作机器学习中处理数据的标配了 ,那么稀疏矩阵的真正需求是什么?答案是空间复杂度和时间复杂度。当涉及数百万行和/或数百列时,pandas DataFrames 变得最糟糕,这是因为 pandas DataFrames 存储数据的方式。例如下面的图,这是 CSV 文件的磁盘和内存大小比较。我们在这里使用的数据集是 Santander Customer Satisfaction 数据集。 途中比较了 CSV 文件在读取为 DataFrame 之前和读取为 DataFrame 之后的磁盘/内存使用情况。

代码语言:javascript
复制
import osimport pandas as pd
#Read csv filedata = pd.read_csv("train.csv")memory_usage = data.to_numpy().nbytes/1e6
#Read the original file size using os moduledisk_usage = os.path.getsize('/content/train.csv')/1e6
#Lets plot resultsplt.figure(figsize=(10,8))plt.bar(x=["CSV","DataFrame"],height=[disk_size,memory_usage])plt.title("Size comparison - CSV vs DataFrame")plt.ylabel("Usage (MB)")plt.show()

可以明显地看到数据大小的差异,可能是因为里面包含了很多0或者空值导致的,本文后面我们会有详细的分析和介绍。

什么是稀疏矩阵?

有两种常见的矩阵类型,密集和稀疏。主要区别在于稀疏指标有很多零值。密集的指标没有。这是一个具有 4 列和 4 行的稀疏矩阵的示例。

在上面的矩阵中,16 个中有 12 个是零。这就引出了一个简单的问题:

我们可以在常规的机器学习任务中只存储非零值来压缩矩阵的大小吗? 简单的答案是:是的,可以!

我们可以轻松地将高维稀疏矩阵转换为压缩稀疏行矩阵(简称 CSR 矩阵)。对于这种压缩我们的要求是压缩后的矩阵可以应用矩阵运算并以有效的方式访问指标,所以CSR并不是唯一方法,还有有更多的选项来存储稀疏矩阵。例如:Dictionary of keys (DOK)、List of Lists (LIL)、Coordinate list (COO)、Compressed row storage (CRS)等。

但是稀疏矩阵的一个主要缺点是访问单个元素变得更加复杂。下面可以为选择不同的方法提供一些参考:

  • 如果关心的是高效修改 - 使用 DOK、LIL 或 COO。这些通常用于构建矩阵;
  • 如果关心的是有效的访问和矩阵操作 - 使用 CSR 或 CSC。

上面说到了很多名词为简单起见我们深入研究一个CSR的示例。考虑下面的矩阵。

将上述矩阵转换为 CSR 矩阵的情况。在这里使用的是 scipy包的sparsemodule。

代码语言:javascript
复制
import numpy as npfrom scipy import sparse#create the metrix with numpym = np.array([[1,0,0,0],            [0,1,2,0],            [0,0,0,0],            [2,1,1,1]])#convert numpy array into scipy csr_matrixcsr_m = sparse.csr_matrix(m)

虽然我们的原始矩阵将数据存储在二维数组中,但转换后的 CSR 矩阵将它们存储在 3 个一维数组中。

值数组 Value array:顾名思义,它将所有非零元素存储在原始矩阵中。数组的长度等于原始矩阵中非零条目的数量。在这个示例中,有 7 个非零元素。因此值数组的长度为 7。

列索引数组 Column index array:此数组存储值数组中元素的列索引。(这里使用从零开始的索引)

行索引数组 Row index array:该数组存储所有当前行和之前行中非零值的累积计数。row_index_array [j] 编码第 j 行上方非零的总数。最后一个元素表示原始数组中非零元素的数量。长度为 m + 1;其中 m 定义为原始矩阵中的行数。

这样上面的矩阵被存储为以下形式:

上面两个数组很好理解,但是第三个行索引数组 Row index array看起来就没有那么直观了:

Row index array的数值个数是#row + 1, 表示该行前面值在values的总数,或者说第一个值在values中的位置。

咱们依次解释下:

  • 第一个值0:前面的values总数是0,也就是values的index起始是0。
  • 第二个值1:表示第3行起始,前一行的只有一个非0值,所以前面的values总数是1,也就是values的index起始是1。
  • 第三个值3:表示第3行起始,前二行的非0值为3(1,1,2),所以前面的values总数是3,也就是values的index起始是3。
  • 第四个值3:表示第4行起始,因为第3行没有非0值,所以非0值的总数还是3。
  • 第五个值4:没有第5行,所以可以认为这个值是整个矩阵中所有非0值的总数。

绘制样本数据

同样我们也可以对稀疏的矩阵进行可视化:

代码语言:javascript
复制
import matplotlib.pyplot as pltplt.style.use('fivethirtyeight')
#read datasetdata = pd.read_csv("train.csv")
#plot samplesplt.figure(figsize=(8,8))plt.spy(data.head(500).T)plt.axis('off')plt.grid(False)plt.show()

这张图他能告诉我们什么?首先,这里是 plt.spy () 函数的介绍:绘制二维数组的稀疏模式。这可视化了数组的非零值。

在上图中,所有黑点代表非零值。所以可以理解为将这些数据转换为稀疏矩阵是值得的,因为能够节省很多的存储。

那么如何判断数据的稀疏程度呢?使用NumPy可以计算稀疏度。

代码语言:javascript
复制
sparsity = 1- np.count_nonzero(data)/ data.sizeprint(sparsity)

在我们使用的数据集运行代码后,会得到 0.906 作为稀疏度。这意味着,超过 90% 的数据点都用零填充。回到最上面的图,这就是上面我们看到为什么pandas占用内存多的原因。

我们为什么要关心稀疏矩阵?

好吧,使用稀疏矩阵有很多很好的理由。他们主要是:

  • 与基本方法相比,可节省大量内存。
  • 与传统方法相比,它通常会减少模型训练时间。

sklearn API 中的几乎所有算法现在都支持 csr_matrix 作为输入,这是一个非常好的消息。例如下面来自 sklearn.ensemble.RandomForestClassifier 的示例:

X {array-like, sparse matrix} 形状 (n_samples, n_features) 训练输入样本。在函数内部它的 dtype 将被转换为 dtype = np.float32。如果提供了稀疏矩阵,则将其转换为稀疏的 csc_matrix。

让我们继续使用数据集进行实验。

内存压缩比较

代码语言:javascript
复制
def get_mem_usage(train,test,labels=['Train','Test'],plot=True):
  """Helper function for plotting in-disk memory usage for pandas df"""
  #get the original memory usage  train_original_size = train.to_numpy().nbytes/1e6  test_original_size = test.to_numpy().nbytes/1e6
  #convert into csr_metrix  train_csr = sparse.csr_matrix(train)  test_csr = sparse.csr_matrix(test)
  #get memory usage  train_csr_size = (train_csr.data.nbytes+train_csr.indptr.nbytes+train_csr.indices.nbytes)/1e6  test_csr_size = (test_csr.data.nbytes+test_csr.indptr.nbytes+test_csr.indices.nbytes)/1e6
  original_sizes = [train_original_size, test_original_size]  sparse_sizes = [train_csr_size, test_csr_size]
  if plot:      width = 0.35      x = np.arange(len(labels))
      fig, ax = plt.subplots(figsize=(10,8))
      rects1 = ax.bar(x - width/2, original_sizes, width, label='Original')      rects2 = ax.bar(x + width/2, sparse_sizes, width, label='Sparse')
      ax.set_ylabel('Memory Usage(MB)')      ax.set_title('Memory Usage Comparison'.title())      ax.set_xticks(x)      ax.set_xticklabels(labels)      ax.legend()
      plt.grid(False)      plt.show()
  else:      return sparse_sizes+original_sizes
from sklearn.model_selection import train_test_split
#train test splitxtrain,xtest,ytrain,ytest = ( train_test_split(X,y,test_size=0.3,random_state=1997))
#plot compressed memory vs original memoryget_mem_usage(xtrain,xtest)

我们的数据集大致压缩为 0.9 倍,上面计算出的数据集的稀疏度也是 0.96,基本类似。

通过这个简单的技巧,我们减少了数据集的内存使用量。让我们继续进行模型训练时间比较。

模型训练时间对比

在这里将使用 sklearn API 测试流行的机器学习算法。

1. LogisticRegression

2. GradientBoostingClassifier

3. LinearSVC

上图中可以看到,LogisticRegression和GradientBoostingClassifier可以明显地提高效率但是,LinearSVC效率不明显,这可能是因为LinearSVC需要投影到更高的维度有关(这个不确定,但是它的算法和LR和GBC不太一样),但是总之,使用稀疏矩阵不仅可以降低内存占用还可以提高训练的效率。

编辑:黄继彦

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据派THU 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 什么是稀疏矩阵?
  • 绘制样本数据
  • 我们为什么要关心稀疏矩阵?
  • 内存压缩比较
  • 模型训练时间对比
相关产品与服务
数据保险箱
数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档