如果对数组进行向量化运算,例如全体四则运算、矩阵乘法、求和、按指标求和等,一定要利用numpy的矩阵乘法dot和einsum。

dot 二维矩阵乘法

numpy的矩阵运算的王牌,做矩阵乘法的首选,优化到了极致。

einsum 一般矩阵乘法

仅次于dot,比numpy的sum、inner、outer、kron都要快一个或者几个数量级。

夸张的是,einsum求和比向量四则运算都要快:1

2

3

4

5a = np.random.random(5000000)

b = np.random.random(5000000)

print(np.isclose(np.einsum('i->',a-b), np.einsum('i->',a) - np.einsum('i->',b)))

%timeit np.einsum('i->',a-b)

%timeit np.einsum('i->',a) - np.einsum('i->',b)

输出:1

2

3True

17.8 ms ± 284 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

5.34 ms ± 72.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

当数据量大时,两次einsum一次标量减法甚至快于一次einsum一次向量减法。

技巧二:避免大量重复调用numpy函数解决小规模问题

涉及到向量运算的计算过程,若能够使用numpy矩阵运算函数一次计算,则千万不要再使用for循环。但是有的情况不可避免使用for循环且循环次数巨大,这种情况应当尽量避免在for中调用numpy函数进行小规模运算,例如每一次循环中应该避免:

使用all、any或者isclose比较两个二、三元向量的分量

直接各自比较即可。

使用random下的函数产生单个随机数

最好在循环外产生一个随机数narray。

每次建立一样的数组(如用来掩模的数组)

循环外建立,循环内调用即可。

小规模数组合并:array((x,y))

尽量省去,除非有运算上的必要。

改变narray数据个数

改变narray长度需要重新申请内存并复制,非常慢,哪怕只加入一个数据。作为替代,频繁添加数据可以用Python的list.append,或者提前申请好narray内存每次直接赋值即可。由于list是动态链表,改变表长是很容易的(至于用栈,就算是深度优化了,在Python中深度优化意义不大)。

技巧三:优先对较低指标求和1

2

3

4

5

6a = np.random.rand(500, 500, 500)

%timeit a[0,:,:].sum()

%timeit a[:,0,:].sum()

%timeit a[:,:,0].sum()

输出:1

2

359.5 µs ± 478 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

119 µs ± 2.32 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

4.88 ms ± 230 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

耗时相差几个数量级。这是由于narray数据结构导致的。所以对于需要大量求和运算的指标尽量放在第一位。

技巧四:使用ravel对矩阵一维化

ravel将多维数组一维化时避免拷贝,所以速度较快。相比之下,flatten一维化返回拷贝所以慢几个数量级,reshape(-1)一维化也可以避免拷贝,但是相比ravel要慢一些。

技巧五:使用矩阵自运算替代运算后赋值

自加、自减等运算要比运算后赋值要快,即a*=2要快于a=a*2。因为不需要重新申请内存。

技巧六:一维数组合并

numpy没有append,因为array的数据结构是块状,改变元素个数需要重新申请内存并复制,所以应当尽量减小数组合并的次数。对于必要的合并,concatenate是比较快速的函数,其次可以用r_[],大约慢一个数量级。转化成list再合并是非常慢的。

技巧七:避免花式索引

花式索引(fancy indexing)即用narray作为index取出一个小数组。因为会效率低而且占内存,不适合大量调用。如果需要可以用布尔掩模或者使用take、compress替代。

技巧八:避免narray.T转置数组

转置涉及到拷贝,非常耗时。一个例子是:在向量或者矩阵乘法中需要用到转置,这种场合可以事先将矩阵定义为良好的形状,或者使用einsum替代dot,付出较小的代价。

容易犯错之处:你的原始narray改变了吗?

将一个narray变量a(或者其切片)赋给另一个变量b之后,对b进行了操作,那么a改变了吗?有的时候我们希望b只是a的一个别名,通过修改b来修改a;但有的时候我们希望原始的a不受干扰。问题是:什么时候原始a会变?什么时候不变?

一、修改元素还是修改整体引用

假如我们希望将一个narray变量a的一部分a[0]赋给另一个变量b,并希望通过修改b来改变a。

原始数组什么时候变:

当我们修改b的元素时,会改变a。1

2

3

4a = np.zeros((2,2))

b = a[0]

b[0] = 1

print(a)

输出:1

2[[ 1. 0.]

[ 0. 0.]]

注意:当b数组的元素不是“数值”而是一个narray的时候,修改b元素即改变b中该narray的指向,同样会改变a。1

2

3

4a = np.zeros((2,2))

b = a

b[0] = np.ones(2)

print(a)

输出:1

2[[ 1. 1.]

[ 0. 0.]]

原始数组什么时候不变:

假如我们希望将b的所有元素都改成1,那么直接将另一个narray赋给b将不能改变a。1

2

3

4a = np.zeros((2,2))

b = a[0]

b = np.ones(10)

print(a)

输出:1

2[[ 0. 0.]

[ 0. 0.]]

希望达到修改a的目的的话,则可以通过对b全部赋值实现。1

2

3

4a = np.zeros((2,2))

b = a[0]

b[:] = np.ones(2)

print(a)

输出:1

2[[ 1. 1.]

[ 0. 0.]]

二、自运算还是运算后赋值

表面上自运算b+=1和运算后赋值b=b+1对b效果是一样的,但是实际上对原始数组a的影响是不一样的。

原始数组什么时候变:

自运算会改变原始数组,因为是原位运算,修改了元素的值。1

2

3

4a = np.zeros((2,2))

b = a[0]

b += 1

print(a)

输出:1

2[[ 1. 1.]

[ 0. 0.]]

原始数组什么时候不变:

运算后赋值不会改变原始数组,因为是运算后将新数组的引用赋给了b。1

2

3

4a = np.zeros((2,2))

b = a[0]

b = b + 1

print(a)

输出:1

2[[ 0. 0.]

[ 0. 0.]]

当然全部赋值是允许的,可以强行改变a。1

2

3

4a = np.zeros((2,2))

b = a[0]

b[:] = b + 1

print(a)

输出:1

2[[ 1. 1.]

[ 0. 0.]]

三、函数传值:python函数传递array时以指针传递

函数传值对被传入narray(实参)的影响,本质上与上面相同。

原始数组什么时候变:

函数传值,相当于将实参赋给形参b=a。假如在函数内改变形参narray元素的值(不仅包括修改“数值”,也包括修改子narray的指向),则实参的元素的值也被改变。改变数值1

2

3

4

5

6def (a):

a[0] = 1

s = np.zeros(3)

print(s)

func(s)

print(s)

输出:1

2[ 0. 0. 0.]

[ 1. 0. 0.]

改变子narray1

2

3

4

5

6def (a):

a[0]=np.ones(10)

b=np.zeros((10,10))

print(b)

func(b)

print(b)

输出:1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20[[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]

[[ 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]

原始数组什么时候不变

在函数内,假如没有修改形参的内容,而是直接将整个形参变量赋为另一个变量,则实参不改变。直接改变形参1

2

3

4

5

6

7def (a):

a=np.ones(10)

print("in func a=",a)

b=np.zeros(10)

print(b)

func(b)

print(b)

输出:1

2

3[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

in func a= [ 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]

[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]

四、在block内新建的narray在block外同样有效1

2

3

4c = 1

if c>0:

data = np.ones(3)

print(data)

输出1[ 1. 1. 1.]

意味着不需要在if、for、while等block外事先声明或者划分内存。注意假如上面的c=0,则会因data未定义报错。这一点与C++变量的作用域截然不同。

技巧X 使用cProfile测试分析函数运行耗时1python -m cProfile -s cumulative filename.py > log

从生成的log文件就可以分析各个函数的调用次数和运行时间。

优先优化占用时间最长、调用次数最高的函数,若某个函数的累积运行时间没有达到总运行时间的10%,就没有对其优化的必要。

参考资料

python矩阵赋值提高速度_Numpy大规模矩阵运算优化加速技巧相关推荐

  1. 什么是赛顿? Python以C的速度

    Python以其成为最方便,功能最丰富,最实用的编程语言之一而闻名. 执行速度? 没那么多. 输入Cython. Cython语言是Python的超集,可编译为C,根据手头的任务,其性能提升范围可从几 ...

  2. python 矩阵元素如何表示_python 怎么给矩阵里的每一个元素赋值

    用python语言如何给列表动态的赋值? 可以使用for或者while循环结合list的append或者insert方法赋值for i in range(10):append(i). a=1#把1赋值 ...

  3. Python中Youki使用索引快速为数组或矩阵赋值的技巧

    1 前言 Python的索引真的很强大,不愧是科学计算第一语言.但是我还是更喜欢Java呀!哈哈哈~ 希望Java未来也可以支持索引数组~ 2 Youki使用索引快速为数组或矩阵赋值的技巧 2.1 构 ...

  4. python矩阵运算程序_用于矩阵运算的Python程序

    python矩阵运算程序 There are following matrix operations, that we can implement with the numpy matrix. 我们可 ...

  5. python结果不能全部显示_numpy矩阵数值太多不能全部显示的解决

    numpy矩阵数值太多不能全部显示的解决 numpy矩阵数值太多不能全部显示,可以运行以下命令令全部数值展示出来 np.set_printoptions(threshold='nan') 补充知识:p ...

  6. python矩阵变化_python矩阵变换

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 感觉差不多了吧! 2.灰度共生矩阵特征量(字写的不好,请见谅)2.1对比度度量 ...

  7. Python学习教程:Python增强赋值及共享引用注意事项

    Python学习教程:Python增强赋值及共享引用注意事项 概述 Python中的增强赋值是从C语言中借鉴出来的,所以这些格式的用法大多和C一致,本身就是对表达式的简写,即二元表达式和赋值语句的结合 ...

  8. 一行代码让 Python 的运行速度提高100倍

    python一直被病垢运行速度太慢,但是实际上python的执行效率并不慢,慢的是python用的解释器Cpython运行效率太差. "一行代码让python的运行速度提高100倍" ...

  9. python矩阵转置_Python 矩阵转置的几种方法小结

    我就废话不多说了,直接上代码吧! #Python的matrix转置 matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]] def printmatrix(m): fo ...

最新文章

  1. RequiredFieldValidator 根据group组来触发验证
  2. 过游戏保护NP或TP的几种方法和思路
  3. 服务器证书在注册表上位置,服务器ssl证书注册表
  4. 何时使用hadoop fs、hadoop dfs与hdfs dfs命令(转)
  5. 读书笔记——Accelerated C++ Chapter 12 使类对象获得数值功能
  6. 时间加密算法_物联网安全:可计算加密算法
  7. MySqlBackup.NET——用于C#,VB.NET,ASP.NET的MySQL备份解决方案
  8. 关于Select option默认选中及查询后选项值保留的问题
  9. leetcode 5724. 绝对差值和
  10. Creator+微信小游戏:(3)微信openID获取(https、wss问题)
  11. excel 删除大量空白行
  12. 科目一知识点分类记忆
  13. Android源码目录结构详解
  14. ODU帧转OTU帧流程
  15. Salesforce中国区或将解散?国产SaaS如何在竞争中扬长避短
  16. LPC2294对片外EEPROM(24C04)进行读写操作,如何确定24C02EEPROM地址
  17. shell脚本(linux)
  18. linux我的世界乱码,我的世界附魔台文字翻译成普通文字 附魔台文字乱码解决办法...
  19. 子网掩码是什么,IP段的24是什么写法
  20. 前端基础HTML、CSS--6(CSS-3)

热门文章

  1. 【HTML+CSS网页设计与布局 从入门到精通】第7章-class、ID选择器,CSS格式
  2. 计算机表格中如何计算数据透视表,在数据透视表中计算值
  3. 过拟合解决方法python_机器学习之过拟合的风险
  4. Hadoop基础知识
  5. android远程调用github仓库的aar文件
  6. java redis工具类_redis Java工具类详解
  7. MySQL迁移安装_mysql数据库安装路径迁移
  8. 笔记:制作游戏所需的数学
  9. RHEL 8 - 用OpenSCAP工具对容器镜像进行漏洞安全合规扫描,并修复
  10. (三)在Azure上创建您的第一个Kubernetes集群