深度学习医学交叉这几年一直是热门方向很多人一开始都是从通用图像分类入手的但真正进到医学影像场景时会发现情况不太一样。数据格式、类别不平衡、小样本、标注质量、设备差异这些问题都会直接影响模型能不能落地。PyTorch 和 ResNet 的组合是目前最稳妥的入门路线也是论文复现时最常见的基准模型。这篇内容围绕“论文带读 代码复现”来展开先讲清楚医学图像分类和通用分类的差异再给出可执行的环境配置、数据准备、模型训练、评估和推断流程最后补上复现论文时容易踩的坑。这类主题最值得先看的不是背下来多少层结构而是能不能在本地环境里完整跑通一条实验链路数据进来之后怎么预处理模型怎么加载预训练权重训练时哪些参数最影响结果测试时怎么算指标以及最终怎么把模型导出用于新的图片预测。下面按实际落地顺序拆一遍。1. 先理解医学图像分类和普通图像分类的差异在哪里很多人第一次做医学图像分类直接套用 ImageNet 上的训练流程结果发现验证集精度很高换到新数据上就不行。这不是模型本身的问题而是医学图像分类和自然图像分类在数据分布、评价标准、任务目标上有本质差异。1.1 医学图像分类解决的是什么问题医学图像分类本质上还是图像分类输入是一张二维或三维图像输出是类别标签。常见任务包括病理切片图像分类判断某区域属于肿瘤还是正常组织。眼底图像分类识别糖尿病视网膜病变等级。皮肤镜图像分类区分良性痣和黑色素瘤。胸部 X 光图像分类判断是否包含肺炎病灶。MRI 影像分类辅助阿尔茨海默病等疾病的筛查。这些任务有一个共同点类别之间差异可能很小背景复杂而且标注成本很高。比如皮肤病变图像良性和恶性之间可能只差一个颜色变化或边缘形态普通分类模型很容易被背景干扰带偏。所以医学图像分类里数据预处理和模型设计的重要性有时候比模型结构本身更大。1.2 ResNet 为什么适合作为医学图像分类的基线ResNet 的核心思想是残差学习。它通过跳跃连接让梯度可以更顺畅地回传从而支持训练更深的网络。对于医学图像数据量通常不大的情况ResNet 有几个非常实际的优势有成熟的 ImageNet 预训练权重可以直接迁移学习。结构成熟PyTorch 官方 torchvision 自带不需要自己从零实现。不同层数可以按数据规模选择数据量小用 ResNet18 或 ResNet34数据量较大再考虑 ResNet50 以上。训练技巧成熟学习率、优化器、数据增强的通用经验基本可以直接复用。做论文复现时ResNet 也经常作为对比实验里的 baseline 模型出现。先把 ResNet 跑通再替换成新的注意力模块、Transformer 结构或其他优化方法这是最常用的复现思路。1.3 医学图像分类里最容易忽略的三个要点第一是类别不平衡。很多时候阳性样本只有几百张阴性样本有几千甚至上万张。直接用交叉熵损失模型会倾向于把大部分样本预测为多数类。这时候要看的不只是准确率还需要看精确率、召回率、F1-score 和混淆矩阵。第二是数据划分方式。医学数据常常来自不同患者、不同设备、不同中心。如果划分时不做患者级别隔离同一个患者的多张图片可能同时出现在训练集和验证集里导致验证指标虚高。第三是预处理一致性。训练时的归一化参数、图像尺寸、灰度通道处理方式推断时也必须保持一致。很多人模型训练得很好最后部署时图片尺寸和归一化方式不一致结果完全不能用。2. 环境准备PyTorch、GPU、依赖库怎么搭配最省事复现代码的第一步不是写模型而是把环境搭好。环境问题占了初学者报错的一半以上。这里给一套稳妥的配置顺序适合在 Windows 或 Ubuntu 上操作。2.1 先确认 PyTorch 版本和 CUDA 的对应关系PyTorch 安装时最容易出问题的是 CUDA 版本不匹配。判断标准很简单先看显卡驱动支持的 CUDA 版本再安装对应版本的 PyTorch。如果机器上有 NVIDIA 显卡先在终端执行nvidia-smi输出里右上角会显示 Driver Version 和 CUDA Version。这里显示的 CUDA Version 是驱动支持的最高版本不一定要和 PyTorch 的 CUDA 版本完全一致PyTorch 运行时要求的是驱动版本不能低于某个门槛。安装 PyTorch 现在最推荐去 PyTorch 官网生成对应的安装命令。比如pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121如果机器没有 NVIDIA 显卡可以安装 CPU 版本pip install torch torchvisionCPU 版本能跑通代码、能做小规模实验但训练速度会明显变慢。如果只是复现论文和验证结构CPU 环境也可以接受但 batch size 和图像尺寸要相应调小。注意不要一上来就装最新版 PyTorch。先确认自己的 torchvision 版本、Python 版本和 CUDA 版本能匹配否则运行时会报 “Detected that PyTorch and torchvision were compiled with different CUDA versions” 之类的错误。2.2 用 Anaconda 创建独立环境强烈建议用 conda 创建独立环境不要直接在 base 环境里装东西。长期开发中不同项目依赖的 PyTorch 版本可能不一样独立环境可以避免版本冲突。conda create -n medical_resnet python3.9 conda activate medical_resnet pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121可选装的基础库pip install numpy pandas matplotlib scikit-learn tqdm pillow opencv-python如果你的任务会用到医学影像专用格式比如 DICOM 或 NIfTI还需要额外装pip install pydicom pip install nibabel2.3 验证 PyTorch 环境是否可用环境配置完成后先跑一段很小的代码确认import torch import torchvision print(torch.__version__) print(torchvision.__version__) print(torch.cuda.is_available())如果torch.cuda.is_available()输出为 False但有 NVIDIA 显卡大概率是 CUDA 版本或驱动版本问题。如果输出 True可以继续查看 GPU 名称print(torch.cuda.get_device_name(0))这一小步非常关键。很多后续报错都源于环境没验证通过就急着跑训练脚本结果连模型都没加载就崩了。3. 数据集准备命名、目录结构和预处理决定一半结果医学图像分类的数据集来源很多常见的有公开数据集和自己收集的临床数据。无论哪种建议先整理成统一的目录结构方便后续写 Dataset。3.1 推荐的数据目录结构最简单的做法就是按类别分文件夹data/ train/ normal/ 001.jpg 002.jpg disease/ 001.jpg 002.jpg val/ normal/ 001.jpg disease/ 001.jpg test/ normal/ 001.jpg disease/ 001.jpg这种结构可以直接配合torchvision.datasets.ImageFolder使用不需要自己解析 JSON 或 CSV。对于分类任务来说这是最省事的方式。如果数据量特别大或者类别信息存在 CSV 里也可以自定义 Dataset。但对于复现论文的初期阶段推荐先用简单的目录结构跑通流程。3.2 数据预处理参数怎么设置医学图像预处理通常包含以下内容from torchvision import transforms train_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(), transforms.RandomRotation(10), transforms.ColorJitter(brightness0.2, contrast0.2), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) val_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])这里需要注意几点mean和std使用的是 ImageNet 的统计值因为加载的是 ImageNet 预训练权重。如果自己从零训练可以用训练集的均值和方法重新计算。验证集和测试集一般只做尺寸调整和归一化不做随机增强。目的是模拟模型在真实数据上的表现。如果医学图像是灰度图需要先转成三通道否则无法直接加载预训练权重。常见做法是from PIL import Image img Image.open(path).convert(RGB)3.3 小样本场景下如何扩充数据医学图像分类里小样本是常态。如果每个类别只有几十张图直接训练很容易过拟合。可以先用以下几种方式扩充随机翻转、旋转、平移、缩放。颜色抖动模拟不同设备、不同光照条件下的图像差异。高斯噪声、模糊等模拟采集噪声。但要注意不是所有医学任务都适合颜色抖动。比如病理图像中的颜色信息可能直接和染色方式相关过度改变颜色反而会破坏语义。更稳妥的做法是先做翻转和旋转这类几何增强再决定是否加颜色增强。4. 模型构建用 PyTorch 加载 ResNet 预训练模型ResNet 在 PyTorch 中可以直接通过 torchvision 加载不需要手动实现每一个残差块。如果只是复现论文官方实现已经足够稳定。4.1 加载 ResNet18 并修改分类头import torch import torch.nn as nn import torchvision.models as models model models.resnet18(pretrainedTrue) num_features model.fc.in_features num_classes 2 model.fc nn.Linear(num_features, num_classes)这段代码做了三件事加载在 ImageNet 上预训练的 ResNet18 权重。获取原始全连接层的输入维度ResNet18 是 512ResNet50 是 2048。替换最后一层为类别数对应的全连接层。这里直接把最后一层替换成nn.Linear(num_features, num_classes)是可以的。如果想要更精细的迁移学习也可以把最后的 fc 层改成包含 Dropout 和全连接的多层结构但大多数场景下一层全连接就够用。4.2 ResNet18、ResNet34、ResNet50 怎么选选择原则很简单数据量很少比如每类几百张优先 ResNet18。参数少不容易过拟合训练快。数据量中等每类几千张可以试 ResNet34 或 ResNet50。数据量很大且计算资源充足再考虑 ResNet50 以上或者和其他更复杂模型做对比。医学图像分类任务里Not all data is equal。如果病灶区域很小比如肺结节在整张 X 光片里只占很小一块ResNet18 不一定够用。这种场景下需要关注的不是单纯加深网络而是输入分辨率、注意力机制和局部区域裁剪。4.3 冻结底层特征还是微调全部参数迁移学习时有两种常见策略只训练最后的全连接层冻结前面的卷积层。全部参数一起微调。初学阶段建议先全部微调但设置一个较小的学习率比如0.0001。因为医学图像和 ImageNet 图像差异较大冻结底层可能导致模型无法适应医学图像的纹理特征。如果你想固定卷积层特征可以这样做for param in model.parameters(): param.requires_grad False for param in model.fc.parameters(): param.requires_grad True这种方式适合目标数据集和 ImageNet 分布非常接近的情况或者数据量非常少、担心过拟合时使用。5. 训练流程数据加载、损失函数、优化器和训练循环训练脚本是复现论文里占用时间最多的部分。如果只是验证模型能不能跑可以直接用通用训练循环。如果要达到论文报告的精度就需要逐步调参了。5.1 数据加载器怎么写from torch.utils.data import DataLoader from torchvision.datasets import ImageFolder train_dataset ImageFolder(data/train, transformtrain_transform) val_dataset ImageFolder(data/val, transformval_transform) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers4) val_loader DataLoader(val_dataset, batch_size32, shuffleFalse, num_workers4)shuffleTrue只在训练集设置验证集不用打乱。num_workers在 Windows 上经常出错如果遇到 DataLoader worker 崩溃可以先设为 0train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers0)在 Linux 服务器上num_workers可以按 CPU 核数上调但并不是越大越好。医学图像解码本身耗时如果 CPU 核数有限num_workers设太高反而会增加进程切换开销。5.2 损失函数和优化器import torch.optim as optim criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr0.0001) scheduler optim.lr_scheduler.StepLR(optimizer, step_size10, gamma0.1)交叉熵损失是分类任务的首选。如果遇到非常严重的类别不平衡可以先不用自定义损失而是通过class_weight参数给少数类更高权重criterion nn.CrossEntropyLoss(weighttorch.tensor([1.0, 3.0]))这里的[1.0, 3.0]表示第二个类别权重更大。具体值要根据类别比例计算比如多数类和少数类数量比是 10:1可以设为[1.0, 10.0]但实际效果需要验证。Adam 和 SGD 都可以。经验上如果使用 ImageNet 预训练模型SGD 配合学习率衰减往往更稳定而 Adam 的优势是收敛快、对学习率更不敏感。复现论文时先看原论文用的是哪个优化器如果没有明确说明可以先从 Adam 开始。5.3 基础训练循环def train_one_epoch(model, loader, criterion, optimizer, device): model.train() running_loss 0.0 correct 0 total 0 for images, labels in loader: images images.to(device) labels labels.to(device) outputs model(images) loss criterion(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step() running_loss loss.item() * images.size(0) _, predicted torch.max(outputs, 1) total labels.size(0) correct (predicted labels).sum().item() epoch_loss running_loss / total epoch_acc correct / total return epoch_loss, epoch_acc这里每步都做了一次optimizer.zero_grad()这是标准做法避免梯度累积。验证时需要注意model.eval()和torch.no_grad()def validate(model, loader, criterion, device): model.eval() running_loss 0.0 correct 0 total 0 with torch.no_grad(): for images, labels in loader: images images.to(device) labels labels.to(device) outputs model(images) loss criterion(outputs, labels) running_loss loss.item() * images.size(0) _, predicted torch.max(outputs, 1) total labels.size(0) correct (predicted labels).sum().item() epoch_loss running_loss / total epoch_acc correct / total return epoch_loss, epoch_acc5.4 主训练循环和模型保存device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device) num_epochs 50 best_val_acc 0.0 for epoch in range(num_epochs): train_loss, train_acc train_one_epoch( model, train_loader, criterion, optimizer, device ) val_loss, val_acc validate(model, val_loader, criterion, device) scheduler.step() print(fEpoch {epoch1}/{num_epochs} | fTrain Loss: {train_loss:.4f} | Train Acc: {train_acc:.4f} | fVal Loss: {val_loss:.4f} | Val Acc: {val_acc:.4f}) if val_acc best_val_acc: best_val_acc val_acc torch.save(model.state_dict(), best_model.pth)model.state_dict()只保存模型参数不保存优化器状态和训练 epoch。推荐保存这个格式用于加载模型预测如果要断点续训则需要额外保存优化器状态。6. 评估模型准确率不是唯一指标医学图像分类里准确率高不等于模型好用。尤其当类别不平衡时准确率会掩盖模型在少数类上的糟糕表现。6.1 计算精确率、召回率和 F1-scorefrom sklearn.metrics import classification_report, confusion_matrix, roc_auc_score def evaluate_model(model, loader, device): model.eval() all_labels [] all_preds [] all_probs [] with torch.no_grad(): for images, labels in loader: images images.to(device) labels labels.to(device) outputs model(images) probs torch.softmax(outputs, dim1) _, preds torch.max(outputs, 1) all_labels.extend(labels.cpu().numpy()) all_preds.extend(preds.cpu().numpy()) all_probs.extend(probs.cpu().numpy()) print(classification_report(all_labels, all_preds)) cm confusion_matrix(all_labels, all_preds) print(Confusion Matrix:) print(cm) return all_labels, all_preds, all_probs如果是二分类还可以计算 AUCauc roc_auc_score(all_labels, [p[1] for p in all_probs]) print(fAUC: {auc:.4f})AUC 只看模型对正负样本排序能力不依赖具体阈值所以在医学诊断场景里更常用。6.2 从混淆矩阵定位问题混淆矩阵可以直观看出模型在哪个类别上更容易出错。如果假阴性非常多说明模型容易漏诊这在医学场景里通常比假阳性更严重。此时可以提高少数类权重。调整分类阈值降低正类判定门槛。收集更多少数类样本。对少数类做更强的数据增强。如果整体准确率挺好但某个类别召回率很低问题大概率出在数据不平衡或特征区分度不够而不一定是网络结构不够深。6.3 可视化错误样本训练结束后把预测错误的图片收集出来看看往往能发现标注问题或预处理问题。比如图像方向不一致、病灶被裁剪掉、某些类别本身存在歧义。这一步在实际工作中非常有用。很多模型性能提不上去最后发现是数据集本身存在错误标注。7. 用训练好的模型做单张图片推断训练完成之后最终要落到的场景是输入一张新的医学图像输出类别和置信度。7.1 加载模型并预测单张图片import torch import torchvision.models as models from torchvision import transforms from PIL import Image device torch.device(cuda if torch.cuda.is_available() else cpu) model models.resnet18(pretrainedFalse) num_features model.fc.in_features model.fc torch.nn.Linear(num_features, 2) model.load_state_dict(torch.load(best_model.pth, map_locationdevice)) model.to(device) model.eval() infer_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) image Image.open(test_image.jpg).convert(RGB) image infer_transform(image).unsqueeze(0).to(device) with torch.no_grad(): outputs model(image) probs torch.softmax(outputs, dim1) confidence, pred torch.max(probs, dim1) class_names [normal, disease] print(fPredicted class: {class_names[pred.item()]}) print(fConfidence: {confidence.item():.4f})pretrainedFalse表示不加载 ImageNet 权重因为我们随后会用自己训练好的权重覆盖。map_locationdevice是为了在没有 GPU 的机器上也能加载 GPU 训练出来的权重。7.2 推断阶段最容易踩的坑推断阶段最常见的问题是训练和推断预处理不一致。举一个典型例子训练时用的是灰度图转三通道推断时直接读取为 RGB训练时 Resize 到 224推断时用了原始尺寸。这些问题不会直接报错但会导致预测结果明显变差。另外要注意如果训练时做了随机的水平翻转等增强推断阶段不要加随机增强。推断只做固定操作。8. 论文复现时怎么读代码和调参复现医学图像分类论文和复现通用图像分类论文思路类似但有几个地方要格外仔细。8.1 先复现数字再理解方法建议按这个顺序来读一篇论文和相关代码确认任务分类对象是什么类别数是多少评价指标是什么。确认数据集用了哪个公开数据集训练集、验证集、测试集怎么划分。确认模型backbone 是什么预训练权重是什么最后几层怎么改的。确认训练配置优化器、学习率、batch size、训练轮数、学习率衰减策略。确认数据预处理和增强策略。先把代码跑通得到和论文接近的数字。再去看论文里提出的核心改进点尝试单独修改模型结构或损失函数观察指标变化。很多人在第 6 步之前就急着改模型结果连 baseline 都复现不出来。先复现 baseline再验证改进这才是稳妥的复现顺序。8.2 ResNet 复现时的常用改进方向如果论文里提出了一种新的注意力模块或特征融合方式复现时可以先在 ResNet 上做小实验保留原始 ResNet18加一个新的模块对比基线模型和加了模块之后的性能差异。具体做法是自定义一个新的模型类class CustomResNet(nn.Module): def __init__(self, num_classes2): super().__init__() self.backbone models.resnet18(pretrainedTrue) self.backbone.fc nn.Identity() self.attention YourAttentionModule() # 自定义模块 self.fc nn.Linear(512, num_classes) def forward(self, x): features self.backbone(x) attended self.attention(features) out self.fc(attended) return out这里nn.Identity()用来去掉原始全连接层让backbone只输出特征向量。之后再接自定义模块和新的分类头。如果自定义模块比较轻量可以用相同训练配置做对比实验。如果训练不稳定优先检查学习率、残差连接和输入输出维度。8.3 显存不够时的调参顺序显存不足是复现论文时最常碰到的问题之一。优先调整以下参数按危险程度从低到高减小 batch size从 32 减到 16 或 8。降低输入图像分辨率从 224 减到 192 或 160但要重新检查模型对任务是否仍然适用。使用混合精度训练torch.cuda.amp可以降低显存占用。梯度累积如果 batch size 必须保持 32可以每次前向 8 张累积 4 次再更新。从这里开始影响会逐渐变大换更小的模型比如 ResNet18 替代 ResNet50。冻结部分底层卷积层减少需要计算梯度的参数量。注意降低 batch size 通常会影响 BatchNorm 的统计稳定性。如果 batch size 很小比如 2 或 4可以考虑使用更大的 batch 做一轮预实验或者把 BatchNorm 换成 GroupNorm。9. 常见问题和排查链路医学图像分类复现过程里报错和结果异常是常态。遇到问题不要急着怀疑模型结构按照下面的顺序排查。9.1 训练开始就报错如果脚本一运行就报错优先检查数据路径是否存在ImageFolder只能识别按类别分好的文件夹别把图片直接放 data/train 根目录。图片是否损坏用 PIL 打开看看损坏文件会导致 DataLoader 中断。图片通道数灰度图需要转成 RGB否则输入维度不匹配预训练模型。标签是否连续从 0 开始ImageFolder会根据文件夹名按字母排序生成标签确认类别顺序符合预期。9.2 模型训练正常但验证集精度一直很低训练精度和验证精度都很低说明模型没学到有效特征。优先检查是否忘记model.train()和model.eval()切换。数据增强是否过强把语义信息破坏掉了。学习率是否设置过大或过小。标签和图像是否对得上数据加载有没有错位。预训练模型是否真的加载成功。有时候代码写错模型从头训练效果自然差很多。验证精度正常但测试时不正常多数情况是预处理不一致或数据分布不同。9.3 训练损失下降但准确率不上升这种情况常见于类别极端不平衡。模型学会了输出多数类损失看起来在下降但少数类完全没有被识别出来。这时候要去看混淆矩阵而不是只看训练损失。如果确认是不平衡可以用weighted sampler或者类别加权损失。PyTorch 中可以用WeightedRandomSampler调整采样概率from torch.utils.data import WeightedRandomSampler labels train_dataset.targets class_counts [sum(labels i) for i in range(num_classes)] samples_weight [1.0 / class_counts[label] for label in labels] samples_weight torch.tensor(samples_weight, dtypetorch.float) sampler WeightedRandomSampler(samples_weight, num_sampleslen(samples_weight), replacementTrue) train_loader DataLoader(train_dataset, batch_size32, samplersampler)9.4 模型结果震荡不收敛如果训练损失上下跳动验证集指标忽高忽低优先做三件事降低学习率比如从0.001降到0.0001。增大 batch size让梯度的噪声更小。加入学习率衰减比如每 10 个 epoch 乘以 0.1。如果仍然不收敛检查数据是否存在标签噪声或者类别本身区分度不够。10. 复现论文后的下一步优化方向代码跑通、指标达标之后医学图像分类项目还能继续往下走。这里列出几个常见的延伸方向。10.1 从单中心到多中心数据的适配医学图像分类模型真实落地时常常会遇到设备差异和中心差异。同一个模型在 A 医院数据上效果好在 B 医院数据上可能明显下降。遇到这种情况可以先把多个中心的图像统计信息拉出来对比一下比如像素均值、方差、图像尺寸、颜色分布。如果差异很大需要对图像做标准化或域适应这已经不是单纯改 ResNet 结构能解决的问题。10.2 从分类到定位分类只能告诉医生“这张图有没有病”但医生更需要知道“病灶在哪里”。这时需要从图像分类扩展到目标检测或分割。ResNet 经常被用作检测或分割模型的 backbone。比如 Faster R-CNN 的骨干网络可以用 ResNet50U-Net 的编码器部分也可以用 ResNet 结构。这意味着当前分类项目跑通后你学习到的模型构建和数据流经验可以直接复用到更复杂任务上。10.3 从单模型到模型集成医学图像分类对误诊容忍度很低单模型稳定性有限。一个常见做法是训练多个模型再进行投票或平均分别训练 ResNet18、ResNet34、ResNet50。也可以训练同一个模型多次每次用不同的随机种子。推理时对多个模型的 softmax 概率取平均。模型集成一般能带来稳定提升但训练和推断成本会成倍增加。要在实际应用中权衡。11. 关于深度学习环境配置和长期维护的一些建议PyTorch 环境配置看起来是一步的事情实际上需要长期维护。版本升级、显卡驱动更新、新项目依赖冲突这些都是常态。11.1 记录环境信息建议在每个项目里维护一个requirements.txt或environment.yml记录核心依赖和版本pip freeze requirements.txt如果是 Anaconda 环境可以conda env export environment.yml下次换电脑或换服务器可以直接按这个文件重建环境。11.2 固定随机种子训练代码里应该固定随机种子保证实验可复现import random import numpy as np import torch def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) torch.backends.cudnn.deterministic True torch.backends.cudnn.benchmark False注意torch.backends.cudnn.deterministic True会让训练略微变慢因为算法选择被固定了。如果只是日常调试可以先关闭等最终实验时再打开。11.3 日志和输出管理复现实验时一定要把日志记录下来。建议至少记录每次实验的配置参数。每个 epoch 的损失和准确率。最佳模型对应的验证指标。数据和预处理版本。可以用简单的文本日志也可以用 TensorBoard 或 wandb。刚开始不需要引入复杂工具先保证能回看历史实验。12. 最后想提醒的几个关键点医学图像分类和通用分类最大的区别不在于模型结构而在于对数据、评价指标和实际应用约束的理解。第一不要迷信高准确率。医学场景更关注漏诊率也就是假阴性。一个把阳性样本漏掉的模型即使整体准确率再高也没有临床使用价值。第二不要忽略数据本身的特殊性。图像分辨率、成像设备、患者隐私、标注一致性这些都会影响模型的实际效果。代码能跑通只是开始能够持续稳定地产出正确结果才是关键。第三ResNet 在 2025 年看来已经不是最新鲜的结构但它依然是复现论文、建立 baseline、验证新方法时最可靠的起点。先把 ResNet 流程吃透再去看 Transformer、多模态模型和各种新的注意力机制你会发现核心思路是相通的。如果只是想复现论文和完成课程项目按照上面的流程把环境、数据、训练、评估、推断跑通就足够支撑后续调整了。如果要继续往医学图像算法方向深入还要补上医学影像格式处理、模型可解释性、多中心验证和合规方面的工作那已经是另一个阶段的事情。