scipy.sparse稀疏矩阵内积点乘--效率优化!

2023-11-07

在使用scipy和numpy做数据计算时,感觉运行速度较慢,但是程序已经到了使用多数计算使用内积运算地步了,真的不知道该如何优化。如果能够优化下内积运算该有多好啊,奔着这个目标,希望能够写一篇文章盘点各种内积优化方法,也算是贡献自己的微薄之力。

开篇我写两点自己经验,抛砖引玉,希望大家多多提意见。由于自己对对于Scipy和Numpy熟悉度不够,所以有不正确的地方,还请大家多多斧正。

在说我的优化之前,先啰嗦下:scipy.sparse的矩阵包中,牵扯到矩阵运算,矩阵的格式优选csr_matrix和csc_matrix。不然速度肯定慢的你怀疑人生。

特别说明1本文的实验在ipython或者jupyter环境进行,时间消耗测试使用的是“%timeit”命令,Scipy版本为“0.19.1”。

特别说明2在程序中,很多非计算操作,如:list转稀疏矩阵、矩阵转置、矩阵拼接和矩阵更新等,由于它们具有内存操作,所以时间代价相当昂贵,并且可以提前处理,所以在测量时间消耗时,无需将他们的时间消耗也计算在内。在性能优化中,有两条原则相当重要:减少内存操作和减少CPU命令数。更多详情查看《Python高性能编程》第6章。

特别说明3如果你是计算专业的在读生,那么学好《计算机架构导论》、《操作系统》、《数据结构》、《离散数学》。前两本书让你在硬件和操作系统层次明白编程语言的特性,配上一些相关书籍,你会很快明白为什么会快,为什么会慢,为什么有些语言风格会快,有些则慢。后两本则告诉你如何优化你的算法,好比:现在从山北到山南,你可以从北山脚爬到山顶再到南山脚,也可以围着山跑,从北山脚跑到南山脚。当然,这些书的用处,绝不仅于此,它也是科班生与培训班生的区别。计算机编程不是学好几门编程语言和数据结构那么简单。

一、大小矩阵内积运算

当两个规模相当的矩阵做内积时,选择CSC或CSR并没有太大差别,时间效果相当。但是当为一大一小矩阵时,就有一些技巧,可以节约时间。假设B为大矩阵,S为小矩阵。

  • 当CSR格式时,S×B速度较快,与B×S相比节约了一半时间。
  • 当CSC格式时,B×S速度较快,与S×B相比节约一半时间。
    上述两种方法,时间相近,不分伯仲之间。

以下是我的计算例子。

import scipy.sparse as sp

def is_csr_instance(mtx):
    if isinstance(mtx, sp.csr_matrix):
        return True
    else:
        return False
    
def is_csc_instance(mtx):
    if isinstance(mtx, sp.csc_matrix):
        return True
    else:
        return False

a_mtx = sp.csc_matrix([[1., 1., 3.]*120])
mtx = sp.csc_matrix([[1., 0., 0.]*120]*30000)

is_csc_instance(a_mtx), is_csc_instance(mtx)

mtx.shape, a_mtx.shape

mtx_T = mtx.T
mtx_T = mtx_T.tocsc()

print is_csc_instance(mtx_T), is_csr_instance(mtx_T)

print u"\n\ncsc little×big"
print type(a_mtx), type(mtx_T)
print a_mtx.shape, mtx_T.shape
%timeit c = a_mtx.dot(mtx_T)

print u"\n\ncsr little×big"
a_mtx_r = a_mtx.tocsr()
mtx_T_r = mtx_T.tocsr()
print type(a_mtx_r), type(mtx_T_r)
print a_mtx_r.shape, mtx_T_r.shape
%timeit c = a_mtx_r.dot(mtx_T_r)

a_mtx_T = a_mtx.T
a_mtx_T = a_mtx_T.tocsc()
mtx_T.shape, a_mtx_T.shape

print "\n\ncsc big×little"
print type(mtx), type(a_mtx_T)
print mtx.shape, a_mtx_T.shape
%timeit c = mtx.dot(a_mtx_T)

print "\n\ncsr big×little"
mtx = mtx.tocsr()
a_mtx_T = a_mtx_T.tocsr()
print type(mtx), type(a_mtx_T)
print mtx.shape, a_mtx_T.shape
%timeit c = mtx.dot(a_mtx_T)


输出如下:

csc little×big
<class 'scipy.sparse.csc.csc_matrix'> <class 'scipy.sparse.csc.csc_matrix'>
(1, 360) (360, 30000)
100 loops, best of 3: 17.4 ms per loop


csr little×big
<class 'scipy.sparse.csr.csr_matrix'> <class 'scipy.sparse.csr.csr_matrix'>
(1, 360) (360, 30000)
100 loops, best of 3: 8.13 ms per loop


csc big×little
<class 'scipy.sparse.csc.csc_matrix'> <class 'scipy.sparse.csc.csc_matrix'>
(30000, 360) (360, 1)
100 loops, best of 3: 8.31 ms per loop


csr big×little
<class 'scipy.sparse.csr.csr_matrix'> <class 'scipy.sparse.csr.csr_matrix'>
(30000, 360) (360, 1)
100 loops, best of 3: 17.6 ms per loop

二 多矩阵内积优化

不好意思,这条优化有时有效有时无效,所以暂时不要完全相信,欢迎各位对此条多提意见。

当有多个矩阵进行内积计算时,可以通过矩阵拼接将多次内积计算合并为一次节约时间。时间优化效果与矩阵的中需要计算的非零数据次数成反比,需要计算的次数越多,节约的时间越少。假设稀疏矩阵中,非零元素随机出现,那么需要计算的非零数据次数非常少,所以有近似结论:矩阵越稀疏,需要计算的非零数据越少,节约的时间越多。矩阵稠密度是非零元素个数与矩阵总元素数的比值。

本实验有两个组,对照组为一个1×N与一个M×N的矩阵做四次内积,实验组为一个1×4N的矩阵与一个M×4N的矩阵做一次内积。实验分3次:例1,例2和例3:

  • 例1中,两个矩阵稠密度为100%,对照组时间消耗略高。
  • 例2中,两个矩阵稠密度为33.34%,对照组时间较高。
  • 例3中,两个矩阵稠密度分别为16.7%和8.3%,对照组时间消耗明显很高。

实验公共代码

import scipy.sparse as sp

def quadra_dot(a_mtx, b_mtx):
    a = a_mtx * b_mtx
    b = a_mtx * b_mtx
    c = a_mtx * b_mtx
    d = a_mtx * b_mtx
    
def uni_dot(a_mtx, b_mtx):
    a = a_mtx * b_mtx

def density(mtx):
    non_zeros_numbers = len(mtx.data) * 1.0
    m, n = mtx.shape
    print non_zeros_numbers / (m*n)

例1

a_mtx = sp.csr_matrix([[2.23, 1.56, 3.47]*120]*300)
mtx = sp.csr_matrix([[1.07, 2.19, 3.12]*120]*30000)

print(u"对照组:")
b_mtx = mtx.T
b_mtx = b_mtx.tocsr()

print type(a_mtx), type(b_mtx), a_mtx.shape, b_mtx.shape
# 测试时间消耗
%timeit quadra_dot(a_mtx, b_mtx)

print(u"实验组:")
c_mtx = sp.vstack((b_mtx, b_mtx))
c_mtx = sp.vstack((c_mtx, b_mtx))
c_mtx = sp.vstack((c_mtx, b_mtx))

a_mtx = sp.hstack((a_mtx, a_mtx))
a_mtx = sp.hstack((a_mtx, a_mtx))

c_mtx = c_mtx.tocsr()
a_mtx = a_mtx.tocsr()

print type(a_mtx), type(c_mtx), a_mtx.shape, c_mtx.shape
%timeit uni_dot(a_mtx, c_mtx)

例1输出:

对照组:
<class 'scipy.sparse.csr.csr_matrix'> <class 'scipy.sparse.csr.csr_matrix'> (300, 360) (360, 30000)
1 loop, best of 3: 29.8 s per loop

实验组:
<class 'scipy.sparse.csr.csr_matrix'> <class 'scipy.sparse.csr.csr_matrix'> (300, 1440) (1440, 30000)
1 loop, best of 3: 28 s per loop

例2

a_mtx = sp.csr_matrix([[2.23, 1.56, 3.47]*120]*300)
mtx = sp.csr_matrix([[1.07, 2.19, 3.12]*120]*30000)
density(a_mtx)
density(mtx)

# 代码与例1的对应部分相同,不在重复
...

例2输出:

density 0.3333
density 0.3333
对照组:
<class 'scipy.sparse.csr.csr_matrix'> <class 'scipy.sparse.csr.csr_matrix'> (300, 360) (360, 30000)
1 loop, best of 3: 9.06 s per loop

实验组:
<class 'scipy.sparse.csr.csr_matrix'> <class 'scipy.sparse.csr.csr_matrix'> (300, 1440) (1440, 30000)
1 loop, best of 3: 8.85 s per loop

例3

a_mtx = sp.csr_matrix([[0., 0., 0., 0., 13.23, 0., 0., 0., 1.32, 0., 0., 0., 0., 0., 0., 0., 13.23, 0., 0., 0., 1.32, 0., 0., 0.]*5]*300)
mtx = sp.csr_matrix([[1.07, 0., 0., 0., 1.30, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.0, 0., 0., 0.]*5]*30000)
density(a_mtx)
density(mtx)

# 代码与例1的对应部分相同,不在重复
...

例2输出:

density 0.166666
density 0.083333
对照组:
<class 'scipy.sparse.csr.csr_matrix'> <class 'scipy.sparse.csr.csr_matrix'> (300, 120) (120, 30000)
1 loop, best of 3: 559 ms per loop

实验组:
<class 'scipy.sparse.csr.csr_matrix'> <class 'scipy.sparse.csr.csr_matrix'> (300, 480) (480, 30000)
1 loop, best of 3: 374 ms per loop

三 稀疏矩阵归一化和转置,不会影响矩阵计算性能

相同格式的稀疏矩阵做点乘速度很快,不同格式速度仅仅慢一丢丢。比如归一化和转置之后, 不转格式不会影响速度.

某些情况下在点乘计算前,需要进行归一化操作,比如计算cosine相似度,需要对两个稀疏矩阵分别做行归一化和列归一化,或者转置。在进行归一化或者转置后,矩阵的格式可能会发生改变.

这里使用的是sklearn.preprocessing.normalize函数进行归一化的。对于稀疏矩阵,行归一化的返回值是CSR矩阵,列归一化的返回值是CSC矩阵(实验结果见下面代码);之所以这么这么做,是为了提高计算速度,同时也降低计算难度,sklearn的做法是:如果是sparse矩阵,当是行归一化时,就将原始矩阵转为CSR格式,这样就可以对矩阵的data(data是sparse.csr_matrix的一个属性)中的每行的元素,进行快速归一化。当列归一化时,转为CSC矩阵,然后对data中的列元素进行快速归一化。如果你不明白为什么如此操作的好处,请参看稀疏矩阵压缩原理

转置操作输入CSR矩阵返回CSC矩,阵输入CSC矩阵返回CSR矩阵。至于转置为何也会改变矩阵格式,答案也是速度快,编码简单,为什么呢?自己动手计算一下吧。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

scipy.sparse稀疏矩阵内积点乘--效率优化! 的相关文章

随机推荐

  • 创建一个数组, 实现函数init()初始化数组、 实现empty()清空数组、 实现reverse()函数完成数组元素的逆置。 要求:自己设计函数的参数,返回值

    创建一个数组 实现函数init 初始化数组 实现empty 清空数组 实现reverse 函数完成数组元素的逆置 要求 自己设计函数的参数 返回值 include
  • IDEA构建Android项目失败

    一同步项目就报 This version of the Android Support plugin for IntelliJ IDEA or Android Studio cannot open this project please r
  • HTTP 网关

    本文摘自书籍 HTTP 权威指南 此系列文章对应 github地址 网关 HTTP 扩展和接口的发展是由用户需求驱动的 要在 Web 上发布更复杂资源的需求出现时 人们很快就明确了一点 单个应用程序无法处理所有这些能想到的资源 为了解决这个
  • 智能人像处理-ON1 Portrait AI 2021.1 v15.1.0工具

    介绍 ON1 Portrait AI是一款人像AI智能处理软件 可以根据自身喜好对图像进行修复 重点是对人脸的一些修饰项目 虽然没有PS功能齐全 但对于人脸处理方面来说要更加细腻方便 可以一键优化人脸效果 只需使用ON1 Portrait
  • XXX iPhone has denied the launch request

    在Xcode运行 应用的时候 出现 iPhone has denied the launch request 这个问题 目前我遇到的原因是 Signing 需要重新配置一下 重新选一下Automatically manage signing
  • 不会服务治理,还怎么搞微服务?

    目录 单体架构 微服务架构 服务治理之注册与发现和负载均衡 服务治理之限流熔断 服务治理之服务监控 今天给大家分享一个话题 是关于微服务架构的服务治理的 很多小伙伴可能都觉得自己玩儿过微服务架构 然后可能也听说过服务治理 但是服务治理到底是
  • 大数据是什么意思?

    一 大数据的概念 大数据是指无法在一定时间内用常规软件工具对其内容进行抓取 管理和处理的数据集合 大数据技术 是指从各种各样类型的数据中 快速获得有价值信息的能力 适用于大数据的技术 包括大规模并行处理 MPP 数据库 数据挖掘电网 分布式
  • stm32中使用cJSON

    STM32中使用cJSON cJSON 下载地址 https github com DaveGamble cJSON 将其拉取到本地是有很多文件 但只有两个比较重要 cJSON c cJSON h 我们将其添加到自己工程目录下 其中 在进行
  • 【华为面试题】深度优先搜索(一)

    题目 Jungle居住在蓝鲸城 一个拥有规则街道的城市 然而 街道每天的封闭情况都是不同的 为了测试Jungle的导航技巧 我们设置了以下挑战 Jungle必须从他的家 表示为 S 出发 前往公司 表示为 T 街道图由以下元素构成 代表可走
  • 华三交换机端口镜像抓包实战

    目录 1 端口镜像的使用场景 2 华三交换机配置端口镜像 web 命令行 3 wireshark分析配置端口镜像前后抓包的数据区别 1 端口镜像的使用场景 端口镜像 Mirror Port 功能通过在交换机或路由器上 将一个或多个源端口的数
  • Qt学习笔记(QFile)

    文件操作 基础课以文件操作结尾 QFile 无非就是读和写操作 QFile file 路径 file open 打开方式 QIODevice ReadOnly file readAll readLine file atEnd 判断是否到文件
  • C++实现 快速排序

    目录 一 快速排序主函数 代码如下 二 分区函数 1 选取支点 2 定义左右指针 移动指针 3 返回分割点的位置 代码如下 三 swap函数 元素互换 代码如下 四 printArr函数 打印输出 代码如下 完整代码如下 测试方法如下 运行
  • 小米VS华为:水军?黑稿?到底是谁黑了谁?

    那边罗永浩和黄章互相吐槽还没结束 雷军又向华为开炮 雷军发微博称 其被华为水军黑了 事情的起因其实很简单 一位微博名为 IT华少 的网友称 小米手机4的芯片没有进行点胶处理 所以认定其 做工粗糙 不如华为的荣耀6 雷军在看到华强电子产业研究
  • CentOS7 系统简单 Python 环境使用

    文章目录 1 CentOS7 系统简单 Python 环境使用 1 1 查看当前系统 Python 版本 1 2 使用 CentOS7 系统中的 Python3 版本 1 3 CentOS7 系统中 Pycharm 环境使用 1 4 Pyc
  • 数据库ALTER语句使用

    ALTER语句使用 ALTER是数据库DDL语言的一部分 其操作对像主要是可以是表中的字段和索引 一般被用来修改上述对象的部分 1 操作表 1 1 表中增加列 ALTER TABLE tbl name ADD COLUMN column n
  • xgboost 安装问题(xgboost library (xgboost.dll) could not be loaded)win10+ anaconda3.8+pycharm最新社区版

    最近打算使用XGBOOST跑跑数据 奈何换了电脑 安装过程一把泪 1 搜索大部分安装办法 https blog csdn net qazplm155357 article details 107313915 utm medium distr
  • Spring Boot 性能优化几点

    点击下方 IT牧场 选择 设为星标 程序员大目 IT牧场公众号 BAT 技术专家分享开发 架构 运维相关干货 159篇原创内容 公众号 文章来源 http a nxw so 1biCvy 目录 异步执行 增加内嵌 Tomcat 的最大连接数
  • Bert的NSP任务的loss原理

    Bert的NSP任务是预测上句和下句的关系 对一个句子的表征可以用CLS的embedding bert的NSP任务 NSP 是一个预测两段文本是否在原文本中连续出现的二元分类损失 NSP 是一种二进制分类损失 用于预测原始文本中是否有两个片
  • 彻底理解Linux下动态替换.so的方法

    0x00 背景 hdfs增加了一个native方法 打成了libhadoop so这个动态库 需要分发到线上的各个Datanode上以便升级 在灰度分发到datanode时遇到了可复现的问题 即datanode进程肯定会core dump
  • scipy.sparse稀疏矩阵内积点乘--效率优化!

    在使用scipy和numpy做数据计算时 感觉运行速度较慢 但是程序已经到了使用多数计算使用内积运算地步了 真的不知道该如何优化 如果能够优化下内积运算该有多好啊 奔着这个目标 希望能够写一篇文章盘点各种内积优化方法 也算是贡献自己的微薄之