街景字符识别赛题 TASK4-模型训练与验证

4.模型训练与验证

4.1 学习目标

  • 理解验证集的作用,并使用训练集和验证集完成训练
  • 学会使用pytorch环境下的模型读取和加载,并了解调参流程

4.2 验证集

一般情况下,将总样本分成三个部分:

  • 训练集(Train Set):用于训练和调整模型参数的数据集。
  • 验证集(Validation Set):用于验证模型精度和调整模型参数。
  • 测试集(Test Set): 验证模型的泛化能力。

需要注意验证集的分布应该与测试集尽量保持一致,不然模型在验证集上的精度就失去了指导意义。

验证集划分的方法:

  • 留出法(Hold-out)
    直接将训练集划分成两部分,新的训练集和验证集。这种划分方式的优点是最为直接简单;缺点是只得到了一份验证集,有可能导致模型在验证集上过拟合。留出法应用场景是数据量比较大的情况。
  • 交叉验证法(Cross-Validation,CV)
    将训练集划分成K份,将其中的K-1份作为训练集,剩余的1份作为验证集,循环K训练。这种划分方式是所有的训练集都是验证集,最终模型验证精度是K份平均得到。这种方式的优点是验证集精度比较可靠,训练K次可以得到K个有多样性差异的模型;CV验证的缺点是需要训练K次,不适合数据量很大的情况。
  • 自助采样法(BootStrap)
    通过有放回的采样方式得到新的训练集和验证集,每次的训练集和验证集都是有区别的。这种划分方式一般适用于数据量较小的情况。
    在这里插入图片描述

4.3 baseline的代码解读

  • train()函数
def train(train_loader, model, criterion, optimizer, epoch):
    model.train()	    # 切换模型为训练模式,自动计算梯度
    train_loss = []		# 记录每一个iteration的的loss,求平均便是整个epoch的误差
    
    for i, (input, target) in enumerate(train_loader):  #  从训练样本中取出batch_size个样本训练
        if use_cuda:
            input = input.cuda()
            target = target.cuda()
            
        c0, c1, c2, c3, c4 = model(input)
        target = target.long()  
        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()			  # loss反向传播, 
        optimizer.step()             # 优化器更新权重
        
        train_loss.append(loss.item())
    return np.mean(train_loss)
  • validate()函数
def validate(val_loader, model, criterion):
    model.eval()       # 切换模型为预测模型,不记录模型梯度信息
    val_loss = []		  # 记录每一个iteration的的loss,求平均便是整个epoch的误差

    # 不记录模型梯度信息
    with torch.no_grad():
        for i, (input, target) in enumerate(val_loader):
            if use_cuda:
                input = input.cuda()
                target = target.cuda()
            
            c0, c1, c2, c3, c4 = model(input)
            target = target.long()
            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)
  • predict()函数
def predict(test_loader, model, tta=10):
    model.eval()
    test_pred_tta = None
    
    # TTA 次数
    for _ in range(tta):
        test_pred = []
    
        with torch.no_grad():
            for i, (input, target) in enumerate(test_loader):
                if use_cuda:
                    input = input.cuda()
                
                c0, c1, c2, c3, c4 = model(input)
                target = target.long()
                if use_cuda:
                    output = np.concatenate([
                        c0.data.cpu().numpy(), 
                        c1.data.cpu().numpy(),
                        c2.data.cpu().numpy(), 
                        c3.data.cpu().numpy(),
                        c4.data.cpu().numpy()], axis=1)  # 将预测五位数字从右到左连起来
                else:
                    output = np.concatenate([
                        c0.data.numpy(), 
                        c1.data.numpy(),
                        c2.data.numpy(), 
                        c3.data.numpy(),
                        c4.data.numpy()], axis=1)
                
                test_pred.append(output)


        test_pred = np.vstack(test_pred) # 将每个batch_size的预测结从上到下连起来
        if test_pred_tta is None:
            test_pred_tta = test_pred
        else:
            test_pred_tta += test_pred
    
    return test_pred_tta
  • 主体函数
model = SVHN_Model1()   # 导入模型结构

criterion = nn.CrossEntropyLoss()  # 定义损失函数为交叉熵
optimizer = torch.optim.Adam(model.parameters(), 0.001) # 定义优化器
best_loss = 1000.0 # 定义初始loss

use_cuda = True
if use_cuda:
    model = model.cuda()
train_loss_all = [] # 记录train每一个epoch的loss
val_loss_all = []   # 记录val每一个epoch的loss
val_acc = []         	# 记录val 准确率的变化情况
for epoch in range(25):
    train_loss = train(train_loader, model, criterion, optimizer, epoch)
    val_loss = validate(val_loader, model, criterion)
    
    train_loss_all.append(train_loss)
    val_loss_all.append(val_loss)
    
    val_label = [''.join(map(str, x)) for x in val_loader.dataset.img_label]  # 用map函数做类型转换,读取出label
    val_predict_label = predict(val_loader, model, 1)   # 得到预测的label
    val_predict_label = np.vstack([
        val_predict_label[:, :11].argmax(1),
        val_predict_label[:, 11:22].argmax(1),
        val_predict_label[:, 22:33].argmax(1),
        val_predict_label[:, 33:44].argmax(1),
        val_predict_label[:, 44:55].argmax(1),
    ]).T																	# 取出预测概率最大的作为预测值, 并将5位连在一起
    val_label_pred = []
    for x in val_predict_label:
        val_label_pred.append(''.join(map(str, x[x!=10]))) # 将预测值为10的剔除
    
    val_char_acc = np.mean(np.array(val_label_pred) == np.array(val_label)) # 求准确率
    val_acc.append(val_char_acc)
    print('Epoch: {0}, Train loss: {1} \t Val loss: {2}'.format(epoch, train_loss, val_loss))
    print('Val Acc', val_char_acc)
    # 记录下验证集精度
    if val_loss < best_loss:
        best_loss = val_loss
        # print('Find better model in Epoch {0}, saving model.'.format(epoch))
        torch.save(model.state_dict(), './model_2.pt')
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值