张量的广播和科学计算
云间之龙

张量的广播和科学运算

作为pytorch中执行深度学习的基本数据类型,张量也拥有非常多的数学与运算函数和方法,以及对应的一系列计算规则。在pytorch中,能够作用于tensor的运算被统一称作为算子

  • 数学运算的分类
  1. 逐点运算(pointwise ops)对tensor中每个元素执行相同的运算操作
  2. 规约运算(reduction ops)对某一张两进行操作得出某种总结值
  3. 比较运算(comparison ops)对多个张量进行比较运算的相关方法
  4. 谱运算(spectral ops)涉及信号处理傅里叶变化的操作
  5. BLAS和LAPACK运算 基础线性代数程序集(Basic Linear Algeria Subprograms)和线性代数包(Linear Algeria Package)中定义的、主要用于现行代数科学计算的函数和方法
  6. 其他运算 其他未被归类的数学运算

一、张量的广播(broadcast)特性

允许不同形状的张量之间进行计算

1.相同形状的张量计算

1
2
import torch
import numpy as np
1
2
t1=torch.arange(3)
t1
tensor([0, 1, 2])
1
t1+t1
tensor([0, 2, 4])
1
t1*t1
tensor([0, 1, 4])
1
t1**t1
tensor([1, 1, 4])
1
t1/t1
tensor([nan, 1., 1.])
1
[0,1,2]+[0,1,2]
[0, 1, 2, 0, 1, 2]

2. 不同形状的张量计算

广播的特性是在不同形状的张量进行计算时,一个或多个张量通过隐式转化为相同形状的两个张量,但是,并非任何两个不同形状的张量都可以通过广播特性进行计算

2.1 标量和任意形状的张量

标量可以和任意形状的张量进行计算,计算过程就是标量和张量的每一个元素进行计算

1
t1+1
tensor([1, 2, 3])
1
t1*2
tensor([0, 2, 4])
1
(t1+1)**2
tensor([1, 4, 9])
2.2 相同维度、不同形状的计算
1
2
t2=torch.zeros(3,4)
t2.shape
torch.Size([3, 4])
1
2
t3=torch.zeros(3,4,5)
t3
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.]]])
1
2
t21=torch.ones(1,4)
t21
tensor([[1., 1., 1., 1.]])
1
t2
tensor([[0., 0., 0., 0.],
        [0., 0., 0., 0.],
        [0., 0., 0., 0.]])
1
t21+t2
tensor([[1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]])

此处的广播相当于将t21的形状(1,4)拓展成了t2(3,4)
即复制了第一行三次,然后二者相加
也可理解为 t21的第一行和t2的三行分别进行相加

1
t2
tensor([[0., 0., 0., 0.],
        [0., 0., 0., 0.],
        [0., 0., 0., 0.]])
1
2
t22=torch.arange(9).reshape(3,3)
t22
tensor([[0, 1, 2],
        [3, 4, 5],
        [6, 7, 8]])
1
t22.size()
torch.Size([3, 3])
1
t2.size()
torch.Size([3, 4])

广播规则:

  1. 两个张量在一个维度一致,另一个维度上其中一个张量为1
1
2
t3=torch.ones(3,1)
t3.size()
torch.Size([3, 1])
1
t2+t3
tensor([[1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]])
  1. t3的形状为(3,1),t31的形状为(1,3),二者的形状在两个分量上均不同,但都有1存在,因此也可以广播
1
2
t31=torch.ones(1,3)
t31
tensor([[1., 1., 1.]])
1
t31+t3
tensor([[2., 2., 2.],
        [2., 2., 2.],
        [2., 2., 2.]])

三维张量的广播

1
2
t3=torch.zeros(3,4,5)
t3
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.]]])
1
2
t31=torch.ones(3,4,1)
t31
tensor([[[1.],
         [1.],
         [1.],
         [1.]],

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

        [[1.],
         [1.],
         [1.],
         [1.]]])
1
t3+t31
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
2
t32=torch.ones(3,1,5)
t32
tensor([[[1., 1., 1., 1., 1.]],

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

        [[1., 1., 1., 1., 1.]]])
1
t32+t3
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
t32+t31 # 一维度一致,另两个维度不同,但都有 1
tensor([[[2., 2., 2., 2., 2.],
         [2., 2., 2., 2., 2.],
         [2., 2., 2., 2., 2.],
         [2., 2., 2., 2., 2.]],

        [[2., 2., 2., 2., 2.],
         [2., 2., 2., 2., 2.],
         [2., 2., 2., 2., 2.],
         [2., 2., 2., 2., 2.]],

        [[2., 2., 2., 2., 2.],
         [2., 2., 2., 2., 2.],
         [2., 2., 2., 2., 2.],
         [2., 2., 2., 2., 2.]]])
2.3不同维度的张量计算过程中的广播

低维张量升维,只需要将更高维度上填充1即可

1
2
t2=torch.arange(4).reshape(2,2)
t2
tensor([[0, 1],
        [2, 3]])
1
t2.reshape(1,2,2)
tensor([[[0, 1],
         [2, 3]]])
1
t2.reshape(1,1,2,2)
tensor([[[[0, 1],
          [2, 3]]]])
1
2
t3=torch.zeros(3,2,2)
t3+t2
tensor([[[0., 1.],
         [2., 3.]],

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

        [[0., 1.],
         [2., 3.]]])
1
2
t2=torch.ones(2,1)
t2
tensor([[1.],
        [1.]])
1
2
t3=torch.zeros(3,2,3)
t3+t2
tensor([[[1., 1., 1.],
         [1., 1., 1.]],

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

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

二、逐点运算

tensor基本数学运算

函数 描述
torch.add(t1,t2) t1+t2
torch.subtract(t1,t2) t1-t2
torch.multiply(t1,t2) t1*t2
torch.divide(t1,t2) t1/t2
1
2
t1=torch.tensor([1,2])
t1
tensor([1, 2])
1
2
t2=torch.tensor([3,4])
t2
tensor([3, 4])
1
torch.add(t1,t2)
tensor([4, 6])
1
torch.multiply(t1,t2)
tensor([3, 8])

tensor数值调整函数

函数 描述
torch.abs() 返回绝对值
torch.ceil() 向上取整
torch.floor() 向下取整
torch.round() 四舍五入
torch.neg() 取反
1
2
t=torch.randn(5)
t
tensor([-0.4222, -2.3513, -2.0652, -0.3838, -0.2116])
1
torch.round(t)
tensor([-0., -2., -2., -0., -0.])
1
t.abs_()
tensor([0.4222, 2.3513, 2.0652, 0.3838, 0.2116])
1
t.neg_()
tensor([-0.4222, -2.3513, -2.0652, -0.3838, -0.2116])
数学运算函数 数学公式 描述
幂运算
torch.exp(t) $ y_{i} = e^{x_{i}} $ 返回以e为底、t中元素为幂的张量
torch.expm1(t) $ y_{i} = e^{x_{i}} $ - 1 对张量中的所有元素计算exp(x) - 1
torch.exp2(t) $ y_{i} = 2^{x_{i}} $ 逐个元素计算2的t次方。
torch.pow(t,n) $\text{out}_i = x_i ^ \text{exponent} $ 返回t的n次幂
torch.sqrt(t) $ \text{out}{i} = \sqrt{\text{input}{i}} $ 返回t的平方根
torch.square(t) $ \text{out}_i = x_i ^ \text{2} $ 返回输入的元素平方。
对数运算
torch.log10(t) $ y_{i} = \log_{10} (x_{i}) $ 返回以10为底的t的对数
torch.log(t) $ y_{i} = \log_{e} (x_{i}) $ 返回以e为底的t的对数
torch.log2(t) $ y_{i} = \log_{2} (x_{i}) $ 返回以2为底的t的对数
torch.log1p(t) $ y_i = \log_{e} (x_i $ + 1) 返回一个加自然对数的输入数组。
1
torch.pow(torch.tensor(2),2)
tensor(4)
  • tensor的大多数科学运算具有一定的静态性
    静态性就是对输入的张量类型有明确的要求,例如部分函数只能输入浮点型张量,而不能输入整形张量
1
2
t=torch.arange(1,4)
t.dtype
torch.int64
1
t
tensor([1, 2, 3])
1
torch.exp(t)
tensor([ 2.7183,  7.3891, 20.0855])
1
2
t1=t.float()
t1
tensor([1., 2., 3.])
1
torch.exp(t1)
tensor([ 2.7183,  7.3891, 20.0855])
1
torch.expm1(t1)
tensor([ 1.7183,  6.3891, 19.0855])
1
torch.pow(t,2)
tensor([1, 4, 9])
1
torch.pow(t,0.5)
tensor([1.0000, 1.4142, 1.7321])
1
torch.exp(torch.log(t1))
tensor([1., 2., 3.])
  • 排序运算:sort
    在pytorch中,sort函数将同时返回排序结果和对应的索引值的排列
1
2
t=torch.tensor([1,3,2,4])
t
tensor([1, 3, 2, 4])
1
torch.sort(t)
torch.return_types.sort(
values=tensor([1, 2, 3, 4]),
indices=tensor([0, 2, 1, 3]))
1
torch.sort(t,descending=True)
torch.return_types.sort(
values=tensor([4, 3, 2, 1]),
indices=tensor([3, 1, 2, 0]))

三、规约运算

规约运算指针对某张量进行某种总结,最后得出一个具体总结值的函数。主要包含数据科学领域内的诸多统计分析函数,如均值、极值、方差、中位数函数等等。

函数 描述
torch.mean(t) 返回张量均值
torch.var(t) 返回张量方差
torch.std(t) 返回张量标准差
torch.var_mean(t) 返回张量方差和均值
torch.std_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个值对应的指标
1
2
t=torch.arange(10).float()
t
tensor([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
1
torch.std_mean(t)
(tensor(3.0277), tensor(4.5000))
1
torch.prod(torch.tensor([1,2,3,4]))
tensor(24)
  • dist计算距离
    dist函数可计算闵式距离(闵可夫斯基距离),通过输入不同的p值,可以计算多种类型的距离,如欧式距离,街道距离等。闵可夫斯基距离公式表示如下:

$$D(x,y)=(\sum_{u=1}^{n}{|x_{u}-y_{u}|}^{p} )^{1/p}$$

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

1
2
t1=torch.tensor([1.0,2])
t2=torch.tensor([3.0,4])
1
torch.dist(t1,t2,2)
tensor(2.8284)
1
torch.sqrt(torch.tensor(8.0))
tensor(2.8284)
1
torch.dist(t1,t2,1)
tensor(4.)
  • 规约运算的维度
    由于规约运算是一个序列返回一个结果,因此若是针对高维张良,则可能指定某维度进行计算。
1
2
t2=torch.arange(12).float().reshape(3,4)
t2
tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.]])
1
t2.shape
torch.Size([3, 4])
1
torch.sum(t2,dim=0)
tensor([12., 15., 18., 21.])
1
torch.sum(t2,dim=1)
tensor([ 6., 22., 38.])
1
2
t3=torch.arange(24).float().reshape(2,3,4)
t3
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.]]])
1
t3.shape
torch.Size([2, 3, 4])
1
torch.sum(t3,dim=0)
tensor([[12., 14., 16., 18.],
        [20., 22., 24., 26.],
        [28., 30., 32., 34.]])
1
torch.sum(t3,dim=1)
tensor([[12., 15., 18., 21.],
        [48., 51., 54., 57.]])
1
torch.sum(t3,dim=2)
tensor([[ 6., 22., 38.],
        [54., 70., 86.]])
  • 二维张量的排序
1
2
t22=torch.randn(3,4)
t22
tensor([[-0.1265,  0.2807,  0.7180,  0.8772],
        [ 1.9022,  0.3509, -0.4520, -2.3835],
        [-0.5028, -0.8871, -2.2325,  0.7211]])
1
torch.sort(t22) # 默认情况下,按照行进行升序排序
torch.return_types.sort(
values=tensor([[-0.1265,  0.2807,  0.7180,  0.8772],
        [-2.3835, -0.4520,  0.3509,  1.9022],
        [-2.2325, -0.8871, -0.5028,  0.7211]]),
indices=tensor([[0, 1, 2, 3],
        [3, 2, 1, 0],
        [2, 1, 0, 3]]))
1
torch.sort(t22,dim=1,descending=True) # 按列降序排序
torch.return_types.sort(
values=tensor([[ 0.8772,  0.7180,  0.2807, -0.1265],
        [ 1.9022,  0.3509, -0.4520, -2.3835],
        [ 0.7211, -0.5028, -0.8871, -2.2325]]),
indices=tensor([[3, 2, 1, 0],
        [0, 1, 2, 3],
        [3, 0, 1, 2]]))

四、比较运算

比较运算是一类较为简单的运算类型,和python原生的布尔运算类似,常用于不同张量之间的逻辑运算,最终返回逻辑运算结果(逻辑类型张量)基本比较运算函数如下所示:

函数 描述
torch.eq(t1,t2) 等效==
torch.equal(t1,t2) 判断两个张量是否是相同的张量
torch.gt(t1,t2) 等效>
torch.lt(t1,t2) 等效于<
torch.ge(t1,t2) 等效>=
torch.le(t1,t2) 等效<=
torch.ne(t1,t2) 等效!=
1
2
3
t1=torch.tensor([1.0,3,4])
t2=torch.tensor([1.0,2,5])
t1==t2
tensor([ True, False, False])
1
torch.equal(t1,t2)
False
1
torch.eq(t1,t2)
tensor([ True, False, False])
1
torch.lt(t1,t2)
tensor([False, False,  True])