• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • vbs
  • DOS/BAT
  • hta/htc
  • python
  • perl
  • VBA
  • ColdFusion
  • ruby
  • PowerShell
  • Lua
  • Golang
  • linux shell
您的位置:首页 > 脚本语言 >python > Python中优化NumPy包使用性能的教程

Python中优化NumPy包使用性能的教程

作者:goldensun 字体:[增加 减小] 来源:互联网

goldensun 通过本文主要向大家介绍了python numpy教程,python numpy安装教程,python如何安装numpy,python2.7安装numpy,python numpy等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

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

相比于原生的Python,利用NumPy数组可以获得显著的性能加速,尤其是当你的计算遵循单指令多数据流(SIMD)范式时。然而,利用NumPy也有可能有意无意地写出未优化的代码。

在这篇文章中,我们将看到一些技巧,这些技巧可以帮助你编写高效的NumPy代码。我们首先看一下如何避免不必要的数组拷贝,以节省时间和内存。因此,我们将需要深入NumPy的内部。
学习避免不必要的数据拷贝

NumPy数组计算可能涉及到内存块之间的内部拷贝。有时会有不必要的拷贝,此时应该避免。相应地这里有一些技巧,可以帮助你优化你的代码。

import numpy as np
</div>

查看数组的内存地址

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

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

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

a = np.zeros(10); aid = id(a); aid
71211328
b = a.copy(); id(b) == aid
False
</div>

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

id(a), id(a[1:])
(71211328, 71211336)
</div>

在这篇文章中,我们将确保函数用到的数组具有相同的偏移量。下边是一个判断两个数组是否共享相同数据的更可靠的方案:

 

def get_data_base(arr):
  """For a given Numpy array, finds the base array that "owns" the actual data."""
  base = arr
  while isinstance(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
</div>

感谢Michael Droettboom指出这种更精确的方法,提出这个替代方案。
就地操作和隐式拷贝操作

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

a *= 2; id(a) == aid
True
 
c = a * 2; id(c) == aid
False
</div>

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

%%timeit a = np.zeros(10000000)
a *= 2
10 loops, best of 3: 19.2 ms per loop
 
%%timeit a = np.zeros(10000000)
b = a * 2
10 loops, best of 3: 42.6 ms per loop
</div>

4. 重塑一个数组可能涉及到拷贝操作,也可能涉及不到。原因将在下面解释。例如,重塑一个二维矩阵不涉及拷贝操作,除非它被转置(或更一般的非连续操作):

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

</div>

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

b = a.reshape((1, -1)); id(b) == aid
True
</div>

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

c = a.T.reshape((1, -1)); id(c) == aid
False
</div>

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

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

d = a.flatten(); id(d) == aid
False
 
e = a.ravel(); id(e) == aid
True
 
%timeit a.flatten()
1000000 loops, best of 3: 881 ns per loop
 
%timeit a.ravel()
1000000 loops, best of 3: 294 ns per loop
</div>

广播规则

6. 广播规则允许你在形状不同但却兼容的数组上进行计算。换句话说,你并不总是需要重塑或铺平数组,使它们的形状匹配。下面的例子说明了两个向量之间进行矢量积的两个方法:第一个方法涉及到数组的变形操作,第二个方法涉及到广播规则。显然第二个方法是要快得多。
 

n = 1000
 
a = np.arange(n)
ac = a[:, np.newaxis]
ar = a[np.newaxis, :]
 
%timeit np.tile(ac, (1, n)) * np.tile(ar, (n, 1))
100 loops, best of 3: 10 ms per loop
 
%timeit ar * ac
100 loops, best of 3: 2.36 ms per loop
</div>

在NumPy数组上进行高效的选择

NumPy提供了多种数组分片的方式。数组视图涉及到一个数组的原始数据缓冲区,但具有不同的偏移量,形状和步长。NumPy只允许等步长选择(即线性分隔索引)。NumPy还提供沿一个轴进行任意选择的特定功能。最后,花式索引(fancy indexing)是最一般的选择方法,但正如我们将要在文章中看到的那样,它同时也是最慢的。如果可能,我们应该选择更快的替代方法。

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

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

数组视图和花式索引

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

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

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

id(b1) == aid, id(b2) == aid
(True, False)
</div>

4. 比较一下两个方法的执行效率。
 

%timeit a[::10]
1000000 loops, best of 3: 804 ns per loop
 
%timeit a[np.arange(0, n, 10)]
100 loops, best of 3: 14.1 ms per loop
</div>

花式索引慢好几个数量级,因为它要复制一个大数组。
替代花式索引:索引列表

5. 当需要沿一个维度进行非等步长选择时,数组视图就无能为力了。然而,替代花式索引的方法在这种情况下依然存在。给定一个索引列表,NumPy的函数可以沿一个轴执行选择操作。
 

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

第二个方法更快一点:
 

%timeit a[i]
100 loops, best of 3: 13 ms per loop
 
%timeit np.take(a, i, axis=0)
100 loops, best of 3: 4.87 ms per loop
</div>

替代花式索引:布尔掩码

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

i = np.random.random_sample(n) < .5
</div>

可以使用花式索引或者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.8 ms per loop
 
%timeit np.compress(i, a, axis=0)
10 loops, best of 3: 24.1 ms per loop
</div>

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

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

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

在本节中,我们将看到使用NumPy时底层会发生什么,从而让我们理解该文章中的优化技巧

您可能想查找下面的文章:

  • Python科学计算之NumPy入门教程
  • Python中优化NumPy包使用性能的教程
  • Python中的Numpy入门教程
  • Python中的Numpy入门教程

相关文章

  • python登录pop3邮件服务器接收邮件的方法
  • python网络编程学习笔记(九):数据库客户端 DB-API
  • Python HTTP客户端自定义Cookie实现实例
  • 使用Python脚本来控制Windows Azure的简单教程
  • 学习python处理python编码问题
  • Pycharm学习教程(1) 定制外观
  • Python中random模块用法实例分析
  • Python使用ftplib实现简易FTP客户端的方法
  • Python的Flask框架中使用Flask-SQLAlchemy管理数据库的教程
  • Python用zip函数同时遍历多个迭代器示例详解

文章分类

  • vbs
  • DOS/BAT
  • hta/htc
  • python
  • perl
  • VBA
  • ColdFusion
  • ruby
  • PowerShell
  • Lua
  • Golang
  • linux shell

最近更新的内容

    • python获取Linux下文件版本信息、公司名和产品名的方法
    • Python实现遍历目录的方法【测试可用】
    • linux下python抓屏实现方法
    • python使用Queue在多个子进程间交换数据的方法
    • 使用Python读写及压缩和解压缩文件的示例
    • python抓取网页中的图片示例
    • Python删除空文件和空文件夹的方法
    • python中xrange和range的区别
    • 在Python中使用Mako模版库的简单教程
    • 举例讲解Python中metaclass元类的创建与使用

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有