对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,贴一个官网解释。
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常用的评估指标,参考博文。
后面将进行训练好的图像分类模型的应用部分,我十分期待。