PyTorch深度学习实战(4)—— Tensor的基本操作

PyTorch背后的设计核心是张量和计算图,张量实现了高性能的数据结构和运算,计算图通过计算梯度以优化网络参数。本文将介绍PyTorch的张量系统(Tensor)和自动微分系统(autograd)。

Tensor,又名张量,读者可能对这个名词似曾相识,它不仅在PyTorch中出现过,而且也是其他深度学习框架(如TensorFlow、MXNet等)中重要的数据结构。从工程角度讲,可以简单地认为Tensor是一个支持高效科学计算的数组。Tensor可以是一个数(标量)、一维数组(向量)、二维数组(如矩阵、黑白图片)或者更高维的数组(如高阶数据、视频等)。Tensor与NumPy的ndarray用法类似,而PyTorch的Tensor支持GPU加速。

本文将系统地讲解Tensor的基本用法,力求面面俱到,但不会涉及每个函数。对于Tensor的其他函数及其用法,读者可在IPython/Notebook中使用函数名加?查看帮助文档,或查阅PyTorch的官方文档。

学习过NumPy的读者对本节内容会比较熟悉,因为Tensor的接口设计与NumPy类似。本节不要求读者事先掌握NumPy的相关知识。

从接口的角度来说,对Tensor的操作可以分为以下两类。

  • 形如torch.function的操作,如torch.save等。
  • 形如tensor.function的操作,如tensor.view等。

为了方便用户使用,对Tensor的大部分操作同时支持这两类接口,在本文中不做具体区分。例如,torch.sum(a, b)等价于a.sum(b)

从存储的角度来说,对Tensor的操作可以分为以下两类。

  • 不会修改自身存储数据的操作,如 a.add(b), 加法的结果会返回一个新的Tensor。
  • 会修改自身存储数据的操作,如 a.add_(b), 加法的结果会存储在a中,并返回这个结果。

函数名以_结尾的函数都是inplace方式,即会修改调用者自己存储的数据,这一点在实际应用中需加以区分。

1.创建Tensor

在PyTorch中创建Tensor的方法有很多种,具体如表3-1所示。

:常见的创建Tensor的方法

函数

功能

Tensor(*sizes)

基础构造函数

tensor(data,)

类似np.array的构造函数

ones(*sizes)

返回全1的Tensor

zeros(*sizes)

返回全0的Tensor

eye(*sizes)

对角线为1,其余值为0的Tensor

arange(s,e,step)

从s到e,步长为step

linspace(s,e,steps)

从s到e,均匀切分成steps份

rand/randn(*sizes)

均匀/标准分布

normal(mean,std)/uniform(from,to)

正态分布/均匀分布

randperm(m)

随机排列

tensor.new_*/torch.*_like

创建一个相同形状,用*类型去填充的张量,具有相同的torch.dtype和torch.device

表3-1中的创建方法都可以在创建Tensor的时候指定它的数据类型dtype和存放设备device(CPU/GPU)。其中,在使用函数torch.Tensor()新建一个Tensor的时候,有下面几种方式。

  • 接收对象为一个list,根据list的数据创建Tensor。
  • 根据指定的形状新建Tensor。
  • 输入数据是其他的Tensor。

下面举例说明:

In: # 查看PyTorch的版本号
    import torch as t
    t.__version__Out:'1.8.0'In: # 指定Tensor的形状
    a = t.Tensor(2, 3)
    a # 数值取决于内存空间的状态,print时候可能overflow
Out:tensor([[-8.9209e-11,  4.5846e-41, -8.9209e-11],
            [ 4.5846e-41,  4.4400e-29,  3.0956e-41]])

In: # 用list数据创建Tensor
    b = t.Tensor([[1,2,3],[4,5,6]])
    b
Out:tensor([[1., 2., 3.],
            [4., 5., 6.]])
In: b.tolist() # 把Tensor转为list
Out:[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]

In: # 输入数据是一个Tensor
    c = t.Tensor(t.rand(2, 3))
    c
Out: tensor([[0.4217, 0.3367, 0.4271],
             [0.9251, 0.4068, 0.6382]])

通过tensor.size()可以查看Tensor的形状,它返回一个torch.Size对象。该对象虽然是tuple的子类,但是在作为torch.Tensor()的输入对象时,它与tuple略有区别。


In: b_size = b.size()
    b_size
 Out:torch.Size([2, 3])
 
 In: # 创建一个和b形状一样的Tensor c
    c = t.Tensor(b_size)
    # 创建一个元素为23的Tensor d
    d = t.Tensor((2, 3))
    c, d
 Out:(tensor([[0., 0., 0.],
             [0., 0., 0.]]), tensor([2., 3.]))

注意:使用torch.Tensor(*sizes)创建Tensor时,系统不会马上分配空间,只会计算剩余的内存是否足够使用,在真正使用到创建的Tensor时才会分配空间。其他操作都是在创建完Tensor之后马上进行空间分配的。在创建Tensor时,读者很容易混淆torch.Tensor()torch.tensor(),二者的区别如下。

  • torch.Tensor()是Python类,默认是torch.FloatTensor()。运行torch.Tensor([2,3])会直接调用Tensor类的构造函数__init__(),生成结果是单精度浮点类型的Tensor。关于Tensor的类型,将在下一小节介绍。
  • torch.tensor()是Python函数,函数的原型为:torch.tensor(data, dtype=None, device=None, requires_grad=False),其中data支持list、tuple、array、scalar等类型的数据。torch.tensor()直接从data中进行数据拷贝,并根据原数据的类型生成相应类型的Tensor。

由于torch.tensor()能够根据数据类型生成对应类型的Tensor,而且接口与NumPy更像,因此在实际应用中,笔者更加推荐使用torch.tensor()创建一个新的Tensor。下面举例说明:

In: # torch.Tensor()可以直接创建空的张量
    t.Tensor()
 
 Out:tensor([])
 
 In: # torch.tensor()不可以直接创建空的张量,必须传入一个data
    # t.tensor() 
    # TypeError: tensor() missing 1 required positional arguments: "data"
    t.tensor(()) # ()等效于一个空数据Out:tensor([])
 
 In: a = t.tensor([2, 3]) # t.tensor会从数据中推理出所需的数据类型
    print(a.type())
    b = t.Tensor([2, 3]) # t.Tensor默认是FloatTensor
    print(b.type())
 
 Out:torch.LongTensor
    torch.FloatTensor
 
 In: import numpy as np
    arr = np.ones((2, 3), dtype=np.float64)
    a = t.tensor(arr)  # 也可以使用t.from_numpy(arr),但实现有区别,见3.1.3
    a
 Out:tensor([[1., 1., 1.],
            [1., 1., 1.]], dtype=torch.float64)

其他的创建Tensor方法举例如下:

In: # 创建一个形状是(2,3),值全为1的Tensor
    t.ones(2, 3)
 Out:tensor([[1., 1., 1.],
            [1., 1., 1.]])

torch.ones()类似的函数还有torch.ones_like(input)。其中,输入input是一个Tensor,函数返回一个与之大小相同,值全部为1的新Tensor。也就是说,torch.ones_like(input)等价于torch.ones(input.size(), dtype=input.dtype, layout=input.layout, device=input.device)

In: input_tensor = t.tensor([[1, 2, 3], [4, 5, 6]])
    t.ones_like(input_tensor)
Out:tensor([[1, 1, 1],
            [1, 1, 1]])

In: # 创建一个形状是(2,3),值全0的Tensor
    t.zeros(2, 3)
Out:tensor([[0., 0., 0.],
            [0., 0., 0.]])
 
In: # 创建一个对角线值为1,其余值为0的Tensor,不要求行列数一致
    t.eye(2, 3, dtype=t.int)
Out:tensor([[1, 0, 0],
            [0, 1, 0]], dtype=torch.int32)
 
In: # 创建一个起始值为1,上限为6,步长为2的Tensor
    t.arange(1, 6, 2)
Out:tensor([1, 3, 5])

In: # 创建一个均匀间距的Tensor,将1到10的数分为3份
    t.linspace(1, 10, 3)
Out:tensor([ 1.0000,  5.5000, 10.0000])

In: # 创建一个形状是(2,3)的Tensor,取值是从标准正态分布中抽取的随机数
    t.randn(2, 3)
Out:tensor([[ 1.3969, -1.5042, -0.8430],
            [-0.8707, -1.0794, -1.3357]])

In: # 创建一个长度为5、随机排列的Tensor
    t.randperm(5) 
Out:tensor([2, 4, 0, 3, 1])

In: # 创建一个大小为(2,3),值全为1的Tensor,保留原始的torch.dtype和torch.device
    a = t.tensor((), dtype=t.int32)
    a.new_ones((2, 3))
Out:tensor([[1, 1, 1],
            [1, 1, 1]], dtype=torch.int32)
 
In: # 统计a中的元素总数,两种方式等价
    a.numel(), a.nelement()
Out:(6, 6)

2.Tensor的类型

Tensor的类型可以细分为设备类型(device)和数据类型(dtype)。其中,设备类型分为CUDA和CPU,数值类型有bool、float、int等。Tensor的数据类型如表3-2所示,每种数据类型都有CPU和GPU版本。读者可以通过tensor.device获得Tensor的设备类型,通过tensor.dtype获得Tensor的数据类型。

Tensor的默认数据类型是FloatTensor,读者可以通过torch.set_default_tensor_type 修改默认的Tensor类型(如果默认类型为GPU Tensor,那么所有操作都将在GPU上进行)。了解Tensor的类型对分析内存占用很有帮助,例如,一个形状为$(1000, 1000, 1000)$ 的FloatTensor,它有$1000\times 1000\times 1000=10^9$ 个元素,每个元素占$32 \text{bit} \div 8 = 4 \text{Byte}$ 内存,所以这个Tensor占大约4GB内存/显存。HalfTensor是专门为GPU版本设计的,同样的元素个数,HalfTensor的显存占用只有FloatTensor的一半,因此使用HalfTensor可以极大缓解GPU显存不足的问题。需要注意的是,HalfTensor所能表示的数值大小和精度有限,可能会出现数据溢出等问题

:Tensor的数据类型

Data type

dtype

CPU tensor

GPU tensor

32-bit 浮点型

torch.float32 or torch.float

torch.FloatTensor

torch.cuda.FloatTensor

64-bit 浮点型

torch.float64 or torch.double

torch.DoubleTensor

torch.cuda.DoubleTensor

16-bit 半精度浮点型

torch.float16 or torch.half

torch.HalfTensor

torch.cuda.HalfTensor

8-bit 无符号整型

torch.uint8

torch.ByteTensor

torch.cuda.ByteTensor

8-bit 有符号整型

torch.int8

torch.CharTensor

torch.cuda.CharTensor

16-bit 有符号整型

torch.int16 or torch.short

torch.ShortTensor

torch.cuda.ShortTensor

32-bit 有符号整型

torch.int32 or torch.int

torch.IntTensor

torch.cuda.IntTensor

64-bit 有符号整型

torch.int64 or torch.long

torch.LongTensor

torch.cuda.LongTensor

布尔型

torch.bool

torch.BoolTensor

torch.cuda.BoolTensor

不同类型Tensor之间相互转换的常用方法如下。

  • 最通用的做法是tensor.type(new_type),同时还有tensor.float()tensor.long()tensor.half()等快捷方法。
  • CPU Tensor与GPU Tensor之间的互相转换通过tensor.cuda()tensor.cpu()实现,此外还可以使用tensor.to(device)
  • 创建同种类型的张量:torch.*_liketensor.new_*,这两种方法适用于编写设备兼容的代码。其中,torch.*_like(tensorA) 可以生成和tensorA拥有同样属性(如类型、形状和CPU/GPU)的新Tensor;tensor.new_*(new_shape) 可以新建一个不同形状、但是拥有相同属性的Tensor。

下面举例说明Tensor之间的相互转换:

In: # 更改默认Tensor的类型
    a = t.rand(2, 3)
    print(a.dtype)
    # 设置默认类型为DoubleTensor
    t.set_default_tensor_type('torch.DoubleTensor')
    a = t.rand(2, 3)
    print(a.dtype)
    # 恢复之前的默认设置
    t.set_default_tensor_type('torch.FloatTensor')
 
 Out:torch.float32
    torch.float64
 
 In: # 通过type方法和快捷方法修改Tensor的类型
    b1 = a.type(t.FloatTensor)
    b2 = a.float()
    b3 = a.type_as(b1) # 等价于a.type(b.dtype)或a.type(b.type())
    a.dtype, b1.dtype, b2.dtype, b3.dtype
 
 Out:(torch.float64, torch.float32, torch.float32, torch.float32)
 
 In: # 之所以new_*方法相当于利用DoubleTensor的构造函数,是因为此时a是torch.float64类型
    a.new_ones(2, 4)
 
 Out:tensor([[1., 1., 1., 1.],
            [1., 1., 1., 1.]], dtype=torch.float64)
 
 In: # new_*方法同时还会复制Tensor的device
    a = t.randn(2, 3).cuda()
    a.new_ones(2, 4)
 
 Out: tensor([[1., 1., 1., 1.],
             [1., 1., 1., 1.]], device='cuda:0')

3.索引操作

在NumPy中经常使用索引操作获取指定位置的数据。Tensor支持与NumPy类似的索引操作,下面通过一些示例讲解常用的索引操作。其中,大多数索引操作通过修改Tensor的stride等属性与原Tensor共享内存,即修改了其中一个Tensor,另一个Tensor会跟着改变。

In: a = t.randn(3, 4)
    a
Out:tensor([[-0.0317,  1.7469, -1.4530, -0.4462],
            [ 2.5300, -1.0586, -1.0968,  0.0187],
            [-0.5891,  0.1420,  0.3084, -0.5744]])

In: print("查看第1行结果:", a[0])
    print("查看第2列结果:", a[:,1])
    print("查看第2行最后两个元素:", a[1, -2:])
Out:查看第1行结果: tensor([-0.0317,  1.7469, -1.4530, -0.4462])
    查看第2列结果: tensor([ 1.7469, -1.0586,  0.1420])
    查看第2行最后两个元素: tensor([-1.0968,  0.0187])
 
In: # 返回一个BoolTensor
    print(a > 0) # 布尔型
    print((a > 0).int()) # 整型
Out:tensor([[False,  True, False, False],
            [ True, False, False,  True],
            [False,  True,  True, False]])
    tensor([[0, 1, 0, 0],
            [1, 0, 0, 1],
            [0, 1, 1, 0]], dtype=torch.int32)
 
In: # 返回Tensor中满足条件的结果,下面两种写法等价
    # 选择返回的结果与原Tensor不共享内存空间
    print(a[a > 0])
    print(a.masked_select(a>0)) 
    # 用torch.where保留原始的索引位置,不满足条件的位置置0
    print(t.where(a > 0, a, t.zeros_like(a)))
 
Out:tensor([1.7469, 2.5300, 0.0187, 0.1420, 0.3084])
    tensor([1.7469, 2.5300, 0.0187, 0.1420, 0.3084])
    tensor([[0.0000, 1.7469, 0.0000, 0.0000],
            [2.5300, 0.0000, 0.0000, 0.0187],
            [0.0000, 0.1420, 0.3084, 0.0000]])

Python中常用的选择函数如表3-3所示。

:常用的选择函数

函数

功能

index_select(input, dim, index)

在指定维度dim上选取,比如选取某些行、某些列

masked_select(input, mask)

例子如上,a[a>0],使用BoolTensor进行选取

non_zero(input)

非0元素的下标

gather(input, dim, index)

根据index,在dim维度上选取数据,输出的size与index一样

其中,gather是一个比较复杂的操作,对一个二维的Tensor,每个位置的元素输出如下:

out[i][j] = input[index[i][j]][j]  # dim=0
out[i][j] = input[i][index[i][j]]  # dim=1

In: a = t.arange(0, 16).view(4, 4)
    a
 
 Out:tensor([[ 0,  1,  2,  3],
            [ 4,  5,  6,  7],
            [ 8,  9, 10, 11],
            [12, 13, 14, 15]])
 
 In: # 选取对角线的元素
    index = t.tensor([[0,1,2,3]])
    a.gather(0, index)
 
 Out:tensor([[ 0,  5, 10, 15]])In: # 选取反对角线上的元素
    index = t.tensor([[3,2,1,0]]).t()
    a.gather(1, index)
 
 Out:tensor([[ 3],
            [ 6],
            [ 9],
            [12]])
 
 In: # 选取正反对角线上的元素
    index = t.tensor([[0,1,2,3],[3,2,1,0]]).t()
    b = a.gather(1, index)
    b
 Out:tensor([[ 0,  3],
            [ 5,  6],
            [10,  9],
            [15, 12]])

gather相对应的逆操作是scatter_gather将数据从input中按index取出,scatter_按照index将数据写入。注意:scatter_函数是inplace操作,会直接对当前数据进行修改。

out = input.gather(dim, index)

-->近似逆操作

out = Tensor()
out.scatter_(dim, index)

In: # 将正反对角线元素放至指定位置
    c = t.zeros(4,4).long()
    c.scatter_(1, index, b)
 
 Out:tensor([[ 0,  0,  0,  3],
            [ 0,  5,  6,  0],
            [ 0,  9, 10,  0],
            [12,  0,  0, 15]])

对Tensor进行任意索引操作得到的结果仍然是一个Tensor,如果想获取标准的Python对象数值,那么需要调用tensor.item()。这个方法只对仅包含一个元素的Tensor适用。

In: t.Tensor([1.]).item()
    # t.Tensor([1, 2]).item()  ->
    # raise ValueError: only one element tensors can be converted to Python scalars
Out:1.0

4.拼接操作

拼接操作是指将多个Tensor在指定维度上拼(Concatenate)在一起的操作,常用的拼接操作函数如表3-4所示。

:常用的拼接函数

函数

功能

cat(tensors, dim)

将多个Tensor在指定维度dim上进行拼接

stack(tensors, dim)

将多个Tensor沿一个新的维度进行拼接

函数catstack的输入对象都是多个Tensor组成的一个序列(如列表、元组等),所有的Tensor在拼接之外的维度必须相同或者可以进行广播。catstack在指定维度时稍有区别,cat会将多个Tensor在维度dim上进行拼接,而stack指定的维度dim是一个新的维度,最终是在这个新的维度上进行的拼接。下面举例说明两个函数的用法和区别:

In: a = t.arange(6).view(2, 3)
    aOut:tensor([[0, 1, 2],
            [3, 4, 5]])
In: # cat函数在dim=0上进行拼接
    t.cat((a, a), 0)
    # 等价于t.cat([a, a], 0)
Out:tensor([[0, 1, 2],
            [3, 4, 5],
            [0, 1, 2],
            [3, 4, 5]])
In: # cat函数在dim=1上进行拼接
    t.cat((a, a), 1)
    # 等价于t.cat([a, a], 1)
Out:tensor([[0, 1, 2, 0, 1, 2],
            [3, 4, 5, 3, 4, 5]])
In: # stack函数在dim=0上进行拼接
    b = t.stack((a, a), 0)
    b
Out:tensor([[[0, 1, 2],
             [3, 4, 5]],
            [[0, 1, 2],
             [3, 4, 5]]])
In: # 注意输出形状的改变
    b.shape
Out:torch.Size([2, 2, 3])

从上面的例子可以看出,stack函数会在Tensor上扩展一个新的维度,然后基于这个维度完成Tensor的拼接。

5.高级索引

目前,PyTorch已经支持绝大多数NumPy风格的高级索引。高级索引虽然可以看成是基本索引操作的扩展,但是操作结果一般不和原始的Tensor共享内存。

In: x = t.arange(0,16).view(2,2,4)
    x
Out:tensor([[[ 0,  1,  2,  3],
             [ 4,  5,  6,  7]],
            [[ 8,  9, 10, 11],
             [12, 13, 14, 15]]])
In: x[[1, 0], [1, 1], [2, 0]] # x[1,1,2]和x[0,1,0]
Out:tensor([14,  4])

In: x[[1, 0], [0], [1]] # 等价于x[1,0,1],x[0,0,1]
Out:tensor([9, 1])

6.逐元素操作

逐元素(point-wise,又名element-wise)操作会对Tensor的每一个元素进行操作,此类操作的输入与输出形状一致。常用的逐元素操作如表3-5所示。

:常用的逐元素操作

函数

功能

abs/sqrt/div/exp/fmod/log/pow...

绝对值/平方根/除法/指数/求余/对数/求幂...

cos/sin/asin/atan2/cosh...

三角函数

ceil/round/floor/trunc

上取整/四舍五入/下取整/只保留整数部分

clamp(input, min, max)

超过min和max部分截断

sigmod/tanh/...

激活函数

PyTorch对很多操作都实现了运算符重载,读者可以很方便地直接使用。例如,torch.pow(a,2) 等价于a ** 2torch.mul(a,2)等价于a * 2

截断函数clamp(x, min, max)通常用在需要比较大小的地方,它的运算规则如下所示:

下面举例说明一些常见的逐元素操作:

In: a = t.arange(0, 6).float().view(2, 3)
    t.cos(a)Out:tensor([[ 1.0000,  0.5403, -0.4161],
            [-0.9900, -0.6536,  0.2837]])In: # 取模运算的运算符重载,二者等价
    print(a % 3)
    print(t.fmod(a, 3))Out:tensor([[0., 1., 2.],
            [0., 1., 2.]])
    tensor([[0., 1., 2.],
            [0., 1., 2.]])In: # 将a的值进行上下限截断
    print(a)
    print(t.clamp(a, min=2, max=4))Out:tensor([[0., 1., 2.],
            [3., 4., 5.]])
    tensor([[2., 2., 2.],
            [3., 4., 4.]])

7.归并操作

归并操作只使用Tensor中的部分元素进行计算,其输出结果的形状通常小于输入形状。用户可以沿着某一维度进行指定的归并操作,例如,加法sum既可以计算整个Tensor的和,又可以计算Tensor中每一行或每一列的和。常用的归并操作如表3-6所示。

:常用的归并操作

函数

功能

mean/sum/median/mode

均值/求和/中位数/众数

norm/dist

范数/距离

std/var

标准差/方差

cumsum/cumprod

累加/累乘

大多数执行归并操作的函数都有一个维度参数dim,它用来指定这些操作是在哪个维度上执行的。关于dim(对应于NumPy中的axis)的解释众说纷纭,这里提供一个简单的记忆方式。

假设输入的形状是(m, n, k),有如下三种情况。

  • 如果指定dim=0,那么输出的形状是(1, n, k)或者(n, k)。
  • 如果指定dim=1,那么输出的形状是(m, 1, k)或者(m, k)。
  • 如果指定dim=2,那么输出的形状是(m, n, 1)或者(m, n)。

输出形状中是否有维度1,取决于参数keepdim,如果指定keepdim=True,那么结果就会保留维度1。注意:以上只是经验总结,并非所有函数都符合这种形状变化方式,如cumsum,下面举例说明:

In: # 注意对比是否保留维度1的区别
    b = t.ones(2, 3)
    print(b.sum(dim=0, keepdim=True ), b.sum(dim=0, keepdim=True ).shape)
    print(b.sum(dim=0, keepdim=False), b.sum(dim=0, keepdim=False).shape)
Out:
    tensor([[2., 2., 2.]]) torch.Size([1, 3])
    tensor([2., 2., 2.]) torch.Size([3])

In: a = t.arange(2, 8).view(2, 3)
    print(a)
    print(a.cumsum(dim=1)) # 沿着行累加
Out:tensor([[2, 3, 4],
            [5, 6, 7]])
    tensor([[ 2,  5,  9],
            [ 5, 11, 18]])

8.比较函数

PyTorch的部分比较函数是逐元素比较,操作类似于逐元素操作,另外一些类似于归并操作。常用的比较函数如表3-7所示。

:常用的比较函数

函数

功能

gt/lt/ge/le/eq/ne

大于/小于/大于等于/小于等于/等于/不等于

topk(input, k)

返回最大的k个数和它们的索引

sort(input, dim)

对指定维度进行排序

argsort(input, dim)

返回指定维度排序结果的索引

max/min

比较两个Tensor的最大、最小值

allclose(tensor1, tensor2)

比较两个浮点类型Tensor近似相等

表3-7中第一行的比较操作因为已经实现了运算符重载,所以可以使用a>=ba>ba!=ba==b,其返回结果是一个BoolTensor,可以用来选取元素。max/min这两个操作比较特殊,以max为例,它有以下三种使用情况。

  • torch.max(tensor):返回Tensor中最大的一个数。
  • torch.max(tensor, dim):指定维上最大的数,返回Tensor和索引。
  • torch.max(tensor1, tensor2): 返回两个Tensor对应位置上较大的元素。

下面举例说明:

In: a = t.linspace(0, 15, 6).view(2, 3)
    b = t.linspace(15, 0, 6).view(2, 3)
    print(a > b)
    print("a中大于b的元素: ", a[a > b]) # 返回a中大于b的元素
    print("a中最大的元素: ", t.max(a))  # 返回a中最大的元素Out:tensor([[False, False, False],
            [ True,  True,  True]])
    a中大于b的元素:  tensor([ 9., 12., 15.])
    a中最大的元素:  tensor(15.)In: t.max(b, dim=1)
    # 第一个返回值156分别表示第1行和第2行最大的元素
    # 第二个返回值的00表示每行最大元素的索引Out:torch.return_types.max(
    values=tensor([15.,  6.]),
    indices=tensor([0, 0]))In: t.max(a, b) # 返回两个Tensor对应位置上较大的元素Out:tensor([[15., 12.,  9.],
            [ 9., 12., 15.]])In: a = t.tensor([2, 3, 4, 5, 1])
    t.topk(a, 3) # 返回最大的3个值和它们对应的索引Out:torch.return_types.topk(
    values=tensor([5, 4, 3]),
    indices=tensor([3, 2, 1]))
    
In: a = t.randn(2, 3)
    a
Out:tensor([[-0.1712,  0.2442, -1.1505],
            [-0.0754, -0.1402,  1.1420]])

In: t.argsort(a, dim=1) # 第一行的数据是-1.1505<-0.1712<0.2442,对应的索引是2,0,1
Out:tensor([[2, 0, 1],
            [1, 0, 2]])

在比较两个整型Tensor时,可以使用符号==直接进行比较。对于有精度限制的浮点数,需要使用allclose函数进行比较:

In: a = t.tensor([1.000001, 1.000001, 0.999999])
    b = t.ones_like(a) # [1., 1., 1.]
    print(a == b)
    t.allclose(a, b)
Out:tensor([False, False, False])
    True

9.其他函数

PyTorch 1.8版本新增了快速傅里叶变换FFT(torch.fft)和线性代数模块(torch.linalg),常用的线性代数函数如表3-8所示。

:常用的线性代数函数

函数

功能

linalg.det()

行列式

linalg.matrix_rank()

矩阵的秩

linalg.norm()

矩阵或向量范数

linalg.inv()

矩阵的逆

linalg.pinv()

矩阵的伪逆(Moore-Penrose广义逆矩阵)

linalg.svd()

奇异值分解

linalg.qr()

QR分解

fft.fft()

一维离散傅里叶变换

fft.ifft()

一维离散傅里叶逆变换

此外,在torch.distributions中,PyTorch提供了可自定义参数的概率分布函数和采样函数,其中封装了伯努利分布、柯西分布、正态分布、拉普拉斯分布等。关于这些函数的详细用法,读者可以参考PyTorch的官方文档。

  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shangjg3

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值