模型训练是反复寻找最优参数,从而实现误差最小化的过程。其中最重要的元素包括:模型、损失函数以及优化算法。
微调resnet18的CNN+5层全连接层
损失函数是评估模型性能的指标,其中:
优化算法的目标函数是一个基于训练数据集的损失函数,优化目标在于降低训练误差。梯度下降是求解最优参数的常用方法之一,权重更新通过后向算法实现,旨在通过沿着多维误差的负梯度方向减少误差直至收敛。
根据计算梯度所使用样本的数量可以分为梯度下降(batch gradient decent)和随机梯度下降(Stochastic gradient decent)、 小批量随机梯度下降(Mini-batch gradient decent),其中:
本次baseline中使用的是batch=40的小批量随机梯度下降(Mini-batch gradient decent)方法。
后向算法的流程如下:
在第5步更新权重时,涉及学习率(learning rate)。这可以看做梯度下降的幅度,过大可能导致误差偏大,过小则消耗过多训练时间。学习率的自动调节方法:Adagrad【对低频的参数做较大的更新,对高频的做较小的更新】,Adam【存储过去梯度的平方 vt 的指数衰减平均值和 mt 的指数衰减平均值】。本次baseline使用Adam适应性学习方法自动调节学习率。
为了理解
随着模型复杂度和模型训练轮数的增加,CNN模型在训练集上的误差会降低,但在测试集上的误差会逐渐降低,然后逐渐升高,而我们为了追求的是模型在测试集上的精度越高越好。
导致模型过拟合的情况有很多种原因,其中最为常见的情况是模型复杂度(Model Complexity )太高,导致模型学习到了训练数据的方方面面,学习到了一些细枝末节的规律。
解决上述问题最好的解决方法:构建一个与测试集尽可能分布一致的样本集(可称为验证集),在训练过程中不断验证模型在验证集上的精度,并以此控制模型的训练。
对于所提供的数据,尤其是数据量较大时,采用留出法直接将训练集划分成两部分——新的训练集和验证集。这种划分方式的优点是最为直接简单;缺点是只得到了一份验证集,有可能导致模型在验证集上过拟合。
#模型:[微调resnet18的CNN+5层全连接层](https://blog.csdn.net/charie411/article/details/106347323)
model = SVHN_Model2()#https://blog.csdn.net/charie411/article/details/106347323
#损失函数:交叉熵
loss = nn.CrossEntropyLoss (size_average=False)
#优化算法:基于mini-batch梯度下降的Adam学习率自适应方法
optimizer = torch.optim.Adam(model.parameters(), 0.001)
def train(train_loader, model, loss, optimizer):
# 切换模型为训练模式
model.train()
#Tensor 是这个包的核⼼类,如果将其属性 .requires_grad 设置为 True ,它将开始追踪(track)在其上的所有操作(这样就可以利⽤链式法则进⾏梯度传播了)。完成计算后,可以调⽤ .backward() 来完成所有梯度计算。此 Tensor 的梯度将累积到 .grad 属性中
for i, (input, target) in enumerate(train_loader):
c1, c2, c3, c4, c5 = model(data[0])
sumloss = loss(c1, data[1][:, 0]) + \
loss(c2, data[1][:, 1]) + \
loss(c3, data[1][:, 2]) + \
loss(c4, data[1][:, 3]) + \
loss(c5, data[1][:, 4]))
sumloss /= 6#平均
optimizer.zero_grad()#注意:grad在反向传播过程中是累加的,这意味着
#每⼀次运⾏反向传播,梯度都会累加之前的梯度,所以⼀般在反向传播之前需
#把梯度清零。
sumloss.backward()#后向传播完成所有梯度计算
optimizer.step()#迭代模型参数
def validate(val_loader, model, loss):
# 切换模型为预测模型
model.eval()
val_loss = []
# 不记录模型梯度信息
with torch.no_grad():
for i, (input, target) in enumerate(val_loader):
c1, c2, c3, c4, c5 = model(data[0])
sumloss = loss(c1, data[1][:, 0]) + \
loss(c2, data[1][:, 1]) + \
loss(c3, data[1][:, 2]) + \
loss(c4, data[1][:, 3]) + \
loss(c5, data[1][:, 4]))
sumloss /= 6
val_loss.append(sumloss.item())
return np.mean(val_loss)
best_loss = 1000.0
for epoch in range(10): #设定训练轮数,当轮数达到要求时停止训练。
print('Epoch: ', epoch)
#train_loader见https://blog.csdn.net/charie411/article/details/106304031
train(train_loader, model, loss, optimizer)
val_loss = validate(val_loader, model, criterion)
# 记录下验证集精度
if val_loss < best_loss:
best_loss = val_loss
torch.save(model.state_dict(), './model.pt')#保持模型参数
《机器学习_学习笔记(all in one)_V0.96.pdf》