Lesson 3.张量的广播和科学运算

131 篇文章 1 订阅
110 篇文章 12 订阅

Lesson 3.张量的广播和科学运算

转载自:https://www.bilibili.com/video/BV14X4y1A7KT?p=3

  • 数学运算与算子

  作为PyTorch中执行深度学习的基本数据类型,张量(Tensor)也拥有非常多的数学运算函数和方法,以及对应的一系列计算规则。在PyTorch中,能够作用与Tensor的运算,被统一称作为算子。并且相比于NumPy,PyTorch给出了更加规范的算子(运算)的分类,从而方便用户在不同场景下调用不同类型的算子(运算)。

  • 数学运算的分类
    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):其他未被归类的数学运算。

  由于谱运算(Spectral Ops)前期不会涉及,而要理解傅里叶变换本身需要更多额外的数学基础,而很多其他运算,我们在前面介绍张量的基本方法时已经介绍,因此接下来将主要围绕逐点运算、规约运算、比较运算和线性代数运算四块进行讲解,而线性代数部分由于涉及到大量的数学内容,因此将放在Lesson 4中单独进行讲解。

import torch
import numpy as np

关于数学运算的另一种分类方法,是根据运算使用场景进行分类,如基础数学运算、数理统计运算等。由于PyTorch官网是按照六类算子进行的分类,因此本节将结合两种分类方法进行讲解。

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

  在具体介绍张量的运算操作之前,我们先要了解张量的运算规则,其中最重要的一点,就是张量具备和NumPy相同的广播特性,也就是允许不同形状的张量之间进行计算。

1.相同形状的张量计算

  根据官网说法,“same shapes are always broadcastable”,相同形状数组总是可以进行广播计算。这里简单强调一下,虽然我们往往觉得不同形状之间的张量计算才是应用到广播特性,但其实相同形状的张量计算,尽管是对应位置元素进行计算,但本质上也是应用到了广播特性。

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

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

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 相同维度、不同形状的张量之间计算

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

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

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

  对于返回结果,我们可以看成是一个序列,代表着张量各维度的信息。当然,对于二维张量,由于我们可以将其视作一个矩阵,因此我们可以说t2是一个拥有三行四列的二维张量,但这种理解方式对于更高维度张量就存在一定的局限,因此我们需要树立另外一种理解方法,那就是: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])

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

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

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

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

而t21和t2的实际计算过程如下:

1

注意理解:此处的广播相当于将t22的形状(1, 4)拓展成了t2的(3, 4),也就是复制了第一行三次,然后二者进行相加。当然,也可以理解成t22的第一行和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.]])

t2和t22实际计算过程如下:

2
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-61-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]])

二者计算过程如下:

[外链图片转存中…(img-UF5hyFOf-1639646728927)]

三维张量的广播

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.]]])

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

t3.shape
torch.Size([3, 4, 5])
t33 = torch.ones(1, 1, 5)
t33
tensor([[[1., 1., 1., 1., 1.]]])
t3 + t33
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.]]])

t3和t33计算过程如下

3

注:此处标注的两次广播,我们也可认为上述全部过程的实现是一次“大的”广播。同时,此处最开始的t33也就相当于一个一维的、包含五个元素的张量,因此上述过程也可视为一个一维张量和一个三维张量计算时的广播过程。

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.]]])

思考:形状为(2,1)的张量和形状为(3,2,3)的张量可以进行广播计算么?计算过程是怎样的?

二、逐点运算(Pointwise Ops)

  PyTorch中逐点运算大部分都是可以针对Tensor中每个元素都进行的数学科学运算,并且都是较为通用的数学科学运算,和NumPy中针对Array的科学运算类似。在PyTorch中文文档中有全部运算符的相关介绍,此处仅针对常用计算函数进行介绍。
  逐点运算主要包括数学基本运算、数值调整运算和数据科学运算三块,相关函数如下:

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
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])

Tensor数值调整函数

函数描述
torch.abs(t)返回绝对值
torch.ceil(t)向上取整
torch.floor(t)向下取整
torch.round(t)四舍五入取整
torch.neg(t)返回相反的数
t = torch.randn(5)
t
tensor([-0.5184, -0.4910, -0.1381, -0.2500, -0.4295])
torch.round(t)
tensor([-1., -0., -0., -0., -0.])
torch.abs(t)
tensor([0.5184, 0.4910, 0.1381, 0.2500, 0.4295])
torch.neg(t)
tensor([0.5184, 0.4910, 0.1381, 0.2500, 0.4295])

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

t                # t本身并未发生变化
tensor([-0.5184, -0.4910, -0.1381, -0.2500, -0.4295])

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

t.abs_()
tensor([0.5184, 0.4910, 0.1381, 0.2500, 0.4295])
t
tensor([0.5184, 0.4910, 0.1381, 0.2500, 0.4295])
t.neg_()
tensor([-0.5184, -0.4910, -0.1381, -0.2500, -0.4295])
t
tensor([-0.5184, -0.4910, -0.1381, -0.2500, -0.4295])

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

t.exp_()
tensor([0.5955, 0.6120, 0.8710, 0.7788, 0.6508])
t
tensor([0.5955, 0.6120, 0.8710, 0.7788, 0.6508])

Tensor常用科学计算

数学运算函数数学公式描述
幂运算
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)返回一个加自然对数的输入数组。
三角函数运算
torch.sin(t)三角正弦。
torch.cos(t)元素余弦。
torch.tan(t)逐元素计算切线。
  • tensor的大多数科学计算只能作用于tensor对象
# 计算2的2次方
torch.pow(2, 2)
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-124-b05e9bca6d33> in <module>
----> 1 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)

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

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

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

t = torch.arange(1, 4)
t.dtype
torch.int64
torch.exp(t)
---------------------------------------------------------------------------

RuntimeError                              Traceback (most recent call last)

<ipython-input-103-ad3d5759497c> in <module>
----> 1 torch.exp(t)


RuntimeError: exp_vml_cpu not implemented for 'Long'

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

t1 = t.float()
t1
tensor([1., 2., 3.])
torch.exp(t1)
tensor([ 2.7183,  7.3891, 20.0855])
torch.expm1(t1)
tensor([ 1.7183,  6.3891, 19.0855])

注,此处返回结果是 e t − 1 e^{t} - 1 et1,在数值科学计算中,expm1函数和log1p函数是一对对应的函数关系,后面再介绍log1p的时候会讲解这对函数的实际作用。

torch.exp2(t1)
tensor([2., 4., 8.])
torch.pow(t, 2)
tensor([1, 4, 9])

注意区分2的t次方和t的2次方

torch.square(t)
tensor([1, 4, 9])
torch.sqrt(t1)
tensor([1.0000, 1.4142, 1.7321])
torch.pow(t1, 0.5)
tensor([1.0000, 1.4142, 1.7321])

开根号也就相当于0.5次幂

torch.log10(t1)
tensor([0.0000, 0.3010, 0.4771])
torch.log(t1)
tensor([0.0000, 0.6931, 1.0986])
torch.log2(t1)
tensor([0.0000, 1.0000, 1.5850])

同时,我们也可简单回顾幂运算和对数运算之间的关系

torch.exp(torch.log(t1))
tensor([1., 2., 3.])
torch.exp2(torch.log2(t1))
tensor([1., 2., 3.])
  • log1p和expm1

log1p是进行$ y_i = \log_{e} (x_i $ + 1)计算,而expm则是进行$ y_{i} = e^{x_{i}} $ - 1计算,二者互为逆运算。

torch.expm1(torch.log1p(t1))
tensor([1., 2., 3.])

而log1p的实际应用场景有两个,其一是进行数据分布转化操作,另一种则是防止极小的数在进行对数运算时丢失有效位数。

  • 大多数的数理统计分析,在建模过程中,都对数据的分布有较为严格的要求,但真实的数据往往不能满足特定的分布,因此很多时候我们会先对其进行一定程度上的数值转化,而log运算就是常用的将偏态分布的数据转化为高斯分布所使用的函数,并且在此过程中,log1p的效果往往会好于log的效果;
  • 另一种使用场景,当处理的数据数值非常小时,使用对数运算往往会因为精度不够而使得计算无法进行(log0没有数学意义),此时使用log1p则可很好的避免这种情况。
m = torch.tensor(-2.6529) * 1e-20
m
tensor(-2.6529e-20)
# 转化为一个趋近于1的数再运算
torch.log1p(m)
tensor(-2.6529e-20)
# 此时直接使用log无法执行运算
torch.log(m)
tensor(nan)
# 无法运算就无法还原
torch.exp(torch.log(m))
tensor(nan)
# 无损还原
torch.expm1(torch.log1p(m))
tensor(-2.6529e-20)

在很多场景中,由于计算精度问题导致无法计算,并进一步导致数据信息损失,将会是非常致命的,典型场景如PCA主成分分析,相关问题会在算法讲解中进一步讨论。

  • 排序运算: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]))

三、规约运算

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

Tensor统计分析函数

函数描述
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个值对应的指标
# 生成浮点型张量
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)
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.topk(t, 2)

  • dist计算距离

  dist函数可计算闵式距离(闵可夫斯基距离),通过输入不同的p值,可以计算多种类型的距离,如欧式距离、街道距离等。闵可夫斯基距离公式如下:

$ D(x,y) = (\sum^{n}_{u=1}|x_u-y_u|^{p})^{1/p}$

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

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.)
  • 规约运算的维度

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

# 创建一个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.]])
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.]])

理解:dim参数和shape返回结果一一对应。

  • 二维张量的排序

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

t22 = torch.randn(3, 4)             # 创建二维随机数张量
t22
tensor([[-0.7236, -0.6408, -1.9364,  1.6072],
        [-1.9563,  0.2702,  0.0382,  0.1915],
        [-0.7853,  0.2478,  1.2953,  0.4663]])
# 默认情况下,是按照行进行升序排序
torch.sort(t22)
torch.return_types.sort(
values=tensor([[-1.9364, -0.7236, -0.6408,  1.6072],
        [-1.9563,  0.0382,  0.1915,  0.2702],
        [-0.7853,  0.2478,  0.4663,  1.2953]]),
indices=tensor([[2, 0, 1, 3],
        [0, 2, 3, 1],
        [0, 1, 3, 2]]))
torch.sort?
[1;31mDocstring:[0m
sort(input, dim=-1, descending=False, *, out=None) -> (Tensor, LongTensor)

Sorts the elements of the :attr:`input` tensor along a given dimension
in ascending order by value.

If :attr:`dim` is not given, the last dimension of the `input` is chosen.

If :attr:`descending` is ``True`` then the elements are sorted in descending
order by value.

A namedtuple of (values, indices) is returned, where the `values` are the
sorted values and `indices` are the indices of the elements in the original
`input` tensor.

Args:
    input (Tensor): the input tensor.
    dim (int, optional): the dimension to sort along
    descending (bool, optional): controls the sorting order (ascending or descending)

Keyword args:
    out (tuple, optional): the output tuple of (`Tensor`, `LongTensor`) that can
        be optionally given to be used as output buffers

Example::

    >>> x = torch.randn(3, 4)
    >>> sorted, indices = torch.sort(x)
    >>> sorted
    tensor([[-0.2162,  0.0608,  0.6719,  2.3332],
            [-0.5793,  0.0061,  0.6058,  0.9497],
            [-0.5071,  0.3343,  0.9553,  1.0960]])
    >>> indices
    tensor([[ 1,  0,  2,  3],
            [ 3,  1,  0,  2],
            [ 0,  3,  1,  2]])

    >>> sorted, indices = torch.sort(x, 0)
    >>> sorted
    tensor([[-0.5071, -0.2162,  0.6719, -0.5793],
            [ 0.0608,  0.0061,  0.9497,  0.3343],
            [ 0.6058,  0.9553,  1.0960,  2.3332]])
    >>> indices
    tensor([[ 2,  0,  0,  1],
            [ 0,  1,  1,  2],
            [ 1,  2,  2,  0]])
[1;31mType:[0m      builtin_function_or_method
# 修改dim和descending参数,使得按列进行降序排序
torch.sort(t22, dim = 0, descending=True)
torch.return_types.sort(
values=tensor([[-0.7236,  0.2702,  1.2953,  1.6072],
        [-0.7853,  0.2478,  0.0382,  0.4663],
        [-1.9563, -0.6408, -1.9364,  0.1915]]),
indices=tensor([[0, 1, 2, 0],
        [2, 2, 1, 2],
        [1, 0, 0, 1]]))

四、比较运算

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

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各元素是否不相同,等效!=
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])
张量在数学上是一个多阶阵列,它可以表示任意维度的向量或矩阵,每个维度都有一定的大小。在张量符号中,一般使用下标或索引来标识各个维度,例如: - 一个一阶张量(标量)可以用 \( a_0 \) 表示; - 一个二阶张量(矩阵)可以用 \( A_{ij} \) 或 \( A^{(i,j)} \) 来表示,其中 i 和 j 分别代表行和列; - 高阶张量的索引更多,比如三维张量 \( B_{ijk} \) 等。 张量运算法则主要有以下几种: 1. **加法和减法**:两个张量只有形状和大小相同时才能相加减,\( A + B \) 和 \( A - B \),对应位置的元素相加减。 2. **数乘**:常数乘张量,对于标量c,\( cA \) 的结果是将c与张量的所有元素逐个相乘。 3. **点积(内积)**:针对向量张量,\( \mathbf{a} \cdot \mathbf{b} = \sum_i a_i b_i \),是两个向量的元素对应相乘然后求和。 4. **外积(乘积)**:对于向量张量,\( \mathbf{a} \otimes \mathbf{b} \) 或者 \( A \times B \) 可能生成一个新的更高阶张量。 5. **转置**:对于二维张量,\( A^T \) 将矩阵的行变成列,或 \( A_{ji} \) 或 \( A^{(j,i)} \)。 6. **矩阵乘法**:当左矩阵的列数等于右矩阵的行数时,可以进行矩阵乘法,结果形成新的矩阵。 7. **张量分解**:常见的有SVD(奇异值分解)和CP分解(CANDECOMP/PARAFAC分解)等,用于分析高维数据的内在结构。 在实际的数值计算库(如NumPy、TensorFlow)中,这些操作都已封装成函数,方便用户进行张量运算
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值