当前位置: 首页 > article >正文

【深度学习】运算符

运算符

我们的兴趣不仅限于读取数据和写入数据。

我们想在这些数据上执行数学运算,其中最简单且最有用的操作是按元素(elementwise)运算。

它们将标准标量运算符应用于数组的每个元素。
对于将两个数组作为输入的函数,按元素运算将二元运算符应用于两个数组中的每对位置对应的元素。

我们可以基于任何从标量到标量的函数来创建按元素函数。

在数学表示法中,我们将通过符号 f : R → R f: \mathbb{R} \rightarrow \mathbb{R} f:RR来表示一元标量运算符(只接收一个输入)。这意味着该函数从任何实数( R \mathbb{R} R)映射到另一个实数。
同样,我们通过符号 f : R , R → R f: \mathbb{R}, \mathbb{R} \rightarrow \mathbb{R} f:R,RR表示二元标量运算符,这意味着该函数接收两个输入,并产生一个输出。
给定同一形状的任意两个向量 u \mathbf{u} u v \mathbf{v} v和二元运算符 f f f,我们可以得到向量 c = F ( u , v ) \mathbf{c} = F(\mathbf{u},\mathbf{v}) c=F(u,v)。具体计算方法是 c i ← f ( u i , v i ) c_i \gets f(u_i, v_i) cif(ui,vi),其中 c i c_i ci u i u_i ui v i v_i vi分别是向量 c \mathbf{c} c u \mathbf{u} u v \mathbf{v} v中的元素。在这里,我们通过将标量函数升级为按元素向量运算来生成向量值 F : R d , R d → R d F: \mathbb{R}^d, \mathbb{R}^d \rightarrow \mathbb{R}^d F:Rd,RdRd

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

x = torch.tensor([1.0, 2, 4, 8])
y = torch.tensor([2, 2, 2, 2])
x + y, x - y, x * y, x / y, x ** y  # **运算符是求幂运算

在这里插入图片描述
按元素”方式可以应用更多的计算),包括像求幂这样的一元运算符。

torch.exp(x)

在这里插入图片描述

除了按元素计算外,我们还可以执行线性代数运算,包括向量点积和矩阵乘法。我们将在:numref:sec_linear-algebra中解释线性代数的重点内容。

[我们也可以把多个张量连结(concatenate)在一起],把它们端对端地叠起来形成一个更大的张量。

我们只需要提供张量列表,并给出沿哪个轴连结。
下面的例子分别演示了当我们沿行(轴-0,形状的第一个元素)和按列(轴-1,形状的第二个元素)连结两个矩阵时,会发生什么情况。
我们可以看到,第一个输出张量的轴-0长度(6)是两个输入张量轴-0长度的总和(3 + 3);第二个输出张量的轴-1长度(8)是两个输入张量轴-1长度的总和(4 + 4)。

X = torch.arange(12, dtype=torch.float32).reshape((3,4))
Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)

在这里插入图片描述

以下是对这段代码中张量拼接过程的详细介绍:

### 1. 张量 `X` 的创建与初始化
   - `torch.arange(12, dtype=torch.float32)`:这一步创建了一个从 0 开始,步长为 1,到 11 结束(包含 0 和 11)的一维张量,并且指定数据类型为 `float32`。这个一维张量包含了 `[0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11.]` 这些元素。
   - `reshape((3,4))`:将上述一维张量重塑为一个 3 行 4 列的二维张量 `X`。具体的排列方式是:
     - 第一行:`[0., 1., 2., 3.]`
     - 第二行:`[4., 5., 6., 7.]`
     - 第三行:`[8., 9., 10., 11.]`

### 2. 张量 `Y` 的创建与初始化
   - `torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])`:直接创建了一个 3 行 4 列的二维张量 `Y`,其中元素的值为 `[[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]]`。由于其中有一个元素 `2.0` 是浮点数,所以整个张量的数据类型自动推断为 `float32`。

### 3. 沿维度 0(行)拼接 `X` 和 `Y`
   - `torch.cat((X, Y), dim=0)`:
     - `torch.cat` 函数用于将多个张量拼接在一起。这里的参数 `(X, Y)` 表示要拼接的张量序列,即先拼接 `X`,再拼接 `Y`。
     - `dim=0` 指定了拼接的维度为第 0 维,也就是行维度。
     - 拼接过程如下:
       - 首先将 `X` 的第一行 `[0., 1., 2., 3.]` 作为新张量的第一行。
       - 接着将 `X` 的第二行 `[4., 5., 6., 7.]` 作为新张量的第二行。
       - 然后将 `X` 的第三行 `[8., 9., 10., 11.]` 作为新张量的第三行。
       - 再将 `Y` 的第一行 `[2., 1., 4., 3.]` 作为新张量的第四行。
       - 之后将 `Y` 的第二行 `[1., 2., 3., 4.]` 作为新张量的第五行。
       - 最后将 `Y` 的第三行 `[4., 3., 2., 1.]` 作为新张量的第六行。
     - 最终得到的拼接结果是一个 6 行 4 列的张量:
...
tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.],
        [ 2.,  1.,  4.,  3.],
        [ 1.,  2.,  3.,  4.],
        [ 4.,  3.,  2.,  1.]])
...

### 4. 沿维度 1(列)拼接 `X` 和 `Y`
   - `torch.cat((X, Y), dim=1)`:
     - 同样使用 `torch.cat` 函数,参数 `(X, Y)` 不变,表示还是拼接这两个张量。
     - `dim=1` 指定了拼接的维度为第 1 维,也就是列维度。
     - 拼接过程如下:
       - 对于新张量的第一行,先取 `X` 的第一行 `[0., 1., 2., 3.]`,然后将 `Y` 的第一行 `[2., 1., 4., 3.]` 依次拼接在后面,得到 `[0., 1., 2., 3., 2., 1., 4., 3.]` 作为新张量的第一行。
       - 对于新张量的第二行,先取 `X` 的第二行 `[4., 5., 6., 7.]`,然后将 `Y` 的第二行 `[1., 2., 3., 4.]` 依次拼接在后面,得到 `[4., 5., 6., 7., 1., 2., 3., 4.]` 作为新张量的第二行。
       - 对于新张量的第三行,先取 `X` 的第三行 `[8., 9., 10., 11.]`,然后将 `Y` 的第三行 `[4., 3., 2., 1.]` 依次拼接在后面,得到 `[8., 9., 10., 11., 4., 3., 2., 1.]` 作为新张量的第三行。
     - 最终得到的拼接结果是一个 3 行 8 列的张量:

tensor([[ 0., 1., 2., 3., 2., 1., 4., 3.],
[ 4., 5., 6., 7., 1., 2., 3., 4.],
[ 8., 9., 10., 11., 4., 3., 2., 1.]])


通过这个例子,可以清楚地看到在 `PyTorch` 中如何根据不同的维度进行张量的拼接操作,这在深度学习的数据处理和模型构建中是非常常见和重要的操作。例如,在处理不同批次的数据时,可能需要沿行拼接;而在将不同特征的张量组合在一起时,可能需要沿列拼接。

有时,我们想[通过逻辑运算符构建二元张量]。
X == Y为例:对于每个位置,如果XY在该位置相等,则新张量中相应项的值为1。这意味着逻辑语句X == Y在该位置处为真,否则该位置为0。

X == Y

在这里插入图片描述

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

X.sum()

在这里插入图片描述

广播机制

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

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

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

a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
a, b

在这里插入图片描述

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

a + b

在这里插入图片描述

索引和切片

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

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

X[-1], X[1:3]

在这里插入图片描述

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

X[1, 2] = 9
X

在这里插入图片描述

如果我们想[为多个元素赋值相同的值,我们只需要索引所有元素,然后为它们赋值。]
例如,[0:2, :]访问第1行和第2行,其中:代表沿轴1(列)的所有元素。虽然我们讨论的是矩阵的索引,但这也适用于向量和超过2个维度的张量。

X[0:2, :] = 12
X

在这里插入图片描述

节省内存

[运行一些操作可能会导致为新结果分配内存]。例如,如果我们用Y = X + Y,我们将取消引用Y指向的张量,而是指向新分配的内存处的张量。

在下面的例子中,我们用Python的id()函数演示了这一点,它给我们提供了内存中引用对象的确切地址。

运行Y = Y + X后,我们会发现id(Y)指向另一个位置。这是因为Python首先计算Y + X,结果分配新的内存,然后使Y指向内存中的这个新位置。

before = id(Y)
Y = Y + X
id(Y) == before

在这里插入图片描述

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

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

幸运的是,(执行原地操作)非常简单。我们可以使用切片表示法将操作的结果分配给先前分配的数组,

例如Y[:] = <expression>。为了说明这一点,我们首先创建一个新的矩阵Z,其形状与另一个Y相同,使用zeros_like来分配一个全0的块。

Z = torch.zeros_like(Y)
print('id(Z):', id(Z))
Z[:] = X + Y
print('id(Z):', id(Z))

在这里插入图片描述

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

before = id(X)
X += Y
id(X) == before

在这里插入图片描述

转换为其他Python对象

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

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

A = X.numpy()
B = torch.tensor(A)
type(A), type(B)

在这里插入图片描述
要(将大小为1的张量转换为Python标量),我们可以调用item函数或Python的内置函数。

a = torch.tensor([3.5])
a, a.item(), float(a), int(a)

在这里插入图片描述

a.item():item()方法用于获取张量中仅包含一个元素时的该元素值(如果张量包含多个元素,使用item()会报错),这里会返回浮点数3.5。


http://www.kler.cn/a/500107.html

相关文章:

  • 树莓集团:数字资产什么意思?包括哪些?
  • vscode 无法使用npm, cmd命令行窗口可以正常执行
  • OpenCV的双边滤波函数
  • RabbitMQ 交换机、队列和路由键的命名规范
  • 大数据高级ACP学习笔记(4)
  • 【数据库】四、数据库管理与维护
  • 初识verilog HDL
  • 数学建模_基于支持回归向量机SVR的回归预测之预测新数据+Matlab代码包教会使用,直接替换数据即可
  • PHP与ThinkPHP连接数据库示例
  • 【漫话机器学习系列】043.提前停止训练(Early Stopping)
  • Java(五十)java-IO流-缓冲流(BufferedInputStream和BufferedOutputStream)
  • ubuntu NVIDIA 驱动程序安装指南
  • 手游业务该如何选择服务器?
  • 有心力场的两体问题
  • 第 32 章 - Elasticsearch 的应用场景与技术解决方案
  • 【MySQL】SQL菜鸟教程(一)
  • CES Asia 2025科技创新奖申报火热进行中,23家企业积极参与
  • 《拉依达的嵌入式\驱动面试宝典》—操作系统篇(四)
  • 【EI,Scopus检索 | 往届均已检索见刊】第四届智能系统、通信与计算机网络国际学术会议(ISCCN 2025)
  • 基于php的web系统漏洞攻击靶场设计与实践