之前我们讲了如何从零搭建一个神经网络,但是我们要知道,只搭建是没用的,因为不经过训练的模型就像没上过学的孩子,什么知识也不知道,那就不叫人工智能了,叫人工智障。
上一节我们搞的是一个可以用于图片分类的神经网络,在输入的地方我们没弄数据集,知识给X随机初始化了一些数字,这次为了更直观得看一下效果,这里我们用Fashion-MNIST数据集做一下示范。
我们的模型有不同的参数($w$,$b$),因为这些参数能让模型学到对应的分布,训练模型也就意味着让模型获得比较好的参数。
现在我们已经有模型和数据集了,我们就可以使用数据优化我们的模型,进行训练、验证、测试我们的模型了。
模型训练过程是一个不断迭代的过程,在每个循环(epoch)中,模型都会根据输入获得一个对应的输出,并通过loss计算这个输出和真实输出的偏差,并收集这个偏差关于参数的导数,并使用梯度下降优化参数。
更详细的可以看这个视频:反向传播演算 - YouTube
代码
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor
training_data = datasets.FashionMNIST(
root="data",
train=True,
download=True,
transform=ToTensor()
)
test_data = datasets.FashionMNIST(
root="data",
train=False,
download=True,
transform=ToTensor()
)
train_dataloader = DataLoader(training_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)
这一段代码就是导包,然后加载torchvision的FashionMNIST数据集,我们可以看到同时加载了训练集和测试集,训练集用于模型的训练,测试集用于测试模型效果。
然后使用DataLoader
将数据加载进来,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),
nn.ReLU(),
nn.Linear(512, 512),
nn.ReLU(),
nn.Linear(512, 10),
)
def forward(self, x):
x = self.flatten(x)
logits = self.linear_relu_stack(x)
return logits
model = NeuralNetwork()
这里就是搭建一个神经网络,网络由一个展开层、三个线性层组成,使用的激活函数是ReLU激活函数,最后
搭建网络看不懂可以看LolitaAnn的:[零基础用PyTorch搭建自己的神经网络](https://blog.51cto.com/Lolitann/5949989
代码运行之后你可以看到嘎嘎给你把数据集安排好了。
超参数
超参数是训练人员手动调整的参数,用于控制模型的优化过程。设置不同的超参数数值会影响模型的训练和收敛效果。
在我们这里例子中我们需要定义以下几个超参数:
-
学习率
learning_rate
,每个batch、epoch模型参数的更新量。学习率如果设置的太小会导致学习速率较慢,如果设置的较大,可能会导致模型反复横跳无法优化。 -
一个批量的大小
batch_size
,模型一次读一张图太慢了,所以我们让模型一次获得一组图,这个参数就是每次给网络的样本数量。 -
迭代次数
epoch
,就是你要训练几个循环。
learning_rate = 1e-3
batch_size = 64
epochs = 5
我们设置好超参数之后们就可以进行循环来训练优化我们的模型了!每次循环称之为一个epoch。
每个epoch由两部分组成:
-
训练 - 使用训练集迭代,以寻找收敛到最佳参数。
-
验证、测试 使用测试集,检查模型的性能是否在不断提高。
损失函数
之前讲创建神经网络的时候我们说过,一个没有经过训练的模型,如果你给他测试集,他肯定胡说八道瞎猜答案的,因为他不知道正确答案是什么。现在我们要训练模型了,怎么让模型知道对错,就需要使用损失函数。
损失函数的作用是:衡量所获得的结果与目标值的相似程度。
我们在这个博客里的这个图像分类的例子,在训练过程中需要最小化的损失函数(交叉熵损失)。为了计算损失,我们使用给定数据样本的输入进行预测,并使用损失函数将其与真实数据标签值进行比较。
# Initialize the loss function
loss_fn = nn.CrossEntropyLoss()
优化器
模型优化过程就是在每次迭代中调整模型参数,减少模型误差。优化算法定义了整个优化过程,我们在这里使用随机梯度下降。所有的优化逻辑都封装在了optimizer
对象中,我们今天的例子使用的SDG优化器,Pytorch还提供了许多不同的优化器,感兴趣的可以看 torch.optim — PyTorch 1.12 documentation
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
开始训练!
现在一切准备完毕,我们可以安排上我们的训练和测试过程了!
def train(dataloader, model, loss_fn, optimizer):
# size是看数据集有多大,这里FashionMNIST数据集的测试集有60000张图。
size = len(dataloader.dataset)
for batch, (X, y) in enumerate(dataloader):
# 使用模型预测结果
pred = model(X)
# 计算loss
loss = loss_fn(pred, y)
# 反向传递过程
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 每100个batch输出一次结果
if batch % 100 == 0:
loss, current = loss.item(), batch * len(X)
print(f"loss: {loss:>7f} [{current:>5d}/{size:>5d}]")
这里再补充一下反向传播过程的代码解析:
-
optimizer.zero_grad()
:我们进行下一次batch梯度计算的时候,要清空上一个batch的梯度计算结果,因此这里在下一次梯度更新的时候,先使用optimizer.zero_grad
把梯度信息设置为0。 -
loss.backward()
:使用autograd沿着计算图反向传播,根据链式法则计算你所需要的梯度结果。 -
optimizer.step()
:用来更新参数。
def test(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"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
然后是测试部分的代码。我们可以看到有个with torch.no_grad()
,这个之前在torch.autograd原理讲过,在不需要计算梯度的时候加速运算过程。
下边这段代码是初始化损失函数和优化器,然后我们就可以设定epoch数量对模型进行学习过程进行跟踪了。
# 初始化损失函数和优化器
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
# 设定训练迭代的次数
epochs = 10
for t in range(epochs):
print(f"Epoch {t+1}\n-------------------------------")
train(train_dataloader, model, loss_fn, optimizer)
test(test_dataloader, model, loss_fn)
print("Done!")
之后你就可以看到输出长这个样子,一直到epoch 10,最后训练结束显示Done!
Epoch 1 <br> ------------------------------- loss: 2.298552 [ 0/60000]<br> loss: 2.287774 [ 6400/60000]<br> loss: 2.263033 [12800/60000]<br> loss: 2.262209 [19200/60000]<br> loss: 2.262916 [25600/60000]<br> loss: 2.220241 [32000/60000]<br> loss: 2.238093 [38400/60000]<br> loss: 2.196644 [44800/60000]<br> loss: 2.190552 [51200/60000]<br> loss: 2.169663 [57600/60000]<br> Test Error:<br> Accuracy: 32.2%, Avg loss: 2.160499<br> ……<br> Done!