2.1。数据操作
为了完成任何事情,我们需要某种方式来存储和处理数据。通常,我们需要对数据做两件事:(i)获取数据;(ii)将它们放入计算机后进行处理。没有某种存储方式来获取数据是没有意义的,因此让我们首先通过合成数据进行操作。首先,我们介绍nn-维数组(ndarray
),是MXNet用来存储和转换数据的主要工具。在MXNet中, ndarray
是一个类,我们称任何实例为“ an ndarray
”。
如果您使用过NumPy(Python中使用最广泛的科学计算软件包),那么您会发现本节很熟悉。那是设计使然。我们将MXNet的ndarray设计为具有一些杀手级功能的NumPy的ndarray的扩展。首先,MXNet 的ndarray
支持在CPU,GPU和分布式云体系结构上的异步计算,而NumPy仅支持CPU计算。其次,MXNet的 ndarray
支持自动区分。这些属性使MXNet 的
适于深度学习。在整本书中, 除非另有说明,否则我们所说的ndarray
是指MXNet 的ndarray
。
2.1.1。入门
在本节中,我们旨在帮助您入门和运行,为您提供在学习本书时将要使用的基本数学和数值计算工具。如果您难以理解某些数学概念或库函数,请不要担心。以下各节将在实际示例的背景下重新访问该材料,并且将逐渐沉迷。另一方面,如果您已经有一些背景并且想更深入地研究数学内容,则跳过此部分。
首先,我们从MXNet 导入np
(numpy
)和npx
(numpy_extension
)模块。在这里,该np
模块包含NumPy支持的功能,而该npx
模块包含一组扩展,这些扩展是为在类似NumPy的环境中进行深度学习而开发的。当使用ndarray
时,我们几乎总是调用该set_np
函数:这是为了使MXNet的其他组件兼容ndarray处理。
from mxnet import np, npx
npx.set_np()
ndarray
代表(可能是多维的)数值数组。在一个轴上,ndarray
对应于(在数学上)向量。在两个轴上,an ndarray
对应于矩阵。具有两个以上轴的数组没有特殊的数学名称-我们简称为张量tensors。
首先,可以使用arange创建行向量x,该向量包含以0开头的前12个整数。尽管默认情况下它们创建为浮点数。ndarray中的每个值都称为ndarray的元素。例如,有 1212中的元素ndarray
x
。除非另有说明,否则新内容ndarray
将存储在主存储器中并指定用于基于CPU的计算。
x = np.arange(12)
x
array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11.])
我们可以访问ndarray
的形状通过检查它的(沿每个轴的长度)shape
属性。
x.shape
(12,)
如果我们只想知道中的元素总数ndarray
,即所有形状元素的乘积,我们可以检查其size属性。因为我们在这里处理向量,所以其形状的单个元素与其大小相同。
x.size
12
要在ndarray
不更改元素数量或元素值的情况下更改其形状,我们可以调用该reshape
函数。例如,我们可以将ndarray x从形状为(12,)的行向量转换为形状为(3,4)的矩阵。这个新ndarray
值包含完全相同的值,但将它们视为以如下形式组织的矩阵33 行和 44列。重申一下,尽管形状发生了变化,但其中的元素x
没有变化。请注意,size
重塑不会改变。
x = x.reshape(3, 4)
x
array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
无需通过手动指定每个尺寸来重塑形状。如果我们的目标形状是具有形状(高度,宽度)的矩阵,则在知道宽度之后,将隐式给出高度。为什么我们必须自己进行分割?在上面的示例中,要获得具有 33 行,我们都指定它应该有 33 行和 44列。幸运的是,ndarray
可以自动计算出其余维度。我们通过放置要自动推断-1
的尺寸来调用此功能 ndarray
。在我们的例子中,我们可以等效地调用x.reshape(-1,4)或x.reshape(3,-1),而不是调用x.reshape(3,4)。
该empty
方法抢占了一块内存,将我们交还给矩阵,而无需更改其任何条目的值。这非常有效,但是我们必须小心,因为条目可能采用任意值,包括非常大的值!
np.empty((3, 4))
array([[-4.2474481e+24, 4.5601055e-41, 1.3561645e-37, 3.0909842e-41],
[ 0.0000000e+00, 0.0000000e+00, 0.0000000e+00, 0.0000000e+00],
[ 0.0000000e+00, 0.0000000e+00, 0.0000000e+00, 0.0000000e+00]])
通常,我们希望矩阵用零,一,一些其他常数或从特定分布中随机采样的数字初始化。 我们可以创建一个表示张量的ndarray,其所有元素都设置为0,形状为(2,3,4),如下所示:
np.zeros((2, 3, 4))
array([[[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来创建张量,如下所示:
np.ones((2, 3, 4))
array([[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]],
[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]]])
通常,我们想从某个概率分布中随机采样ndarray中每个元素的值。 例如,当我们构造数组作为神经网络中的参数时,通常会随机初始化它们的值。 以下代码段创建形状为(3,4)的ndarray。 从标准高斯分布(正态分布)中随机抽取每个元素,平均值为0,标准差为1。
np.random.normal(0, 1, size=(3, 4))
array([[ 2.2122064 , 1.1630787 , 0.7740038 , 0.4838046 ],
[ 1.0434405 , 0.29956347, 1.1839255 , 0.15302546],
[ 1.8917114 , -1.1688148 , -1.2347414 , 1.5580711 ]])
我们还可以通过提供包含数值的Python列表(或列表列表)来指定所需ndarray中每个元素的确切值。 在这里,最外面的列表对应于轴 0,内部列表指向轴1。
np.array([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
array([[2., 1., 4., 3.],
[1., 2., 3., 4.],
[4., 3., 2., 1.]])
2.1.2. Operations
我们的兴趣不仅限于从数组读取数据或向数组写入数据。我们要对那些数组执行数学运算。一些最简单和最有用的操作是元素操作。这些将标准标量运算应用于数组的每个元素。对于将两个数组作为输入的函数,按元素运算将一些标准的二进制运算符应用于两个数组中每对对应的元素。我们可以从从标量映射到标量的任何函数创建元素级函数。
用数学表示法,我们将通过签名来表示这样的一元标量运算符(接受一个输入) f:R→R。这仅表示该函数正在映射任何实数(R)移到另一个。同样,我们用签名表示一个二进制标量运算符(接受两个实数输入,并产生一个输出) f:R,R→R。给定任意两个向量u 和 v 具有相同的形状和一个二元运算符f,我们可以产生一个向量 c=F(u,v) 通过设置 ci←f(ui,vi)对全部 i,在哪里 ci,ui和 vi 是 ith 向量元素 c,u和 v。在这里,我们产生了向量值 F:Rd,Rd→Rd通过将标量函数提升lifting为元素向量运算。
在MXNet,公共标准的算术运算符(+
,-
,*
, /
,和**
)已经全部提升lefted的elementwise操作为任意形状的任何相同形状的张量。我们可以在形状相同的任何两个张量上调用元素操作。在以下示例中,我们使用逗号来表示5-element元组,其中每个元素都是元素操作的结果。
x = np.array([1, 2, 4, 8]) y = np.array([2, 2, 2, 2]) x + y, x - y, x * y, x / y, x ** y # The ** operator is exponentiation
(array([ 3., 4., 6., 10.]), array([-1., 0., 2., 6.]), array([ 2., 4., 8., 16.]), array([0.5, 1. , 2. , 4. ]), array([ 1., 4., 16., 64.]))
可以逐元素应用更多操作,包括一元运算符(如幂运算)。
np.exp(x)
array([2.7182817e+00, 7.3890562e+00, 5.4598148e+01, 2.9809580e+03])
除了逐元素计算,我们还可以执行线性代数运算,包括矢量点积和矩阵乘法。我们将在第2.3节中解释线性代数的关键位(没有假定的先验知识)。
我们还可以将多个ndarray连接在一起,将它们端对端堆叠以形成更大的ndarray。 我们只需要提供ndarray的列表,并告诉系统沿着哪个轴连接即可。 下面的示例显示了当我们沿着行(轴0,形状的第一个元素)与列(轴1,形状的第二个元素)连接两个矩阵时发生的情况。 我们可以看到,第一个输出ndarray的axis-0长度(6)是两个输入ndarrays的axis-0长度(3 + 3)的和。 而第二个输出ndarray的axis-1长度(8)是两个输入ndarrays的axis-1长度(4 + 4)的总和。
x = np.arange(12).reshape(3, 4) y = np.array([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]]) np.concatenate([x, y], axis=0), np.concatenate([x, y], axis=1)
(array([[ 0., 1., 2., 3.], [ 4., 5., 6., 7.], [ 8., 9., 10., 11.], [ 2., 1., 4., 3.], [ 1., 2., 3., 4.], [ 4., 3., 2., 1.]]), array([[ 0., 1., 2., 3., 2., 1., 4., 3.], [ 4., 5., 6., 7., 1., 2., 3., 4.], [ 8., 9., 10., 11., 4., 3., 2., 1.]]))