转自:http://blog.csdn.net/pipisorry/article/details/39087583

http://blog.csdn.net/pipisorry/article/details/39087583

Introduction

NumPy提供了一个特殊的数据类型ndarray,其在向量计算上做了优化。这个对象是科学数值计算中大多数算法的核心。

相比于原生的Python,利用NumPy数组可以获得显著的性能加速,尤其是当你的计算遵循单指令多数据流(SIMD)范式时。

然而,利用NumPy也有可能有意无意地写出未优化的代码。下面这些技巧可以帮助你编写高效的NumPy代码。

避免不必要的数据拷贝

查看数组的内存地址

1. 查看静默数组拷贝的第一步是在内存中找到数组的地址。下边的函数就是做这个的:

 
def id(x):
    # This function returns the memory block address of an array.
    returnx.__array_interface__['data'][0]

2. 有时你可能需要复制一个数组,例如你需要在操作一个数组时,内存中仍然保留其原始副本。

 
=np.zeros(10); aid=id(a); aid
71211328
=a.copy();id(b)==aid
False

Note:python列表的浅拷贝和深拷贝-列表拷贝一节

具有相同数据地址(比如id函数的返回值)的两个数组,共享底层数据缓冲区。然而,共享底层数据缓冲区的数组,只有当它们具有相同的偏移量(意味着它们的第一个元素相同)时,才具有相同的数据地址。共享数据缓冲区,但偏移量不同的两个数组,在内存地址上有细微的差别:

 
id(a),id(a[1:])
(71211328,71211336)

在这篇文章中,我们将确保函数用到的数组具有相同的偏移量。

下边是一个判断两个数组是否共享相同数据的更可靠的方案

 
def get_data_base(arr):
    """For a given Numpy array, finds the base array that "owns" the actual data."""
    base = arr
    whileis instance(base.base, np.ndarray):
        base = base.base
    return base
def arrays_share_data(x, y):
    return get_data_base(x)is get_data_base(y)
print(arrays_share_data(a,a.copy()), arrays_share_data(a,a[1:]))
False True

感谢Michael Droettboom指出这种更精确的方法,提出这个替代方案。

Note:a和a[1:]id虽然不同,但是他们是共享内存的。

就地操作和隐式拷贝操作

3. 数组计算包括就地操作(下面第一个例子:数组修改)或隐式拷贝操作(第二个例子:创建一个新的数组)。

1
2
3
4
5
*=2;id(a)==aid
True
=a*2;id(c)==aid
False

一定要选择真正需要的操作类型。隐式拷贝操作很明显很慢,如下所示:

1
2
3
4
5
6
7
%%timeit a=np.zeros(10000000)
*=2
10 loops, best of 3:19.2ms per loop
%%timeit a=np.zeros(10000000)
=a*2
10 loops, best of 3:42.6ms per loop

4. 重塑数组可能涉及到拷贝操作,也可能涉及不到。

例如,重塑一个二维矩阵不涉及拷贝操作,除非它被转置(或更一般的非连续操作)

1
2
=np.zeros((10,10)); aid = id(a); aid
53423728

重塑一个数组,同时保留其顺序,并不触发拷贝操作。

1
2
=a.reshape((1,-1));id(b)==aid
True

转置一个数组会改变其顺序,所以这种重塑会触发拷贝操作。

1
2
=a.T.reshape((1,-1));id(c)==aid
False

因此,后边的指令比前边的指令明显要慢。

5. 数组的flatten和revel方法将数组变为一个一维向量(铺平数组)。flatten方法总是返回一个拷贝后的副本,而revel方法只有当有必要时才返回一个拷贝后的副本(所以该方法要快得多,尤其是在大数组上进行操作时)。

1
2
3
4
5
6
7
8
9
10
11
=a.flatten();id(d)==aid
False
=a.ravel();id(e)==aid
True
%timeit a.flatten()
1000000 loops, best of 3:881ns per loop
%timeit a.ravel()
1000000loops, best of3:294ns per loop

广播规则

广播规则允许你在形状不同但却兼容的数组上进行计算。换句话说,你并不总是需要重塑或铺平数组,使它们的形状匹配。

广播规则描述了具有不同维度和/或形状的数组仍可以用于计算。一般的规则是:当两个维度相等,或其中一个为1时,它们是兼容的。NumPy使用这个规则,从后边的维数开始,向前推导,来比较两个元素级数组的形状。最小的维度在内部被自动延伸,从而匹配其他维度,但此操作并不涉及任何内存复制

下面的例子说明了两个向量之间进行矢量积的两个方法:第一个方法涉及到数组的变形操作,第二个方法涉及到广播规则。显然第二个方法是要快得多。

[python] view plaincopy print?
  1. n =1000
  2. a =np.arange(n)
  3. ac =a[:, np.newaxis]
  4. ar =a[np.newaxis, :]
  5. %timeit np.tile(ac, (1, n))* np.tile(ar, (n,1))
  6. 100 loops, best of 3:10 ms per loop
  7. %timeit ar* ac
  8. 100 loops, best of 3:2.36 ms per loop

[numpy教程- 通用函数ufunc- 广播规则]

NumPy数组进行高效的选择

NumPy提供了多种数组分片的方式。

数组视图涉及到一个数组的原始数据缓冲区,但具有不同的偏移量,形状和步长。NumPy只允许等步长选择(即线性分隔索引)。

NumPy还提供沿一个轴进行任意选择的特定功能。

最后,花式索引(fancy indexing)是最一般的选择方法,但正如我们将要在文章中看到的那样,它同时也是最慢的。

1. 创建一个具有很多行的数组。我们将沿第一维选择该数组的分片。

 
n, d =100000,100
=np.random.random_sample((n, d)); aid=id(a)

数组视图和花式索引

2. 每10行选择一行,这里用到了两个不同的方法(数组视图和花式索引)。

 
b1 =a[::10]
b2 =a[np.arange(0, n,10)]
np.array_equal(b1, b2)
True

3. 数组视图指向原始数据缓冲区,而花式索引产生一个拷贝副本。

 
id(b1)==aid,id(b2)==aid
(True,False)

两个方法的执行效率,花式索引慢好几个数量级,因为它要复制一个大数组。

替代花式索引:索引列表

当需要沿一个维度进行非等步长选择时,数组视图就无能为力了。

然而,替代花式索引的方法在这种情况下依然存在。给定一个索引列表,NumPy的函数可以沿一个轴执行选择操作。

 
=np.arange(0, n,10)
b1 =a[i]
b2 =np.take(a, i, axis=0)
np.array_equal(b1, b2)
True

第二个方法更快一点:

1
2
3
4
5
%timeit a[i]
100 loops, best of 3:13ms per loop
%timeit np.take(a, i, axis=0)
100 loops, best of 3:4.87ms per loop

替代花式索引:布尔掩码

当沿一个轴进行选择的索引是通过一个布尔掩码向量指定时,compress函数可以作为花式索引的替代方案。

1
=np.random.random_sample(n) < .5

可以使用花式索引或者np.compress函数进行选择。

 
b1 =a[i]
b2 =np.compress(i, a, axis=0)
np.array_equal(b1, b2)
True
%timeit a[i]
10 loops, best of 3:59.8ms per loop
%timeit np.compress(i, a, axis=0)
10 loops, best of 3:24.1ms per loop

第二个方法同样比花式索引快得多。

花式索引是进行数组任意选择的最一般方法。然而,往往会存在更有效、更快的方法,应尽可能首选那些方法。

当进行等步长选择时应该使用数组视图,但需要注意这样一个事实:视图涉及到原始数据缓冲区。

为什么NumPy数组如此高效?

一个NumPy数组基本上是由元数据(维数、形状、数据类型等)和实际数据构成。数据存储在一个均匀连续的内存块中,该内存在系统内存(随机存取存储器,或RAM)的一个特定地址处,被称为数据缓冲区。这是和list等纯Python结构的主要区别,list的元素在系统内存中是分散存储的。这是使NumPy数组如此高效的决定性因素。

为什么这会如此重要?主要原因是:

1. 低级语言比如C,可以很高效的实现数组计算(NumPy的很大一部分实际上是用C编写)。例如,知道了内存块地址和数据类型,数组计算只是简单遍历其中所有的元素。但在Python中使用list实现,会有很大的开销。

2. 内存访问模式中的空间位置访问会产生显著地性能提高,尤其要感谢CPU缓存。事实上,缓存将字节块从RAM加载到CPU寄存器。然后相邻元素就能高效地被加载了(顺序位置,或引用位置)。

3. 数据元素连续地存储在内存中,所以NumPy可以利用现代CPU的矢量化指令,像英特尔的SSE和AVX,AMD的XOP等。例如,为了作为CPU指令实现的矢量化算术计算,可以加载在128,256或512位寄存器中的多个连续的浮点数。

4. NumPy可以通过Intel Math Kernel Library (MKL)与高度优化的线性代数库相连,比如BLAS和LAPACK。NumPy中一些特定的矩阵计算也可能是多线程,充分利用了现代多核处理器的优势。

总之,将数据存储在一个连续的内存块中,根据内存访问模式,CPU缓存和矢量化指令,可以确保以最佳方式使用现代CPU的体系结构。

就地操作和隐式拷贝操作之间的区别

让我们解释一下技巧3。类似于a *= 2这样的表达式对应一个就地操作,即数组的所有元素值被乘以2。相比之下,a = a*2意味着创建了一个包含a*2结果值的新数组,变量a此时指向这个新数组。旧数组变为了无引用的,将被垃圾回收器删除。第一种情况中没有发生内存分配,相反,第二种情况中发生了内存分配。

更一般的情况,类似于a[i:j]这样的表达式是数组某些部分的视图:它们指向包含数据的内存缓冲区。利用就地操作改变它们,会改变原始数据。因此,a[:] = a * 2的结果是一个就地操作,和a = a * 2不一样。

知道NumPy的这种细节可以帮助你解决一些错误(例如数组因为在一个视图上的一个操作,被无意中修改),并能通过减少不必要的副本数量,优化代码的速度和内存消耗。

为什么有些数组不进行拷贝操作,就不能被重塑?

一个转置的二维矩阵不依靠拷贝就无法进行铺平。一个二维矩阵包含的元素通过两个数字(行和列)进行索引,但它在内部是作为一个一维连续内存块存储的,可使用一个数字访问。

有多个在一维内存块中存储矩阵元素的方法:我们可以先放第一行的元素,然后第二行,以此类推,或者先放第一列的元素,然后第二列,以此类推。第一种方法叫做行优先排序,而后一种方法称为列优先排序。这两种方法之间的选择只是一个内部约定问题:NumPy使用行优先排序,类似于C,而不同于FORTRAN。

更一般的情况,NumPy使用步长的概念进行多维索引和元素的底层序列(一维)内存位置之间的转换。array[i1, i2]和内部数据的相关字节地址之间的具体映射关系为:

1
offset = array.strides[0] * i1 + array.strides[1] * i2

重塑一个数组时,NumPy会尽可能通过修改步长属性来避免拷贝。例如,当转置一个矩阵时,步长的顺序被翻转,但底层数据仍然是相同的。然而,仅简单地依靠修改步长无法完成铺平一个转置数组的操作(尝试下!),所以需要一个副本。

Recipe 4.6(NumPy中使用步长技巧)包含步长方面更广泛的讨论。同时,Recipe4.7(使用步长技巧实现一个高效的移动平均算法)展示了如何使用步伐加快特定数组计算。

from:http://blog.csdn.net/pipisorry/article/details/39087583

ref:Getting the Best Performance out of NumPy

IPython Cookbook

如何写出比 MATLAB 更快的矩阵运算程序?

Numpy使用MKL库提升计算性能

转载于:https://www.cnblogs.com/McKean/p/6221053.html

[转]numpy性能优化相关推荐

  1. Pandas、Numpy 性能优化秘籍(全)

    pandas.numpy是Python数据科学中非常常用的库,numpy是Python的数值计算扩展,专门用来处理矩阵,它的运算效率比列表更高效.pandas是基于numpy的数据处理工具,能更方便的 ...

  2. 【Python】Pandas、Numpy性能优化秘籍(全)

    pandas.numpy是Python数据科学中非常常用的库,numpy是Python的数值计算扩展,专门用来处理矩阵,它的运算效率比列表更高效.pandas是基于numpy的数据处理工具,能更方便的 ...

  3. Python性能优化

    http://blog.csdn.net/pipisorry/article/details/39998317 python 作为脚本的一个不足之处,那就是执行效率和性能不够理想,特别是在 perfo ...

  4. Python 和 C/C++ 拓展程序的性能优化

    背景 在 MegEngine imperative runtime 的早期开发中,我们面临着一些的性能优化问题.除了一些已知需要重构的地方(早期设计时为了开发效率而牺牲性能的妥协),还存在一些未知的性 ...

  5. pythonic风格_【Python】性能优化之Pythonic风格化代码

    所谓Pythonic 指的是非常具有python风格往往也是比较高效.前人性能优化过的书写方式.除了积累和多读源码以外并没有很好的方式锻炼这部分的水平. 常见Tips 以下涉及到一些最常用的代码片段P ...

  6. Keras深度学习实战(3)——神经网络性能优化技术详解

    Keras深度学习实战(3)--神经网络性能优化技术详解 0. 前言 1. 缩放输入数据集 1.1 数据集缩放的合理性解释 1.2 使用缩放后的数据集训练模型 2. 输入值分布对模型性能的影响 3. ...

  7. python代码风格程序越复杂越高级_Python中代码风格的改变和相应的性能优化

    使用现代风格改善你的代码 一旦你开始使用 Python 3,你就有机会接触新的特性来改善你的代码.这篇文章中提到的很多东西实际上在 Python 3 之前就已经被支持了.但我还是要提一下它们,因为知道 ...

  8. python 性能优化

    文章目录 性能测试 运行速度 内存消耗 并行加速 分布式 并行 多线程 多进程 框架 即时编译 njit case1 计算熵 case2 找到最大概率类别 case3 计算两两准确率 GPU 使用工具 ...

  9. 1.python性能优化

    Python性能优化--1 1.去掉不必要的显式for,改为向量化计算(numpy) 2.使用numba加速 3.使用多进程(开核) 4.使用sklearn.extenals.joblib扩展库 5. ...

最新文章

  1. 谷歌为语言AI模型推TensorFlow.Text
  2. 从源码分析DEARGUI之add_plot和add_line_series
  3. .net版 类似火车头的网页采集
  4. C/C++难题的高赞回答「中文版」,帮你整理好了
  5. Factors 分解质因数
  6. 51nod 1414 冰雕 思路:暴力模拟题
  7. Struts2学习笔记(一)--入门常见配置
  8. 大数的四则运算(加法、减法、乘法、除法)
  9. 很气很气——在python中切换selenium中的窗口
  10. .htaccess 重定向_如何使用.htaccess将HTTP重定向到HTTPS
  11. matlab用解析法求二自由度阻尼系统的自由振动(先求系统状态方程
  12. Lucene学习笔记
  13. VC++ SetLayeredWindowAttributes 部分窗口透明鼠标穿透
  14. 【常用软件下载地址】java开发官方资源下载网址大全(持续更新)
  15. 优秀的音频EQ均衡器音效源码
  16. Mugeda(木疙瘩)H5案例课—重力感应类H5-岑远科-专题视频课程
  17. COM 组件创建实例失败,原因是出现以下错误: 8001010a解决办法
  18. 与师生谈人工智能3:精确定义之病
  19. 微信公众号——网页授权
  20. 【Python】如何用pyth做游戏脚本(太简单了吧)

热门文章

  1. 如何取消JS事件的派发——stopPropagation()
  2. html页面加文字横向滚动,js实现文字横向滚动
  3. java 接口 返回值_java api返回值的标准化详解
  4. textarea实现datalist效果_手把手撸代码实现Virtual Dom amp;amp; Diff
  5. java配置文件中的plugin,启用ContextReplacementPlugin以忽略webpack中的配置和测试设置文件...
  6. express与mysql数据库_express无法连接到mysql数据库
  7. 工业机器人调运角度_站在全球角度,看待中国工业机器人
  8. java解码base64的png图片_使用PHP对图片进行base64编码和解码(png、jpg,声音、视频)...
  9. 光复用技术中三种重要技术_传感器在机器人技术研究发展历程中扮演着重要角色...
  10. cameraraw预设_PS Camera Raw 导入预设以及分组