深度学习 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) e^t^ 返回以e为底,t中元素为幂的张量
torch.expm(t) e^t^-1 对张量中的所有元素计算exp(t) - 1
torch.exp2(t) 2^t^ 逐个元素计算2的t次方
torch.pow(t, n) t^n^ 返回t的n次幂
torch.sqrt(t) t^1/2^ 返回t的平方根
torch.square(t) t^2^ 返回输入的元素平方
torch.log10(t) log~10~t 返回以10为底的t的对数
torch.log(t) log~e~t 返回以e为底的t的对数
torch.log2(t) log~2~t 返回以2为底的t的对数
torch.log1p(t) log~e~(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])

此处返回结果是e^t^ - 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])
相关推荐
学习嵌入式的小羊~1 小时前
RV1126+FFMPEG推流项目(7)AI音频模块编码流程
人工智能·ffmpeg·音视频
程序猿阿伟2 小时前
《在ArkTS中实现模型的可视化调试和监控:探索与实践》
人工智能
算家云2 小时前
OpenAI推出首个AI Agent!日常事项自动化处理!
人工智能·自动化·openai·算家云·tasks
白白糖6 小时前
深度学习 Pytorch 张量的索引、分片、合并以及维度调整
人工智能·pytorch·python·深度学习
白白糖6 小时前
深度学习 Pytorch 张量(Tensor)的创建和常用方法
人工智能·pytorch·python·深度学习
Noos_7 小时前
AI面试官
人工智能
lovelin+v175030409667 小时前
从零到一:构建高效稳定的电商数据API接口
大数据·网络·人工智能·爬虫·python
深度学习实战训练营7 小时前
基于机器学习的电信用户流失预测与数据分析可视化
人工智能·机器学习·数据分析
小白狮ww8 小时前
LTX-Video 高效视频生成模型,一键处理图片&文字
图像处理·人工智能·深度学习·机器学习·音视频·视频生成·ai 视频
⁢Easonhe8 小时前
《基于卷积神经网络的星图弱小目标检测》论文精读
人工智能·目标检测·cnn