训练
def train(train_loader, model, criterion, optimizer):
# 切换模型为训练模式
model.train()
train_loss = []
for i, (input, target) in enumerate(train_loader):
if use_cuda:
input = input.cuda()
target = target.cuda()
target = target.long()
c0, c1, c2, c3, c4 = model(input)
loss = criterion(c0, target[:, 0]) + \
criterion(c1, target[:, 1]) + \
criterion(c2, target[:, 2]) + \
criterion(c3, target[:, 3]) + \
criterion(c4, target[:, 4])
# loss /= 6
optimizer.zero_grad()
loss.backward()
optimizer.step()
if i % 100 == 0:
print(loss.item())
train_loss.append(loss.item())
return np.mean(train_loss)
验证
def validate(val_loader, model, criterion):
# 切换模型为预测模型
model.eval()
val_loss = []
# 不记录模型梯度信息
with torch.no_grad():
for i, (input, target) in enumerate(val_loader):
if use_cuda:
input = input.cuda()
target = target.cuda()
target = target.long()
c0, c1, c2, c3, c4 = model(input)
loss = criterion(c0, target[:, 0]) + \
criterion(c1, target[:, 1]) + \
criterion(c2, target[:, 2]) + \
criterion(c3, target[:, 3]) + \
criterion(c4, target[:, 4])
# loss /= 6
val_loss.append(loss.item())
return np.mean(val_loss)
观察这两个函数,最大的不同在于输入的参数和梯度的使用,这两者决定了网络内部参数是否改变。训练的部分每个tensor都附加了梯度,因为需要用label来矫正网络的参数。但是在训练中可能会产生过拟合,这种情形下,验证的意义就提现出来了,验证是用理论上和训练数据集独立同分布的数据去评判结果的好坏,不能改变被评判的网络内部结构,所以没有附加梯度,没有传入优化函数。
另一个感觉有意思的就是model.eval()和model.train(),网上说model.train()启用 BatchNormalization 和 Dropout,model.eval()不启用 BatchNormalization 和 Dropout。但是在自定义model的时候似乎并没有自己写这两种处理方式,看来在继承nn.Model的时候,这些就已经内部定义了。
整合起来,具体的寻优过程就比较好理解了,就是设定的epoch次数内,每次都进行训练和验证,最优的验证结果就对应最优模型。从这里也可以看到eval()的意义,如果验证时还有Dropout,那验证的方法的就相当于每次都不同,结果也就失去了意义。