(一)Datawhale AI夏令营第三期 - 脑PET图像分析和疾病预测挑战赛——环境搭建及 PyTorch Baseline跑通
上期完成Baseline跑通后,这期主要是对Baseline的优化,整队图像分类问题,主要有以下几类优化方法。
由于本次任务提供的训练集较少,正负样本各25个,共计50个训练样本,因此需对图像进行增强。以下是进行图像增强的方法
首先安装并引入数据增强库albumentations
import albumentations as A
pip install albumentations
在读取数据时设置图像增强
train_loader = torch.utils.data.DataLoader(
XunFeiDataset(train_path[:-10],
A.Compose([
A.RandomRotate90(),#随机旋转90度
A.RandomCrop(120, 120),#随机裁剪
A.HorizontalFlip(p=0.5),#随机水平翻转
A.RandomBrightnessContrast(p=0.5),#随机亮度对比度
])
), batch_size=2, shuffle=True, num_workers=0, pin_memory=False
)
上面代码主要对图像做了以下增强,由于数据集的图片为灰度的医学图像,不够直观,所有下面采用网图进行举例,增强效果案例左边为原图,右边为增强后图片
A.RandomRotate90(),#随机旋转90度(括号内可添加参数p(0-1)表示旋转概率)
A.RandomCrop(120, 120),#随机裁剪
将图像随机截取120*120大小部分
A.HorizontalFlip(p=0.5),#随机水平翻转,翻转概率为50%
A.RandomBrightnessContrast(p=0.5),#随机亮度对比度概率为50%
除此之外,还有RGBShift(RGB平移),RandomGamma 随机Gamma 等方法,大家可以参考
albumentations官方文档
Albumentations数据增强方法
针对灰度图像RGB平移这一类方法不会起作用,一般增加对比度和亮度效果比较明显
交叉验证(Cross-Validation,简称CV)是一种在机器学习中用于评估模型性能和选择参数的技术。它的目标是充分利用有限的数据,减少因数据分布不均匀或随机性带来的评估误差。交叉验证将数据分成多个子集(折叠),然后多次训练和测试模型,以获取更准确的性能评估。
本次直接使用sk-learn的交叉验证库
首先引入对应库
from sklearn.model_selection import train_test_split, StratifiedKFold, KFold
随后设置K者基本参数
#n_splits=10设置为10折验证
#random_state=233设置随机状态为233(这里随便设置一个整数就行了)
#shuffle=True开启数据随机打乱
skf = KFold(n_splits=10, random_state=233, shuffle=True)
在数据载入时使用交叉验证方式(对于新版的numpy有一个小坑,需要使用np.array(train_path)将数组转换为np arry才能运行,不然会报错)
for fold_idx, (train_idx, val_idx) in enumerate(skf.split(train_path, train_path)):
#测试集读取
train_loader = torch.utils.data.DataLoader(
XunFeiDataset(np.array(train_path)[train_idx],#此处需将train_path这个数组前置转换为np arry
A.Compose([
A.RandomRotate90(),
A.RandomCrop(120, 120),
A.HorizontalFlip(p=0.5),
A.RandomBrightnessContrast(p=0.5),
# A.RandomGamma(),
])
), batch_size=1, shuffle=True, num_workers=0, pin_memory=False
)
#验证集读取
val_loader = torch.utils.data.DataLoader(
XunFeiDataset(np.array(train_path)[val_idx],#针对原始的数据读取方法主要就是修改了这里
A.Compose([
A.RandomCrop(120, 120),
A.RandomRotate90(),
A.HorizontalFlip(p=0.5),
A.RandomBrightnessContrast(p=0.5),
# A.RandomGamma(),
])
), batch_size=1, shuffle=False, num_workers=0, pin_memory=False
)
设置模型训练参数并进行模型训练
model = XunFeiNet()#设置训练模型
model = model.to('cuda')#选择使用cuda训练
criterion = nn.CrossEntropyLoss().cuda()#设置损失函数
optimizer = torch.optim.AdamW(model.parameters(), 0.001)#设置优化器及学习率
for _ in range(10):#进行十次训练
train_loss = train(train_loader, model, criterion, optimizer)
val_acc = validate(val_loader, model, criterion)
train_acc = validate(train_loader, model, criterion)
print(train_loss, train_acc, val_acc)输出训练损失,训练集准确率,验证集准确率
torch.save(model.state_dict(), './resnet18_fold{0}.pt'.format(fold_idx))#将该折模型参数保存起来
预测时使用多模型进行预测
pred = None
for model_path in ['resnet18_fold0.pt', 'resnet18_fold1.pt', 'resnet18_fold2.pt',
'resnet18_fold3.pt', 'resnet18_fold4.pt', 'resnet18_fold5.pt',
'resnet18_fold6.pt', 'resnet18_fold7.pt', 'resnet18_fold8.pt',
'resnet18_fold9.pt']:
model = XunFeiNet()#设置预测模型
model = model.to('cuda')#设置为cuda计算
model.load_state_dict(torch.load(model_path))#读取模型参数
#进行模型预测
for _ in range(10):
if pred is None:
pred = predict(test_loader, model, criterion)
else:
pred += predict(test_loader, model, criterion)
原Baseline使用的是Resnet18模型,也可进行模型更换;来提高模型效果,更换模型后主要需要修改两个地方,第一个是模型的第一层,一个是模型的最后层输出层,下面已将Resnet18更换为Vgg19进行举例:
原始Baseline Resnet18代码如下:
class XunFeiNet(nn.Module):
def __init__(self):
super(XunFeiNet, self).__init__()
model = models.resnet18(True)
model.conv1 = torch.nn.Conv2d(50, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
model.avgpool = nn.AdaptiveAvgPool2d(1)
model.fc = nn.Linear(512, 2)
self.resnet = model
def forward(self, img):
out = self.resnet(img)
return out
首先我们需要查看vgg19网络模型结构,如下图
针对以上网络模型主要修改如下
1.默认的Vgg19是针对RGB图像的因此默认输入适配3通道图像,本次Baseline使用50通道图像,因此需修改第一层
2.默认vgg19模型最后输出为1000个分类,本次Baseline为2分类问题,因此需要修改最后的全连接层输出为2
修改后代码如下:
class XunFeiNet(nn.Module):
def __init__(self):
super(XunFeiNet, self).__init__()
model = models.vgg19(True)#修改模型为vgg19并启用预训练模型
model.features[0]=nn.Conv2d(50, 16, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)#将第一层输入修改为本次数据的50通道
model.classifier[6]=nn.Linear(4096,2,bias=True)#将最后一程输出修改为2
self.resnet = model
def forward(self, img):
out = self.resnet(img)
return out
经过以上调整,成功将分数提升到了0.74214,后续将继续对模型进行优化