- 深度学习入门:基于Python的理论与实现
- (日)斋藤康毅
- 1682字
- 2024-08-06 10:59:31
1.5 NumPy
在深度学习的实现中,经常出现数组和矩阵的计算。NumPy 的数组类(numpy.array
)中提供了很多便捷的方法,在实现深度学习时,我们将使用这些方法。本节我们来简单介绍一下后面会用到的 NumPy。
1.5.1 导入NumPy
NumPy 是外部库。这里所说的“外部”是指不包含在标准版 Python 中。因此,我们首先要导入 NumPy 库。
>>> import numpy as np
Python 中使用 import
语句来导入库。这里的 import numpy as np
,直译的话就是“将 numpy 作为 np 导入”的意思。通过写成这样的形式,之后 NumPy 相关的方法均可通过 np
来调用。
1.5.2 生成NumPy数组
要生成 NumPy 数组,需要使用 np.array()
方法。np.array()
接收 Python 列表作为参数,生成 NumPy 数组(numpy.ndarray
)。
>>> x = np.array([1.0, 2.0, 3.0]) >>> print(x) [ 1. 2. 3.] >>> type(x) <class 'numpy.ndarray'>
1.5.3 NumPy的算术运算
下面是 NumPy 数组的算术运算的例子。
>>> x = np.array([1.0, 2.0, 3.0]) >>> y = np.array([2.0, 4.0, 6.0]) >>> x + y # 对应元素的加法 array([ 3., 6., 9.]) >>> x - y array([ -1., -2., -3.]) >>> x * y # element-wise product array([ 2., 8., 18.]) >>> x / y array([ 0.5, 0.5, 0.5])
这里需要注意的是,数组 x
和数组 y
的元素个数是相同的(两者均是元素个数为 3 的一维数组)。当 x
和 y
的元素个数相同时,可以对各个元素进行算术运算。如果元素个数不同,程序就会报错,所以元素个数保持一致非常重要。另外,“对应元素的”的英文是 element-wise,比如“对应元素的乘法”就是 element-wise product。
NumPy 数组不仅可以进行 element-wise 运算,也可以和单一的数值(标量)组合起来进行运算。此时,需要在 NumPy 数组的各个元素和标量之间进行运算。这个功能也被称为广播(详见后文)。
>>> x = np.array([1.0, 2.0, 3.0]) >>> x / 2.0 array([ 0.5, 1. , 1.5])
1.5.4 NumPy的N维数组
NumPy 不仅可以生成一维数组(排成一列的数组),也可以生成多维数组。比如,可以生成如下的二维数组(矩阵)。
>>> A = np.array([[1, 2], [3, 4]]) >>> print(A) [[1 2] [3 4]] >>> A.shape (2, 2) >>> A.dtype dtype('int64')
这里生成了一个 2 × 2 的矩阵 A
。另外,矩阵 A
的形状可以通过 shape
查看,矩阵元素的数据类型可以通过 dtype
查看。下面,我们来看一下矩阵的算术运算。
>>> B = np.array([[3, 0],[0, 6]]) >>> A + B array([[ 4, 2], [ 3, 10]]) >>> A * B array([[ 3, 0], [ 0, 24]])
和数组的算术运算一样,矩阵的算术运算也可以在相同形状的矩阵间以对应元素的方式进行。并且,也可以通过标量(单一数值)对矩阵进行算术运算。这也是基于广播的功能。
>>> print(A) [[1 2] [3 4]] >>> A * 10 array([[ 10, 20], [ 30, 40]])
NumPy 数组(np.array
)可以生成 N 维数组,即可以生成一维数组、二维数组、三维数组等任意维数的数组。数学上将一维数组称为向量,将二维数组称为矩阵。另外,可以将一般化之后的向量或矩阵等统称为张量(tensor)。本书基本上将二维数组称为“矩阵”,将三维数组及三维以上的数组称为“张量”或“多维数组”。
1.5.5 广播
NumPy 中,形状不同的数组之间也可以进行运算。之前的例子中,在 2×2 的矩阵 A
和标量 10
之间进行了乘法运算。在这个过程中,如图 1-1 所示,标量 10
被扩展成了 2 × 2 的形状,然后再与矩阵 A
进行乘法运算。这个巧妙的功能称为广播(broadcast)。
图 1-1 广播的例子:标量 10 被当作 2 × 2 的矩阵
我们通过下面这个运算再来看一个广播的例子。
>>> A = np.array([[1, 2], [3, 4]]) >>> B = np.array([10, 20]) >>> A * B array([[ 10, 40], [ 30, 80]])
在这个运算中,如图 1-2 所示,一维数组 B
被“巧妙地”变成了和二维数组 A
相同的形状,然后再以对应元素的方式进行运算。
图 1-2 广播的例子 2
综上,因为 NumPy 有广播功能,所以不同形状的数组之间也可以顺利地进行运算。
1.5.6 访问元素
元素的索引从 0 开始。对各个元素的访问可按如下方式进行。
>>> X = np.array([[51, 55], [14, 19], [0, 4]]) >>> print(X) [[51 55] [14 19] [ 0 4]] >>> X[0] # 第0行 array([51, 55]) >>> X[0][1] # (0,1)的元素 55
也可以使用 for
语句访问各个元素。
>>> for row in X: ... print(row) ... [51 55] [14 19] [0 4]
除了前面介绍的索引操作,NumPy 还可以使用数组访问各个元素。
>>> X = X.flatten() # 将X转换为一维数组 >>> print(X) [51 55 14 19 0 4] >>> X[np.array([0, 2, 4])] # 获取索引为0、2、4的元素 array([51, 14, 0])
运用这个标记法,可以获取满足一定条件的元素。例如,要从 X
中抽出大于 15 的元素,可以写成如下形式。
>>> X > 15 array([ True, True, False, True, False, False], dtype=bool) >>> X[X>15] array([51, 55, 19])
对 NumPy 数组使用不等号运算符等(上例中是 X > 15
),结果会得到一个布尔型的数组。上例中就是使用这个布尔型数组取出了数组的各个元素(取出 True
对应的元素)。
Python 等动态类型语言一般比 C 和 C++ 等静态类型语言(编译型语言)运算速度慢。实际上,如果是运算量大的处理对象,用 C/C++ 写程序更好。为此,当 Python 中追求性能时,人们会用 C/C++ 来实现处理的内容。Python 则承担“中间人”的角色,负责调用那些用 C/ C++ 写的程序。NumPy 中,主要的处理也都是通过 C 或 C++ 实现的。因此,我们可以在不损失性能的情况下,使用 Python便利的语法。