(202301)pytorch图像分类全流程实战Task3:迁移学习微调训练

TASK3:迁移学习微调训练

(202301)pytorch图像分类全流程实战Task3:迁移学习微调训练_第1张图片

对B站up同济子豪兄的图像分类系列的学习(大佬的完整代码在GitHub开源) 

为何进行迁移学习微调训练

首先需要对行为的目的有一定的认识。

为什么要迁移学习:

在机器学习与深度学习任务中自己不能够给模型提供足够的参数(数据量)是一个常见的阻碍,迁移学习的方法使得我们可以直接将例如ImageNet这样的数据集训练出的模型用于自己的任务,降低了成本提高了效率。

为什么要微调训练:

在实际操作中,由于面临的问题不同,前人训练出的模型并不能够直接应用于我们的任务,因此需要针对自己的任务调整加载的预训练模型。

首先导包。

import torch
import torchvision
import torch.nn as nn
import torch.nn.functional as F

from torchvision import models
import torch.optim as optim
from torch.optim import lr_scheduler

选择一:只微调训练模型最后一层(全连接分类层)

model = models.resnet18(pretrained=True) # 载入预训练模型

# 修改全连接层,使得全连接层的输出与当前数据集类别数对应
# 新建的层默认 requires_grad=True
model.fc = nn.Linear(model.fc.in_features, n_class)

# 只微调训练最后一层全连接层的参数,其它层冻结
optimizer = optim.Adam(model.fc.parameters())

如果这时出现预训练模型载入失败的问题,可以自己下载权重文件并载入。

注意:这种选择适用于自己的数据集与模型来源的数据集(此处是ImageNet)差别较小且数据量少的情况。

选择二:微调训练所有层

model = models.resnet18(pretrained=True) # 载入预训练模型
model.fc = nn.Linear(model.fc.in_features, n_class)
optimizer = optim.Adam(model.parameters())

注意:这种方式依然载入了预训练模型,适用于相似度较高且自己的数据量较大的情况。

选择三:随机初始化模型全部权重,从头训练所有层

model = models.resnet18(pretrained=False) # 只载入模型结构,不载入预训练权重参数
model.fc = nn.Linear(model.fc.in_features, n_class)
optimizer = optim.Adam(model.parameters())

注意:没有载入预训练模型而只采用了模型结构,适用于相似度较低且数据量较大的情况。

选择四:冻结预训练模型的前几层,训练后面的层

代码(略),一般来说,迁移学习中会选择冻结除全连接层之前的所有层,采用迭代冻结的方式;若层数比较少,也可以使用model.get_layer('layer_name').trainable= False来进行单层冻结。

注意:这种方式适用于自身数据量小且相似度低的情况

数据加载

假定已经进行建立了类别的字典映射与图像预处理函数。

采用DataLoader进行数据加载

关于dataloader,贴一个官网解释。

Dataloarder是数据集和采样器的结合,并在给定数据集上提供可迭代的对象。

DataLoader支持具有单进程或多进程加载、自定义加载顺序以及可选的自动批处理(排序)和内存固定的映射样式和可迭代样式数据集。

(百度翻译与人工,勿喷)

from torch.utils.data import DataLoader

BATCH_SIZE = 32

# 训练集的数据加载器
train_loader = DataLoader(train_dataset,
                          batch_size=BATCH_SIZE,
                          shuffle=True,
                          num_workers=4
                         )

# 测试集的数据加载器
test_loader = DataLoader(test_dataset,
                         batch_size=BATCH_SIZE,
                         shuffle=False,
                         num_workers=4
                        )

 训练准备

使用sklearn.metrics中的函数作为评价指标并建立日志。

此处以训练集训练为例。

from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import accuracy_score
from sklearn.metrics import f1_score
from sklearn.metrics import roc_auc_score

def train_one_batch(images, labels):
    '''
    运行一个 batch 的训练,返回当前 batch 的训练日志
    '''
    
    # 获得一个 batch 的数据和标注
    images = images.to(device)
    labels = labels.to(device)
    
    outputs = model(images) # 输入模型,执行前向预测
    loss = criterion(outputs, labels) # 计算当前 batch 中,每个样本的平均交叉熵损失函数值
    
    # 优化更新权重
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    
    # 获取当前 batch 的标签类别和预测类别
    _, preds = torch.max(outputs, 1) # 获得当前 batch 所有图像的预测类别
    preds = preds.cpu().numpy()
    loss = loss.detach().cpu().numpy()
    outputs = outputs.detach().cpu().numpy()
    labels = labels.detach().cpu().numpy()
    
    log_train = {}
    log_train['epoch'] = epoch
    log_train['batch'] = batch_idx
    # 计算分类评估指标
    log_train['train_loss'] = loss
    log_train['train_accuracy'] = accuracy_score(labels, preds)
    # log_train['train_precision'] = precision_score(labels, preds, average='macro')
    # log_train['train_recall'] = recall_score(labels, preds, average='macro')
    # log_train['train_f1-score'] = f1_score(labels, preds, average='macro')
    
    return log_train

训练开始前,训练集建立日志。

epoch = 0
batch_idx = 0
best_test_accuracy = 0

# 训练日志-训练集
df_train_log = pd.DataFrame()
log_train = {}
log_train['epoch'] = 0
log_train['batch'] = 0
images, labels = next(iter(train_loader))
log_train.update(train_one_batch(images, labels))
df_train_log = df_train_log.append(log_train, ignore_index=True)

进行训练

通过对准确率的评估来确定当前次数的训练中权重文件是否导出。 

#保存最新的最佳模型文件
if log_test['test_accuracy'] > best_test_accuracy: 
    # 删除旧的最佳模型文件(如有)
    old_best_checkpoint_path = 'checkpoints/best-{:.3f}.pth'.format(best_test_accuracy)
    if os.path.exists(old_best_checkpoint_path):
        os.remove(old_best_checkpoint_path)
    # 保存新的最佳模型文件
    new_best_checkpoint_path='checkpoints/best{:.3f}.pth'.format(log_test['test_accuracy'])
    torch.save(model, new_best_checkpoint_path)
    print('保存新的最佳模型', 'checkpoints/best-{:.3f}.pth'.format(best_test_accuracy))
    best_test_accuracy = log_test['test_accuracy']

最后可以得到一个准确率最高的模型。

通过如下语句进行调用。

model = torch.load('checkpoints/best-{:.3f}.pth'.format(best_test_accuracy))

随后就是正常的model.eval()即可。

model.eval()
print(evaluate_testset())

"""我的输出结果为:
    {'epoch': 30, 'test_loss': 0.0075489273, 'test_accuracy': 0.9598059598059598, 'test_precision': 0.9598247568858117, 'test_recall': 0.955258523413758, 'test_f1-score': 0.9555942621622581}
"""

训练日志可视化

可视化的目的主要是使得数据的传达更加直观,便于人们分析与决策。

根据创建日志时的指标,我们可以将训练集的损失函数与准确度、测试集的准确度、精度、召回率、F1值使用matplotlib进行可视化。

另外,也可以通过tensorboard、wandb等可视化工具在训练过程中进行可视化。

补充:

机器学习sklearn.metrics常用的评估指标,参考博文。

后面将进行训练好的图像分类模型的应用部分,我十分期待。

你可能感兴趣的:(参与dw开源学习,pytorch,分类,深度学习)