深度学习零基础小白,个人由于对深度学习与计算机视觉有着浓厚的兴趣,本来打算在暑假里自学,非常幸运地看到了Datawhale的夏令营通知,于是毫不犹豫地报了名,进行CV方向的学习,也非常感谢Datawhale能提供此次的机会帮助像我一样的小白进行CV入门,接下来记录一下第一次学习的收获与心得。
环境配置
在线云平台的使用
- 百度AI studio的使用
- 学习使用colab(需要科学上网)
https://zhuanlan.zhihu.com/p/527663163 这个colab使用教程蛮好的。
简单介绍一下colab,colab是谷歌的一个在线py开发云平台,可以使用notebook格式一段一段运行代码(有兴趣也可以去了解一下jupyter notebook),且每个人可以使用免费的T4GPU高达12G的显存,缺点就是长时间不运行他会自动断掉。
实践环境配置
AI环境配置:(两者配合食用更佳)
- 视频讲解:AI夏令营:手把手带你配置AI环境_哔哩哔哩_bilibili
- 图文材料:https://gitee.com/anine09/learn-python-the-smart-way-v2/blob/main/slides/chapter_0-Installation.ipynb
baseline跑通
第三期赛题
赛题入口:脑PET图像分析和疾病预测挑战赛
- 大赛简介
大赛提供了脑PET数据集,包含了老年人受试志愿者的脑PET影像资料,包括确诊为轻度认知障碍(MCI)患者的脑部影像数据和健康人(NC)的脑部影像数据,每类各25张,一共50张。要求参赛者使用数据集训练模型并进行结果测试,评估标准采用F1_score,分数越高,效果越好。 - 大赛理解
数据集一共有两种标签(NC和MCI),所以可以把题目转化为图像二分类的任务去处理。
PS:大赛提供的数据集的图像格式为nii,简单百度了一下,nii是一种常用于神经影像学领域的文件格式,用于存储医学三维或四维图像数据,是非常规的图片格式因此需要其他的库先读取和处理这种格式的图像数据再进行后续的分析和建模。
代码分析
baseline使用的是基于统计的方法,不需要使用深度学习的模型,只需要读取图片,提取相关特征然后训练机器学习的模型即可。
数据准备
import glob # 获取文件路径
import numpy as np
import pandas as pd
import nibabel as nib # 处理医学图像数据
from nibabel.viewers import OrthoSlicer3D # 图像可视化
from collections import Counter # 计数统计
其中导入的glob功能是读取数据集的所有路径,nibabel主要负责nii格式图片的读取,counter主要是计数统计
nibabel这个库需要自己在最开始安装。
!pip install nibabel
但是运行时出现报错,是因为清华源这几天出了一点问题,换一个源就可以了,我换成了腾讯云,只需要在原代码后面粘贴-i http://mirrors.cloud.tencent.com/pypi/simple --trusted-host mirrors.cloud.tencent.com就可以了,运行成功会出现下图:
数据预处理
# 读取训练集文件路径
train_path = glob.glob('./脑PET图像分析和疾病预测挑战赛数据集/Train/*/*')
test_path = glob.glob('./脑PET图像分析和疾病预测挑战赛数据集/Test/*')
# 打乱训练集和测试集的顺序
np.random.shuffle(train_path)
np.random.shuffle(test_path)
通过执行 np.random.shuffle(train_path),训练集中的文件路径顺序会被随机打乱,使得每次训练时样本的顺序都是不同的。同样,np.random.shuffle(test_path) 也会对测试集中的文件路径顺序进行随机打乱。这样做的目的是为了防止模型过度拟合特定的训练顺序,从而提高模型的泛化能力。通过随机化训练和测试集的顺序,可以更好地确保模型对不同样本的表现一致性,从而更好地评估和使用模型。
特征提取
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']
nii格式的图片是多通道的形式,可以抽取其中的10个通道,假设一共有20个通道,那选择数就有C(20,10)个,保证了随机性,extract_feature()里面先从四维数组中选择了第一个通道的所有时间点,然后再从这些时间点中随机选择了第三维的 10 个通道。并且人工提取了零向量、非零向量的数量,平均值,标准差等一系列图像特征进行储存。
模型训练
# 对训练集进行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] # 类别
)
创建一个空列表 train_feat,用于存储训练集的特征以及类别信息。接下来的 for _ in range(30): 循环将会执行 30 次。这是一个外层循环,目的是多次进行特征提取并构建训练集,从而增加数据的多样性。在循环内部的 for path in train_path:,会对训练集中的每个图像路径进行迭代。这是一个内层循环,用于遍历训练集中的每个图像文件。
在每次内层循环中,extract_feature(path) 被调用,这会从指定路径的脑PET图像文件中提取特征。提取的特征将会包括该图像的各种统计信息以及类别信息,然后将这些特征以列表的形式添加到 train_feat 列表中。换句话说,这个循环的目的是将训练集中每个图像的特征和类别信息提取出来,并添加到 train_feat 列表中。循环执行 30 次,每次都会对每个图像进行特征提取,从而得到一个包含多次特征提取结果的列表。
接下来的代码与上述过程类似,但是针对的是测试集。首先创建一个空列表 test_feat,用于存储测试集的特征。然后在循环内部,对测试集中的每个图像路径进行迭代,从每个图像中提取特征,并将特征添加到 test_feat 列表中。
最后,使用训练集的特征和类别信息,通过逻辑回归模型进行训练。LogisticRegression 类是用于创建逻辑回归模型的对象。通过 m.fit() 方法,将训练集的特征(输入)和类别(输出)传递给模型进行训练。逻辑回归模型将尝试学习如何根据特征来预测类别。
整个代码的目的是构建特征数据,然后使用逻辑回归模型对特征进行训练,从而为后续的测试样本预测提供基础。每次循环的目的是增加训练集的多样性,以提高模型的鲁棒性。
预测与结果提交
# 对测试集进行预测并进行转置操作,使得每个样本有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)
这一部分代码的主要目的是对测试集的样本进行预测,然后通过投票的方式选择每个样本的最终预测类别,并生成一个包含预测结果的 CSV 文件,以便提交和分析。刚看到代码时一头雾水,于是就每一行代码都去查阅了一下具体是什么意思。以下是整理的每一行的含义以及重要的方法。
test_pred = m.predict(np.array(test_feat)[:, :-1].astype(np.float32)):使用训练好的逻辑回归模型 m 对测试集的特征进行预测。这将生成一个预测结果数组 test_pred,其中每个元素是预测的类别标签(‘NC’ 或 ‘MCI’)。test_pred = test_pred.reshape(30, -1).T:将 test_pred 数组进行转置操作,使得每个测试样本有 30 次预测结果。这是为了后续进行投票操作,每个样本的预测结果都有多次预测。test_pred_label = [Counter(x).most_common(1)[0][0] for x in test_pred]:对每个样本的 30 次预测结果进行投票,选出最多的类别作为该样本的最终预测类别。这将生成一个列表 test_pred_label,其中包含了每个样本的最终预测类别。submit = pd.DataFrame(…):创建一个 pandas DataFrame,用于生成提交结果。DataFrame 包括两列:‘uuid’(样本ID)和 ‘label’(预测类别)。submit = submit.sort_values(by=‘uuid’):按照样本ID对提交结果的 DataFrame 进行排序,以确保结果按照样本ID的顺序排列。submit.to_csv(‘submit.csv’, index=None):将最终的提交结果保存为 CSV 文件,文件名为 ‘submit.csv’。这个文件可以用于提交给竞赛或分析预测结果。
提交结果
预测结果评分为0.495,并不算高,后续还可以通过数据增强、调整超参数或者使用CNN等更高效的方法来训练模型。
下图为baseline整体逻辑导图。
baseline总结
baseline主要通过逻辑回归的方法进行模型训练,其存在一些优缺点:
优势:
1.解释性强: 逻辑回归是一种线性模型,其预测结果相对容易解释,可以理解每个特征对预测的影响程度。
2.计算效率高: 逻辑回归计算速度较快,适用于大规模数据集和较大特征空间。
3.简单快速: 逻辑回归是一个相对简单的模型,不需要大量的超参数调整。
局限性:
1.这种方法需要手动设计和提取特征,依赖于领域知识,且特征的质量对模型影响很大。
2.局限于线性关系: 逻辑回归只能建模线性关系,对于非线性关系的数据表现较差。
3.泛化能力受限: 逻辑回归在面对高维和复杂数据时,泛化能力可能受到限制,可能会出现欠拟合或过拟合。
结语
baseline使用了简单的逻辑回归方法,对于小白来说十分友好,听了讲解后大概明白了每一行代码的含义,里面尝试了一种基于统计值的特征提取方法,为构建模型提供了基础数据。训练结果并不高,算法还可以选择其他准确率更高的比如CNN等,但是对于刚入门的我来说能够跑通baseline已经欣喜若狂了,接下来打算慢慢学习一些优化的算法来提高结果。