大赛地址
https://aistudio.baidu.com/projectdetail/6554002?sUid=377372&shared=1&ts=1689768420818
任务介绍
基于脑PET图像的疾病预测挑战赛
近年来,深度学习在医学图像处理和疾病预测方面取得了显著的进展。脑PET(Positron Emission Tomography)图像是一种用于研究脑部功能和代谢活动的重要影像技术。在本篇博客中,我们将使用Python编程语言和一些常用的深度学习库,展示如何处理脑PET图像数据,并构建逻辑回归模型来进行脑PET图像的疾病预测。
数据集介绍
为研究基于脑PET图像的疾病预测,本次大赛提供了海量脑PET数据集作为脑PET图像检测数据库的训练样本,参赛者需根据提供的样本构建模型,对轻度认知障碍进行分析和预测。脑PET图像检测数据库,记录了老年人受试志愿者的脑PET影像资料,其中包括确诊为轻度认知障碍(MCI)患者的脑部影像数据和健康人(NC)的脑部影像数据。 被试者按医学诊断分为两类:
NC:健康
MCI:轻度认知障碍
本次大赛所用脑PET图像检测数据库,图像格式为nii。
baseline流程
将baseline部署到自己机器上的,Windows系统
导入相关的包
import glob # 获取文件路径
import numpy as np
import pandas as pd
import nibabel as nib # 处理医学图像数据
from nibabel.viewers import OrthoSlicer3D # 图像可视化
from collections import Counter # 计数统计
数据预处理
数据预处理的时候,没有先把数据打乱,而是提起出路径,将路径打乱。因为路径方便存储,而直接打乱数据内存消耗大
# 读取训练集文件路径
train_path = glob.glob('./脑PET图像分析和疾病预测挑战赛数据集/Train/*/*')
test_path = glob.glob('./脑PET图像分析和疾病预测挑战赛数据集/Test/*')
print(len(train_path))
print(len(test_path))
# 打乱训练集和测试集的顺序
np.random.shuffle(train_path)
np.random.shuffle(test_path)
特征提取
对于深度学习任务,特征提取是非常重要的一步。在本例中,我们定义了一个函数extract_feature,用于从脑PET图像中提取特征。
extract_feature函数从文件路径加载PET图像数据,并从中随机选择10个通道。然后,它计算了一系列统计特征,如非零像素数量、零像素数量、平均值、标准差等。最后,函数根据文件路径判断样本类别,并将提取到的特征和类别作为返回值。
def extract_feature(path):
# 加载PET图像数据
img = nib.load(path)
# 获取第一个通道的数据
img = img.dataobj[:, :, :, 0]
# 随机筛选其中的10个通道提取特征
random_img = img[:, :, np.random.choice(range(img.shape[2]), 10)]
# 对图片计算统计值
feat = [
(random_img != 0).sum(), # 非零像素的数量
(random_img == 0).sum(), # 零像素的数量
random_img.mean(), # 平均值
random_img.std(), # 标准差
len(np.where(random_img.mean(0))[0]), # 在列方向上平均值不为零的数量
len(np.where(random_img.mean(1))[0]), # 在行方向上平均值不为零的数量
random_img.mean(0).max(), # 列方向上的最大平均值
random_img.mean(1).max() # 行方向上的最大平均值
]
# 根据路径判断样本类别('NC'表示正常,'MCI'表示异常)
if 'NC' in path:
return feat + ['NC']
else:
return feat + ['MCI']
为什么要提取这些特征:
在这个特征提取函数中,提取了以下几个统计特征:
1.非零像素的数量
2.零像素的数量
3.平均值
4.标准差
5.列方向上非零均值的数量
6.行方向上非零均值的数量
7.列方向上的最大平均值
8.行方向上的最大平均值
9.这些特征被提取的考量有:
capturing overall intensity distribution - 通过均值、标准差等可以反映整体强度分布
highlighting sparse structures - 非零像素数量可以反映稀疏结构
summarizing shape/structure - 非零均值数量、最大均值可以反映形状结构信息
easy and fast to compute - 这些统计特征计算简单快速
working with random slices - 通过随机选取slice,可以计算稳定的统计特征
effective for anomaly detection - 平均值、极值等参数在异常检测中很有效
generalizable to new data - 这些特征对新的PET数据也具有普适性
结合病例的PET图像差异,这些特征可以用来区分正常和异常样本。
值得注意的是:
if 'NC' in path:
return feat + ['NC']
else:
return feat + ['MCI']
这个代码段在test路径中没有NC,返回值都加入了MCI并没有影响,因为最后预测数据的时候没用用最后一列。
模型训练
在这一步骤中,我们将利用extract_feature函数提取训练集和测试集的特征,并使用逻辑回归模型对训练集进行训练。
在这里,我们通过循环将特征提取过程重复进行30次,这是为了增加训练样本的多样性。然后,我们使用逻辑回归模型LogisticRegression来训练数据。在训练完成后,模型已经学习到了从特征到类别的映射关系。
# 对训练集进行30次特征提取,每次提取后的特征以及类别('NC'表示正常,'MCI'表示异常)被添加到train_feat列表中。
train_feat = []
for _ in range(30):
for path in train_path:
train_feat.append(extract_feature(path))
# 对测试集进行30次特征提取
test_feat = []
for _ in range(30):
for path in test_path:
test_feat.append(extract_feature(path))
# 使用训练集的特征作为输入,训练集的类别作为输出,对逻辑回归模型进行训练。
from sklearn.linear_model import LogisticRegression
m = LogisticRegression(max_iter=1000)
m.fit(
np.array(train_feat)[:, :-1].astype(np.float32), # 特征
np.array(train_feat)[:, -1] # 类别
)
对测试机和训练集都进行了30次的训练,并将训练集分为data、label丢入了LogisticRegression进行训练
这一类机器学习模型的分类效果还是较差
预测与结果提交
步骤五:预测与结果提交
在这一步骤中,我们使用训练好的逻辑回归模型对测试集进行预测,并将预测结果进行投票,选出最多的类别作为该样本的最终预测类别。最后,我们将预测结果存储在CSV文件中并提交结果。
具体来说,我们使用了Counter来统计每个样本的30次预测结果中最多的类别,并将结果存储在test_pred_label列表中。然后,我们将样本ID和对应的预测类别存储在一个DataFrame中,并将其按照ID排序后保存为CSV文件,这样我们就得到了最终的结果提交文件。
# 对测试集进行预测并进行转置操作,使得每个样本有30次预测结果。
test_pred = m.predict(np.array(test_feat)[:, :-1].astype(np.float32))
test_pred = test_pred.reshape(30, -1).T
# 对每个样本的30次预测结果进行投票,选出最多的类别作为该样本的最终预测类别,存储在test_pred_label列表中。
test_pred_label = [Counter(x).most_common(1)[0][0] for x in test_pred]
# 生成提交结果的DataFrame,其中包括样本ID和预测类别。
submit = pd.DataFrame(
{
'uuid': [int(x.split('/')[-1][:-4]) for x in test_path], # 提取测试集文件名中的ID
'label': test_pred_label # 预测的类别
}
)
# 按照ID对结果排序并保存为CSV文件
submit = submit.sort_values(by='uuid')
submit.to_csv('submit.csv', index=None)
最后使用 m.predict()函数进行预测,因为之前进行了30次的特征提取,所以对每个样本的30次预测结果进行投票,选出最多的类别作为该样本的最终预测类别,存储在test_pred_label列表中。
同时,部署在Windows上有个小坑,就是路径问题:
原代码:
'uuid': [int(x.split('/')[-1][:-4]) for x in test_path], # 提取测试集文件名中的ID
报错:
因为在windows上路径表达不同,代码应该改为:
'uuid': [int(x.split('\\')[-1][:-4]) for x in test_path], # 提取测试集文件名中的ID