1 . 逐元素操作的输入和输出形状一致。常见的操作如下表。

函数 功能
abs/sqrt/div/exp/fmod/log/pow… 绝对值/平方根/除法/指数/求余/求幂…
cos/sin/asin/atan2/cosh… 相关三角函数
ceil/round/floor/trunc 上取整/四舍五入/下取整/只保留整数部分
clamp(input, min, max) 超过 min 和 max 部分截断
sigmod/tanh.. 激活函数

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

2 . 归并操作会使输出形状小于输入形状,并可以沿着某一维度进行执行操作,如加法 sum,既可以计算整个 tensor 的和,也可以计算 tensor 中每一行或每一列的和,常用的归并操作如下表。

函数 功能
mean/sum/median/mode 均值/和/中位数/众数
norm/dist 范数/距离
std/var 标准差/方差
cumsum/cumprod 累加/累乘

以上大多数函数都有一个参数 dim,用来指定这些操作是在哪个维度上执行的。

假设输入的形状是 (m, n, k):

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

size 中是否有 “1”,取决于参数 keepdim,keepdim=True 会保留维度 1,注意,以上只是经验总结,并非所有的函数都符合这种形状变化方式。

Input:

1
2
3
4
5
6
7
8
9
10
11
12
import torch as t

b = t.ones(2, 3)

print(b.sum(dim=0, keepdim=True)) # 保留维度 1
print(b.sum(dim=0, keepdim=False)) # 不保留维度 1

print(b.sum(dim=1))

a = t.arange(0, 6).view(2, 3)
print(a)
print(a.cumsum(dim=1)) # 沿着行累加

Output:

3 . 常用的比较函数表。

函数 功能
gt/lt/ge/le/eq/ne 大于/小于/大于等于/小于等于/等于/不等
topk 最大的 k 个数
sort 排序
max/min 比较两个 tensor 最大最小值

表中第一行的比较操作已经实现了运算符重载,因此可以使用 a>=ba>ba!=ba==b,其返回结果是一个 ByteTensor,可用来选取元素。max/min 这两个操作比较特殊,以 max 为例说明如下:

  • torch.max(tensor):返回 tensor 中最大的那个数;
  • torch.max(tensor, dim):指定维度上最大的数,同时返回 tensor 和下标;
  • torch.max(tensor1, tensor2):返回两个 tensor 相比较大的元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import torch as t

a = t.linspace(0, 15, 6).view(2, 3)
print(a)

b = t.linspace(15, 0, 6).view(2, 3)
print(b)

print(a > b)

print(a[a > b]) # a 中对应位置大于 b 的元素

print(t.max(a))

print(t.max(b, dim=1)) # 分别返回对应维度的最大值和最大值所在的下标

print(t.max(a, b))

print(t.clamp(a, min=10)) # 比较 a 和 10 较大的元素

Output:

4 . 常用的线性代数函数表如下。

函数 功能
trace 矩阵的迹
diag 对角线元素
triu/tril 矩阵的上三角/下三角,可指定偏移量
mm/bmm 矩阵乘法,batch 的矩阵乘法
addmm/addbmm/addmv/addr/badbmm… 矩阵运算
t 转置
dot/cross 內积/外积
inverse 求逆矩阵
svd 奇异值分解

需要注意的是,矩阵的转置会导致存储空间不连续,需要调用 .contiguous 方法将其转为连续。

5 . Numpy 和 Tensor 可以相互转换,共享内存,但是当 Numpy 的数据类型和 Tensor 的数据类型不一样的时候,数据会被复制,不会共享内存。

Input:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import torch as t
import numpy as np

a = np.ones([2, 3])
print(a.dtype)

b = t.Tensor(a) # 此处进行拷贝,不共享内存
print(b.dtype)

c = t.from_numpy(a) # 注意 c 的类型(DoubleTensor)
print(c.dtype)

a[0, 1] = 100
print(b) # b 与 a 不共享内存,a 改变但 b 不变
print(c) # c 与 a 共享内存

Output:

6 . 广播法则是科学计算中经常使用的一个技巧,它在快速执行向量化的同时不会占用额外的内存/显存。

Numpy 的广播法则定义如下:

  • 让所有输入数组都向其中 shape 最长的数组看齐,shape 中不足的部分通过在前面加 1 补齐;
  • 两个数组在某一维度的长度要么一致,要么其中一个为 1,否则不能计算;
  • 当输入数组的某个维度的长度为 1 时,计算时沿此维度复制扩充成一样的形状。

PyTorch 已经支持了自动广播法则,但是我们还是通过以下两个函数手动实现一下广播法则以加深理解吧。

  • unsqueeze 或者 view,或者 tensor[None],为数据某一维的形状补 1,实现法则 1;
  • expand 或者 expand_as,重复数组,实现法则 3,该操作不会复制数组,所以不会占用额外的空间。

注意:repeat 实现与 expand 相类似的功能,但是 repeat 会把相同数据复制多份,因此会占用额外的空间。

Input:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import torch as t

a = t.ones(3, 2)
b = t.zeros(2, 3, 1)

# 自动广播法则
# 第一步: a 是 2 维,b 是 3 维,所以先在较小的 a 前面补 1,
# 即: a.unsqueeze(0),a 的形状变为 (1, 3, 2),b 的形状是 (2, 3, 1)
# 第二步: a 和 b 在第一维和第三维形状不一致,其中一个为 1,
# 可以利用广播法则,两者都扩展成 (2, 3, 2)

print(a + b)

# 手动广播法则
# 或者 a.view(1, 3, 2).expand(2, 3, 2) + b.expand(2, 3, 2)
print(a[None].expand(2, 3, 2) + b.expand(2, 3, 2))

Output:


笔记来源:《pytorch-book》