一,数据操作
①获取数据
②处理数据
N维数组,也称为张量(tensor)。 使用过Python中NumPy计算包的读者会对本部分很熟悉。 无论使用哪个深度学习框架,它的张量类(在MXNet中为ndarray
, 在PyTorch和TensorFlow中为Tensor
)都与Numpy的ndarray
类似。 但深度学习框架又比Numpy的ndarray
多一些重要功能: 首先,GPU很好地支持加速计算,而NumPy仅支持CPU计算; 其次,张量类支持自动微分。
张量表示一个由数值组成的数组,这个数组可能有多个维度。 具有一个轴的张量对应数学上的向量(vector); 具有两个轴的张量对应数学上的矩阵(matrix); 具有两个轴以上的张量没有特殊的数学名称。
使用 arange
创建一个行向量 x,
x = np.arange(12)
可以通过张量的shape
属性来访问张量(沿每个轴的长度)的形状 。x.shape
如果只想知道张量中元素的总数,即形状的所有元素乘积,可以检查它的大小(size)
改变一个张量的形状而不改变元素数量和元素值,可以调用reshape
函数。X = x.reshape(3, 4)
有时我们想通过从某个特定的概率分布中随机采样来得到张量中每个元素的值。 例如,当我们构造数组来作为神经网络中的参数时,我们通常会随机初始化参数的值。 以下代码创建一个形状为(3,4)的张量。 其中的每个元素都从均值为0、标准差为1的标准高斯分布(正态分布)中随机采样。
③运算符
我们想在这些数据上执行数学运算,其中最简单且最有用的操作是按元素(elementwise)运算。 它们将标准标量运算符应用于数组的每个元素。 对于将两个数组作为输入的函数,按元素运算将二元运算符应用于两个数组中的每对位置对应的元素。 我们可以基于任何从标量到标量的函数来创建按元素函数。
④广播机制
在上面的部分中,我们看到了如何在相同形状的两个张量上执行按元素操作。 在某些情况下,即使形状不同,我们仍然可以通过调用 广播机制(broadcasting mechanism)来执行按元素操作。 这种机制的工作方式如下:
-
通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状;
-
对生成的数组执行按元素操作。
-
由于
a
和b
分别是3×1和1×2矩阵,如果让它们相加,它们的形状不匹配。 我们将两个矩阵广播为一个更大的3×2矩阵,如下所示:矩阵a
将复制列, 矩阵b
将复制行,然后再按元素相加。
⑤索引与切片
就像在任何其他Python数组中一样,张量中的元素可以通过索引访问。 与任何Python数组一样:第一个元素的索引是0,最后一个元素索引是-1; 可以指定范围以包含第一个元素和最后一个之前的元素。
如下所示,我们可以用[-1]
选择最后一个元素,可以用[1:3]
选择第二个和第三个元素:
如果我们想为多个元素赋值相同的值,我们只需要索引所有元素,然后为它们赋值。 例如,[0:2, :]
访问第1行和第2行,其中“:”代表沿轴1(列)的所有元素。 虽然我们讨论的是矩阵的索引,但这也适用于向量和超过2个维度的张量。
⑥节省内存
运行一些操作可能会导致为新结果分配内存。 例如,如果我们用Y = X + Y
,我们将取消引用Y
指向的张量,而是指向新分配的内存处的张量。
在下面的例子中,我们用Python的id()
函数演示了这一点, 它给我们提供了内存中引用对象的确切地址。 运行Y = Y + X
后,我们会发现id(Y)
指向另一个位置。 这是因为Python首先计算Y + X
,为结果分配新的内存,然后使Y
指向内存中的这个新位置。
这可能是不可取的,原因有两个:
-
首先,我们不想总是不必要地分配内存。在机器学习中,我们可能有数百兆的参数,并且在一秒内多次更新所有参数。通常情况下,我们希望原地执行这些更新;
-
如果我们不原地更新,其他引用仍然会指向旧的内存位置,这样我们的某些代码可能会无意中引用旧的参数
-
幸运的是,执行原地操作非常简单。 我们可以使用切片表示法将操作的结果分配给先前分配的数组,例如
Y[:] = <expression>
。 为了说明这一点,我们首先创建一个新的矩阵Z
,其形状与另一个Y
相同, 使用zeros_like
来分配一个全0的块。
如果在后续计算中没有重复使用X
, 我们也可以使用X[:] = X + Y
或X += Y
来减少操作的内存开销。
⑦转化为其他python对象
将深度学习框架定义的张量转换为NumPy张量(ndarray
)很容易,反之也同样容易。 转换后的结果不共享内存。 这个小的不便实际上是非常重要的:当在CPU或GPU上执行操作的时候, 如果Python的NumPy包也希望使用相同的内存块执行其他操作,人们不希望停下计算来等它。
⑧小结
-
深度学习存储和操作数据的主要接口是张量(n维数组)。它提供了各种功能,包括基本数运算、广播、索引、切片、内存节省和转换其他Python对象。