基于WIN10的64位系统演示
一、写在前面
(1)RegNet
RegNet (Regulated Networks) 是一种由 Facebook AI 的研究者们在 2020 年提出的神经网络架构,旨在探索网络架构设计的各种可能性,并找出最优的网络设计规则。RegNet 的核心理念是网络的深度(Depth)、宽度(Width)以及每层的时间/空间分辨率(Resolution)之间存在某种规律性的关系。通过系统地研究这些关系,可以发现一种规则,以此设计出在特定任务上性能优越的神经网络。
RegNet 采用了一种名为 "AnyNet" 的参数化模型设计方案,通过对深度、宽度和分辨率三个关键参数进行优化,以探索最佳的网络设计。为此,研究者们使用了一种简单且高效的量化线性模型,通过这种模型可以生成一系列结构各异但性能相近的网络,使得网络在特定任务上达到最佳性能。
RegNet 架构在一系列视觉任务上都表现出了出色的性能,如 ImageNet 分类、物体检测和语义分割等,证明了其有效性和泛化性能。在资源受限的场景下,RegNet 也可以通过减少网络的深度和宽度,以适应各种计算和存储需求。
(2)RegNet的预训练版本
本文使用的是Facebook的高级深度学习框架PyTorchImageModels (timm),需安装此库。该库提供了多种预训练的RegNet模型,这些模型主要包括RegNetX和RegNetY两个系列。每个系列都包括了多种不同复杂度的模型,以满足不同的计算需求:
RegNetX 系列:
regnetx_002 (RegNetX-200MF)
regnetx_004 (RegNetX-400MF)
regnetx_006 (RegNetX-600MF)
regnetx_008 (RegNetX-800MF)
regnetx_016 (RegNetX-1.6GF)
regnetx_032 (RegNetX-3.2GF)
regnetx_040 (RegNetX-4.0GF)
regnetx_064 (RegNetX-6.4GF)
regnetx_080 (RegNetX-8.0GF)
regnetx_120 (RegNetX-12GF)
regnetx_160 (RegNetX-16GF)
regnetx_320 (RegNetX-32GF)
RegNetY 系列:
regnety_002 (RegNetY-200MF)
regnety_004 (RegNetY-400MF)
regnety_006 (RegNetY-600MF)
regnety_008 (RegNetY-800MF)
regnety_016 (RegNetY-1.6GF)
regnety_032 (RegNetY-3.2GF)
regnety_040 (RegNetY-4.0GF)
regnety_064 (RegNetY-6.4GF)
regnety_080 (RegNetY-8.0GF)
regnety_120 (RegNetY-12GF)
regnety_160 (RegNetY-16GF)
regnety_320 (RegNetY-32GF)
每种模型后面的括号中的内容表示的是该模型的理论计算量。例如,RegNetY-200MF表示这个模型的理论计算量为200M FLOPs,即200百万浮点运算。这个计算量可以被用来大致比较模型的复杂度。
二、RegNet迁移学习代码实战
我们继续胸片的数据集:肺结核病人和健康人的胸片的识别。其中,肺结核病人700张,健康人900张,分别存入单独的文件夹中。
(a)导入包
import copy
import torch
import torchvision
import torchvision.transforms as transforms
from torchvision import models
from torch.utils.data import DataLoader
from torch import optim, nn
from torch.optim import lr_scheduler
import os
import matplotlib.pyplot as plt
import warnings
import numpy as np
warnings.filterwarnings("ignore")
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 设置GPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
(b)导入数据集
import torch
from torchvision import datasets, transforms
import os
# 数据集路径
data_dir = "./MTB"
# 图像的大小
img_height = 100
img_width = 100
# 数据预处理
data_transforms = {
'train': transforms.Compose([
transforms.RandomResizedCrop(img_height),
transforms.RandomHorizontalFlip(),
transforms.RandomVerticalFlip(),
transforms.RandomRotation(0.2),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),
'val': transforms.Compose([
transforms.Resize((img_height, img_width)),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),
}
# 加载数据集
full_dataset = datasets.ImageFolder(data_dir)
# 获取数据集的大小
full_size = len(full_dataset)
train_size = int(0.7 * full_size) # 假设训练集占80%
val_size = full_size - train_size # 验证集的大小
# 随机分割数据集
torch.manual_seed(0) # 设置随机种子以确保结果可重复
train_dataset, val_dataset = torch.utils.data.random_split(full_dataset, [train_size, val_size])
# 将数据增强应用到训练集
train_dataset.dataset.transform = data_transforms['train']
# 创建数据加载器
batch_size = 32
train_dataloader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=4)
val_dataloader = torch.utils.data.DataLoader(val_dataset, batch_size=batch_size, shuffle=True, num_workers=4)
dataloaders = {'train': train_dataloader, 'val': val_dataloader}
dataset_sizes = {'train': len(train_dataset), 'val': len(val_dataset)}
class_names = full_dataset.classes
(c)导入RegNet
import timm
# 定义RegNet模型
model = timm.create_model('regnety_040', pretrained=True) # 你可以选择适合你需求的RegNet版本,这里以RegNetY-40GF为例
num_ftrs = model.head.fc.in_features
# 根据分类任务修改最后一层
model.head.fc = nn.Linear(num_ftrs, len(class_names))
model = model.to(device)
# 打印模型摘要
print(model)
(d)编译模型
# 定义损失函数
criterion = nn.CrossEntropyLoss()
# 定义优化器
optimizer = optim.Adam(model.parameters())
# 定义学习率调度器
exp_lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)
# 开始训练模型
num_epochs = 10
best_model_wts = copy.deepcopy(model.state_dict())
best_acc = 0.0
# 初始化记录器
train_loss_history = []
train_acc_history = []
val_loss_history = []
val_acc_history = []
for epoch in range(num_epochs):
print('Epoch {}/{}'.format(epoch, num_epochs - 1))
print('-' * 10)
# 每个epoch都有一个训练和验证阶段
for phase in ['train', 'val']:
if phase == 'train':
model.train() # Set model to training mode
else:
model.eval() # Set model to evaluate mode
running_loss = 0.0
running_corrects = 0
# 遍历数据
for inputs, labels in dataloaders[phase]:
inputs = inputs.to(device)
labels = labels.to(device)
# 零参数梯度
optimizer.zero_grad()
# 前向
with torch.set_grad_enabled(phase == 'train'):
outputs = model(inputs)
_, preds = torch.max(outputs, 1)
loss = criterion(outputs, labels)
# 只在训练模式下进行反向和优化
if phase == 'train':
loss.backward()
optimizer.step()
# 统计
running_loss += loss.item() * inputs.size(0)
running_corrects += torch.sum(preds == labels.data)
epoch_loss = running_loss / dataset_sizes[phase]
epoch_acc = (running_corrects.double() / dataset_sizes[phase]).item()
# 记录每个epoch的loss和accuracy
if phase == 'train':
train_loss_history.append(epoch_loss)
train_acc_history.append(epoch_acc)
else:
val_loss_history.append(epoch_loss)
val_acc_history.append(epoch_acc)
print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))
# 深拷贝模型
if phase == 'val' and epoch_acc > best_acc:
best_acc = epoch_acc
best_model_wts = copy.deepcopy(model.state_dict())
print()
print('Best val Acc: {:4f}'.format(best_acc))
# 加载最佳模型权重
#model.load_state_dict(best_model_wts)
#torch.save(model, 'regnet_best_model.pth')
#print("The trained model has been saved.")
(e)Accuracy和Loss可视化
epoch = range(1, len(train_loss_history)+1)
fig, ax = plt.subplots(1, 2, figsize=(10,4))
ax[0].plot(epoch, train_loss_history, label='Train loss')
ax[0].plot(epoch, val_loss_history, label='Validation loss')
ax[0].set_xlabel('Epochs')
ax[0].set_ylabel('Loss')
ax[0].legend()
ax[1].plot(epoch, train_acc_history, label='Train acc')
ax[1].plot(epoch, val_acc_history, label='Validation acc')
ax[1].set_xlabel('Epochs')
ax[1].set_ylabel('Accuracy')
ax[1].legend()
#plt.savefig("loss-acc.pdf", dpi=300,format="pdf")
观察模型训练情况:
蓝色为训练集,橙色为验证集。
(f)混淆矩阵可视化以及模型参数
from sklearn.metrics import classification_report, confusion_matrix
import math
import pandas as pd
import numpy as np
import seaborn as sns
from matplotlib.pyplot import imshow
# 定义一个绘制混淆矩阵图的函数
def plot_cm(labels, predictions):
# 生成混淆矩阵
conf_numpy = confusion_matrix(labels, predictions)
# 将矩阵转化为 DataFrame
conf_df = pd.DataFrame(conf_numpy, index=class_names ,columns=class_names)
plt.figure(figsize=(8,7))
sns.heatmap(conf_df, annot=True, fmt="d", cmap="BuPu")
plt.title('Confusion matrix',fontsize=15)
plt.ylabel('Actual value',fontsize=14)
plt.xlabel('Predictive value',fontsize=14)
def evaluate_model(model, dataloader, device):
model.eval() # 设置模型为评估模式
true_labels = []
pred_labels = []
# 遍历数据
for inputs, labels in dataloader:
inputs = inputs.to(device)
labels = labels.to(device)
# 前向
with torch.no_grad():
outputs = model(inputs)
_, preds = torch.max(outputs, 1)
true_labels.extend(labels.cpu().numpy())
pred_labels.extend(preds.cpu().numpy())
return true_labels, pred_labels
# 获取预测和真实标签
true_labels, pred_labels = evaluate_model(model, dataloaders['val'], device)
# 计算混淆矩阵
cm_val = confusion_matrix(true_labels, pred_labels)
a_val = cm_val[0,0]
b_val = cm_val[0,1]
c_val = cm_val[1,0]
d_val = cm_val[1,1]
# 计算各种性能指标
acc_val = (a_val+d_val)/(a_val+b_val+c_val+d_val) # 准确率
error_rate_val = 1 - acc_val # 错误率
sen_val = d_val/(d_val+c_val) # 灵敏度
sep_val = a_val/(a_val+b_val) # 特异度
precision_val = d_val/(b_val+d_val) # 精确度
F1_val = (2*precision_val*sen_val)/(precision_val+sen_val) # F1值
MCC_val = (d_val*a_val-b_val*c_val) / (np.sqrt((d_val+b_val)*(d_val+c_val)*(a_val+b_val)*(a_val+c_val))) # 马修斯相关系数
# 打印出性能指标
print("验证集的灵敏度为:", sen_val,
"验证集的特异度为:", sep_val,
"验证集的准确率为:", acc_val,
"验证集的错误率为:", error_rate_val,
"验证集的精确度为:", precision_val,
"验证集的F1为:", F1_val,
"验证集的MCC为:", MCC_val)
# 绘制混淆矩阵
plot_cm(true_labels, pred_labels)
# 获取预测和真实标签
train_true_labels, train_pred_labels = evaluate_model(model, dataloaders['train'], device)
# 计算混淆矩阵
cm_train = confusion_matrix(train_true_labels, train_pred_labels)
a_train = cm_train[0,0]
b_train = cm_train[0,1]
c_train = cm_train[1,0]
d_train = cm_train[1,1]
acc_train = (a_train+d_train)/(a_train+b_train+c_train+d_train)
error_rate_train = 1 - acc_train
sen_train = d_train/(d_train+c_train)
sep_train = a_train/(a_train+b_train)
precision_train = d_train/(b_train+d_train)
F1_train = (2*precision_train*sen_train)/(precision_train+sen_train)
MCC_train = (d_train*a_train-b_train*c_train) / (math.sqrt((d_train+b_train)*(d_train+c_train)*(a_train+b_train)*(a_train+c_train)))
print("训练集的灵敏度为:",sen_train,
"训练集的特异度为:",sep_train,
"训练集的准确率为:",acc_train,
"训练集的错误率为:",error_rate_train,
"训练集的精确度为:",precision_train,
"训练集的F1为:",F1_train,
"训练集的MCC为:",MCC_train)
# 绘制混淆矩阵
plot_cm(train_true_labels, train_pred_labels)
效果不错,但是第一次遇到灵敏度大于特异度的模型:
(g)AUC曲线绘制
from sklearn import metrics
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.pyplot import imshow
from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns
import pandas as pd
import math
def plot_roc(name, labels, predictions, **kwargs):
fp, tp, _ = metrics.roc_curve(labels, predictions)
plt.plot(fp, tp, label=name, linewidth=2, **kwargs)
plt.plot([0, 1], [0, 1], color='orange', linestyle='--')
plt.xlabel('False positives rate')
plt.ylabel('True positives rate')
ax = plt.gca()
ax.set_aspect('equal')
# 确保模型处于评估模式
model.eval()
train_ds = dataloaders['train']
val_ds = dataloaders['val']
val_pre_auc = []
val_label_auc = []
for images, labels in val_ds:
for image, label in zip(images, labels):
img_array = image.unsqueeze(0).to(device) # 在第0维增加一个维度并将图像转移到适当的设备上
prediction_auc = model(img_array) # 使用模型进行预测
val_pre_auc.append(prediction_auc.detach().cpu().numpy()[:,1])
val_label_auc.append(label.item()) # 使用Tensor.item()获取Tensor的值
auc_score_val = metrics.roc_auc_score(val_label_auc, val_pre_auc)
train_pre_auc = []
train_label_auc = []
for images, labels in train_ds:
for image, label in zip(images, labels):
img_array_train = image.unsqueeze(0).to(device)
prediction_auc = model(img_array_train)
train_pre_auc.append(prediction_auc.detach().cpu().numpy()[:,1]) # 输出概率而不是标签!
train_label_auc.append(label.item())
auc_score_train = metrics.roc_auc_score(train_label_auc, train_pre_auc)
plot_roc('validation AUC: {0:.4f}'.format(auc_score_val), val_label_auc , val_pre_auc , color="red", linestyle='--')
plot_roc('training AUC: {0:.4f}'.format(auc_score_train), train_label_auc, train_pre_auc, color="blue", linestyle='--')
plt.legend(loc='lower right')
#plt.savefig("roc.pdf", dpi=300,format="pdf")
print("训练集的AUC值为:",auc_score_train, "验证集的AUC值为:",auc_score_val)
ROC曲线如下:
优秀的ROC曲线!
三、调整过程
作为一个轻量级别的网络,AUC达到89%还是很不错的了,又是一个在移动端部署的有利模型。
四、RegNet、SqueezeNet、ShuffleNet、Nasnet、ResNet50、InceptionResnetV2、Mobilenet、Efficientnet、DenseNet201、Inception V3和VGG19的对比
选择最合适的模型时,还需要考虑其他因素,比如任务性质、硬件限制、数据集大小和复杂性等等。在某些情况下,小型模型(如SqueezeNet或ShuffleNet)可能会有更好的性能,因为它们可以在较低的计算成本下运行。在其他情况下,更复杂的模型(如EfficientNet或ResNet)可能会提供更高的精度。
五、数据
链接:https://pan.baidu.com/s/15vSVhz1rQBtqNkNp2GQyVw?pwd=x3jf
提取码:x3jf