PyTorch深度学习实战(4)—— Tensor的基本操作
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_sizeOut:torch.Size([2, 3])In: # 创建一个和b形状一样的Tensor c
c = t.Tensor(b_size)# 创建一个元素为2和3的Tensor d
d = t.Tensor((2, 3))
c, dOut:(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默认是FloatTensorprint(b.type())Out:torch.LongTensor
torch.FloatTensorIn: import numpy as np
arr = np.ones((2, 3), dtype=np.float64)
a = t.tensor(arr) # 也可以使用t.from_numpy(arr),但实现有区别,见3.1.3节
aOut: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.*_like 和 tensor.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.float64In: # 通过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.dtypeOut:(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: # 返回一个BoolTensorprint(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保留原始的索引位置,不满足条件的位置置0print(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=1In: a = t.arange(0, 16).view(4, 4)
aOut: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)
bOut: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沿一个新的维度进行拼接 |
函数cat和stack的输入对象都是多个Tensor组成的一个序列(如列表、元组等),所有的Tensor在拼接之外的维度必须相同或者可以进行广播。cat和stack在指定维度时稍有区别,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 ** 2,torch.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>=b、a>b、a!=b和a==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)# 第一个返回值15和6分别表示第1行和第2行最大的元素# 第二个返回值的0和0表示每行最大元素的索引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的官方文档。