李沐 数据操作实现【动手学深度学习v2】

入门

本节的目标是帮助读者了解并运行一些在阅读本书的过程中会用到的基本数值计算工具。 如果你很难理解一些数学概念或库函数,请不要担心。 后面的章节将通过一些实际的例子来回顾这些内容。 如果你已经具有相关经验,想要深入学习数学内容,可以跳过本节。

(首先,我们导入torch。请注意,虽然它被称为PyTorch,但是代码中使用torch而不是pytorch)

 [张量表示一个由数值组成的数组,这个数组可能有多个维度]。 具有一个轴的张量对应数学上的向量(vector); 具有两个轴的张量对应数学上的矩阵(matrix); 具有两个轴以上的张量没有特殊的数学名称。

首先,我们可以使用 arange 创建一个行向量 x。这个行向量包含以0开始的前12个整数,它们默认创建为整数。也可指定创建类型为浮点数。张量中的每个值都称为张量的元素(element)。例如,张量 x 中有 12 个元素。除非额外指定,新的张量将存储在内存中,并采用基于CPU的计算。

[可以通过张量的shape属性来访问张量(沿每个轴的长度)的形状] (和张量中元素的总数)。

如果只想知道张量中元素的总数,即形状的所有元素乘积,可以检查它的大小(size)。 因为这里在处理的是一个向量,所以它的shape与它的size相同。

[要想改变一个张量的形状而不改变元素数量和元素值,可以调用reshape函数]

例如,可以把张量x从形状为(12,)的行向量转换为形状为(3,4)的矩阵。 这个新的张量包含与转换前相同的值,但是它被看成一个3行4列的矩阵。 要重点说明一下,虽然张量的形状发生了改变,但其元素值并没有变。 注意,通过改变张量的形状,张量的大小不会改变。

我们不需要通过手动指定每个维度来改变形状。 也就是说,如果我们的目标形状是(高度,宽度), 那么在知道宽度后,高度会被自动计算得出,不必我们自己做除法。 在上面的例子中,为了获得一个3行的矩阵,我们手动指定了它有3行和4列。 幸运的是,我们可以通过-1来调用此自动计算出维度的功能。 即我们可以用x.reshape(-1,4)x.reshape(3,-1)来取代x.reshape(3,4)

有时,我们希望[使用全0、全1、其他常量,或者从特定分布中随机采样的数字]来初始化矩阵。 我们可以创建一个形状为(2,3,4)的张量,其中所有元素都设置为0。代码如下:

同样,我们可以创建一个形状为(2,3,4)的张量,其中所有元素都设置为1。代码如下:

有时我们想通过从某个特定的概率分布中随机采样来得到张量中每个元素的值。

例如,当我们构造数组来作为神经网络中的参数时,我们通常会随机初始化参数的值。 以下代码创建一个形状为(3,4)的张量。 其中的每个元素都从均值为0、标准差为1的标准高斯分布(正态分布)中随机采样。

我们还可以[通过提供包含数值的Python列表(或嵌套列表),来为所需张量中的每个元素赋予确定值]。 在这里,最外层的列表对应于轴0,内层的列表对应于轴1。

运算符

我们的兴趣不仅限于读取数据和写入数据。 我们想在这些数据上执行数学运算,其中最简单且最有用的操作是按元素(elementwise)运算。 它们将标准标量运算符应用于数组的每个元素。 对于将两个数组作为输入的函数,按元素运算将二元运算符应用于两个数组中的每对位置对应的元素。 我们可以基于任何从标量到标量的函数来创建按元素函数。

对于任意具有相同形状的张量, [常见的标准算术运算符(+-*/**)都可以被升级为按元素运算]。 我们可以在同一形状的任意两个张量上调用按元素操作。 在下面的例子中,我们使用逗号来表示一个具有5个元素的元组,其中每个元素都是按元素操作的结果。

(“按元素”方式可以应用更多的计算),包括像求幂这样的一元运算符。

除了按元素计算外,我们还可以执行线性代数运算,包括向量点积和矩阵乘法。

我们将在 :numref:sec_linear-algebra中解释线性代数的重点内容。

[我们也可以把多个张量连结(concatenate)在一起]

 把它们端对端地叠起来形成一个更大的张量。 我们只需要提供张量列表,并给出沿哪个轴连结。 下面的例子分别演示了当我们沿行(dim=0 轴-0,形状的第一个元素)按列(dim=0 轴-1,形状的第二个元素)连结两个矩阵时,会发生什么情况。

第一个输出张量的轴-0长度(6)是两个输入张量轴-0长度的总和(3+3).6行4列

第二个输出张量的轴-1长度(8)是两个输入张量轴-1长度的总和(4+4).3行8列

ps:张量元素类型为float32

有时,我们想[通过逻辑运算符构建二元张量]。

X == Y为例: 对于每个位置,如果XY在该位置相等,则新张量中相应项的值为1。 这意味着逻辑语句X == Y在该位置处为真,否则该位置为0。

[对张量中的所有元素进行求和,会产生一个单元素张量。]

广播机制

:label:subsec_broadcasting

在上面的部分中,我们看到了如何在相同形状的两个张量上执行按元素操作。 在某些情况下,[*即使形状不同,我们仍然可以通过调用 广播机制(broadcasting mechanism)来执行按元素操作*]。 这种机制的工作方式如下:

  1. 通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状;
  2. 对生成的数组执行按元素操作。

在大多数情况下,我们将沿着数组中长度为1的轴进行广播,如下例子:

由于ab分别是3×1和1×2矩阵,如果让它们相加,它们的形状不匹配。 我们将两个矩阵广播为一个更大的3×2矩阵,如下所示:矩阵a将复制列, 矩阵b将复制行,然后再按元素相加。

更多尝试:

索引和切片

就像在任何其他Python数组中一样,张量中的元素可以通过索引访问。 与任何Python数组一样:第一个元素的索引是0,最后一个元素索引是-1; 可以指定范围以包含第一个元素和最后一个之前的元素。

如下所示,我们[可以用[-1]选择最后一个元素(最后一行),可以用[1:3]选择第二个和第三个元素](行1 行2)

[除读取外,我们还可以通过指定索引来将元素写入矩阵]

修改第1行,第2列的元素,行与列都是从0开始的。

如果我们想[为多个元素赋值相同的值,我们只需要索引所有元素,然后为它们赋值]

例如,[0:2, :]访问第1行和第2行,其中“:”代表沿轴1(列)的所有元素。 虽然我们讨论的是矩阵的索引,但这也适用于向量和超过2个维度的张量。

节省内存

[运行一些操作可能会导致为新结果分配内存]。

例如,如果我们用Y = X + Y,我们将取消引用Y指向的张量,而是指向新分配的内存处的张量。

在下面的例子中,我们用Python的id()函数演示了这一点, 它给我们提供了内存中引用对象的确切地址。 运行Y = Y + X后,我们会发现id(Y)指向另一个位置。 这是因为Python首先计算Y + X,为结果分配新的内存,然后使Y指向内存中的这个新位置。

id()函数类似于c++中的指针

这可能是不可取的,原因有两个:

  1. 首先,我们不想总是不必要地分配内存。在机器学习中,我们可能有数百兆的参数,并且在一秒内多次更新所有参数。通常情况下,我们希望原地执行这些更新;
  2. 如果我们不原地更新,其他引用仍然会指向旧的内存位置,这样我们的某些代码可能会无意中引用旧的参数。

幸运的是,(执行原地操作)非常简单。

我们可以使用切片表示法将操作的结果分配给先前分配的数组,例如Y[:] = <expression>

为了说明这一点,我们首先创建一个新的矩阵Z其形状与另一个Y相同, 使用zeros_like来分配一个全0的块

[如果在后续计算中没有重复使用X, 我们也可以使用X[:] = X + YX += Y来减少操作的内存开销。]

转换为其他Python对象

 将深度学习框架定义的张量[转换为NumPy张量(ndarray]很容易,反之也同样容易。

torch张量和numpy数组将共享它们的底层内存,就地操作更改一个张量也会同时更改另一个张量。

要(将大小为1的张量转换为Python标量),我们可以调用item函数Python的内置函数

小结

  • 深度学习存储和操作数据的主要接口是张量(𝑛维数组)。它提供了各种功能,包括基本数学运算、广播、索引、切片、内存节省和转换其他Python对象
  • 31
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值