Pytorch Learning Tour

This article records my process of learning Pytorch.

Chapter2

神经网络

神经网络的通用训练步骤

(1)定义一个包含可学习参数的神经网络。

(2)加载用于训练该网络的数据集。

(3)进行前向传播得到网络的输出结果,计算损失(网络输出结果与正确结果的差距)。

(4)进行反向传播,更新网络参数。

(5)保存网络模型。

定义网络

在定义网络时,模型需要继承nn.Module,并实现它的forward方法。其中,网络里含有可学习参数的层应该放在构造函数__init__()中,如果某一层(如ReLU)不含有可学习参数,那么它既可以放在构造函数中,又可以放在forward方法中。这里将这些不含有可学习参数的层放在forward方法中,并使用nn.functional实现:

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
32
import torch.nn as nn
import torch.nn.functional as F


class Net(nn.Module):
def __init__(self):
# nn.Module子类的函数必须在构造函数中执行父类的构造函数
# 下式等价于nn.Module.__init__(self)
super().__init__()

# 卷积层,'1'表示输入图片为单通道, '6'表示输出通道数,'5'表示卷积核为5×5
self.conv1 = nn.Conv2d(1, 6, 5)
# 卷积层,'6'表示输入图片为单通道, '16'表示输出通道数,'5'表示卷积核为5×5
self.conv2 = nn.Conv2d(6, 16, 5)
# 仿射层/全连接层,y = Wx + b
self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)

def forward(self, x):
# 卷积 -> 激活 -> 池化
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
# 改变Tensor的形状,-1表示自适应
x = x.view(x.size()[0], -1)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x

net = Net()
print(net)
1
2
3
4
5
6
7
Out:Net(
(conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
(conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
(fc1): Linear(in_features=400, out_features=120, bias=True)
(fc2): Linear(in_features=120, out_features=84, bias=True)
(fc3): Linear(in_features=84, out_features=10, bias=True)
)

用户只需要在nn.Module的子类中定义了forward函数,backward函数就会自动实现(利用autograd)。在forward函数中不仅可以使用Tensor支持的任何函数,还可以使用if、for、print、log等Python语法,写法和标准的Python写法一致。

使用net.parameters()可以得到网络的可学习参数,使用net.named_parameters()可以同时得到网络的可学习参数及其名称。

注意:torch.nn只支持输入mini-batch,不支持一次只输入一个样本。如果只输入一个样本,那么需要使用 input.unsqueeze(0)将batch_size设为1。例如, nn.Conv2d的输入必须是4维,形如$\text{nSamples} \times \text{nChannels} \times \text{Height} \times \text{Width}$。如果一次输入只有一个样本,那么可以将$\text{nSample}$设置为1,即$1 \times \text{nChannels} \times \text{Height} \times \text{Width}$。

损失函数

torch.nn实现了神经网络中大多数的损失函数,例如nn.MSELoss用来计算均方误差,nn.CrossEntropyLoss用来计算交叉熵损失等。

1
2
3
4
5
6
7
input = t.randn(1, 1, 32, 32)
output = net(input)
target = t.arange(0, 10).view(1, 10).float()
criterion = nn.MSELoss()
loss = criterion(output, target)
print(loss)
# Out:tensor(28.1249, grad_fn=<MseLossBackward>)

当调用loss.backward()时,计算图会动态生成并自动微分,自动计算图中参数(parameters)的导数,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
In: # 运行.backward,观察调用之前和调用之后的grad
net.zero_grad() # 把net中所有可学习参数的梯度清零
print('反向传播之前 conv1.bias的梯度')
print(net.conv1.bias.grad)
loss.backward()
print('反向传播之后 conv1.bias的梯度')
print(net.conv1.bias.grad)

'''Out:反向传播之前 conv1.bias的梯度
tensor([0., 0., 0., 0., 0., 0.])
反向传播之后 conv1.bias的梯度
tensor([ 0.0020, -0.0619, 0.1077, 0.0197, 0.1027, -0.0060])'''

优化器

在完成反向传播中所有参数的梯度计算后,需要使用优化方法来更新网络的权重和参数。常用的随机梯度下降法(SGD)的更新策略如下:

1
2
3
4
# weight = weight - learning_rate * gradient
learning_rate = 0.01
for f in net.parameters():
f.data.sub_(f.grad.data * learning_rate) # inplace减法

torch.optim中实现了深度学习中大多数优化方法,例如RMSProp、Adam、SGD等,

  1. 随机梯度下降(SGD)
  • 优点
    • 简单易实现。
    • 在大规模数据集和高维空间中依然有效,因为它每次更新只考虑一个样本,计算效率较高。
  • 缺点
    • 收敛速度可能比较慢,尤其是在梯度较小的平坦区域。
    • 可能会陷入局部最优解。
  1. 动量(Momentum)SGD
  • 优点
    • 通过累积过去梯度的信息来加速SGD,在相关方向上加快学习速度,减缓在非相关方向上的学习速度,从而加快收敛。
  • 缺点
    • 需要选择额外的动量参数,增加了调参的复杂度。
  1. RMSProp
  • 优点
    • 通过调整学习率来加快训练速度,适合处理非平稳目标——对于RNN的效果很好。
    • 能够在很多非凸优化问题中快速收敛。
  • 缺点
    • 和Momentum一样,需要设置更多的超参数(如学习率和衰减系数)。
  1. Adam(Adaptive Moment Estimation)
  • 优点
    • 结合了Momentum和RMSProp的优点,对学习率进行自适应调整。
    • 通常在很多不同的深度学习模型中表现良好,被广泛使用。
  • 缺点
    • 相对于简单的SGD,计算资源消耗更大。
    • 在某些情况下,Adam的自适应学习率可能导致收敛到次优解。

因此,通常情况下用户不需要手动实现上述代码。下面举例说明如何使用torch.optim进行网络的参数更新:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
In: import torch.optim as optim
#新建一个优化器,指定要调整的参数和学习率
optimizer = optim.SGD(net.parameters(), lr = 0.01)

# 在训练过程中
# 先梯度清零(与net.zero_grad()效果一样)
optimizer.zero_grad()

# 计算损失
output = net(input)
loss = criterion(output, target)

#反向传播
loss.backward()

#更新参数
optimizer.step()

数据加载与预处理

在深度学习中,数据加载及预处理是非常繁琐的过程。幸运的是,PyTorch提供了一些可以极大简化和加快数据处理流程的工具:DatasetDataLoader。同时,对于常用的数据集,PyTorch提供了封装好的接口供用户快速调用,这些数据集主要保存在torchvision中。torchvision是一个视觉工具包,它提供了许多视觉图像处理的工具,主要包含以下三部分。

  • datasets:提供了常用的数据集,如MNIST、CIFAR-10、ImageNet等。
  • models:提供了深度学习中经典的网络结构与预训练模型,如ResNet、MobileNet等。
  • transforms:提供了常用的数据预处理操作,主要包括对Tensor、PIL Image等的操作。

Chapter3

Author

OnlyourMiracle

Posted on

2024-03-24

Updated on

2024-03-26

Licensed under

Comments