第三课:张量的广播和科学运算

  • 数学运算的分类
    PyToch总共为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.其他运算(Other Ops):其他未被归类的数
import torch
import numpy as np

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

1.相同形状的张量计算

t1 = torch.arange(3)
t1
#tensor([0, 1, 2])
 
t1 + t1                      # 对应位置元素依次相加
#tensor([0, 2, 4])

2.不同形状的张量计算

广播的特性是在不同形状的张量进行计算时,一个或多个张量通过隐式转化,转化成相同形状的两个张量,从而完成计算的特性。但并非任何两个不同形状的张量都可以通过广播特性进行计算,因此,我们需要了解广播的基本规则及其核心依据。

2.1 标量和任意形状的张量

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

t1 + 1                                     # 1是标量,可以看成是零维
#tensor([1, 2, 3])
 
# 二维加零维
t1 + torch.tensor(1)
#tensor([1, 2, 3])
 
t2 = torch.zeros((3, 4))
t2
#tensor([[0., 0., 0., 0.],
#        [0., 0., 0., 0.],
#        [0., 0., 0., 0.]])
 
t2 + 1
#tensor([[1., 1., 1., 1.],
#        [1., 1., 1., 1.],
#        [1., 1., 1., 1.]])

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

  • (必须有一个维度为1,并且高纬度为1或者高纬度相同低纬度为1,才可以扩展)

对于不同形状的张量计算,我们首先需要回顾张量的形状属性,并深化对其的理解。

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

t2.shape
#torch.Size([3, 4])

t2是由3个一维张量组成,并且该一维张量、每个都包含四个元素。类似的,我们可以创建更高维度张量并对其形状进行解释。

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.]]])
 
t3.shape
#torch.Size([3, 4, 5])

t2
#tensor([[0., 0., 0., 0.],
#        [0., 0., 0., 0.],
#        [0., 0., 0., 0.]])
 
t2.shape
#torch.Size([3, 4])
 
t21 = torch.ones(1, 4)
t21
#tensor([[1., 1., 1., 1.]])

t21 + t2
#tensor([[1., 1., 1., 1.],
#        [1., 1., 1., 1.],
#        [1., 1., 1., 1.]])

我们可以将t3解释为:t3是3个二维张量组成了三维张量,并且这些每个二维张量,都是由四个包含五个元素的一维张量所组成。由二维拓展至三维,即可拓展至N维。

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

而t21和t2的实际计算过程如下:
在这里插入图片描述
注意理解:此处的广播相当于将t21的形状(1, 4)拓展成了t2的(3, 4),也就是复制了第一行三次,然后二者进行相加。当然,也可以理解成t21的第一行和t2的三行分别进行了相加。

t22 = torch.ones(3, 1)
t22
#tensor([[1.],
#        [1.],
#        [1.]])
 
t2
#tensor([[0., 0., 0., 0.],
#        [0., 0., 0., 0.],
#        [0., 0., 0., 0.]])
 
t2.shape
#torch.Size([3, 4])
 
t22 + t2              # 形状为(3,1)的张量和形状为(3,4)的张量相加,可以广播
#tensor([[1., 1., 1., 1.],
#        [1., 1., 1., 1.],
#        [1., 1., 1., 1.]])

在这里插入图片描述

t23 = torch.ones(2, 4)
t23.shape
#torch.Size([2, 4])
 
t2.shape
#torch.Size([3, 4])
 
'''注:此时t2和t23的形状第一个分量维度不同,但二者取值均不为1,因此无法广播'''
t2 + t23
---------------------------------------------------------------------------
#RuntimeError                              Traceback (most recent call last)
#<ipython-input-21-994547ec6516> in <module>
#----> 1 t2 + t23
#
#RuntimeError: The size of tensor a (3) must match the size of tensor b (2) at non-#singleton dimension 0
t24 = torch.arange(3).reshape(3, 1)
t24
#tensor([[0],
#        [1],
#        [2]])
 
t25 = torch.arange(3).reshape(1, 3)
t25
#tensor([[0, 1, 2]])
 
'''此时,t24的形状是(3, 1),而t25的形状是(1, 3),二者的形状在两个份量上均不相同,但都有存在1的情况,因此也是可以广播的'''
t24 + t25
#tensor([[0, 1, 2],
#        [1, 2, 3],
#        [2, 3, 4]])

在这里插入图片描述
三维张量的广播

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

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

在理解相同维度、不同形状的张量广播之后,对于不同维度的张量之间的广播其实就会容易很多,因为对于不同维度的张量,我们首先可以将低维的张量升维,然后依据相同维度不同形状的张量广播规则进行广播。而低维向量的升维也非常简单,只需将更高维度方向的形状填充为1即可,例如:

# 二维张量转化为三维张量
t2 = torch.arange(4).reshape(2, 2)
t2
#tensor([[0, 1],
#        [2, 3]])
 
# 转化为三维张量
t2.reshape(1, 2, 2)
#tensor([[[0, 1],
#         [2, 3]]])
'''转化之后表示只包含一个二维张量的三维张量,且二维张量就是t2'''
 
# 转化为四维张量
t2.reshape(1, 1, 2, 2)
#tensor([[[[0, 1],
#          [2, 3]]]])
'''转化之后表示只包含一个三维张量的四维张量,且三维张量只包含一个二维张量,且二维张量就是t2'''
 
t3 = torch.zeros(3, 2, 2)
'''t3和t2的计算过程,就相当于形状为(1,2,2)和(3,2,2)的两个张量进行计算'''
 
t3 + t2
#tensor([[[0., 1.],
#         [2., 3.]],
#
#        [[0., 1.],
#         [2., 3.]],
#
#        [[0., 1.],
#         [2., 3.]]])
 
t3 + t2.reshape(1, 2, 2)
#tensor([[[0., 1.],
#         [2., 3.]],
#
#        [[0., 1.],
#         [2., 3.]],
#
#        [[0., 1.],
#         [2., 3.]]])

二、逐点运算(Pointwise Ops)

在这里插入图片描述

t1 = torch.tensor([1, 2])
t1
#tensor([1, 2])
 
t2 = torch.tensor([3, 4])
t2
#tensor([3, 4])
 
torch.add(t1, t2)
#tensor([4, 6])
 
t1 + t2
#tensor([4, 6])
 
t1 / t2
#tensor([0.3333, 0.5000])

在这里插入图片描述

t = torch.randn(5)
t
#tensor([ 1.1971,  1.7523,  1.5678, -2.2227, -0.3082])
 
torch.round(t)
#tensor([ 1.,  2.,  2., -2., -0.])
 
torch.abs(t)
#tensor([1.1971, 1.7523, 1.5678, 2.2227, 0.3082])
 
torch.neg(t)
#tensor([-1.1971, -1.7523, -1.5678,  2.2227,  0.3082])
 
'''注:虽然此类型函数是数值调整函数,但并不会对原对象进行调整,而是输出新的结果。'''
t                # t本身并未发生变化
#tensor([ 1.1971,  1.7523,  1.5678, -2.2227, -0.3082])

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

t.abs_()
#tensor([1.1971, 1.7523, 1.5678, 2.2227, 0.3082])
 
t
#tensor([1.1971, 1.7523, 1.5678, 2.2227, 0.3082])
 
t.neg_()
#tensor([-1.1971, -1.7523, -1.5678, -2.2227, -0.3082])
 
t
#tensor([-1.1971, -1.7523, -1.5678, -2.2227, -0.3082])
 
'''除了上述数值调整函数有对应的同名方法外,本节介绍的许多科学计算都有同名方法。'''
t.exp_()
#tensor([0.3021, 0.1734, 0.2085, 0.1083, 0.7348])
 
t
#tensor([0.3021, 0.1734, 0.2085, 0.1083, 0.7348])

在这里插入图片描述

  • tensor的大多数科学计算只能作用于tensor对象
# 计算2的2次方
torch.pow(2, 2)
#---------------------------------------------------------------------------
#TypeError                                 Traceback (most recent call last)
#<ipython-input-125-89fb35bb773f> in <module>
#      1 # 计算2的2次方
#----> 2 torch.pow(2, 2)
#
#TypeError: pow() received an invalid combination of arguments - got (int, int), #but expected one of:
# * (Tensor input, Tensor exponent, *, Tensor out)
# * (Number self, Tensor exponent, *, Tensor out)
# * (Tensor input, Number exponent, *, Tensor out)
 
torch.pow(torch.tensor(2), 2)
#tensor(4)
  • tensor的大多数科学运算具有一定的静态性
    所谓静态性,指的是对输入的张量类型有明确的要求,例如部分函数只能输入浮点型张量,而不能输入整型张量。
t = torch.arange(1, 4)
t.dtype
#torch.int64
 
torch.exp(t)
#tensor([ 2.7183,  7.3891, 20.0855])

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

排序运算:sort:(在PyTorch中,sort排序函数将同时返回排序结果和对应的索引值的排列。)

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

三、规约运算

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

# 生成浮点型张量
t = torch.arange(10).float()
t
#tensor([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
 
# 计算均值
torch.mean(t)
#tensor(4.5000)
 
# 计算标准差、均值
torch.std_mean(t)
#(tensor(3.0277), tensor(4.5000))
 
# 计算最大值
torch.max(t)
#tensor(9.)
 
# 返回最大值的索引
torch.argmax(t)
#tensor(9)
 
# 计算中位数
torch.median(t)
#tensor(4.)
 
# 求和
torch.sum(t)
#tensor(45.)
 
# 求积
torch.prod(t)
#tensor(0.)
 
torch.prod(torch.tensor([1, 2, 3]))
#tensor(6)

dist计算距离

dist函数可计算闵式距离(闵可夫斯基距离),通过输入不同的p值,可以计算多种类型的距离,如欧式距离、街道距离等。闵可夫斯基距离公式如下:
在这里插入图片描述
p取值为2时,计算欧式距离

t1 = torch.tensor([1.0, 2])
t1
#tensor([1., 2.])
 
t2 = torch.tensor([3.0, 4])
t2
#tensor([3., 4.])
 
t
#tensor([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
 
torch.dist(t1, t2, 2)
#tensor(2.8284)
 
torch.sqrt(torch.tensor(8.0))
#tensor(2.8284)
 
'''p取值为1时,计算街道距离'''
torch.dist(t1, t2, 1)
#tensor(4.)

规约运算的维度(理解:dim参数和shape返回结果一一对应)

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

# 创建一个3*3的二维张量
t2 = torch.arange(12).float().reshape(3, 4)
t2
#tensor([[ 0.,  1.,  2.,  3.],
#        [ 4.,  5.,  6.,  7.],
#        [ 8.,  9., 10., 11.]])
 
t2.shape
#torch.Size([3, 4])
 
# 按照第一个维度求和(每次计算三个)、按列求和
torch.sum(t2, dim = 0)
#tensor([12., 15., 18., 21.])
 
# 按照第二个维度求和(每次计算四个)、按行求和
torch.sum(t2, dim = 1)
#tensor([ 6., 22., 38.])
 
# 创建一个2*3*4的三维张量
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.]]])
 
t3.shape
#torch.Size([2, 3, 4])
 
torch.sum(t3, dim = 0)
#tensor([[12., 14., 16., 18.],
#        [20., 22., 24., 26.],
#        [28., 30., 32., 34.]])
#.       0+12 1+13...
 
torch.sum(t3, dim = 1)
#tensor([[12., 15., 18., 21.],
#        [48., 51., 54., 57.]])
 
torch.sum(t3, dim = 2)
#tensor([[ 6., 22., 38.],
#        [54., 70., 86.]])

二维张量的排序

t22 = torch.randn(3, 4)             # 创建二维随机数张量
t22
#tensor([[-0.7781, -2.3530,  1.6711, -0.2056],
#        [-0.2429,  0.6134,  0.8724,  0.5893],
#        [ 1.2448,  0.3643, -0.2511,  1.4750]])
 
# 默认情况下,是按照行进行升序排序
torch.sort(t22)
#torch.return_types.sort(
#values=tensor([[-2.3530, -0.7781, -0.2056,  1.6711],
#        [-0.2429,  0.5893,  0.6134,  0.8724],
#        [-0.2511,  0.3643,  1.2448,  1.4750]]),
#indices=tensor([[1, 0, 3, 2],
#        [0, 3, 1, 2],
#        [2, 1, 0, 3]]))
 
# 修改dim和descending参数,使得按列进行降序排序
torch.sort(t22, dim = 0, descending=True)
#torch.return_types.sort(
#values=tensor([[ 1.2448,  0.6134,  1.6711,  1.4750],
#        [-0.2429,  0.3643,  0.8724,  0.5893],
#        [-0.7781, -2.3530, -0.2511, -0.2056]]),
#indices=tensor([[2, 1, 0, 2],
#        [1, 2, 1, 1],
#        [0, 0, 2, 0]]))

四、比较运算

比较运算是一类较为简单的运算类型,和Python原生的布尔运算类似,常用于不同张量之间的逻辑运算,最终返回逻辑运算结果(逻辑类型张量)。基本比较运算函数如下所示:
在这里插入图片描述

t1 = torch.tensor([1.0, 3, 4])
t2 = torch.tensor([1.0, 2, 5])
 
t1 == t2
#tensor([ True, False, False])
 
torch.equal(t1, t2)          # 判断t1、t2是否是相同的张量
#False
 
torch.eq(t1, t2)
#tensor([ True, False, False])
 
t1 > t2
#tensor([False,  True, False])
 
t1 >= t2
#tensor([ True,  True, False])
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值