摘要:
合集:AI案例-CV-医疗
数据集:科大讯飞2020脑PET图像数据集
数据集价值:利用脑PET图像检测出早期阿尔茨海默综合症病灶
AI问题:图像分类
解决方案:PyTorch框架、EfficientNet模型
一、赛题简介
脑PET全称为脑部正电子发射计算机断层显像(brain positron emission tomography PET),是反映脑部病变的基因、分子、代谢及功能状态的显像。它是利用正电子核素标记葡萄糖等人体代谢物作为显像剂,通过病灶对显像剂的摄取来反映其代谢变化,从而为临床提供疾病的生物代谢信息,为脑癫痫病、脑肿瘤、帕金森病、阿尔茨海默综合症等提供了有效的检测手段。可利用脑PET图像检测出早期阿尔茨海默综合症病灶,并提前介入治疗,从而延缓发病,对后续患者康复治疗有着积极的意义。因此本赛题以阿尔茨海默综合症为例对脑PET图像进行分析与疾病预测。
为研究基于脑PET图像的疾病预测,本次大赛提供了海量脑PET数据集作为脑PET图像检测数据库的训练样本,参赛需根据提供的样本构建模型,对阿尔茨海默综合症进行分析和预测。依据提交的结果文件,采用F1-score指标对参赛的算法性能进行评价。
二、数据集内容
脑PET图像检测数据库,记录了老年人受试志愿者的脑PET影像资料,其中50%被确诊为轻度认知障碍(MCI),25%AD患者,25%健康人,以及跟踪调查受试者信息(包括性别和年龄)。被试按医学诊断分为三类:
CN:健康
AD:阿尔茨海默综合症
MCI:轻度认知障碍
数据集中的阿尔茨海默综合症训练图片样例:

数据集分为全量数据版dataset10000和开发版dataset,不同版本的图片个数不一样。训练和测试数据分别存储在以AD、CN、MCI为名字的文件目录下。
分类 | 全量版 | 开发版 |
---|---|---|
训练集CN分类 | 3000 | 1000 |
训练集AD分类 | 3000 | 1000 |
训练集MCI分类 | 4000 | 0 |
测试集 | 2000 | 1000 |
数据集版权许可协议
Deed – CC0 1.0 通用 – Creative Commons
三、识别样例
EfficientNet模型
EfficientNet 是由 Google Research 提出的一个深度卷积神经网络架构,设计时侧重于高效性。与传统的卷积神经网络相比,EfficientNet 在相同计算预算下,提供了更高的精度。它使用了一种新的结构化搜索方法,结合了 深度、宽度 和 分辨率 的平衡,通过对这些因素进行系统性调整,达到了更高的效率。
EfficientNet 的核心思想是通过 复合缩放(compound scaling)来同时优化模型的三个重要因素:
- 深度(Depth):网络的层数。
- 宽度(Width):每层的神经元个数。
- 输入分辨率(Resolution):输入图像的分辨率。
传统的方法通常分别优化这三个因素,而 EfficientNet 通过复合缩放方法,在一个统一的框架内同时调整它们,找到最优的比例,使得在同样计算量(FLOPs)下获得更高的准确度。
安装开发包
选择合适的CUDA版本进行安装,例如pytorch==2.4.1,并安装efficientnet-pytorch:
conda create -n pytorch241-gpu python=3.10
conda activate pytorch241-gpu
conda install pytorch==2.4.1 torchvision==0.19.1 torchaudio==2.4.1 pytorch-cuda=12.1 -c pytorch -c nvidia
conda install -c conda-forge efficientnet-pytorch
请参考文章《安装深度学习框架PyTorch》。
1、数据处理
这段代码定义了一个名为QRDataset
的类,该类继承自PyTorch的Dataset
类,用于处理图像数据集。这个类主要用于处理二维码图像数据集,并对图像进行预处理。
crop_1 方法:该方法首先加载图像并将其转换为 RGB 格式,然后将图像转换为 NumPy 数组。使用 np.where(img > 50) 查找所有像素值大于 50 的坐标,并根据这些坐标裁剪图像的边界。最后,调用 crop_2() 方法将裁剪后的图像填充为正方形。
crop_2 方法:该方法将图像裁剪为正方形,首先计算图像的宽和高,并对较短的一边进行填充,使得两边相等,避免图像比例失真。使用 np.pad() 函数进行填充,将填充区域设置为零(黑色)。填充是对图像的上下和左右进行的,使图像最终变成正方形。
源码:crop.py
class QRDataset(Dataset):
def __init__(self, train_jpg, transform=None):
self.train_jpg = train_jpg
if transform is not None:
self.transform = transform
else:
self.transform = None
def crop_2(self, img):
# 以最长的一边为边长,把短的边补为一样长,做成正方形,避免resize时会改变比例
dowm = img.shape[0]
up = img.shape[1]
max1 = max(dowm, up)
dowm = (max1 - dowm) // 2
up = (max1 - up) // 2
dowm_zuo, dowm_you = dowm, dowm
up_zuo, up_you = up, up
if (max1 - img.shape[0]) % 2 != 0:
dowm_zuo = dowm_zuo + 1
if (max1 - img.shape[1]) % 2 != 0:
up_zuo = up_zuo + 1
matrix_pad = np.pad(img, pad_width=((dowm_zuo, dowm_you), # 向上填充n个维度,向下填充n个维度
(up_zuo, up_you), # 向左填充n个维度,向右填充n个维度
(0, 0)) # 通道数不填充
, mode="constant", # 填充模式
constant_values=(0, 0))
img = matrix_pad
return img
def crop_1(self, img_path):
img = Image.open(img_path).convert('RGB')
img = np.array(img)
# print(img.shape)
index = np.where(img > 50) # 找出像素值大于50的所以像素值的坐标
# print(index)
x = index[0]
y = index[1]
max_x = max(x)
min_x = min(x)
max_y = max(y)
min_y = min(y)
max_x = max_x + 10
min_x = min_x - 10
max_y = max_y + 10
min_y = min_y - 10
if max_x > img.shape[0]:
max_x = img.shape[0]
if min_x < 0:
min_x = 0
if max_y > img.shape[1]:
max_y = img.shape[1]
if min_y < 0:
min_y = 0
img = img[min_x:max_x, min_y:max_y, :]
return self.crop_2(img)
设置源数据集工作目录:dataset,数据处理后文件目录:dataset-enhancement。执行python crop.py。
if __name__ == '__main__':
path = r'./dataset/train/AD' # 原始图片路径
save_path = r'./dataset-enhancement/train/AD' # 保存的图片路径
first(path, save_path)
2、创建模型
这段代码定义了一个名为 DogeNet
的 PyTorch 神经网络模型类,DogeNet
类通过以下步骤创建了一个基于 EfficientNet-B8 的神经网络:
- 从 EfficientNet-B8 模型加载预定义架构(但没有加载预训练权重)。
- 将最后的全连接层(
_fc
)替换为一个新的输出层,输出类别数为 3(适应三分类问题)。 - 在
forward
方法中定义前向传播过程。
这个模型适用于需要使用 EfficientNet-B8 特征提取能力,并且最终需要进行三分类任务的情况。
源码:1_train.py
class DogeNet(nn.Module):
def __init__(self):
super(DogeNet, self).__init__()
# 创建一个未经过预训练的EfficientNet模型
model = EfficientNet.from_name('efficientnet-b8')
in_channel = model._fc.in_features
model._fc = nn.Linear(in_channel, 3)
self.efficientnet = model
def forward(self, img):
out = self.efficientnet(img)
return out
3、训练
这段代码定义了一个名为train
的函数,用于训练深度学习模型。它接受以下参数:
train_loader
: 一个PyTorch DataLoader对象,用于加载训练数据。model
: 要训练的深度学习模型。criterion
: 损失函数,用于计算模型预测与真实标签之间的差异。optimizer
: 优化器,用于更新模型参数以最小化损失函数。epoch
: 当前的训练轮次。
函数内部定义了一些用于度量和记录训练过程的变量,如batch_time
(每个批次的时间)、losses
(损失值)、top1
(Top-1准确率)和top5
(Top-5准确率)。然后,将模型设置为训练模式,并开始训练循环。在每次迭代中,首先将输入数据和目标标签移动到GPU上(如果可用)。然后计算模型的输出,并使用损失函数计算损失值。接下来,计算预测的Top-1和Top-5准确率,并更新相应的度量变量。之后,使用优化器计算梯度并更新模型参数。最后,记录每个批次的时间。
在训练循环中,每隔100个批次,使用progress.pr2int(i)
打印当前批次的索引。训练结束后,返回整个训练轮次的平均损失值。
这个函数可以用于训练各种深度学习模型,只需根据具体任务调整损失函数、优化器和数据加载器即可。
def train(train_loader, model, criterion, optimizer, epoch):
batch_time = AverageMeter('Time', ':6.3f')
# data_time = AverageMeter('Data', ':6.3f')
losses = AverageMeter('Loss', ':.4')
top1 = AverageMeter('Acc@1', ':6.2f')
top5 = AverageMeter('Acc@5', ':6.2f')
progress = ProgressMeter(len(train_loader), batch_time, losses, top1)
# switch to train mode
model.train()
end = time.time()
epoch_loss = []
for i, (input, target) in enumerate(train_loader):
input = input.cuda(non_blocking=True)
target = target.cuda(non_blocking=True)
# compute output
output = model(input)
# loss = criterion(output, target)
loss = CrossEntropyLoss_label_smooth(output, target, num_classes=3) # 加2
# '''warm up module'''
# if epoch<warm_epoch:
# warm_up=min(1.0,warm_up+0.9/warm_iteration)
# loss*=warm_up
# measure accuracy and record loss
acc1, acc5 = accuracy(output, target, topk=(1, 3))
losses.update(loss.item(), input.size(0))
epoch_loss.append(loss.item())
top1.update(acc1[0], input.size(0))
top5.update(acc5[0], input.size(0))
# compute gradient and do SGD step
optimizer.zero_grad()
loss.backward()
optimizer.step()
# measure elapsed time
batch_time.update(time.time() - end)
end = time.time()
if i % 100 == 0:
progress.pr2int(i)
return np.mean(epoch_loss)
4、执行训练
训练数据集工作路径:dataset-enhancement
python 1_train.py
可发现损失函数值在逐步下降。
K=20 epochs=30 batch_size=10 resume=False lr=0.01
use_gpu True
K/Epoch[0/20 0/30]:
[ 0/95] Time 325.143 (325.143) Loss 1.12 (1.12) Acc@1 30.00 ( 30.00)
[ 1/95] Time 50.279 (187.711) Loss 0.854 (0.9872) Acc@1 80.00 ( 55.00)
[ 2/95] Time 78.843 (151.422) Loss 0.7479 (0.9074) Acc@1 100.00 ( 70.00)
[ 3/95] Time 78.636 (133.225) Loss 0.7117 (0.8585) Acc@1 100.00 ( 77.50)
...
5、预测
基于训练后的模型,已知一图片进行类别预测:
源码:2_predict.py
def predict(test_loader, model, tta=10):
# switch to evaluate mode
model.eval()
test_pred_tta = None
for _ in range(tta):
test_pred = []
with torch.no_grad():
end = time.time()
for i, (input, target) in enumerate(test_loader):
input = input.cuda()
target = target.cuda()
# compute output
output = model(input)
output = output.data.cpu().numpy()
test_pred.append(output)
test_pred = np.vstack(test_pred)
if test_pred_tta is None:
test_pred_tta = test_pred
else:
test_pred_tta += test_pred
return test_pred_tta
use_gpu = torch.cuda.is_available()
model = DogeNet().cuda()
model.load_state_dict(torch.load(args.save_dir + '/' + model_path)) # 模型文件路径,默认放在args.save_dir下
# model = nn.DataParallel(model).cuda()
if test_pred is None:
test_pred = predict(test_loader, model, 5)
else:
test_pred += predict(test_loader, model, 5)
源码开源协议
作者:wangying1586