PyTorch-Part1——基本概念

[TOC]

资源汇总(后续放入总结篇)

  1. PyTorch-handbook 中文手册:与 PyTorch 版本保持一致。
  2. 《Pytorch模型训练实用教程》:PyTorch 模型训练方面的干货教程。特别工业化,真的非常棒。
  3. PyTorch官方教程中文版:标准教程,主要是因为有stn。
  4. 《深度学习框架PyTorch:入门与实践》:理论和实战,动漫头像生成器。
  5. Awesome-Pytorch-list:庞大的 PyTorch 资源库。
  6. PyTorch Examples:入门案例,可以在这个基础上增改自己的代码。
  7. PyTorch Forums:PyTorch 官方论坛,可以经常翻阅,减少弯路。
  1. 检查是否可导
  2. 哪些操作不可微

本篇笔记只记录 PyTorch 常用操作

  1. 按照训练顺序记录各步骤常用方法
  2. 实战干货总结
  3. 不同网络案例代码
  4. 底层剖析与数学原理
  5. tf1转pytorch

Pytorch 简介

  • Torch 是一个与 Numpy 类似的张量(Tensor)操作库,与 Numpy 不同的是 Torch 对GPU支持的很好,Lua 是 Torch 的上层包装。
  • PyTorch 和 Torch 使用包含所有相同性能的C库:TH, THC, THNN, THCUNN,只是使用了不同的上层包装语言。
  • PyTorch 框架设计相当简洁优雅且高效快速。
  • 与 google 的 Tensorflow 类似,FAIR 的支持足以确保 PyTorch 获得持续的开发更新。
  • PyTorch 拥有完善的文档,作者亲自维护论坛。

PyTorch 安装与测试

张量

创建张量

  • 快速测试可以经常使用 torch.rand()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import torch

# 创建未初始化的5行3列的矩阵,注意和 torch.zeros 是不同的
x = torch.empty(5, 3)
# 创建一个随机初始化的矩阵
x = torch.rand(5, 3)
# 创建一个 0 填充的矩阵,数据类型为 long,long 不允许计算梯度
x = torch.zeros(5, 3, dtype=torch.long)
# 创建 tensor 并使用现有数据初始化,只要有一个是 float,则都为 float
x = torch.tensor([5.5, 3])
# _like 方法: 根据现有的张量创建相同大小的张量
x = torch.randn_like(x, dtype=torch.float)
x = torch.ones_like(x).to(torch.float)
# 获取张量大小。size() 和 shape 是等价的。
print(x.size())
print(x.shape)
  • 可以好好体会下面一个例子: batch_size=2 channel=3 size=(h=4, w=5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
>>>  x = torch.rand((2, 3, 4, 5), dtype=torch.float, requires_grad=True)
tensor([[[[0.0628, 0.6673, 0.3958, 0.0904, 0.2442],
[0.4635, 0.0213, 0.2310, 0.1643, 0.7705],
[0.6754, 0.9084, 0.3516, 0.8552, 0.5362],
[0.0650, 0.8016, 0.1424, 0.3343, 0.0216]],

[[0.2237, 0.5758, 0.1204, 0.8498, 0.4453],
[0.0703, 0.1054, 0.4191, 0.1271, 0.9603],
[0.4301, 0.9627, 0.9707, 0.9125, 0.9281],
[0.4365, 0.1514, 0.9759, 0.4679, 0.8695]],

[[0.4225, 0.5115, 0.2755, 0.1248, 0.8858],
[0.4288, 0.8042, 0.2394, 0.6829, 0.5082],
[0.7765, 0.7435, 0.2163, 0.9029, 0.6852],
[0.2889, 0.3367, 0.8794, 0.9265, 0.6639]]],


[[[0.8373, 0.0672, 0.8151, 0.7912, 0.2508],
[0.9232, 0.5653, 0.1964, 0.0986, 0.5448],
[0.8444, 0.8974, 0.0763, 0.9074, 0.7959],
[0.6146, 0.1738, 0.0814, 0.7200, 0.0448]],

[[0.9523, 0.3441, 0.1840, 0.9286, 0.4231],
[0.9800, 0.4126, 0.8632, 0.8323, 0.2245],
[0.9756, 0.5459, 0.1382, 0.2115, 0.0617],
[0.8045, 0.4060, 0.6943, 0.0992, 0.4955]],

[[0.0712, 0.9649, 0.7187, 0.5300, 0.8720],
[0.2673, 0.9442, 0.5604, 0.2986, 0.2902],
[0.8061, 0.5989, 0.4864, 0.7042, 0.1167],
[0.6609, 0.0652, 0.9130, 0.8308, 0.6552]]]], requires_grad=True)

操作张量

  • 加法
1
2
3
4
5
6
7
8
9
10
11
12
13
import torch

x = torch.ones(5, 3)
y = torch.ones(5, 3)
# 1. 直接运算符相加
z = x + y
# 2. .add() 方法
z = torch.add(x, y)
# 3. 替换指定张量
z = torch.empty(5, 3)
torch.add(x, y, out=z)
# 4. 将 x 加在y上
y.add_(x)

任何 以 _ 结尾的操作都会用结果替换原变量。例如:x.copy_(y)x.t_(),都会改变 x

  • 使用索引切片操作张量
1
2
3
4
x = torch.ones(5, 3)
print(x)
print(x[:, 1])
print(x[:, [1]]) # 依然保持二维
  • torch.view():改变张量的维度和大小
1
2
3
4
x = torch.rand((4, 4), dtype=torch.float)
y = x.view(16)
z = x.view(-1, 1, 8) # -1 为自动推断
print(x.size(), y.size(), z.size())
  • .item():以Python数据类型获取张量中的数值
1
2
3
x = torch.randn(1)
print(x)
print(type(x.item()))

100+ Tensor operations, including transposing, indexing, slicing, mathematical operations, linear algebra, random numbers, etc., are described here https://pytorch.org/docs/torch.

NumPy 转换

Torch Tensor与NumPy数组共享底层内存地址,修改任何一个都会导致另一个变化。

  • .numpy():将一个 Torch Tensor 转换为 NumPy 数组
1
2
3
4
5
6
7
8
9
10
a = torch.ones(5)  # tensor([1., 1., 1., 1., 1.])
b = a.numpy() # [1. 1. 1. 1. 1.]
# 共享内存的操作
a.add_(1)
print(a) # tensor([2., 2., 2., 2., 2.])
print(b) # [2. 2. 2. 2. 2.]
# 不共享内存的操作
a = a + 1
print(a) # tensor([2., 2., 2., 2., 2.])
print(b) # [1. 1. 1. 1. 1.]
  • torch.from_numpy(a):NumPy Array 转化成 Torch Tensor
1
2
3
4
5
6
7
8
a = np.ones(5)
b = torch.from_numpy(a)
# 共享内存的操作
a += 1
np.add(a, 1, out=a)
# 不共享内存的操作
a = a + 1
a = np.add(a, 1)

CUDA 张量

  • torch.device("cuda:0"):参数可以为 "cuda:0"/"cuda"/"cpu"
  • device参数:可传参同上。
  • .to(device):可以指定数据类型,也移动到指定设备。
    • 同时指定时顺序需要为:.to(device, torch.float)
    • 注意:只调用 .to("cuda") 并没有复制张量到 GPU 上,而是返回了一个 copy。所以,需要把它赋值给一个新的张量并在GPU上使用这个张量。
1
2
3
4
5
6
7
x = torch.randn(3)
device = torch.device("cuda:0") # 创建 CUDA 设备对象
y = torch.ones_like(x, device=device) # 直接在 CUDA 中创建张量
x = x.to(device) # .to("cuda") 将张量移动到 cuda 中
z = x + y
print(z)
print(z.to("cpu", torch.double)) # 同时指定设备与类型

自动求导机制(Autograd)

  • torch.autograd 包是 PyTorch 中所有神经网络的核心,它为张量上的所有操作提供了自动求导。
  • torch.autograd 是一个在运行时定义的框架,这意味着反向传播是根据你的代码来确定如何运行,并且每次迭代可以是不同的。

张量(Tensor)

  • torch.Tensor 是这个包的核心类。

    • 如果设置 .requires_grad=True,那么将会追踪所有对于该张量的操作。当完成计算后通过调用 .backward(),自动计算所有的梯度,这个张量的所有梯度将会积累到 .grad 属性。
    • 要阻止张量跟踪历史记录,可以调用 .detach() 方法将其与计算历史记录分离,并禁止跟踪它将来的计算记录。
    • 为了防止跟踪历史记录(和使用内存),可以将代码块包装在with torch.no_grad():中。在评估模型时特别有用,因为模型可能具有 requires_grad = True 的可训练参数,但是我们不需要梯度计算。
  • 在自动梯度计算中还有另外一个重要的类 Function.

    • TensorFunction 互相连接并生成一个非循环图,它表示和存储了完整的计算历史。每个张量都有一个 .grad_fn 属性,这个属性引用了一个创建了 TensorFunction (除非这个张量是用户手动创建的,即,这个张量的 grad_fnNone)。
    • 如果需要计算导数,你可以在 Tensor 上调用 .backward()。 如果 Tensor 是一个标量(即它包含一个元素数据)则不需要为 backward() 指定任何参数,但是如果它有更多的元素,你需要指定一个gradient 参数来匹配张量的形状。
  • 在其他的文章中可能会看到说将 Tensor 包裹到 Variable 中提供自动梯度计算。Variable 在0.41版中已经被标注为过期了,现在可以直接使用 Tensor,官方文档:https://pytorch.org/docs/stable/autograd.html#variable-deprecated

1
2
3
4
5
6
7
x = torch.ones(2, 2, requires_grad=True)
print(x) # requires_grad=True
y = x + 2 # <AddBackward0 object at 0x0000025D5A03A860>
print(y) # 进行了一次加运算得出结果 y,因此自动生成了 grad_fn 追踪张量操作,但还没有生成梯度

z = y * y * 3 # grad_fn=<MulBackward0>
out = z.mean() # grad_fn=<MeanBackward0>

梯度

  • image-20210912171608055
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 现在让我们来看一个vector-Jacobian product的例子
x = torch.randn(3, requires_grad=True)
y = x * 2
while y.data.norm() < 1000:
y = y * 2
print(y)

# 在这个情形中,y不再是个标量。torch.autograd无法直接计算出完整的雅可比行列,但是如果我们只想要vector-Jacobian product,只需将向量作为参数传入backward:
gradients = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(gradients)
print(x.grad)

# 如果.requires_grad=True但是你又不希望进行autograd的计算, 那么可以将变量包裹在 with torch.no_grad()中:
print(x.requires_grad)
print((x ** 2).requires_grad)
with torch.no_grad():
print((x ** 2).requires_grad)

autogradFunction 的官方文档 https://pytorch.org/docs/autograd

model.train():
在使用pytorch构建神经网络的时候,训练过程中会在程序上方添加一句model.train(),作用是启用batch normalization和drop out。

model.eval():
测试过程中会使用model.eval(),这时神经网络会沿用batch normalization的值,并不使用drop out。