百度智能云建站,推广网站推荐,拓者设计吧官网效果图,公司名字大全20000个四个字#x1f368; 本文为#x1f517;365天深度学习训练营 中的学习记录博客#x1f356; 原作者#xff1a;K同学啊 一、实验介绍
本实验包含 645 名阿尔茨海默病受试者#xff0c;分为 AD、CN 和 MCI 组#xff0c;数据集包含 3D MRI 图像与一份CSV数据#xff0c;MRI数据… 本文为365天深度学习训练营 中的学习记录博客 原作者K同学啊 一、实验介绍
本实验包含 645 名阿尔茨海默病受试者分为 AD、CN 和 MCI 组数据集包含 3D MRI 图像与一份CSV数据MRI数据与CSV中的数据通过受试者ID进行关联。
AD (Alzheimer’s Disease)指的是已经确诊的阿尔茨海默病这是一种神经退行性疾病通常表现为认知功能的显著下降特别是记忆丧失、语言问题、以及其他认知能力的严重损害。AD 是痴呆最常见的原因。CN (Cognitively Normal)代表认知正常指的是没有出现明显的认知障碍或记忆问题的人。这类人在认知测试中表现正常不显示出认知功能的下降。MCI (Mild Cognitive Impairment)指的是轻度认知障碍这是介于认知正常和痴呆之间的一个中间阶段。MCI 的患者虽然表现出一些认知功能的下降特别是在记忆方面但这些问题尚未严重到影响日常生活的程度。MCI 有可能发展为阿尔茨海默病但并不是所有的 MCI 患者都会最终患上 AD。
二、实验环境 语言环境python 3.8编译器Pycharm深度学习环境Pytorch显卡GeForce RTX 4090D 三、准备工作
1. 设置GPU
import torch
import torch.nn as nn
import torchvision.transforms as transforms
import torchvision
from torchvision import transforms, datasetsimport os, PIL, pathlib, random
import pandas as pd
import nibabel as nib
import matplotlib.pyplot as plt
from torch.utils.data import Dataset
import torch.utils.data as data
import numpy as np
import torch.nn.functional as F# 设置GPU
device torch.device(cuda if torch.cuda.is_available() else cpu)2. 导入CSV数据
df pd.read_csv(./K1data/ADNI_1p5T_Filtered.csv,usecols[Image Data ID, Group, Sex, Age])
df.head(3)# 对sex列做one-hot编码
df pd.get_dummies(df, columns[Sex])
df.head(3)3. 导入mgz数据
labels []
img_paths []
strnums []for index, row in df.iterrows():labels.append(row[Group])# 合成文件路径img_path ./K1data/ row[Group] / row[Image Data ID] .nii_aseg_deep.mgzimg_paths.append(img_path)strnums.append([row[Age], row[Sex_F], row[Sex_M]])strnums[:3]# 定义数据文件夹路径
data_path r./K1data/AD/I48590.nii_aseg_deep.mgz # 替换为AD文件夹的实际路径img_data nib.load(data_path).get_fdata()# 将 NumPy 数组转换为 PyTorch 张量
img_tensor torch.tensor(img_data, dtypetorch.float32)
img_tensor.shape4. 数据可视化
# 加载 .mgz 文件
def load_mgz_file(file_path):return nib.load(file_path).get_fdata()# 可视化三个方向的切片
def visualize_slices(img_data):# 选择每个方向的中间切片slice_axial img_data[img_data.shape[0] // 2, :, :]slice_sagittal img_data[:, img_data.shape[1] // 2, :]slice_coronal img_data[:, :, img_data.shape[2] // 2]# 创建子图并显示fig, axes plt.subplots(1, 3, figsize(12, 4))axes[0].imshow(slice_axial.T, cmapgray, originlower)axes[0].set_title(Axial)axes[1].imshow(slice_sagittal.T, cmapgray, originlower)axes[1].set_title(Sagittal)axes[2].imshow(slice_coronal.T, cmapgray, originlower)axes[2].set_title(Coronal)# 隐藏坐标轴for ax in axes:ax.axis(off)plt.show()# 示例加载并显示 MRI 数据
file_path ./K1data/AD/I31143.nii_aseg_deep.mgz # 替换为实际文件路径
img_data load_mgz_file(file_path)
visualize_slices(img_data) # 轴向切片四、数据预处理
1. 构建数据集
class MyDataset(Dataset):def __init__(self, all_labels, img_paths, strnums, transform):self.img_labels all_labels # 获取标签信息self.img_dir img_paths # 图像目录路径self.strnums strnums # 获取数值信息self.transform transform # 目标转换函数def __len__(self):return len(self.img_labels)def __getitem__(self, index):image nib.load(self.img_dir[index]).get_fdata()if self.img_labels[index] CN:label torch.tensor(0, dtypetorch.long)elif self.img_labels[index] MCI:label torch.tensor(1, dtypetorch.long)else:label torch.tensor(2, dtypetorch.long)strnum torch.tensor(self.strnums[index], dtypetorch.float32)if self.transform:image self.transform(image)return image, strnum, label # 返回图像和标签2. 数据预处理
def interpolate_volume(vol, size, modetrilinear, align_cornersTrue):vol torch.tensor(vol, dtypetorch.float32)vol vol.unsqueeze(0).unsqueeze(0) # 扩展为 (1, 1, D, H, W) 的形状vol_resized F.interpolate(vol, sizesize,modemode, align_cornersalign_corners)return vol_resized.squeeze(0).squeeze(0) # 去掉多余的维度返回 (D_target, H_target, W_target)class ToTensor3D:将 3D numpy.ndarray 转换为 PyTorch Tensor并调整维度顺序为 (C, D, H, W)。def __call__(self, img):# 确保输入为 numpy.ndarrayif isinstance(img, np.ndarray):img torch.from_numpy(img).float()# 调整维度顺序为 (C, D, H, W)这里我们假设只有一个通道return img.unsqueeze(0) # 添加通道维度class Normalize3D:对 3D 图像进行标准化处理。def __init__(self, mean, std):self.mean torch.tensor(mean).view(-1, 1, 1, 1) # 调整维度匹配self.std torch.tensor(std).view(-1, 1, 1, 1)def __call__(self, tensor):return (tensor - self.mean) / self.stddef train_transforms(img):3D 图像的训练数据转换。Args:img (numpy.ndarray): 输入 3D 图像形状为 (D, H, W)。Returns:torch.Tensor: 转换后的图像张量形状为 (C, D, H, W)。img np.array(img)img interpolate_volume(img, size224).numpy()tensor torch.tensor(img, dtypetorch.float32)# Step 3: 标准化处理# 计算适合医学图像的均值和标准差mean tensor.mean()std tensor.std()normalized_tensor Normalize3D(mean[mean], std[std])(tensor)return normalized_tensor# 实例化数据集
total_data MyDataset(labels, img_paths, strnums, transformtrain_transforms)3. 划分数据集
train_size int(0.8 * len(total_data))
test_size len(total_data) - train_size
train_dataset, test_dataset torch.utils.data.random_split(total_data,[train_size, test_size])
train_loader torch.utils.data.DataLoader(train_dataset,batch_size4,shuffleTrue)
test_loader torch.utils.data.DataLoader(test_dataset,batch_size4,shuffleTrue)print(The number of images in a training set is: , len(train_loader) * 4)
print(The number of images in a test set is: , len(test_loader) * 4)
print(The number of batches per epoch is: , len(train_loader))for X_1, X_2, y in test_loader:print(Shape of X_1 [N, C, H, W]: , X_1.shape)print(Shape of X_2: , X_2.shape)print(Shape of X_2: , X_2[:3])print(Shape of y: , y.shape)print(Type of y: , y.type())break五、模型定义
1. 定义CNN模型
class CNNModel(nn.Module):def __init__(self):super(CNNModel, self).__init__()self.conv1 nn.Conv3d(in_channels1, out_channels12, kernel_size3, stride1, padding0)self.bn1 nn.BatchNorm3d(12)self.conv2 nn.Conv3d(in_channels12, out_channels12, kernel_size3, stride1, padding0)self.bn2 nn.BatchNorm3d(12)self.pool1 nn.MaxPool3d(2, 2)self.conv4 nn.Conv3d(in_channels12, out_channels24, kernel_size3, stride1, padding0)self.bn4 nn.BatchNorm3d(24)self.pool2 nn.MaxPool3d(2, 2)self.conv5 nn.Conv3d(in_channels24, out_channels24, kernel_size3, stride1, padding0)self.bn5 nn.BatchNorm3d(24)self.pool3 nn.MaxPool3d(2, 2)self.fc1 nn.Linear(24 * 26 * 26 * 26, 128)def forward(self, x):x F.relu(self.bn1(self.conv1(x)))x F.relu(self.bn2(self.conv2(x)))x self.pool1(x)x F.relu(self.bn4(self.conv4(x)))x self.pool2(x)x F.relu(self.bn5(self.conv5(x)))x self.pool3(x)# print(x.shape)x x.view(-1, 24 * 26 * 26 * 26)x self.fc1(x)return xmodel CNNModel().to(device)from torchinfo import summarysummary(model, (4, 1, 224, 224, 224))2. 定义融合模型
class MultiModalNN(nn.Module):def __init__(self):super(MultiModalNN, self).__init__()self.cnn_model CNNModel() # 图像 CNNself.fc_numeric nn.Linear(3, 64) # 假设数值型数据有 3 个特征self.fc_combined nn.Linear(128 64, 3) # 最终分类或回归层def forward(self, mri_data, numeric_data):# 处理 MRI 数据img_features self.cnn_model(mri_data)# 处理数值型数据num_features F.relu(self.fc_numeric(numeric_data))# print(img_features.shape, num_features.shape)# 合并两种特征combined torch.cat((img_features, num_features), dim1)# 输出分类或回归结果output self.fc_combined(combined)return output# 创建多模态模型
multi_modal_model MultiModalNN().to(device)print(multi_modal_model)3. 定义训练函数
def train(dataloader, model, loss_fn, optimizer):size len(dataloader.dataset) # 训练集的大小num_batches len(dataloader) # 批次数目train_loss, train_acc 0, 0 # 初始化训练损失和正确率for X_1, X_2, y in dataloader: # 获取图片及其标签X_1, X_2, y X_1.to(device), X_2.to(device), y.to(device)# 计算预测误差pred model(X_1, X_2) # 网络输出loss loss_fn(pred, y) # 计算网络输出和真实值之间的差距targets为真实值计算二者差值即为损失# 反向传播optimizer.zero_grad() # grad属性归零loss.backward() # 反向传播optimizer.step() # 每一步自动更新# 记录acc与losstrain_acc (pred.argmax(1) y).type(torch.float).sum().item()train_loss loss.item()train_acc / sizetrain_loss / num_batchesreturn train_acc, train_loss4. 定义测试函数
def test(dataloader, model, loss_fn):size len(dataloader.dataset) # 测试集的大小一共10000张图片num_batches len(dataloader) # 批次数目31310000/32312.5向上取整test_loss, test_acc 0, 0# 当不进行训练时停止梯度更新节省计算内存消耗with torch.no_grad():for X_1, X_2, y in dataloader:X_1, X_2, y X_1.to(device), X_2.to(device), y.to(device)# 计算lossy_pred model(X_1, X_2)loss loss_fn(y_pred, y)test_loss loss.item()test_acc (y_pred.argmax(1) y).type(torch.float).sum().item()test_acc / sizetest_loss / num_batchesreturn test_acc, test_loss六、训练模型
1. 设置超参数
loss_fn nn.CrossEntropyLoss() # 创建损失函数
learn_rate 1e-3 # 学习率
opt torch.optim.Adam(multi_modal_model.parameters(), lrlearn_rate)2. 训练模型
epochs 10
train_loss []
train_acc []
test_loss []
test_acc []for epoch in range(epochs):multi_modal_model.train()epoch_train_acc, epoch_train_loss train(train_loader, multi_modal_model, loss_fn, opt)multi_modal_model.eval()epoch_test_acc, epoch_test_loss test(test_loader, multi_modal_model, loss_fn)train_acc.append(epoch_train_acc)train_loss.append(epoch_train_loss)test_acc.append(epoch_test_acc)test_loss.append(epoch_test_loss)template (Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%, Test_loss:{:.3f})print(template.format(epoch 1, epoch_train_acc * 100, epoch_train_loss, epoch_test_acc * 100, epoch_test_loss))
print(Done)代码输出
Epoch: 1, Train_acc:34.1%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Epoch: 2, Train_acc:31.2%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Epoch: 3, Train_acc:31.2%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Epoch: 4, Train_acc:31.2%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Epoch: 5, Train_acc:31.2%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Epoch: 6, Train_acc:31.2%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Epoch: 7, Train_acc:31.2%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Epoch: 8, Train_acc:31.2%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Epoch: 9, Train_acc:31.2%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Epoch:10, Train_acc:31.2%, Train_loss:nan, Test_acc:27.1%, Test_loss:nan
Done3. 结果分析 准确率Accuracy 训练集和验证集的准确率都非常低且基本保持不变。这表明模型可能没有很好地学习到数据中的模式。 损失Loss 训练损失和测试损失均为 nanNot a Number说明在计算损失时出现了数值不稳定的情况。
4. 可能的原因及解决方案
损失为 nan通常是由于梯度爆炸或反向传播过程中出现了无效的数值操作如除以零、对数运算等。
解决方案 检查输入数据确保所有输入数据都是有效的并且没有异常值。初始化权重适当的权重初始化可以避免梯度消失/爆炸问题。使用梯度裁剪限制梯度的最大范数防止梯度爆炸。调整学习率过高的学习率可能导致梯度爆炸。尝试减小学习率。数据标准化确保输入数据经过适当的标准化处理。
准确率较低且不变模型可能过于简单无法捕捉数据中的复杂模式或者存在过拟合的风险。
解决方案 增加模型复杂度适当增加卷积层的数量或每层的输出通道数。正则化添加 Dropout 层或其他正则化技术来减少过拟合。数据增强通过数据增强技术提高模型的泛化能力。
import matplotlib.pyplot as plt
# 隐藏警告
import warnings
warnings.filterwarnings(ignore) # 忽略警告信息
plt.rcParams[font.sans-serif] [SimHei] # 用来正常显示中文标签
plt.rcParams[axes.unicode_minus] False # 用来正常显示负号
plt.rcParams[figure.dpi] 100 # 分辨率epochs_range range(epochs)plt.figure(figsize(12, 3))
plt.subplot(1, 2, 1)plt.plot(epochs_range, train_acc, labelTraining Accuracy)
plt.plot(epochs_range, test_acc, labelTest Accuracy)
plt.legend(loclower right)
plt.title(Training and Validation Accuracy)plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_loss, labelTraining Loss)
plt.plot(epochs_range, test_loss, labelTest Loss)
plt.legend(locupper right)
plt.title(Training and Validation Loss)
plt.show()七、总结
本实验是基于多模态数据MRI图像和数值特征的AD检测模型在运行原代码的过程中碰到较多问题。第一个是出现“RuntimeError: mat1 and mat2 must have the same dtype, but got Long and Float”的报错。
表示在进行矩阵乘法如 torch.mm 或线性层时输入的数据类型不一致一个是 Long 类型整数类型另一个是 Float 类型浮点数。这里主要是通过在多个步骤中添加了断言检查随后出现第二个报错AssertionError: X_2 must be float32。
表明X_2 的数据类型不是 torch.float32这是在训练数据的预处理或加载过程中出现的问题。随后返回检查数据类型时发现X_2 dtype: torch.int64通过强制转换为folat32后问题似乎得到了解决但更推荐的是在数据准备阶段处理以避免后续的问题
# 确保 strnums 是 numpy.float32 类型
strnums np.array(strnums, dtypenp.float32)我的代码相比原代码主要是修改了三个地方
class ToTensor3D:将 3D numpy.ndarray 转换为 PyTorch Tensor并调整维度顺序为 (C, D, H, W)。def __call__(self, img):# 确保输入为 numpy.ndarrayif isinstance(img, np.ndarray):img torch.from_numpy(img).float()# 调整维度顺序为 (C, D, H, W)这里我们假设只有一个通道return img.unsqueeze(0) # 添加通道维度# 计算适合医学图像的均值和标准差
# 使用统计量对张量进行标准化
mean tensor.mean()
std tensor.std()
normalized_tensor Normalize3D(mean[mean], std[std])(tensor)return normalized_tensorclass CNNModel(nn.Module):def __init__(self):super(CNNModel, self).__init__()self.conv1 nn.Conv3d(in_channels1, out_channels12, kernel_size3, stride1, padding0) # 输入通道数改为1self.bn1 nn.BatchNorm3d(12)self.conv2 nn.Conv3d(in_channels12, out_channels12, kernel_size3, stride1, padding0)self.bn2 nn.BatchNorm3d(12)self.pool1 nn.MaxPool3d(2, 2)self.conv4 nn.Conv3d(in_channels12, out_channels24, kernel_size3, stride1, padding0)self.bn4 nn.BatchNorm3d(24)self.pool2 nn.MaxPool3d(2, 2)self.conv5 nn.Conv3d(in_channels24, out_channels24, kernel_size3, stride1, padding0)self.bn5 nn.BatchNorm3d(24)self.pool3 nn.MaxPool3d(2, 2)self.fc1 nn.Linear(24 * 26 * 26 * 26, 128)最终成功跑通模型期间还因为别的软件开太多内存不够崩了一次但是结果却并不理想。尝试了下列操作结果反而变差了另外两处改进全局标准化因为内存不足没法子减小目标尺寸到64x64x64出现报错……
学习率调整从1e-3 减小到 1e-4梯度裁剪在反向传播后添加梯度裁剪防止梯度爆炸权重初始化使用 He 初始化 (kaiming_normal_) 和 Xavier 初始化 (xavier_uniform_) 来初始化卷积层和线性层的权重
Anyway对菜鸡来说跑通就是胜利吧实验到此告一段落我得去修返回的文章了. 1. 主要深度学习方法 卷积神经网络 (CNN): 用于处理MRI图像数据。通过一系列的3D卷积层 (nn.Conv3d)、批归一化层 (nn.BatchNorm3d) 和池化层 (nn.MaxPool3d) 来提取图像特征。全连接神经网络 (FCN): 用于处理数值型数据年龄、性别等并将其与CNN提取的图像特征进行融合最终通过全连接层进行分类。 2. 多模态融合方式 特征级融合: 首先使用CNN模型对MRI图像数据进行特征提取得到图像特征向量 img_features。然后使用一个全连接层 self.fc_numeric 对数值型数据进行处理得到数值特征向量 num_features。最后将这两个特征向量在维度1上进行拼接 (torch.cat((img_features, num_features), dim1))并通过另一个全连接层 self.fc_combined 进行最终的分类。 3. 难点及解决方案 数据处理和标准化: 难点: MRI图像数据通常具有不同的尺寸和强度范围需要进行标准化处理。解决方案: 使用 interpolate_volume 函数将图像数据插值到统一大小通过计算每个图像自身的均值和标准差进行标准化处理。 模型设计和调参: 难点: 设计合适的CNN结构以及确定全连接层的参数以有效地融合多模态数据并进行准确分类。解决方案: 通过实验和调整卷积层、池化层以及全连接层的参数同时使用交叉熵损失函数 (nn.CrossEntropyLoss) 和Adam优化器 (torch.optim.Adam) 进行训练。 4. 改进方向 数据增强: 对MRI图像进行更多的数据增强操作如旋转、翻转、缩放等以增加数据的多样性提高模型的泛化能力。模型优化: 尝试更深层次的CNN结构如3D ResNet以更好地提取图像特征。调整全连接层的结构和参数优化多模态特征融合的方式。 超参数调优: 使用更系统的超参数调优方法如随机搜索、网格搜索或更高级的调优算法如Optuna找到最优的超参数组合。集成学习: 将多个不同的多模态模型进行集成以提高模型的稳定性和准确性。使用预训练模型: 在大规模医学图像数据集上预训练的模型迁移学习到该任务中以加速收敛并提高性能。