迁移学习是一种机器学习方法,就是把为任务A开发的模型作为初始点,重新使用在为任务B开发模型的过程中。
水果分类模型
在已经预训练好的模型的基础上,在自己的数据集上进行微调,复用预训练模型特征及权重来解决自己的问题
使用深度学习框架Pytorch,在lmageNet预训练图像分类模型(例如Resnet18)基础上,对自己的30类水果图像分类数据集进行迁移学习(transfer learning)微调(fine-tuning)训练,得到自己的图像分类模型。
在训练过程中,记录训练集和测试集的损失函数、准确率、Precision、Recall、f1-score等评估指标,使用wandb可视化面板监控。
为后续的新图像预测、测试集评估、可解释性分析、模型部署,奠定算法基础。
这里使用的云GPU还是老师推荐的:https://featurize.cn/?s=d7ce99f842414bfcaea5662a97581bd1
不过这个要规范归还,不然钱钱会不翼而飞
这个可以不用运行就有结果,时间比较紧的时候可以用,Kaggle那个需要自己运行且总是有点小麻烦,不用还是好用的
所用资料如下:
学习方法:先一遍代码再视频加入笔记
!pip install numpy pandas matplotlib seaborn plotly requests tqdm opencv-python pillow wandb -i https://pypi.tuna.tsinghua.edu.cn/simple
##下载安装Pytorch
!pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu113
##下载中文字体文件
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/SimHei.ttf --no-check-certificate
##创建目录
import os
# 存放结果文件
os.mkdir('output')
# 存放训练得到的模型权重
os.mkdir('checkpoints')
# 存放生成的图表
os.mkdir('图表')
##设置matplotlib中文字体
import matplotlib.pyplot as plt
%matplotlib inline
# # windows操作系统
# plt.rcParams['font.sans-serif']=['SimHei'] # 用来正常显示中文标签
# plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号
# Mac操作系统,参考 https://www.ngui.cc/51cto/show-727683.html
# 下载 simhei.ttf 字体文件
# !wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/SimHei.ttf
# Linux操作系统,例如 云GPU平台:https://featurize.cn/?s=d7ce99f842414bfcaea5662a97581bd1
# 如果报错 Unable to establish SSL connection.,重新运行本代码块即可
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/SimHei.ttf -O /environment/miniconda3/lib/python3.7/site-packages/matplotlib/mpl-data/fonts/ttf/SimHei.ttf --no-check-certificate
!rm -rf /home/featurize/.cache/matplotlib
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline
matplotlib.rc("font",family='SimHei') # 中文字体
plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号
plt.plot([1,2,3], [100,500,300])
plt.title('matplotlib中文字体测试', fontsize=25)
plt.xlabel('X轴', fontsize=15)
plt.ylabel('Y轴', fontsize=15)
plt.show()
前面讲过构建自己的图像分类数据集:构建自己的图像分类数据集【两天搞定AI毕设】_哔哩哔哩_bilibili
##下载样例数据集
# 下载数据集压缩包
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/fruit30/fruit30_split.zip
# 解压
!unzip fruit30_split.zip >> /dev/null
# 删除压缩包
!rm fruit30_split.zip
##查看数据集目录结构
!sudo snap install tree
!tree fruit30_split -L 2
在自己的图像分类数据集上,使用ImageNet预训练图像分类模型初始化,改动分类层,迁移学习微调训练
##设置matplotlib中文字体
# # windows操作系统
# plt.rcParams['font.sans-serif']=['SimHei'] # 用来正常显示中文标签
# plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号
# Mac操作系统,参考 https://www.ngui.cc/51cto/show-727683.html
# 下载 simhei.ttf 字体文件
# !wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/SimHei.ttf
# Linux操作系统,例如 云GPU平台:https://featurize.cn/?s=d7ce99f842414bfcaea5662a97581bd1
# 如果遇到 SSL 相关报错,重新运行本代码块即可
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/SimHei.ttf -O /environment/miniconda3/lib/python3.7/site-packages/matplotlib/mpl-data/fonts/ttf/SimHei.ttf
!rm -rf /home/featurize/.cache/matplotlib
import matplotlib
import matplotlib.pyplot as plt
matplotlib.rc("font",family='SimHei') # 中文字体
plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号
##导入工具包
import time
import os
import numpy as np
from tqdm import tqdm
import torch
import torchvision
import torch.nn as nn
import torch.nn.functional as F
import matplotlib.pyplot as plt
%matplotlib inline
# 忽略烦人的红色提示
import warnings
warnings.filterwarnings("ignore")
##获取计算硬件
# 有 GPU 就用 GPU,没有就用 CPU
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print('device', device)
##图像预处理
from torchvision import transforms
# 训练集图像预处理:缩放裁剪、图像增强、转 Tensor、归一化
train_transform = transforms.Compose([transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
# 测试集图像预处理-RCTN:缩放、裁剪、转 Tensor、归一化
test_transform = transforms.Compose([transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
##载入图像分类数据集
# 数据集文件夹路径
dataset_dir = 'fruit30_split'
train_path = os.path.join(dataset_dir, 'train')
test_path = os.path.join(dataset_dir, 'val')
print('训练集路径', train_path)
print('测试集路径', test_path)
from torchvision import datasets
# 载入训练集
train_dataset = datasets.ImageFolder(train_path, train_transform)
# 载入测试集
test_dataset = datasets.ImageFolder(test_path, test_transform)
print('训练集图像数量', len(train_dataset))
print('类别个数', len(train_dataset.classes))
print('各类别名称', train_dataset.classes)
print('测试集图像数量', len(test_dataset))
print('类别个数', len(test_dataset.classes))
print('各类别名称', test_dataset.classes)
##类别和索引号——对应
# 各类别名称
class_names = train_dataset.classes
n_class = len(class_names)
print(class_names)
# 映射关系:类别 到 索引号
train_dataset.class_to_idx
# 映射关系:索引号 到 类别
idx_to_labels = {y:x for x,y in train_dataset.class_to_idx.items()}
print(idx_to_labels)
# 保存为本地的 npy 文件
np.save('idx_to_labels.npy', idx_to_labels)
np.save('labels_to_idx.npy', train_dataset.class_to_idx)
##定义数据加载器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
)
##查看一个batch的图像和标注
# DataLoader 是 python生成器,每次调用返回一个 batch 的数据
images, labels = next(iter(train_loader))
##可视化一个batch的图像和标注
# 将数据集中的Tensor张量转为numpy的array数据类型
images = images.numpy()
plt.hist(images[5].flatten(), bins=50)
plt.show()
# batch 中经过预处理的图像
idx = 2
plt.imshow(images[idx].transpose((1,2,0))) # 转为(224, 224, 3)
plt.title('label:'+str(labels[idx].item()))
# 原始图像
idx = 2
mean = np.array([0.485, 0.456, 0.406])
std = np.array([0.229, 0.224, 0.225])
plt.imshow(np.clip(images[idx].transpose((1,2,0)) * std + mean, 0, 1))
plt.title('label:'+ pred_classname)
plt.show()
以上为在train loader中调取了一个batch的数据和样本
真实训练中就是一口一口喂
##导入训练需使用的工具包
from torchvision import models
import torch.optim as optim
选择迁移学习训练方式
斯坦福CS231N【迁移学习】中文精讲: 【子豪兄】精讲CS231N斯坦福计算机视觉公开课(2020最新)_哔哩哔哩_bilibili
斯坦福CS231N【迁移学习】官方笔记:CS231n Convolutional Neural Networks for Visual Recognition
不同的迁移学习策略取决于自己数据集的数据分布和ImageNet大数据集的数据分布的差异
##选择一:只微调训练模型最后一层(全连接分类层)
model = models.resnet18(pretrained=True) # 载入预训练模型
# 修改全连接层,使得全连接层的输出与当前数据集类别数对应
# 新建的层默认 requires_grad=True
model.fc = nn.Linear(model.fc.in_features, n_class)
# 只微调训练最后一层全连接层的参数,其它层冻结
optimizer = optim.Adam(model.fc.parameters())
##选择二:微调训练所有层
# 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 = model.to(device)
# 交叉熵损失函数
criterion = nn.CrossEntropyLoss()
# 训练轮次 Epoch
EPOCHS = 20
##模拟—个batch的训练
# 获得一个 batch 的数据和标注
images, labels = next(iter(train_loader))
images = images.to(device)
labels = labels.to(device)
# 输入模型,执行前向预测
outputs = model(images)
# 获得当前 batch 所有图像的预测类别 logit 分数
outputs.shape
# 由 logit,计算当前 batch 中,每个样本的平均交叉熵损失函数值
loss = criterion(outputs, labels)
# 反向传播“三部曲”
optimizer.zero_grad() # 清除梯度
loss.backward() # 反向传播
optimizer.step() # 优化更新
# 获得当前 batch 所有图像的预测类别
_, preds = torch.max(outputs, 1)
##运行完整训练
# 遍历每个 EPOCH
for epoch in tqdm(range(EPOCHS)):
model.train()
for images, labels in train_loader: # 获得一个 batch 的数据和标注
images = images.to(device)
labels = labels.to(device)
outputs = model(images)
loss = criterion(outputs, labels) # 计算当前 batch 中,每个样本的平均交叉熵损失函数值
optimizer.zero_grad()
loss.backward()
optimizer.step()
##在测试集上初步测试
model.eval()
with torch.no_grad():
correct = 0
total = 0
for images, labels in tqdm(test_loader):
images = images.to(device)
labels = labels.to(device)
outputs = model(images)
_, preds = torch.max(outputs, 1) #获取batch置信度最高的类别
total += labels.size(0)
correct += (preds == labels).sum()
print('测试集上的准确率为 {:.3f} %'.format(100 * correct / total))
##保存模型
torch.save(model, 'checkpoints/fruit30_pytorch_20220814.pth')
参考文档
Transfer Learning for Computer Vision Tutorial — PyTorch Tutorials 1.13.1+cu117 documentation
Pytorch预训练图像分类模型识别预测【两天搞定AI毕设】_哔哩哔哩_bilibili
【子豪兄】玩转MNIST数据集_哔哩哔哩_bilibili
【子豪兄Pytorch】二十分钟搭建神经网络分类Fashion-MNIST数据集时尚物品_哔哩哔哩_bilibili
在训练过程中加入了一些模型的可视化和模型的监控与记录日志
##导入工具包
import time
import os
from tqdm import tqdm
import pandas as pd
import numpy as np
import torch
import torchvision
import torch.nn as nn
import torch.nn.functional as F
import matplotlib.pyplot as plt
%matplotlib inline
# 忽略烦人的红色提示
import warnings
warnings.filterwarnings("ignore")
# 获取计算硬件
# 有 GPU 就用 GPU,没有就用 CPU
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print('device', device)
##图像预处理
from torchvision import transforms
# 训练集图像预处理:缩放裁剪、图像增强、转 Tensor、归一化
train_transform = transforms.Compose([transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
# 测试集图像预处理-RCTN:缩放、裁剪、转 Tensor、归一化
test_transform = transforms.Compose([transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
##载入图像分类数据集
# 数据集文件夹路径
dataset_dir = 'fruit30_split'
train_path = os.path.join(dataset_dir, 'train')
test_path = os.path.join(dataset_dir, 'val')
print('训练集路径', train_path)
print('测试集路径', test_path)
from torchvision import datasets
# 载入训练集
train_dataset = datasets.ImageFolder(train_path, train_transform)
# 载入测试集
test_dataset = datasets.ImageFolder(test_path, test_transform)
print('训练集图像数量', len(train_dataset))
print('类别个数', len(train_dataset.classes))
print('各类别名称', train_dataset.classes)
print('测试集图像数量', len(test_dataset))
print('类别个数', len(test_dataset.classes))
print('各类别名称', test_dataset.classes)
##类别和索引号映射字典
# 各类别名称
class_names = train_dataset.classes
n_class = len(class_names)
# 映射关系:类别 到 索引号
train_dataset.class_to_idx
# 映射关系:索引号 到 类别
idx_to_labels = {y:x for x,y in train_dataset.class_to_idx.items()}
print(idx_to_labels)
##定义数据加载器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
)
##导入训练需使用的工具包
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())
##选择二:微调训练所有层
# 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 = model.to(device)
# 交叉熵损失函数
criterion = nn.CrossEntropyLoss()
# 训练轮次 Epoch
EPOCHS = 30
# 学习率降低策略
lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.5) #每隔5个EPOCHS学习率降低一半
##函数:在训练集上训练
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
##函数:在整个测试集上评估
def evaluate_testset():
'''
在整个测试集上评估,返回分类评估指标日志
'''
loss_list = []
labels_list = []
preds_list = []
with torch.no_grad():
for images, labels in test_loader: # 生成一个 batch 的数据和标注
images = images.to(device)
labels = labels.to(device)
outputs = model(images) # 输入模型,执行前向预测
# 获取整个测试集的标签类别和预测类别
_, preds = torch.max(outputs, 1) # 获得当前 batch 所有图像的预测类别
preds = preds.cpu().numpy()
loss = criterion(outputs, labels) # 由 logit,计算当前 batch 中,每个样本的平均交叉熵损失函数值
loss = loss.detach().cpu().numpy()
outputs = outputs.detach().cpu().numpy()
labels = labels.detach().cpu().numpy()
loss_list.append(loss)
labels_list.extend(labels)
preds_list.extend(preds)
log_test = {}
log_test['epoch'] = epoch
# 计算分类评估指标
log_test['test_loss'] = np.mean(loss)
log_test['test_accuracy'] = accuracy_score(labels_list, preds_list)
log_test['test_precision'] = precision_score(labels_list, preds_list, average='macro')
log_test['test_recall'] = recall_score(labels_list, preds_list, average='macro')
log_test['test_f1-score'] = f1_score(labels_list, preds_list, average='macro')
return log_test
##训练开始之前,记录日志
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)
print(df_train_log)
# 训练日志-测试集
df_test_log = pd.DataFrame()
log_test = {}
log_test['epoch'] = 0
log_test.update(evaluate_testset())
df_test_log = df_test_log.append(log_test, ignore_index=True)
print(df_test_log)
开始训练,使用wandb监控记录模型训练过程中的指标
登录wandb
1. 安装wandb: pip install wandb
2. 登录wandb:在命令行中运行wandb login
3. 按提示复制粘则贴APl Key至命令行中
##创建wandb可视化项目
import wandb
wandb.init(project='fruit30', name=time.strftime('%m%d%H%M%S'))
##运行训练
for epoch in range(1, EPOCHS+1):
print(f'Epoch {epoch}/{EPOCHS}')
## 训练阶段
model.train()
for images, labels in tqdm(train_loader): # 获得一个 batch 的数据和标注
batch_idx += 1
log_train = train_one_batch(images, labels)
df_train_log = df_train_log.append(log_train, ignore_index=True)
wandb.log(log_train)
lr_scheduler.step()
## 测试阶段
model.eval()
log_test = evaluate_testset()
df_test_log = df_test_log.append(log_test, ignore_index=True)
wandb.log(log_test)
# 保存最新的最佳模型文件
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']
df_train_log.to_csv('训练日志-训练集.csv', index=False)
df_test_log.to_csv('训练日志-测试集.csv', index=False)
##在测试集上评价
# 载入最佳模型作为当前模型
model = torch.load('checkpoints/best-{:.3f}.pth'.format(best_test_accuracy))
model.eval()
print(evaluate_testset())
##导入工具包
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
##设置matplotlib中文字体
# # windows操作系统
# plt.rcParams['font.sans-serif']=['SimHei'] # 用来正常显示中文标签
# plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号
# Mac操作系统,参考 https://www.ngui.cc/51cto/show-727683.html
# 下载 simhei.ttf 字体文件
# !wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/SimHei.ttf
# Linux操作系统,例如 云GPU平台:https://featurize.cn/?s=d7ce99f842414bfcaea5662a97581bd1
# 如果遇到 SSL 相关报错,重新运行本代码块即可
!wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/SimHei.ttf -O /environment/miniconda3/lib/python3.7/site-packages/matplotlib/mpl-data/fonts/ttf/SimHei.ttf
!rm -rf /home/featurize/.cache/matplotlib
import matplotlib
import matplotlib.pyplot as plt
matplotlib.rc("font",family='SimHei') # 中文字体
##载入训练日志表格
df_train = pd.read_csv('训练日志-训练集.csv')
df_test = pd.read_csv('训练日志-测试集.csv')
print(df_train)
epoch | batch | train_loss | train_accuracy | |
---|---|---|---|---|
0 | 0.0 | 0.0 | 3.568267 | 0.000000 |
1 | 1.0 | 1.0 | 3.721788 | 0.031250 |
2 | 1.0 | 2.0 | 3.540271 | 0.062500 |
3 | 1.0 | 3.0 | 3.493849 | 0.062500 |
4 | 1.0 | 4.0 | 3.758318 | 0.062500 |
... | ... | ... | ... | ... |
4106 | 30.0 | 4106.0 | 0.284847 | 0.937500 |
4107 | 30.0 | 4107.0 | 0.463139 | 0.781250 |
4108 | 30.0 | 4108.0 | 0.230596 | 0.937500 |
4109 | 30.0 | 4109.0 | 0.198852 | 0.937500 |
4110 | 30.0 | 4110.0 | 0.357690 | 0.956522 |
4111 rows × 4 columns
print(df_test)
epoch | test_loss | test_accuracy | test_precision | test_recall | test_f1-score | |
---|---|---|---|---|---|---|
0 | 0.0 | 3.224075 | 0.035250 | 0.017963 | 0.034687 | 0.016466 |
1 | 1.0 | 0.614413 | 0.731911 | 0.773937 | 0.729183 | 0.726178 |
2 | 2.0 | 0.309934 | 0.791280 | 0.805776 | 0.789615 | 0.786697 |
3 | 3.0 | 0.343570 | 0.826531 | 0.838070 | 0.824748 | 0.824690 |
4 | 4.0 | 0.472587 | 0.837662 | 0.847906 | 0.837459 | 0.836966 |
5 | 5.0 | 0.417531 | 0.832096 | 0.843639 | 0.829974 | 0.831244 |
6 | 6.0 | 0.263817 | 0.851577 | 0.858356 | 0.850947 | 0.850593 |
7 | 7.0 | 0.210574 | 0.843228 | 0.855338 | 0.841779 | 0.843126 |
8 | 8.0 | 0.204230 | 0.867347 | 0.874752 | 0.866211 | 0.866007 |
9 | 9.0 | 0.232938 | 0.850649 | 0.860808 | 0.849349 | 0.847877 |
10 | 10.0 | 0.322416 | 0.849722 | 0.865295 | 0.848496 | 0.846039 |
11 | 11.0 | 0.172115 | 0.866419 | 0.877599 | 0.864662 | 0.864998 |
12 | 12.0 | 0.285754 | 0.861781 | 0.875341 | 0.860255 | 0.860449 |
13 | 13.0 | 0.218701 | 0.869202 | 0.875127 | 0.868420 | 0.868948 |
14 | 14.0 | 0.282180 | 0.864564 | 0.869911 | 0.862939 | 0.863354 |
15 | 15.0 | 0.246851 | 0.872913 | 0.880009 | 0.871480 | 0.871678 |
16 | 16.0 | 0.165407 | 0.867347 | 0.874303 | 0.867302 | 0.867304 |
17 | 17.0 | 0.061076 | 0.859926 | 0.868373 | 0.859063 | 0.857848 |
18 | 18.0 | 0.343297 | 0.863636 | 0.873884 | 0.862973 | 0.863741 |
19 | 19.0 | 0.172381 | 0.871058 | 0.879093 | 0.869936 | 0.871374 |
20 | 20.0 | 0.240845 | 0.867347 | 0.873157 | 0.866652 | 0.866904 |
21 | 21.0 | 0.196387 | 0.871985 | 0.883942 | 0.870201 | 0.871514 |
22 | 22.0 | 0.414227 | 0.876623 | 0.882219 | 0.875006 | 0.875602 |
23 | 23.0 | 0.190263 | 0.877551 | 0.882674 | 0.877371 | 0.877009 |
24 | 24.0 | 0.149703 | 0.865492 | 0.876651 | 0.864660 | 0.866338 |
25 | 25.0 | 0.049876 | 0.868275 | 0.879614 | 0.867451 | 0.867966 |
26 | 26.0 | 0.125152 | 0.872913 | 0.876978 | 0.872943 | 0.871724 |
27 | 27.0 | 0.081084 | 0.872913 | 0.879330 | 0.873042 | 0.872056 |
28 | 28.0 | 0.129047 | 0.888683 | 0.895575 | 0.888689 | 0.888649 |
29 | 29.0 | 0.106969 | 0.869202 | 0.880197 | 0.866905 | 0.868556 |
30 | 30.0 | 0.246746 | 0.873840 | 0.880458 | 0.872479 | 0.872441 |
##训练集损失函数
plt.figure(figsize=(16, 8))
x = df_train['batch']
y = df_train['train_loss']
plt.plot(x, y, label='训练集')
plt.tick_params(labelsize=20)
plt.xlabel('batch', fontsize=20)
plt.ylabel('loss', fontsize=20)
plt.title('训练集损失函数', fontsize=25)
plt.savefig('图表/训练集损失函数.pdf', dpi=120, bbox_inches='tight')
plt.show()
##训练集准确率
plt.figure(figsize=(16, 8))
x = df_train['batch']
y = df_train['train_accuracy']
plt.plot(x, y, label='训练集')
plt.tick_params(labelsize=20)
plt.xlabel('batch', fontsize=20)
plt.ylabel('loss', fontsize=20)
plt.title('训练集准确率', fontsize=25)
plt.savefig('图表/训练集准确率.pdf', dpi=120, bbox_inches='tight')
plt.show()
##测试集损失函数
plt.figure(figsize=(16, 8))
x = df_test['epoch']
y = df_test['test_loss']
plt.plot(x, y, label='测试集')
plt.tick_params(labelsize=20)
plt.xlabel('epoch', fontsize=20)
plt.ylabel('loss', fontsize=20)
plt.title('测试集损失函数', fontsize=25)
plt.savefig('图表/测试集损失函数.pdf', dpi=120, bbox_inches='tight')
plt.show()
##测试集评估指标
from matplotlib import colors as mcolors
import random
random.seed(124)
colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'tab:blue', 'tab:orange', 'tab:green', 'tab:red', 'tab:purple', 'tab:brown', 'tab:pink', 'tab:gray', 'tab:olive', 'tab:cyan', 'black', 'indianred', 'brown', 'firebrick', 'maroon', 'darkred', 'red', 'sienna', 'chocolate', 'yellow', 'olivedrab', 'yellowgreen', 'darkolivegreen', 'forestgreen', 'limegreen', 'darkgreen', 'green', 'lime', 'seagreen', 'mediumseagreen', 'darkslategray', 'darkslategrey', 'teal', 'darkcyan', 'dodgerblue', 'navy', 'darkblue', 'mediumblue', 'blue', 'slateblue', 'darkslateblue', 'mediumslateblue', 'mediumpurple', 'rebeccapurple', 'blueviolet', 'indigo', 'darkorchid', 'darkviolet', 'mediumorchid', 'purple', 'darkmagenta', 'fuchsia', 'magenta', 'orchid', 'mediumvioletred', 'deeppink', 'hotpink']
markers = [".",",","o","v","^","<",">","1","2","3","4","8","s","p","P","*","h","H","+","x","X","D","d","|","_",0,1,2,3,4,5,6,7,8,9,10,11]
linestyle = ['--', '-.', '-']
def get_line_arg():
'''
随机产生一种绘图线型
'''
line_arg = {}
line_arg['color'] = random.choice(colors)
# line_arg['marker'] = random.choice(markers)
line_arg['linestyle'] = random.choice(linestyle)
line_arg['linewidth'] = random.randint(1, 4)
# line_arg['markersize'] = random.randint(3, 5)
return line_arg
metrics = ['test_accuracy', 'test_precision', 'test_recall', 'test_f1-score']
plt.figure(figsize=(16, 8))
x = df_test['epoch']
for y in metrics:
plt.plot(x, df_test[y], label=y, **get_line_arg())
plt.tick_params(labelsize=20)
plt.ylim([0, 1])
plt.xlabel('epoch', fontsize=20)
plt.ylabel(y, fontsize=20)
plt.title('测试集分类评估指标', fontsize=25)
plt.savefig('图表/测试集分类评估指标.pdf', dpi=120, bbox_inches='tight')
plt.legend(fontsize=20)
plt.show()
注意事项
严禁把测试集图像用于训练(反向传播更新权重)
抛开baseline基准模型谈性能(速度、精度),都是耍流氓
测试集上的准确率越高,模型就一定越好吗?
常用数据集中存在大量的错标、漏标:https://mp.weixin.qq.com/s/4NbIA4wsNdX-N2uMOUmPLA
创新点展望
更换不同预训练图像分类模型
分别尝试三种不同的迁移学习训练配置:只微调训练模型最后一层(全连接分类层)、微调训练所有层、随机初始化模型全部权重,从头训练所有层
更换不同的优化器、学习率
扩展阅读
同济子豪兄的论文精读视频:Docs
开源图像分类算法库 MMClassificaiton:https://github.com/open-mmlab/mmclassification
**机器学习分类评估指标**
手绘笔记讲解:手绘图解分类器评估指标_哔哩哔哩_bilibili
混淆矩阵:
混淆矩阵详解_哔哩哔哩_bilibili
混淆矩阵交互演示1_哔哩哔哩_bilibili
ROC曲线:
ROC曲线详解_哔哩哔哩_bilibili
ROC曲线交互演示1_哔哩哔哩_bilibili
ROC曲线交互演示2_哔哩哔哩_bilibili
F1-score:F1-Score详解_哔哩哔哩_bilibili
F-beta-score:正负样本不均衡时的分类评估指标_哔哩哔哩_bilibili
训练好图像分类模型之后,做什么?
在新图像、视频、摄像头实时画面预测
在测试集上评估:混淆矩阵、ROC曲线、PR曲线、语义特征降维可视化
可解释性分析:CAM热力图
模型TensorRT部署:智能手机、开发板、浏览器、服务器
转ONNX并可视化模型结构
MMClassification图像分类、MMDeploy模型部署
开发图像分类APP和微信小程序
总结:本节使用深度学习框架Pytorch,在lmageNet预训练图像分类模型基础上,对自己的30类水果图像分类数据集进行迁移学习微调训练(两个函数),得到自己的图像分类模型。
在训练过程中,记录训练集和测试集的损失函数、准确率、Precision、Recall、f1-score等评估指标,使用wandb可视化面板监控。