街景字符识别比赛心得

阿里天池-零基础入门CV赛事(街景字符识别编码)-Task 4 模型训练与验证

  • 1 模型训练与验证
    • 1.1 构造验证集
    • 1.2 模型训练与验证
    • 1.3 模型保存与加载
    • 1.4 模型调参流程

1 模型训练与验证

1.1 构造验证集

在机器学习模型(特别是深度学习模型)的训练过程中,模型是非常容易过拟合的。深度学习模型在不断的训练过程中训练误差会逐渐降低,但测试误差的走势则不一定。

在模型的训练过程中,模型只能利用训练数据来进行训练,模型并不能接触到测试集上的样本。因此模型如果将训练集学的过好,模型就会记住训练样本的细节,导致模型在测试集的泛化效果较差,这种现象称为过拟合(Overfitting)。与过拟合相对应的是欠拟合(Underfitting),即模型在训练集上的拟合效果较差。
导致模型过拟合的情况有很多种原因,其中最为常见的情况是模型复杂度(Model Complexity )太高,导致模型学习到了训练数据的方方面面,学习到了一些细枝末节的规律。

解决上述问题最好的解决方法:从训练集中划分出一个验证集来验证训练模型的好坏,在训练过程中不断验证模型在验证集上的精度,并以此控制模型的训练。

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

验证集的划分有如下几种方式:

  • 留出法(Hold-Out)
    直接将训练集划分成两部分,新的训练集和验证集。这种划分方式的优点是最为直接简单;缺点是只得到了一份验证集,有可能导致模型在验证集上过拟合。留出法应用场景是数据量比较大的情况。

  • k折交叉验证法(Cross Validation,CV)
    将训练集划分成K份,将其中的K-1份作为训练集,剩余的1份作为验证集,循环K训练。这种划分方式是所有的训练集都是验证集,最终模型验证精度是K份平均得到。这种方式的优点是验证集精度比较可靠,训练K次可以得到K个有多样性差异的模型;CV验证的缺点是需要训练K次,不适合数据量很大的情况。

  • 自助采样法(BootStrap)
    通过有放回的采样方式得到新的训练集和验证集,每次的训练集和验证集都是有区别的。这种划分方式一般适用于数据量较小的情况。

1.2 模型训练与验证

  • 构造训练集和验证集
  • 每轮进行训练和验证, 并更具最优验证精度保存模型
import torch.nn as nn
from torch.utils.data.dataset import Dataset

# class SVHN_Model1(nn.Module):
#     def __init__(self):
#         super(SVHN_Model1, self).__init__()
               
#         model_conv = models.resnet18(pretrained=True)
#         model_conv.avgpool = nn.AdaptiveAvgPool2d(1)
#         model_conv = nn.Sequential(*list(model_conv.children())[:-1])
#         self.cnn = model_conv
       
#         self.fc1 = nn.Linear(512, 11)
#         self.fc2 = nn.Linear(512, 11)
#         self.fc3 = nn.Linear(512, 11)
#         self.fc4 = nn.Linear(512, 11)
#         self.fc5 = nn.Linear(512, 11)
   
#     def forward(self, img):        
#         feat = self.cnn(img)
#         # print(feat.shape)
#         feat = feat.view(feat.shape[0], -1)
#         c1 = self.fc1(feat)
#         c2 = self.fc2(feat)
#         c3 = self.fc3(feat)
#         c4 = self.fc4(feat)
#         c5 = self.fc5(feat)
#         return c1, c2, c3, c4, c5
   
train_loader = torch.utils.data.DataLoader(
#     train_dataset,
   batch_size=10, 
   shuffle=True, 
   num_workers=10, 
)
   
val_loader = torch.utils.data.DataLoader(
#     val_dataset,
   batch_size=10, 
   shuffle=False, 
   num_workers=10, 
)

model = SVHN_Model1()
criterion = nn.CrossEntropyLoss (size_average=False)
optimizer = torch.optim.Adam(model.parameters(), 0.001)
best_loss = 1000.0
for epoch in range(20):
   print('Epoch: ', epoch)

   train(train_loader, model, criterion, optimizer, epoch)
   val_loss = validate(val_loader, model, criterion)
   
   # 记录下验证集精度
   if val_loss < best_loss:
       best_loss = val_loss
       torch.save(model.state_dict(), './model.pt')

其中每个Epoch的训练代码如下:

def train(train_loader, model, criterion, optimizer, epoch):
    # 切换模型为训练模式
    model.train()

    for i, (input, target) in enumerate(train_loader):
        c0, c1, c2, c3, c4, c5 = model(data[0])
        loss = criterion(c0, data[1][:, 0]) + \
                criterion(c1, data[1][:, 1]) + \
                criterion(c2, data[1][:, 2]) + \
                criterion(c3, data[1][:, 3]) + \
                criterion(c4, data[1][:, 4]) + \
                criterion(c5, data[1][:, 5])
        loss /= 6
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

其中每个Epoch的验证代码如下:

def validate(val_loader, model, criterion):
    # 切换模型为预测模型
    model.eval()
    val_loss = []

    # 不记录模型梯度信息
    with torch.no_grad():
        for i, (input, target) in enumerate(val_loader):
            c0, c1, c2, c3, c4, c5 = model(data[0])
            loss = criterion(c0, data[1][:, 0]) + \
                    criterion(c1, data[1][:, 1]) + \
                    criterion(c2, data[1][:, 2]) + \
                    criterion(c3, data[1][:, 3]) + \
                    criterion(c4, data[1][:, 4]) + \
                    criterion(c5, data[1][:, 5])
            loss /= 6
            val_loss.append(loss.item())
    return np.mean(val_loss)

1.3 模型保存与加载

torch.save(model_object.state_dict(), 'model.pt')#保存模型
model.load_state_dict(torch.load(' model.pt')) #加载模型

1.4 模型调参流程

深度学习原理少但实践性非常强,基本上很多的模型的验证只能通过训练来完成。同时深度学习有众多的网络结构和超参数,因此需要反复尝试。训练深度学习模型需要GPU的硬件支持,也需要较多的训练时间,如何有效的训练深度学习模型逐渐成为了一门学问。
深度学习有众多的训练技巧,比较推荐的阅读链接有:
http://karpathy.github.io/2019/04/25/recipe/

你可能感兴趣的:(天池街景字符识别比赛,深度学习,python,神经网络,pytorch)