optimization_tutorial 是关于Pytorch中优化器(optimizers)的基础知识。优化器是用来训练神经网络模型的机制,它们帮助调整模型中的参数(weights)来最小化损失(loss)函数。在这篇教程里,你会学到以下内容:
-
优化器的类型:这篇教程介绍了几种常用的优化器,包括随机梯度下降(SGD)、Adam等。
-
如何使用优化器:你会学到如何初始化一个优化器,如何将它与模型链接,以及如何使用优化器来进行模型训练。
-
损失函数:损失函数用来计算模型的误差,这篇教程讲解了如何定义损失函数以及如何计算损失函数的值。
-
反向传播(backpropagation):反向传播是计算模型梯度的重要机制,你会学到如何使用Pytorch中的自动求导机制来进行反向传播。
-
参数调整:最后,你会学到如何通过调整学习率(learning rate)、迭代次数(epochs)以及批量大小(batch size)等参数来提高模型性能。
优化模型参数
现在我们已经有了一个模型和数据,就可以通过在数据上优化其参数来训练、验证和测试模型。训练模型是一个迭代的过程;在每次迭代中,模型猜测输出,计算其猜测的错误(loss),收集错误关于参数的导数(正如我们在前一部分中所看到的那样),并使用梯度下降优化这些参数。
先决条件代码
我们从上一节 Datasets&DataLoaders 和 Build Model 中加载代码。
# 导入所需的包和库
import torch # 导入PyTorch库
from torch import nn # 导入Neural Network模块
from torch.utils.data import DataLoader # 导入DataLoader类
from torchvision import datasets # 从torchvision导入FashionMNIST数据集
from torchvision.transforms import ToTensor # 导入ToTensor函数
# 加载数据集
training_data = datasets.FashionMNIST(
root="~/data", # 数据集存储在data文件夹下
train=True, # 使用训练集
download=True, # 如果数据集不存在,请下载
transform=ToTensor() # 将数据转换为张量
)
# 加载测试集
test_data = datasets.FashionMNIST(
root="~/data", # 数据集存储在data文件夹下
train=False, # 使用测试集
download=True, # 如果数据集不存在,请下载
transform=ToTensor() # 将数据转换为张量
)
# 创建训练集数据加载器
train_dataloader = DataLoader(training_data, batch_size=64)
# 创建测试集数据加载器
test_dataloader = DataLoader(test_data, batch_size=64)
# 定义神经网络类
class NeuralNetwork(nn.Module):
def __init__(self):
super(NeuralNetwork, self).__init__()
self.flatten = nn.Flatten() # 将输入张量铺平
self.linear_relu_stack = nn.Sequential(
nn.Linear(28*28, 512), # 全连接层,28*28为输入维度,512为输出维度
nn.ReLU(), # 激活函数
nn.Linear(512, 512), # 全连接层,512为输入维度,512为输出维度
nn.ReLU(), # 激活函数
nn.Linear(512, 10), # 全连接层,512为输入维度,10为输出维度
)
def forward(self, x):
x = self.flatten(x) # 将输入张量铺平
logits = self.linear_relu_stack(x)
return logits
# 创建一个神经网络模型实例
model = NeuralNetwork()
超参数
超参数是可调整的参数,可以让您控制模型优化过程。不同的超参数值可以影响模型的训练和收敛速率(了解更多关于超参数调整的内容)。
我们为训练定义以下超参数:
- Epochs数量-迭代数据集的次数
- Batch Size-在更新参数之前通过网络传递的数据样本数量
- 学习率-在每个批次/时期中更新模型参数的量。较小的值产生较慢的学习速度,而较大的值可能导致训练过程中出现不可预测的行为。
# - 学习率:0.001 (1e-3)
learning_rate = 1e-3
# - Batch size:64
batch_size = 64
# - Epochs:5
epochs = 5
优化循环
一旦我们设置了超参数,就可以使用优化循环来训练和优化我们的模型。每次优化循环迭代称为一个 Epoch。
每个 Epoch 包括两个主要部分:
- 训练循环 - 遍历训练数据集,尝试收敛到最优参数。
- 验证/测试循环 - 遍历测试数据集,检查模型性能是否改善。
让我们简要了解一下训练循环中使用的一些概念。请直接跳转到
查看优化循环的 full-impl-label
。
损失函数
当我们给出一些训练数据时,未经训练的网络很可能不会给出正确的答案。损失函数度量了所得结果与目标值的不相似程度,我们希望在训练过程中最小化损失函数。要计算损失,我们使用给定数据样本的输入进行预测,然后将它与真实数据标签值进行比较。
常用的损失函数包括回归任务的 nn.MSELoss ,以及分类任务的 nn.NLLLoss ,nn.CrossEntropyLoss 结合了 nn.LogSoftmax
和 nn.NLLLoss
。
我们将模型的输出 logits 传递给 nn.CrossEntropyLoss
,它将对 logits 进行归一化,并计算预测误差。
# 初始化损失函数
loss_fn = nn.CrossEntropyLoss()
优化器
优化是调整模型参数以减少每个训练步骤中的模型误差的过程。 优化算法 定义了该过程的执行方式(在此示例中,我们使用随机梯度下降)。
所有优化逻辑都封装在“优化器”对象中。我们在这里使用SGD优化器;此外,PyTorch中有许多不同的优化器,例如ADAM和RMSProp,
它们对于不同种类的模型和数据效果更好。我们通过注册需要训练的模型参数并传递学习率超参数来初始化优化器。
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
在训练循环中,优化分为三个步骤:
- 调用
optimizer.zero_grad()
来重置模型参数的梯度。梯度默认会累加;为了防止重复计算,我们在每次迭代中显式将它们清零。 - 通过调用
loss.backward()
进行预测损失的反向传播。PyTorch会记录有关每个参数的损失的梯度。 - 一旦我们有了梯度,我们通过调用
optimizer.step()
来使用由反向传播收集的梯度调整参数。
完整实现
我们定义了 train_loop
,它循环我们的优化代码,并定义了 test_loop
,它评估模型对测试数据的性能。
def train_loop(dataloader, model, loss_fn, optimizer):
size = len(dataloader.dataset)
for batch, (X, y) in enumerate(dataloader):
# 计算预测值和损失
pred = model(X)
loss = loss_fn(pred, y)
# 反向传播
loss.backward()
optimizer.step()
optimizer.zero_grad()
if batch % 100 == 0:
loss, current = loss.item(), (batch + 1) * len(X)
# 打印当前的batch数和loss
print(f"loss: {loss:>7f} [{current:>5d}/{size:>5d}]")
def test_loop(dataloader, model, loss_fn):
size = len(dataloader.dataset)
num_batches = len(dataloader)
test_loss, correct = 0, 0
with torch.no_grad():
for X, y in dataloader:
# 预测并计算测试损失
pred = model(X)
test_loss += loss_fn(pred, y).item()
# 计算预测正确的数量
correct += (pred.argmax(1) == y).type(torch.float).sum().item()
test_loss /= num_batches
correct /= size
# 打印测试误差和准确度
print(f"测试错误: \n 准确率: {(100*correct):>0.1f}%, 平均损失: {test_loss:>8f} \n")
我们初始化损失函数和优化器,并将其传递给 train_loop
和 test_loop
函数。
可以增加epochs的数量,以跟踪模型的性能改进。
# 初始化损失函数
loss_fn = nn.CrossEntropyLoss()
# 初始化优化器,使用随机梯度下降
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
# 训练轮数epochs
epochs = 10
for t in range(epochs):
# 打印训练轮次
print(f"Epoch {t+1}\n-------------------------------")
# 训练模型
train_loop(train_dataloader, model, loss_fn, optimizer)
# 在测试集上评估模型
test_loop(test_dataloader, model, loss_fn)
# 打印输出,训练完毕。
print("完毕!")