深度学习 Pytorch 张量的广播和科学运算

python 复制代码
import torch
import numpy as np

12 张量的广播(Broadcast)特性

张量具备和numpy相同的广播特性,也就是允许不同形状的张量之间进行计算。

12.1 相同形状的张量计算

python 复制代码
t = torch.arange(3)
t + t
# output :
tensor([0, 2, 4])

思考:如果两个list相加,是什么结果?

python 复制代码
li = [0, 1, 2]
li + li
# output :
[0, 1, 2, 0, 1, 2]

12.2 不同形状的张量计算

广播的特性 是在不同形状的张量进行计算时,一个或多个张量通过隐式转化 ,转化成相同形状的两个张量

但并非任意两个不同形状的张量都可以通过广播进行计算,因此我们需要了解广播的基本规则。

标量和任意形状的张量

python 复制代码
# 一维标量与零维标量相加
t = torch.arange(3)
t + torch.tensor(1)
# output :
tensor([1, 2, 3])

# 一维张量与标量相加
t = torch.arange(3)
t + 1
# output :
tensor([1, 2, 3])

# 二维张量与标量相加
t2 = torch.zeros((3,4))
t2 + 1
# output :
tensor([[1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]])

由上面代码可知标量可以看成是零维张量与张量进行计算.

相同维度、不同形状的张量之间计算

首先,我们都知道,张量的形状可以用.shape属性查看

python 复制代码
t2 = torch.zeros((3,4))
t2.shape
# output :
torch.Size([3, 4])

对于返回结果,我们可以看成是一个序列,代表着张量各维度的信息。

当然,对于二维张量,由于我们可以将其视为一个矩阵,因此我们可以说t2是一个拥有三行四列的二维张量,但这种理解方法对于更高维度张量就存在一定的局限,因此我们需要树立另外一种理解方法,那就是:t2是由3个一维张量组成,并且每个一维张量都包含4个元素。

类似的,我们可以创建更高维度张量并对其形状进行解释。

python 复制代码
t3 = torch.zeros(3, 4, 5)
t3
# output :
tensor([[[0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0.]],

        [[0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0.]],

        [[0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0.]]])
python 复制代码
t3.shape
# output :
torch.Size([3, 4, 5])

我们可以将t3解释为:t3是由3个二维张量组成了三维张量,并且每个二维张量都是由4个包含5个元素的一维张量所组成。

由二维拓展至三维,即可拓展至N维。

接下来以t2为例,探讨相同维度、不同形状的张量之间的广播规则。

python 复制代码
t2
# output :
tensor([[0., 0., 0., 0.],
        [0., 0., 0., 0.],
        [0., 0., 0., 0.]])
python 复制代码
t21 = torch.ones(1, 4)
t21
# output :
tensor([[1., 1., 1., 1.]])

t21的形状是(1,4),和t2的形状(3,4)在第一个分量上取值不同,但该分量上t21取值为1,因此可以广播,也就是说可以进行计算。

python 复制代码
# 进行广播计算
t21 + t2
# output :
tensor([[1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]])

t21t2的实际计算过程如下:

注意理解 :此处的广播相对于将t21的形状(1,4)拓展成了t2(3,4),也就是复制了第一行三次,然后二者进行相加。

python 复制代码
t22 = torch.ones(3, 1)
# output :
tensor([[1.],
        [1.],
        [1.]])
python 复制代码
# 进行广播计算
t22 + t2
# output :
tensor([[1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]])

形状为(3,1)的张量和形状为(3,4)的张量相加可以广播,实际计算过程如下:

无法广播的情况:

python 复制代码
t23 = torch.ones(2, 4)
# 此时t2和t23的形状第一个分量维度不同,但二者取值均不为1,因此无法广播
t23 + t2
# output :
RuntimeError: The size of tensor a (2) must match the size of tensor b (3) at non-singleton dimension 0
python 复制代码
t24 = torch.arange(3).reshape(3, 1)
t25 = torch.arange(3).reshape(1, 3)

此时,t24的形状是(3,1),而t25的形状是(1,3),二者的形状在两个分量上均不相同,但都有存在1的情况,因此可以广播。

python 复制代码
t24 + t25
# output :
tensor([[0, 1, 2],
        [1, 2, 3],
        [2, 3, 4]])

二者计算过程如下:

来看看三维张量

python 复制代码
t3 = torch.zeros(3, 4, 5)
t3
# output :
tensor([[[0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0.]],

        [[0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0.]],

        [[0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0.]]])
python 复制代码
t31 = torch.ones(3, 4, 1)
t31
# output :
tensor([[[1.],
         [1.],
         [1.],
         [1.]],

        [[1.],
         [1.],
         [1.],
         [1.]],

        [[1.],
         [1.],
         [1.],
         [1.]]])
python 复制代码
# 进行广播计算
t3 + t31
# output :
tensor([[[1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.]],

        [[1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.]],

        [[1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.]]])

两个张量的形状上有两个分量不同时,只要有不同的分量有一个取值为1,则仍然可以广播。

python 复制代码
t33 = torch.ones(1, 1, 5)
t3 + t33
# output :
tensor([[[1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.]],

        [[1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.]],

        [[1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.]]])

t3t33的计算过程如下

不同维度的张量计算过程中广播

在理解相同维度、不同形状的张量广播之后,对于不同维度的张量之间的广播就会容易很多。

因为对于不同维度的张量,我们首先可以将低维的张量升维,然后根据相同维度不同形状的张量广播规则进行广播。

而低维向量对的升维只需将更高维度方向的形状填充为1即可。

复制代码
t2 = torch.arange(4).reshape(2, 2)
t3 = torch.zeros(3, 2, 2)

t2t3进行广播计算时,首先t2的形状会先转化为(1,2,2)再与t3相加,再与形状为(3,2,2)t3相加,就转化为了相同维度不同形状的两个张量进行相加

python 复制代码
t3 + t2
# output : 等价于t3 + t2.reshape(1, 2, 2)
tensor([[[0., 1.],
         [2., 3.]],

        [[0., 1.],
         [2., 3.]],

        [[0., 1.],
         [2., 3.]]])

总结:广播机制的规则

维度补齐:如果两个张量的维度数量不同,则将较小的那个张量的形状前面补1,直到两个张量的维度数量相同。

维度匹配:如果两个张量在某个维度上的大小不一致,但其中一个张量在该维度上的大小是1,则可以在该维度上进行广播。如果两个张量在任何维度上的大小既不相等也不为1,则无法进行广播。

结果形状:广播后的张量形状是每个维度上大小的最大值。

13 逐点计算(Pointwise Ops)

Pytorch中逐点计算大部分都是可以针对Tensor中每个元素都进行的数学科学运算,与Numpy中针对Array的科学运算类似。
逐点运算主要包括数学基本运算、数值调整运算和数据科学运算三块。

Tensor基本数学运算

函数 描述
torch.add(t1, t2) t1、t2两个张量逐个元素相加,等效于t1+t2
torch.subtract(t1, t2) t1、t2两个张量逐个元素相减,等效于t1-t2
torch.multiply(t1, t2) t1、t2两个张量逐个元素相乘,等效于t1*t2
torch.divide(t1, t2) t1、t2两个张量逐个元素相除,等效于t1/t2

示例

python 复制代码
t1 = torch.tensor([1, 2])
t2 = torch.tensor([3, 4])
python 复制代码
torch.add(t1, t2)
# output : 等效于t1 + t2
tensor([4, 6])

Tensor数值调整函数

函数 描述
torch.abs(t) 返回绝对值
torch.ceil(t) 向上取整
torch.floor(t) 向下取整
torch.round(t) 四舍五入取整
torch.neg(t) 返回相反的数
python 复制代码
t = torch.randn(5)
t
# output :
tensor([ 1.6031, -0.2424,  0.0985, -0.1315,  0.3840])
python 复制代码
torch.round(t)
# output :
tensor([2., -0., 0., -0., 0.])
python 复制代码
torch.abs(t)
# output :
tensor([1.6031, 0.2424, 0.0985, 0.1315, 0.3840])
python 复制代码
torch.neg(t)
# output :
tensor([-1.6031,  0.2424, -0.0985,  0.1315, -0.3840])

注: 虽然此类型函数是数值调整函数,但并不会对原对象进行调整,而是输出新的结果。

python 复制代码
t
# output : t本身并未发生变化
tensor([ 1.6031, -0.2424,  0.0985, -0.1315,  0.3840])

而若要对原对象本身进行修改 ,则可考虑使用方法_()的表达形式,对对象本身进行修改。此时方法就是上述同名函数。

python 复制代码
t.abs_()
# output :
tensor([1.6031, 0.2424, 0.0985, 0.1315, 0.3840])
python 复制代码
t
# output :
tensor([1.6031, 0.2424, 0.0985, 0.1315, 0.3840])
python 复制代码
t.neg_()
# output :
tensor([-1.6031, -0.2424, -0.0985, -0.1315, -0.3840])
python 复制代码
t
# output :
tensor([-1.6031, -0.2424, -0.0985, -0.1315, -0.3840])

除了上述数值调整函数有对应的同名方法外,本节介绍的许多科学计算都有同名方法。

Tensor常用科学计算

数学运算函数 数学公式 描述
torch.exp(t) et 返回以e为底,t中元素为幂的张量
torch.expm(t) et-1 对张量中的所有元素计算exp(t) - 1
torch.exp2(t) 2t 逐个元素计算2的t次方
torch.pow(t, n) tn 返回t的n次幂
torch.sqrt(t) t1/2 返回t的平方根
torch.square(t) t2 返回输入的元素平方
torch.log10(t) log10t 返回以10为底的t的对数
torch.log(t) loget 返回以e为底的t的对数
torch.log2(t) log2t 返回以2为底的t的对数
torch.log1p(t) loge(t + 1) 计算输入张量t中每个元素的自然对数加 1
torch.sin(t) 三角正弦
torch.cos(t) 元素余弦
torch.tan(t) 逐元素计算切线

tensor的大多数科学计算只能作用于tensor对象.

python 复制代码
torch.pow(2, 2)
# output :
TypeError: pow() received an invalid combination of arguments - got (int, int), but expected one of:
 * (Tensor input, Tensor exponent, *, Tensor out = None)
 * (Number self, Tensor exponent, *, Tensor out = None)
 * (Tensor input, Number exponent, *, Tensor out = None)
python 复制代码
torch.pow(torch.tensor(2), 2)
# output :
tensor(4)

理解: 相比于python原生数据类型,张量是一类更加特殊的对象,例如张量可以指定运行在CPUGPU上,因此很多张量的科学计算函数都不允许张量和python原生的数值型对象混合使用。

tensor的大多数科学运算具有一定的静态性

静态性指的是对输入的张量类型有明确的要求,例如部分函数只能输入浮点型张量,而不能输入整型张量。

python 复制代码
t = torch.arange(1, 4)
t.dtype
# output :
torch.int64
python 复制代码
torch.exp(t)
# output :
RuntimeError: exp_vml_cpu not implemented for 'long'

虽然python是动态编译的编译语言,但在pytorch中,由于会涉及GPU计算,因此很多时候元素类型不会在实际执行函数计算时进行调整。此处的科学运算大多数都要求对象类型是浮点型,我们需要提前进行类型转化。

python 复制代码
t1 = t.float()
torch.exp(t1)
# output :
tensor([ 2.7183,  7.3891, 20.0855])
python 复制代码
torch.expm1(t1)
# output :
tensor([ 1.7183,  6.3891, 19.0855])

此处返回结果是et - 1,在数值科学计算中,expm1log1p函数是一对对应的函数关系,后面在介绍log1p的时候会讲解这对函数的实际作用。

排序运算:sort

sort函数将同时返回排序结果和对应索引值的排列

python 复制代码
t = torch.tensor([1.0, 3.0, 2.0])
python 复制代码
# 升序排列
torch.sort(t)
# output :
torch.return_types.sort(
values=tensor([1., 2., 3.]),
indices=tensor([0, 2, 1]))
python 复制代码
# 降序排列
torch.sort(t, descending = True)
# output :
torch.return_types.sort(
values=tensor([3., 2., 1.]),
indices=tensor([1, 2, 0]))

14 规约运算

规约运算指的是针对某张量进行某种总结,最后得出一个具体总结值的函数。

此类函数主要包含了数据科学领域内的诸多统计分析函数,如均值、极值、方差、中位差函数等。

Tensor统计分析函数

函数 描述
torch.mean(t) 返回张量均值
torch.var(t) 返回张量方差
torch.std(t) 返回张量标准差
torch.var_mean(t) 返回张量方差和均值
torch.max(t) 返回张量最大值
torch.argmax(t) 返回张量最大值索引
torch.min(t) 返回张量最小值
torch.argmin(t) 返回张量最小值索引
torch.median(t) 返回张量中位数
torch.sum(t) 返回张量求和结果
torch.logsumexp(t) 返回张量各元素求和结果,适用于数据量较小的情况
torch.prod(t) 返回张量累乘结果
torch.dist(t1, t2) 计算两个张量的闵式距离,可使用不同范式
torch.topk(t) 返回t中最大的k个值对应的指标
python 复制代码
t = torch.arange(10).float()
# 计算均值
torch.mean(t)
# output :
tensor(4.5000)

# 计算标准差、均值
torch.std_mean(t)
# output :
(tensor(3.0277), tensor(4.5000))

dist计算距离

复制代码
t1 = torch.tensor([1.0, 2])
t2 = torch.tensor([3.0, 4])

dist函数可计算闵式距离(闵可夫斯基距离),通过不同的p值,可以计算多种类型的距离,如欧式距离、街道距离等。闵可夫斯基距离公式如下:
D ( x , y ) = ( ∑ u = 1 n ∣ x u − y u ∣ p ) 1 / p D(x, y) = \left( \sum_{u=1}^{n} |x_u - y_u|^p \right)^{1/p} D(x,y)=(u=1∑n∣xu−yu∣p)1/p

p取值为2时,计算欧式距离

python 复制代码
torch.dist(t1, t2, 2)	# 相当于torch.sqrt(torch.tensor(8.0))
# output :
tensor(2.8284)

p取值为1时,计算街道距离

python 复制代码
torch.dist(t1, t2, 1)
# output :
tensor(4.)

规约运算的维度

由于规约运算是一个序列返回一个结果,因此若是针对高维张量,则可指定某维度进行计算。

python 复制代码
# 创建一个3*3的二维张量
t2 = torch.arange(12).float().reshape(3, 4)
t2
# output :
tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.]])
python 复制代码
# 按照第一个维度求和(每次计算三个)/按列求和
torch.sum(t2, dim = 0)
# output :
tensor([12., 15., 18., 21.])
python 复制代码
# 按照第二个维度求和(每次计算四个)/按行求和
torch.sum(t2, dim = 1)
# output :
tensor([ 6., 22., 38.])
python 复制代码
# 创建一个2*3*4的三维张量
t3 = torch.arange(24).float().reshape(2, 3, 4)
t3
# output :
tensor([[[ 0.,  1.,  2.,  3.],
         [ 4.,  5.,  6.,  7.],
         [ 8.,  9., 10., 11.]],

        [[12., 13., 14., 15.],
         [16., 17., 18., 19.],
         [20., 21., 22., 23.]]])
python 复制代码
# 按照第一个维度求和(每次计算两个)
torch.sum(t3, dim = 0)
# output :
tensor([[12., 14., 16., 18.],
        [20., 22., 24., 26.],
        [28., 30., 32., 34.]])
python 复制代码
# 按照第二个维度求和(每次计算三个)
torch.sum(t3, dim = 1)
# output :
tensor([[12., 15., 18., 21.],
        [48., 51., 54., 57.]])
python 复制代码
# 按照第三个维度求和(每次计算四个)
torch.sum(t3, dim = 2)
# output :
tensor([[ 6., 22., 38.],
        [54., 70., 86.]])

二维张量的排序

和上述过程类似,在进行排序过程中,二维张量也可以按行或者按列进行排序。

python 复制代码
t22 = torch.randn(3, 4)
t22
# output :
tensor([[ 1.0475,  1.7860, -1.0797, -0.8695],
        [ 0.2919,  0.8268, -0.2798, -1.0550],
        [ 0.4554,  0.0364, -0.2027,  1.0527]])
python 复制代码
# 默认情况下,是按行进行升序排序的
torch.sort(t22)
# output :
torch.return_types.sort(
values=tensor([[-1.0797, -0.8695,  1.0475,  1.7860],
        [-1.0550, -0.2798,  0.2919,  0.8268],
        [-0.2027,  0.0364,  0.4554,  1.0527]]),
indices=tensor([[2, 3, 0, 1],
        [3, 2, 0, 1],
        [2, 1, 0, 3]]))
python 复制代码
# 修改dim和descending参数,使得按列进行降序排序
torch.sort(t22, dim = 1, descending = True)
# output :
torch.return_types.sort(
values=tensor([[ 1.7860,  1.0475, -0.8695, -1.0797],
        [ 0.8268,  0.2919, -0.2798, -1.0550],
        [ 1.0527,  0.4554,  0.0364, -0.2027]]),
indices=tensor([[1, 0, 3, 2],
        [1, 0, 2, 3],
        [3, 0, 1, 2]]))

15 比较运算

比较运算常用于不同张量之间的逻辑运算,最终返回逻辑运算结果(逻辑类型张量)

Tensor比较运算函数

函数 描述
torch.eq(t1, t2) 比较t1、t2各元素是否相等,等效 ==
torch.equal(t1, t2) 判断两个张量是否是相同的张量
torch.gt(t1, t2) 比较t1各元素是否大于t2各元素,等效 >
torch.lt(t1, t2) 比较t1各元素是否小于t2各元素,等效 <
torch.ge(t1, t2) 比较t1各元素是否大于或等于t2各元素,等效 >=
torch.le(t1, t2) 比较t1各元素是否小于等于t2各元素,等效 <=
torch.ne(t1,t2) 比较t1、t2各元素是否不相同,等效 !=
python 复制代码
t1 = torch.tensor([1.0, 3, 4])
t2 = torch.tensor([1.0, 2, 5])
python 复制代码
t1 == t2
# output :	等价于torch.eq(t1, t2)
tensor([ True, False, False])
python 复制代码
torch.equal(t1, t2)
# output :
False
python 复制代码
t1 > t2
# output :
tensor([False,  True, False])
python 复制代码
t1 >= t2
# output :
tensor([ True,  True, False])
相关推荐
不吃辣4901 小时前
vibe coding | 如何做一个 AI 音乐生成工具?
java·人工智能·后端·ai·ai编程
文人sec1 小时前
从接口压测到全链路质量保障:AI智能客服系统的软件测试实践
人工智能
声讯电子1 小时前
实测A-59F音频模组:降噪稳、不啸叫、通话清晰
人工智能·语音识别·ai降噪·usb接口·回音消除
神奇霸王龙1 小时前
2026旗舰六阶段MCP流水线实战指南
人工智能·ai·ai作画·prompt·aigc·mcp
山河不见老1 小时前
【Cursor 、Qoder安装问题】Cursor 、Qoder安装卡在“正在准备安装”问题排查及解决
人工智能·windows·编辑器
新知图书1 小时前
2.3 开发工作流
人工智能·agent·ai agent·智能体
tuanxiang1 小时前
用免费 AI 改写工具处理批量文案的踩坑记录,附完整降噪脚本
人工智能
小邓的技术笔记1 小时前
DeepSeek 大模型落地应用与场景实战指南
大数据·人工智能
石榴1 小时前
从 Codex + Obsidian 到本地 RAG:我的私人知识库实践
人工智能
QZSJTR1 小时前
技术解析|从SEO到GEO:生成式搜索时代实体企业数字化适配方案
人工智能