四、模型训练与验证

为什么需要验证集

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

训练集、验证集和测试集分别有不同的作用

  • 训练集(Train Set):模型用于训练和调整模型参数;

  • 验证集(Validation Set):用来验证模型精度和调整模型超参数;

  • 测试集(Test Set):验证模型的泛化能力。

模型训练与验证

训练集和验证集是分开的,所以模型在验证集上面的精度在一定程度上可以反映模型的泛化能力。在划分验证集的时候,需要注意验证集的分布应该与测试集尽量保持一致,不然模型在验证集上的精度就失去了指导意义。

完整的代码逻辑如下:

# 接第三节定义模型的代码

# 构造训练集
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, 
)

# 每个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)

# 实例化模型
model = SVHN_Model1()
# 定义损失函数
criterion = nn.CrossEntropyLoss (size_average=False)
# 定义优化器
optimizer = torch.optim.Adam(model.parameters(), 0.001)
# 定义最佳损失
best_loss = 1000.0
# 训练20的epoch
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')

模型保存与加载

在Pytorch中模型的保存和加载非常简单,比较常见的做法是保存和加载模型参数:

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

模型调参流程

依照如下顺序:

  • 如果模型训练时误差较大,那么增加模型复杂度,增加训练时间,重新设计模型结构;
  • 如果模型训练好以后,在验证集上的误差较大,那么添加正则化,增加的更多的数据,重新设计优化模型结构;
  • 如果模型在验证集上调好超参数以后,在一部分测试集上测试时误差较大,那么增加更多和测试集数据相似的数据,和现有数据一起人工合成新的训练集数据和验证集数据,重新设计优化模型结构;
  • 如果模型在测试集误差仍然较大,那么增加更多的验证集数据。
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。