图解NumPy常用函数的内在机制机器之

北京有哪些比较好的白癜风医院 https://wapjbk.39.net/yiyuanzaixian/bjzkbdfyy/

支持大量多维数组和矩阵运算的NumPy软件库是许多机器学习开发者和研究者的必备工具,本文将通过直观易懂的图示解析常用的NumPy功能和函数,帮助你理解NumPy操作数组的内在机制。

NumPy是一个基础软件库,很多常用的Python数据处理软件库都使用了它或受到了它的启发,包括pandas、PyTorch、TensorFlow、Keras等。理解NumPy的工作机制能够帮助你提升在这些软件库方面的技能。而且在GPU上使用NumPy时,无需修改或仅需少量修改代码。

NumPy的核心概念是n维数组。n维数组的美丽之处是大多数运算看起来都一样,不管数组有多少维。但一维和二维有点特殊。本文分为三部分:

1.向量:一维数组

2.矩阵:二维数组

3.三维及更高维

本文参考了JayAlammar的文章《AVisualIntrotoNumPy》并将其作为起点,然后进行了扩充,并做了一些细微修改。

NumPy数组和Python列表

乍一看,NumPy数组与Python列表类似。它们都可作为容器,能够快速获取和设置元素,但插入和移除元素会稍慢一些。

NumPy数组完胜列表的最简单例子是算术运算:

除此之外,NumPy数组的优势和特点还包括:

更紧凑,尤其是当维度大于一维时;

当运算可以向量化时,速度比列表更快;

当在后面附加元素时,速度比列表慢;

通常是同质的:当元素都是一种类型时速度很快。

这里O(N)的意思是完成该运算所需的时间和数组的大小成正比,而O*(1)(即所谓的「均摊O(1)」)的意思是完成运算的时间通常与数组的大小无关。

向量:一维数组

向量初始化

为了创建NumPy数组,一种方法是转换Python列表。NumPy数组类型可以直接从列表元素类型推导得到。

要确保向其输入的列表是同一种类型,否则你最终会得到dtype=’object’,这会影响速度,最终只留下NumPy中含有的语法糖。

NumPy数组不能像Python列表一样增长。数组的末端没有留下任何便于快速附加元素的空间。因此,常见的做法是要么先使用Python列表,准备好之后再将其转换为NumPy数组,要么是使用np.zeros或np.empty预先留下必要的空间:

通常我们有必要创建在形状和元素类型上与已有数组匹配的空数组。

事实上,所有用于创建填充了常量值的数组的函数都带有_like的形式:

NumPy中有两个函数能用单调序列执行数组初始化:

如果你需要类似[0.,1.,2.]这样的浮点数数组,你可以修改arange输出的类型:arange(3).astype(float),但还有一种更好的方法。arange函数对类型很敏感:如果你以整型数作为参数输入,它会生成整型数;如果你输入浮点数(比如arange(3.)),它会生成浮点数。

但arange并不非常擅长处理浮点数:

在我们眼里,这个0.1看起来像是一个有限的十进制数,但计算机不这么看。在二进制表示下,0.1是一个无限分数,因此必须进行约分,也由此必然会产生误差。也因为这个原因,如果向arange函数输入带分数部分的step,通常得不到什么好结果:你可能会遇到差一错误(off-by-oneerror)。你可以使该区间的末端落在一个非整数的step数中(solution1),但这会降低代码的可读性和可维护性。这时候,linspace就可以派上用场了。它不受舍入的影响,总能生成你要求的元素数值。不过,使用linspace时会遇到一个常见的陷阱:它统计的是数据点的数量,而不是区间,因此其最后一个参数num通常比你所想的数大1。因此,上面最后一个例子中的数是11,而不是10。

在进行测试时,我们通常需要生成随机数组:

向量索引

一旦你的数组中有了数据,NumPy就能以非常巧妙的方式轻松地提供它们:

除了「花式索引(fancyindexing)」外,上面给出的所有索引方法都被称为「view」:它们并不存储数据,也不会在数据被索引后发生改变时反映原数组的变化情况。

所有包含花式索引的方法都是可变的:它们允许通过分配来修改原始数组的内容,如上所示。这一功能可通过将数组切分成不同部分来避免总是复制数组的习惯。

Python列表与NumPy数组的对比

为了获取NumPy数组中的数据,另一种超级有用的方法是布尔索引(booleanindexing),它支持使用各类逻辑运算符:

any和all的作用与在Python中类似,但不会短路。

不过要注意,这里不支持Python的「三元比较」,比如3=a=5。

如上所示,布尔索引也是可写的。其两个常用功能都有各自的专用函数:过度重载的np.where函数和np.clip函数。它们的含义如下:

向量运算

NumPy在速度上很出彩的一大应用领域是算术运算。向量运算符会被转换到C++层面上执行,从而避免缓慢的Python循环的成本。NumPy支持像操作普通的数那样操作整个数组。

与Python句法一样,a//b表示a除b(除法的商),x**n表示x。

正如加减浮点数时整型数会被转换成浮点数一样,标量也会被转换成数组,这个过程在NumPy中被称为广播(broadcast)。

大多数数学函数都有用于处理向量的NumPy对应函数:

标量积有自己的运算符:

执行三角函数时也无需循环:

我们可以在整体上对数组进行舍入:

floor为舍、ceil为入,around则是舍入到最近的整数(其中.5会被舍掉)

NumPy也能执行基础的统计运算:

NumPy的排序函数没有Python的排序函数那么强大:

Python列表与NumPy数组的排序函数对比

在一维情况下,如果缺少reversed关键字,那么只需简单地对结果再执行反向,最终效果还是一样。二维的情况则会更困难一些(人们正在请求这一功能)。

搜索向量中的元素

与Python列表相反,NumPy数组没有索引方法。人们很久之前就在请求这个功能,但一直还没实现。

Python列表与NumPy数组的对比,index()中的方括号表示可以省略j或同时省略i和j。

一种查找元素的方法是np.where(a==x)[0][0],但这个方法既不优雅,速度也不快,因为它需要检查数组中的所有元素,即便所要找的目标就在数组起始位置也是如此。

另一种更快的方式是使用Numba来加速next((i[0]fori,vinnp.ndenumerate(a)ifv==x),-1)。

一旦数组的排序完成,搜索就容易多了:v=np.searchsorted(a,x);returnvifa[v]==xelse-1的速度很快,时间复杂度为O(logN),但它需要O(NlogN)时间先排好序。

事实上,用C来实现它进而加速搜索并不是问题。问题是浮点比较。这对任何数据来说都不是一种简单直接可用的任务。

比较浮点数

函数np.allclose(a,b)能在一定公差下比较浮点数数组。

函数np.allclose(a,b)的工作过程示例。并没有万能方法!

np.allclose假设所有被比较的数都在典型的1的范围内。举个例子,如果要在纳秒级的速度内完成计算,则需要用默认的atol参数值除以1e9:np.allclose(1e-9,2e-9,atol=1e-17)==False.

math.isclose则不会对要比较的数进行任何假设,而是依赖用户给出合理的abs_tol值(对于典型的1的范围内的值,取默认的np.allcloseatol值1e-8就足够好了):math.isclose(0.1+0.2–0.3,abs_tol=1e-8)==True.

除此之外,np.allclose在绝对值和相对公差的公式方面还有一些小问题,举个例子,对于给定的a和b,存在allclose(a,b)!=allclose(b,a)。这些问题已在(标量)函数math.isclose中得到了解决,我们将在后面介绍它。对于这方面的更多内容,请参阅GitHub上的浮点数指南和对应的NumPy问题(


转载请注明:http://www.aierlanlan.com/grrz/1901.html

  • 上一篇文章:
  •   
  • 下一篇文章: 没有了