街景字符编码识别-Task4:模型训练与验证

街景字符编码识别-Task4:模型训练与验证

4.1 学习目标

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

4.2 构造验证集

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

4.3 模型训练与验证

模型训练基本逻辑结构如下

  • 封装训练集及测试集,方法基本和Task2中一样,代码如下:
import os, sys, glob, shutil, json
import torch.nn as nn
from PIL import Image
import numpy as np
import torch
from torch.utils.data.dataset import Dataset
import torchvision.transforms as transforms
import matplotlib.pyplot as plt

#继承类
class SVHNDataset(Dataset):
    def __init__(self, img_path, img_label, transform=None):
        self.img_path = img_path
        self.img_label = img_label
        #数据增强
        if transform is not None:
            self.transform = transform
        else:
            self.transform = None

    def __getitem__(self, index):
        img = Image.open(self.img_path[index]).convert('RGB')

        if self.transform is not None:
            img = self.transform(img)

        # 原始SVHN中类别10为数字0 用10 填充
        lbl = np.array(self.img_label[index], dtype=np.int)
        lbl = list(lbl) + (6 - len(lbl)) * [10]
        # print(lbl)
        # print(torch.from_numpy(np.array(lbl[:5])))
        return img, torch.from_numpy(np.array(lbl[:6]))

    def __len__(self):
        return len(self.img_path)


train_path = glob.glob('./train_imgs/*.png')
train_path.sort()#从小到大排序
train_json = json.load(open('./train_label.json'))
train_label = [train_json[x]['label'] for x in train_json]#获得标签列表

val_path = glob.glob('./mchar_val/*.png')
val_path.sort()#从小到大排序
val_json = json.load(open('./val.json'))
val_label = [train_json[x]['label'] for x in train_json]#获得标签列表

train_loader = torch.utils.data.DataLoader(
        SVHNDataset(train_path, train_label,
                   transforms.Compose([
                       transforms.Resize((64, 128)),
                       transforms.ColorJitter(0.3, 0.3, 0.2),
                       transforms.RandomRotation(5),
                       transforms.ToTensor(),
                       transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
            ])),
    batch_size=10, # 每批样本个数
    shuffle=False, # 是否打乱顺序
    # num_workers=10, # 读取的线程个数
)

val_loader = torch.utils.data.DataLoader(
        SVHNDataset(val_path, val_label,
                   transforms.Compose([
                       transforms.Resize((64, 128)),
                       transforms.ColorJitter(0.3, 0.3, 0.2),
                       transforms.RandomRotation(5),
                       transforms.ToTensor(),
                       transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
            ])),
    batch_size=10, # 每批样本个数
    shuffle=False, # 是否打乱顺序
    # num_workers=10, # 读取的线程个数
)
  • 搭建训练网络,详解见Task3,代码如下:
# 定义模型
class SVHN_Model1(nn.Module):
    def __init__(self):
        super(SVHN_Model1, self).__init__()
        # CNN提取特征模块
        self.cnn = nn.Sequential(
            nn.Conv2d(3, 16, kernel_size=(3, 3), stride=(2, 2)),
            nn.ReLU(),
            nn.MaxPool2d(2),
            nn.Conv2d(16, 32, kernel_size=(3, 3), stride=(2, 2)),
            nn.ReLU(),
            nn.MaxPool2d(2),
        )
        #
        self.fc1 = nn.Linear(32 * 3 * 7, 11)
        self.fc2 = nn.Linear(32 * 3 * 7, 11)
        self.fc3 = nn.Linear(32 * 3 * 7, 11)
        self.fc4 = nn.Linear(32 * 3 * 7, 11)
        self.fc5 = nn.Linear(32 * 3 * 7, 11)
        self.fc6 = nn.Linear(32 * 3 * 7, 11)

    def forward(self, img):
        feat = self.cnn(img)
        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)
        c6 = self.fc6(feat)
        return c1, c2, c3, c4, c5, c6
model = SVHN_Model1()
# 损失函数
criterion = nn.CrossEntropyLoss()
# 优化器
optimizer = torch.optim.Adam(model.parameters(), 0.005)
  • 构建训练与验证函数,具体如下:
train_loss = []#存放损失数值,用于绘图
val_loss = []
def train(train_loader, model, criterion, optimizer, epoch):
    total_loss = 0
    for i, (images, targets) in enumerate(train_loader):
        c0, c1, c2, c3, c4, c5 = model(images)
        targets = targets.long()
        loss = criterion(c0, targets[:, 0]) + \
               criterion(c1, targets[:, 1]) + \
               criterion(c2, targets[:, 2]) + \
               criterion(c3, targets[:, 3]) + \
               criterion(c4, targets[:, 4]) + \
               criterion(c5, targets[:, 5])
        loss /= 6
        optimizer.zero_grad()
        loss.backward()  # 向后传播
        optimizer.step()
        total_loss += loss.item()
        if (i + 1) % 100 == 0:
            print("Step [{}/{}] Train Loss: {:.4f}"
                  .format(i + 1, len(train_loader), loss.item()))
    print('train_loss:'+str(total_loss / len(train_loader)))
    train_loss.append(total_loss / len(train_loader))
    state = {'model': model.state_dict(), 'optimizer': optimizer.state_dict(), 'epoch': epoch}
    return state #返回训练参数

def val(val_loader, model, criterion):
    total_loss = 0
    # 切换模型为预测模型
    model.eval()
    # 不记录模型梯度信息
    with torch.no_grad():
        for i, (images, targets) in enumerate(val_loader):
            c0, c1, c2, c3, c4, c5 = model(images)
            targets = targets.long()
            loss = criterion(c0, targets[:, 0]) + \
                   criterion(c1, targets[:, 1]) + \
                   criterion(c2, targets[:, 2]) + \
                   criterion(c3, targets[:, 3]) + \
                   criterion(c4, targets[:, 4]) + \
                   criterion(c5, targets[:, 5])
            loss /= 6
            total_loss += loss.item()
        print('val_loss:'+str(total_loss / len(val_loader)))
        mean_loss = total_loss / len(val_loader)
        val_loss.append(mean_loss)
        return mean_loss
  • 模型训练:
#开始训练
epochs = 11
val_loss_best = 10000 #随意定义一个较大的数值
for epoch in range(1, epochs):
    print('epoch:'+str(epoch))
    state = train(train_loader, model, criterion, optimizer, epoch)
    val_lossmean = val(val_loader, model, criterion)
    #判断验证损失,保存最低点的模型参数
    if val_lossmean < val_loss_best:
        val_loss_best = val_lossmean
        best_state = state
    else:
        torch.save(state, './epoch{}.pth'.format(epoch))
# 可视化
def show_curve(train_loss, val_loss, title):
    x= np.array(range(len(ys)))
    y_train = np.array(train_loss)
    y_val = np.array(val_loss)
    plt.plot(x, y_train, c='b')
    plt.plot(x, y_val, c='r')
    plt.axis()
    plt.title('{} curve'.format(title))
    plt.xlabel('epoch')
    plt.ylabel('{}'.format(title))
    plt.show()
show_curve(train_loss, val_loss, 'Loss')

4.4 模型保存与加载

两种方式:

  1. 保存整个模型
torch.save(model, PATH)
model = torch.load(PATH)
model.eval()

2.保存和加载模型参数:

torch.save(model_object.state_dict(), 'model.pt')

model.load_state_dict(torch.load(' model.pt'))

#或者保存较多状态参数
state = {'model': model.state_dict(), 'optimizer': optimizer.state_dict(), 'epoch': epoch}
torch.save(state, 'model.pt')

checkpoint = torch.load('model.pt')
model.load_state_dict(checkpoint['model'])
optimizer.load_state_dict(checkpoint['optimizer'])
start_epoch = checkpoint['epoch']

4.5 模型调参流程

深度学习原理少但实践性非常强,基本上很多的模型的验证只能通过训练来完成。同时深度学习有众多的网络结构和超参数,因此需要反复尝试。训练深度学习模型需要GPU的硬件支持,也需要较多的训练时间,如何有效的训练深度学习模型逐渐成为了一门学问。

深度学习有众多的训练技巧,比较推荐的阅读链接有:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值