街景字符识别4-模型训练与验证

模型训练

模型训练是反复寻找最优参数,从而实现误差最小化的过程。其中最重要的元素包括:模型、损失函数以及优化算法。

1 模型

微调resnet18的CNN+5层全连接层

2 损失函数

损失函数是评估模型性能的指标,其中:

  1. 损失函数与具体的应用目的相关,以目的为导向选择损失函数。比如分类和回归问题会分别以准确率、MSE来评估模型性能。
    街景字符识别4-模型训练与验证_第1张图片
  2. 设计损失函数应该明确模型的真实误差和模型复杂度,既要保证损失函数能够很好的反映训练误差,又要保证模型不至于过度繁琐(过拟合的风险),也就是奥卡姆剃刀原理。

3 优化算法

优化算法的目标函数是一个基于训练数据集的损失函数,优化目标在于降低训练误差。梯度下降是求解最优参数的常用方法之一,权重更新通过后向算法实现,旨在通过沿着多维误差的负梯度方向减少误差直至收敛。

3.1 梯度下降

根据计算梯度所使用样本的数量可以分为梯度下降(batch gradient decent)和随机梯度下降(Stochastic gradient decent)、 小批量随机梯度下降(Mini-batch gradient decent),其中:

  • batch gradient decent:每轮使用全部样本进行参数更新;
  • Stochastic gradient decent:每轮使用1个随机样本进行参数更新;
  • Mini-batch gradient decent:每轮使用b个样本进行更新,b=10.
    三种梯度下降方法对比

本次baseline中使用的是batch=40的小批量随机梯度下降(Mini-batch gradient decent)方法。
街景字符识别4-模型训练与验证_第2张图片

3.2 后向算法

后向算法的流程如下:

  1. 随机初始化权重,
  2. 通过前向传播获得模型输出
  3. 计算损失函数即误差
  4. 后向传播计算误差相对各权重的梯度
  5. 更新权重
  6. 重复2~5减小误差直至收敛。

3.3 自动调节学习率方法

在第5步更新权重时,涉及学习率(learning rate)。这可以看做梯度下降的幅度,过大可能导致误差偏大,过小则消耗过多训练时间。学习率的自动调节方法:Adagrad【对低频的参数做较大的更新,对高频的做较小的更新】,Adam【存储过去梯度的平方 vt 的指数衰减平均值和 mt 的指数衰减平均值】。本次baseline使用Adam适应性学习方法自动调节学习率。

为了理解

3.3.1 Adagrad
3.3.2 Adam

4 验证

目的

随着模型复杂度和模型训练轮数的增加,CNN模型在训练集上的误差会降低,但在测试集上的误差会逐渐降低,然后逐渐升高,而我们为了追求的是模型在测试集上的精度越高越好。
导致模型过拟合的情况有很多种原因,其中最为常见的情况是模型复杂度(Model Complexity )太高,导致模型学习到了训练数据的方方面面,学习到了一些细枝末节的规律。

解决上述问题最好的解决方法:构建一个与测试集尽可能分布一致的样本集(可称为验证集),在训练过程中不断验证模型在验证集上的精度,并以此控制模型的训练。

构造方法

对于所提供的数据,尤其是数据量较大时,采用留出法直接将训练集划分成两部分——新的训练集和验证集。这种划分方式的优点是最为直接简单;缺点是只得到了一份验证集,有可能导致模型在验证集上过拟合。

5 Pytorch实现模型训练与验证

#模型:[微调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')#保持模型参数        

6 参考

《机器学习_学习笔记(all in one)_V0.96.pdf》

你可能感兴趣的:(零基础入门CV赛事-,街景字符编码识别)