在上面一篇文章,我介绍了LeNet的一些基本知识,下面我们来看如何训练这个网络,后续的训练基本上就是按照这个套路来就可以
首先上代码,代码取自《dive into deep learning》
def train(net, train_iter, test_iter, num_epochs, lr, device):
# 首先初始化权重,使用nn.init.xavier_uniform_初始化函数
def init_weights(m):
if type(m) == nn.Linear or type(m) == nn.Conv2d:
nn.init.xavier_uniform_(m.weight)
# 经过初始化的权重应用到网络中,用于初始化模型的权重
net.apply(init_weights)
# 下面是要将模型进行部署,这里是要部署到GPU上
print('training on', device)
net.to(device)
# 定义优化器更新模型参数
optimizer = torch.optim.SGD(net.parameters(), lr=lr)
# 交叉熵损失
loss = nn.CrossEntropyLoss()
animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs],
legend=['train loss', 'train acc', 'test acc'])
timer, num_batches = d2l.Timer(), len(train_iter)
for epoch in range(num_epochs):
# 训练损失之和,训练准确率之和,样本数
metric = d2l.Accumulator(3)
net.train()
for i, (X, y) in enumerate(train_iter):
timer.start()
optimizer.zero_grad()
X, y = X.to(device), y.to(device)
y_hat = net(X)
l = loss(y_hat, y)
l.backward()
optimizer.step()
with torch.no_grad():
metric.add(l * X.shape[0], d2l.accuracy(y_hat, y), X.shape[0])
timer.stop()
train_l = metric[0] / metric[2]
train_acc = metric[1] / metric[2]
if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1:
animator.add(epoch + (i + 1) / num_batches,
(train_l, train_acc, None))
test_acc = evaluate_accuracy_gpu(net, test_iter)
animator.add(epoch + 1, (None, None, test_acc))
print(f'loss {train_l:.3f}, train acc {train_acc:.3f}, '
f'test acc {test_acc:.3f}')
print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec '
f'on {str(device)}')
下面对这段代码进行解释,如有不当敬请指出
-
def train(net, train_iter, test_iter, num_epochs, lr, device):
定义了一个函数 train,接受模型 net、训练集迭代器 train_iter、测试集迭代器 test_iter、迭代的轮数 num_epochs、学习率 lr 和设备类型 device 作为输入参数。 -
def init_weights(m):
定义了一个内部函数 init_weights,这个函数在后面的代码中被用来对模型的权重进行初始化。 -
if type(m) == nn.Linear or type(m) == nn.Conv2d:
在 init_weights 函数中,如果 m 是 nn.Linear 或者 nn.Conv2d 类型的模块,则执行下面的语句。 -
nn.init.xavier_uniform_(m.weight)
用 Xavier 初始化方法对模块 m 的权重进行初始化。Xavier 初始化是一种常用的权重初始化方法,旨在解决深度神经网络中梯度消失和梯度爆炸的问题。这个方法假设前一层的输入和后一层的输出具有相等的方差,从而使得信息能够在网络中保持相对稳定。nn.init.xavier_uniform_()是一个就地操作,它会直接修改所传入张量的值,而不会返回一个新的张量。这意味着无需重新分配内存,可以直接在原地初始化权重。 -
net.apply(init_weights)
调用 apply 方法,将 init_weights 函数应用到 net 模型的所有模块的权重上,用于初始化模型的权重。 -
print(‘training on’, device)
打印当前训练所使用的设备类型。 -
net.to(device)
将模型 net 移动到指定的设备上,以便在该设备上进行训练。 -
optimizer = torch.optim.SGD(net.parameters(), lr=lr)
创建一个随机梯度下降(SGD)优化器对象 optimizer,用于更新模型的参数。传入的参数是模型 net 的可学习参数和学习率 lr。
优化器这里后面开一篇新的帖子记录。 -
loss = nn.CrossEntropyLoss()
创建一个交叉熵损失函数对象 loss,用于计算模型的输出与真实标签之间的损失。 -
animator = d2l.Animator(xlabel=‘epoch’, xlim=[1, num_epochs], legend=[‘train loss’, ‘train acc’, ‘test acc’])
创建一个动画显示对象 animator,用于在训练过程中可视化训练损失、训练准确率和测试准确率的变化。xlabel 参数表示横轴标签为 “epoch”,xlim 参数为横轴的范围,legend 参数表示图例标签。 -
timer, num_batches = d2l.Timer(), len(train_iter)
创建一个计时器对象 timer,用于计算训练每个 epoch 所需的时间,并且获取训练集迭代器 train_iter 的长度作为总批次数 num_batches。 -
for epoch in range(num_epochs):
开始迭代训练的轮数,从 0 到 num_epochs-1。 -
metric = d2l.Accumulator(3)
创建一个累加器对象 metric,用于累计训练损失之和、训练准确率之和和样本数量。 -
net.train()
把模型设置为训练模式,这会启用 dropout 和批量归一化等层的训练行为。 -
for i, (X, y) in enumerate(train_iter):
迭代训练集数据,获取每个批次的图像数据 X 和标签数据 y。 -
optimizer.zero_grad()
清零所有模型参数的梯度。 -
X, y = X.to(device), y.to(device)
将输入数据 X 和标签数据 y 移动到指定的设备上。 -
y_hat = net(X)
使用当前模型 net 对输入数据 X 进行前向传播得到预测值 y_hat。 -
l = loss(y_hat, y)
计算预测值 y_hat 和真实标签 y 之间的损失值。 -
l.backward()
反向传播,计算损失相对于模型参数的梯度。 -
optimizer.step()
根据计算得到的梯度更新模型参数。 -
with torch.no_grad():
在上下文中,禁用梯度计算。 -
metric.add(l * X.shape[0], d2l.accuracy(y_hat, y), X.shape[0])
使用累加器 metric 分别累加当前批次的训练损失、训练准确率和样本数量。 -
timer.stop()
停止计时器。 -
train_l = metric[0] / metric[2]
计算平均训练损失。 -
train_acc = metric[1] / metric[2]
计算平均训练准确率。 -
(i + 1) % (num_batches // 5) == 0 or i == num_batches - 1:
如果迭代达到每个 epoch 的五分之一进度(包括最后一次迭代),执行下面的语句。 -
animator.add(epoch + (i + 1) / num_batches, (train_l, train_acc, None))
添加当前训练损失、训练准确率和 None(表示未知测试准确率)到动画显示对象 animator 中。 -
test_acc = evaluate_accuracy_gpu(net, test_iter)
使用 evaluate_accuracy_gpu 函数计算在 GPU 上测试集的准确率 test_acc。 -
nimator.add(epoch + 1, (None, None, test_acc))
添加当前 epoch 的测试准确率到动画显示对象 animator 中。 -
print(f’loss {train_l:.3f}, train acc {train_acc:.3f}, test acc {test_acc:.3f}')
打印当前 epoch 的训练损失、训练准确率和测试准确率。 -
print(f’{metric[2] * num_epochs / timer.sum():.1f} examples/sec on {str(device)}')
打印每秒处理的样本数量,即训练过程中的平均速度。