ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

医学图像分类实战:肾脏数据集组织、yolov5训练与混淆矩阵评估

医学图像分类实战:肾脏数据集组织、yolov5训练与混淆矩阵评估 简介一套针对肾脏结节与肿瘤识别的医学图像分类数据集包含正常、结节、肿瘤三种标签专为基于深度学习的影像分类场景设计适用于医学影像入门实验、算法验证及竞赛练习。数据已按照训练集、验证集、测试集划分并保存在data目录下的独立文件夹中三类图像数量分别为训练集2800张、验证集800张、测试集400张可直接用作卷积神经网络分类或YOLOv5分类数据集的输入。资源包共2000个文件主要由1998张JPG样本图组成另含1个记录类别名称与索引对应关系的JSON字典文件以及1个用于数据集可视化的Python脚本整体压缩包约151.5MB结构简洁、便于快速检查数据质量。目前已有299人学习下载类别字典清晰标注类别与索引的对应关系可视化脚本便于快速检查样本减少数据整理与预处理的繁琐步骤。对于希望快速获得划分完善、格式规整医学图像数据的研究者这套资源能显著缩短实验前期准备时间。1. 医学图像分类的第一步这份肾脏数据集的目录里藏着什么做医学图像分类的人最怕的不是模型选型而是数据本身没收拾利索。标签对不上、图片混在一起、训练验证切分不干净这些问题往往在项目过半时才暴露一返工就是几天。这次拆的资源是一份已经划分好的肾脏结节、肿瘤图像识别数据集3 分类正常、结节、肿瘤训练集 2800 张、验证集 800 张、测试集 400 张按文件夹组织好还带类别字典 json 和一个可视化脚本可以直接喂给 yolov5 分类模式或任意分类网络。拿到手之后我的第一感觉是这省掉的不是下载时间是整理数据的时间。很多开源数据集只给一堆散图和一个标注文件你得自己写脚本按标签归文件夹、按比例切分稍不注意就漏图或者路径写错。这份资源把 train、valid、test 三套目录都铺好了图片按类别归类路径即标签。对刚上手医学图像分类的开发者来说可以直接跳过数据预处理这个最容易翻车的环节直接进入训练和调参对熟手来说省掉的是重复造轮子的时间把精力放在类别边界和模型评估上。2. 拆开数据集看门道目录、类别字典与 4000 张图的分布逻辑2.1 train / valid / test 三份目录yolov5 分类模式默认的数据组织方式我第一次拿这份资源时先做的不是训练而是把目录结构和文件数量完整数了一遍。分类任务和检测任务不一样检测要读 xml 或 txt 标注分类任务最简单可靠的组织方式就是「一个文件夹一个类别」文件夹名就是标签。yolov5 的 classify/train.py 正是按这个约定读数据的它内部用 torchvision 的 ImageFolder 机制指定根目录后会自动扫描子目录把每个子目录名当作一个类别。# 在项目根目录执行查看数据集目录树 tree -L 3 datadata/ ├── train/ │ ├── normal/ # 正常肾脏影像 │ ├── stone/ # 肾结石 / 结节 │ └── tumor/ # 肿瘤 ├── valid/ # 有些版本写作 valyolov5 两个写法都认 │ ├── normal/ │ ├── stone/ │ └── tumor/ └── test/ ├── normal/ ├── stone/ └── tumor/数完目录我心里的预期是 train 2800、valid 800、test 400实测后跟摘要对得上。这里的逻辑说明很简单yolov5 对训练集和验证集目录名有约定train 和 valid或 val是它扫描的固定关键词test 目录在训练阶段不会被动用只在推理或最终评估时使用。参数说明一下如果目录名写的是 validation 而不是 valid 或 val那个文件夹会被忽略模型把验证集当成了不存在这个问题我见过不止一次。2.2 类别字典 json从文件夹名到训练标签的映射关系数据文件夹本身已经把标签写在路径里了那 json 文件还有什么用它解决的是「数字标签」和「语义标签」的对应关系。模型输出的 argmax 结果是个整数比如 0、1、2你总得知道 1 到底代表结节还是肿瘤这就是类别字典存在的意义。import json with open(data/classes.json, r, encodingutf-8) as f: class_dict json.load(f) print(class_dict) # {0: normal, 1: stone, 2: tumor} # 具体键值顺序以你下载到的 json 为准这里只是示例 # 反向映射训练时模型输出 0/1/2通过它转成可读类别 idx_to_name {int(k): v for k, v in class_dict.items()} name_to_idx {v: k for k, v in idx_to_name.items()}逻辑说明这段代码把 json 读进来后同时生成了两个方向的映射。训练阶段用 name_to_idx 把文件夹名转成目标张量验证和推理阶段用 idx_to_name 把模型输出转成可读的类别文字。参数说明json 里的键不一定是字符串形式的数字有的资源直接写的 {normal: 0}如果你拿到的是这种结构把 int(k) 那行去掉就行读取前先 print 一下原文件是标准动作。有一个容易忽略的点torchvision 的 ImageFolder 在扫描子目录时是按字典序生成 class_to_idx 的和 json 里定义的顺序不一定一致。如果你的类别名是 normal、stone、tumor按字母序恰好是 normal0、stone1、tumor2和 json 一致算运气好如果类别名是中文或者首字母大小写混乱就要以 ImageFolder.class_to_idx 的实际结果为准拿 json 来校验而不是拿来直接替换。2.3 show 脚本先可视化再训练省掉一半冤枉路资源里附带了一个 show 脚本我的习惯是在任何训练开始前先跑它。医学图像分类的视觉检查和自然图像不一样很多图片在普通人眼里长得几乎一样但模型要学习的纹理特征就藏在这些细微差异里。脚本的作用是把每个类别随机抽几张图拼成网格展示出来确认图片能正常读取、没有黑图或损坏文件顺便对类别特征有个直观印象。import matplotlib.pyplot as plt import os import random from PIL import Image data_root data/train classes [d for d in os.listdir(data_root) if os.path.isdir(os.path.join(data_root, d))] fig, axes plt.subplots(len(classes), 4, figsize(12, len(classes) * 3)) for row, cls in enumerate(classes): cls_dir os.path.join(data_root, cls) imgs os.listdir(cls_dir) sample random.sample(imgs, min(4, len(imgs))) for col, img_name in enumerate(sample): img Image.open(os.path.join(cls_dir, img_name)) axes[row, col].imshow(img) axes[row, col].set_title(f{cls}) axes[row, col].axis(off) plt.tight_layout() plt.show()逻辑说明这段脚本遍历训练集下每一个类别文件夹每个类别随机抽 4 张图拼成一行网格。random.sample 做的是无放回抽样避免同一张图重复出现。参数说明figsize 的第二项用 len(classes) * 3 动态计算高度类别越多图越高min(4, len(imgs)) 是为了防止某个类别的图片数量不足 4 张时报错这属于防御性写法资源里每类都远超 4 张但写脚本时保留这个保护没坏处。跑完 show 脚本我还习惯顺手统计一下每个子目录的具体数量这一步在分类任务里的重要性超出很多人想象具体原因下一章展开讲。如果 show 脚本跑出来个别图片是纯黑或者明显是损坏文件趁早剔除而不是等训练到一半 loss 异常再去排查。3. 把图片喂进模型yolov5 分类模式与通用 CNN 两条训练路线3.1 yolov5 分类模式命令行训练与关键参数说明这份数据集直接兼容 yolov5 的分类模式这是最快跑通 baseline 的路线。yolov5 仓库里 classify 目录下提供了独立的训练入口不需要额外写数据加载器目录结构满足约定后一行命令就能开训。git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt # 使用官方分类模型做预训练权重在肾脏数据集上微调 python classify/train.py \ --model yolov5s-cls.pt \ --data ../kidney_dataset/data \ --epochs 50 \ --img 224 \ --batch 64 \ --name kidney_cls_run1这段命令的逻辑说明--data 指向数据集根目录yolov5 会自动寻找该目录下的 train 和 valid或 val子目录--model 指定模型结构同时加载官方在 ImageNet 上预训练好的权重迁移学习可以显著加速收敛--img 224 是把输入图缩放到 224×224这是分类模型在 ImageNet 上的标准输入尺寸改成 320 或 640 可以提升小目标细节的辨识度但显存占用和训练时间会同步上涨。参数说明里有两个值得单独拎出来--batch 64 依赖 GPU 显存在 8GB 显存以下的卡上建议降到 32 或 16否则会出现 CUDA out of memory--name 是实验标签yolov5 会把权重和日志存到 runs/train-cls/name 目录下换一组超参就换一个 name这是最简单的实验管理方式。训练完成后结果目录里会自动生成 confusion matrix 和 results.png那个混淆矩阵图就是后文要重点看的东西之一。我一般会先跑 50 个 epoch 看 loss 曲线是否正常下降再决定要不要加时间。医学图像数据集通常比自然图像更「难学」因为类间差异小50 个 epoch 往往还不够收敛如果还有时间预算加到 100 epoch 效果会更好。yolov5 分类模式默认会做早停patience 默认 100所以不用太担心过拟合跑太久。3.2 通用 CNN用 torchvision 的 ImageFolder 手写一个数据加载器如果不想绑定 yolov5想自己控制模型结构或做更精细的数据增强torchvision 的 ImageFolder 是最顺手的加载方案。它做的事情正是章节 2.2 里说的扫描根目录下的子文件夹把文件夹名映射成整数标签返回 (图像张量, 标签) 配对的数据集。这与本数据集的「文件夹保存」方式是完全对应的不需要额外写标签解析逻辑。import torch import torch.nn as nn from torchvision import datasets, transforms, models # 训练集增强随机水平翻转 颜色扰动抑制医学图像过拟合 transform_train transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(p0.5), transforms.ColorJitter(brightness0.2, contrast0.2), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) # 验证集只做缩放和归一化不做随机增强 transform_val transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) train_ds datasets.ImageFolder(data/train, transformtransform_train) val_ds datasets.ImageFolder(data/valid, transformtransform_val) test_ds datasets.ImageFolder(data/test, transformtransform_val) train_loader torch.utils.data.DataLoader( train_ds, batch_size32, shuffleTrue, num_workers4, pin_memoryTrue) val_loader torch.utils.data.DataLoader( val_ds, batch_size32, shuffleFalse, num_workers4, pin_memoryTrue) # 用 ResNet18 做迁移学习把最后一层全连接换成 3 分类 model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) model.fc nn.Linear(model.fc.in_features, 3) criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.fc.parameters(), lr1e-3)逻辑说明ImageFolder 返回的数据集对象自带 classes 和 class_to_idx 两个属性classes 是文件夹名列表class_to_idx 是名字到数字的映射建议打印出来和类别字典 json 对一遍防止字母序导致的标签错位。验证集的 transform 不能带随机增强否则每次评估结果都有波动无法横向比较。参数说明num_workers 在 Windows 上如果报多线程错误就改成 0pin_memory 只在 GPU 训练时有意义CPU 训练可以删掉。这里的优化器只传了 model.fc.parameters()意思是冻结主干网络只训练新加的分类头先用这个配置跑通流程确认 loss 能降下来再解冻全部层做精调可以省一次从头冷启动的时间。对比 yolov5 路线和通用 CNN 路线前者胜在开箱即用日志、可视化、早停都内置了后者胜在可控性强想换 loss、改 backbone、加自定义增强都方便。两份数据是同一套切换路线的成本只在于重写数据加载器而这一节代码可以直接复用到后续所有分类实验里。3.3 loss 曲线的正常形态与异常形态跑通之后看什么不管用哪条路线训练起来之后第一件事是盯 loss 曲线。一个正常的训练过程train loss 在前 10 个 epoch 应该有明显的下降趋势val loss 紧随其后下降但始终比 train loss 高一些。如果 val loss 先降后升而 train loss 还在降那就是过拟合信号需要增强正则化或提前停止。数据显示这张表的对比场景我在 yolov5 的训练日志里整理过几类常见形态写在这里可以当一份速查表用loss 曲线形态可能原因处理方式train/val loss 都不降学习率过大或过小、数据没对齐先调到 1e-4 试跑 10 epoch确认数据路径正确train loss 降、val loss 升过拟合加数据增强、增大 weight_decay、提前停止两个 loss 都震荡剧烈batch 太小或学习率偏大增大 batch 或降低学习率loss 为 NaN学习率过大、输入含 NaN 像素用小学习率重启检查图片是否损坏这里有个血泪经验如果 train loss 在第一个 epoch 就降到接近 0但不是收敛而是「假拟合」多半是标签和数据对不上——比如所有图片被当成同一类。遇到这种诡异情况第一时间打印一个 batch 的 data 和 label 出来人工核对不要急着调参这是深度学习图像识别项目里最常见的「黑匣子」陷阱。4. 三类标签的四个暗坑正常、结节、肿瘤容易翻车的地方4.1 类别不平衡先数数再训练别等 loss 异常才回头分类任务里第一个暗坑就是类别不平衡。摘要说训练集总量 2800 张但没保证三个类别各 933 张现实是医学图像数据集里正常样本往往比肿瘤样本多得多。如果某个类别只有 500 张而另一个有 1300 张模型会倾向于把模糊样本预测为多数类整体准确率可能不低但少数类的召回率会很难看。import os from collections import Counter def count_images(root): counter Counter() for cls in os.listdir(root): cls_path os.path.join(root, cls) if os.path.isdir(cls_path): counter[cls] len(os.listdir(cls_path)) return counter for split in [train, valid, test]: print(split, dict(count_images(fdata/{split})))逻辑说明这段代码统计每个集合下每个类别的图片数量输出形如 {normal: 1200, stone: 800, tumor: 800} 的字典。拿到数量后做两件事一是确认每个类别数量都在可训练范围内二是算一下最少数量的类别能不能支撑训练。参数说明如果发现某个类别只有一两百张有两种处理路径一是对少数类做过采样在 DataLoader 里设 sampler 按类别权重抽样二是用类别权重加权损失函数把 torch.nn.CrossEntropyLoss 的 weight 参数设为 [1.0, 1.2, 1.5] 这类与样本量成反比的值。这是一个容易被忽略的地方验证集和测试集的比例分布也必须看一眼。很多项目训练集是平衡的但验证集因为随机划分凑巧不平衡导致评估结果虚高或虚低。这份资源已经划分好了我们省了切分的力气但「数数」这一步不能省。4.2 数据泄漏同一来源的图片被拆进 train 和 valid医学图像和自然图像有一个本质区别同一病人的多次扫描切片之间高度相似。如果划分数据时只按文件名随机打乱同一个案例的相邻切片很可能同时出现在训练集和验证集里模型等于提前看过答案验证集指标会虚高上真实场景就露馅。这张表的坑怎么识别看文件名。这份资源的文件名自带.rf.加一串哈希的后缀如果多张图片的哈希前缀相同大概率来自同一次标注批次或同一个病例。检查方法很简单对所有文件名提取哈希段统计同一个哈希前缀下的图片是否被分到了不同集合。import os, re from collections import defaultdict def check_leakage(data_root): seen defaultdict(set) for split in [train, valid, test]: split_dir os.path.join(data_root, split) for cls in os.listdir(split_dir): cls_dir os.path.join(split_dir, cls) if not os.path.isdir(cls_dir): continue for fname in os.listdir(cls_dir): m re.search(r\.rf\.([a-f0-9]), fname) if m: seen[m.group(1)].add(split) leak {k: v for k, v in seen.items() if len(v) 1} return leak leak check_leakage(data) if leak: print(f发现 {len(leak)} 组同源图片被分到多个集合需重新划分) else: print(未发现跨集合同源图片)遇到数据泄漏时不要慌如果只是个别图片串集直接删掉测试集里那些同源图片就行。如果比例很高要考虑重新划分数据划分依据是哈希前缀而不是单张图片文件名。这就是这份资源「按文件夹划分好」背后的另一层价值划分逻辑是可见的排查起来有迹可循。4.3 类别字典与 ImageFolder 的索引顺序不一致这个坑非常隐蔽我翻车过一次。ImageFolder 在构建 class_to_idx 时按文件夹名的字典序排序如果类别文件夹是 normal、stone、tumor索引恰好是 0、1、2但如果有人把文件夹命名成 normal、tumor、stone比如按创建时间排的那索引顺序就变成 normal0、tumor1、stone2而类别字典 json 里可能是 stone1、tumor2两者对不上模型训练时的监督信号就全乱了。# 打印 ImageFolder 实际生成的标签映射和类别字典 json 对比 print(train_ds.class_to_idx) # 例如 {normal: 0, stone: 1, tumor: 2} # 读取 json 里的映射 with open(data/classes.json, r, encodingutf-8) as f: json_map json.load(f) print(json_map) # 如果两者不一致以 ImageFolder 为准因为训练时用的就是它排查方法很简单把两行打印结果并排对照。理论上这份资源在打包时已经做过一致性校验但下载文件的版本可能不同json 也可能被编辑过跑这一步只需要一分钟值得做。如果发现不一致解决方式是修改 json 字典的键顺序而不是去改文件夹名因为文件夹名改起来牵连太多。4.4 随机划分的「看似随机实则不随机」最后一类坑出在复现上。有人训练同一个模型两次结果差异很大于是开始怀疑模型问题或者环境问题但其实问题出在数据加载顺序。PyTorch 的 DataLoader 在 shuffleTrue 时用全局随机数生成器洗牌如果不固定种子每次跑数据顺序都不同结果自然不同。这在调参阶段是灾难你不知道指标变好是参数生效了还是随机性带来的。def seed_everything(seed42): import random import numpy as np random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) seed_everything(42)这段代码要在训练脚本的最顶部执行并且在 DataLoader 创建之前。参数说明PyTorch 的 DataLoader 还可以单独传入 generator torch.Generator().manual_seed(42) 来控制洗牌随机性这样即便全局种子被其他库污染数据顺序依然可控。固定种子的价值在于让每一次实验可复现这是深度学习项目从「玄学」走向「工程」的分水岭。5. 图像识别算法绕不开的三件收尾验证分类报告、混淆矩阵与误判图5.1 分类报告别盯着准确率看每类的 precision / recall / f1训练结束后第一步跑测试集评估。很多人只看整体准确率这对三分类任务来说远远不够尤其在三个类别数量不完全均衡时准确率会被多数类主导。sklearn 的 classification_report 一行代码输出每个类别的精确率、召回率和 F1这三列合起来才能反映模型对每个类的真实辨识能力。import torch from sklearn.metrics import classification_report, confusion_matrix device torch.device(cuda if torch.cuda.is_available() else cpu) model.to(device) model.eval() y_true, y_pred [], [] with torch.no_grad(): for images, labels in test_loader: images images.to(device) outputs model(images) _, predicted torch.max(outputs, 1) y_true.extend(labels.cpu().numpy()) y_pred.extend(predicted.cpu().numpy()) target_names list(train_ds.classes) print(classification_report(y_true, y_pred, target_namestarget_names))逻辑说明model.eval() 是关键它关闭 dropout 和 batch norm 的训练行为否则同一张图多次推理结果不同。torch.max(outputs, 1) 取的是每个样本在 3 个类别上的最大得分索引也就是模型的预测类别。参数说明target_names 直接取 ImageFolder 的 classes 属性保证报告里的行顺序和训练时一致不要手写类名列表省得顺序写错。这个报告还会输出 macro avg 和 weighted avg前者是每个类指标的算术平均后者按样本数加权两者差距大说明模型在少数类上表现偏弱。5.2 混淆矩阵看模型把哪两类搞混了分类报告给的是数值汇总混淆矩阵给的是错误分布的具体位置。一个 3×3 的矩阵第 i 行第 j 列表示「真实类别 i 被预测成 j」的样本数对角线是预测正确的数量。看混淆矩阵我一般先找非对角线的最大值那个位置就是模型最容易混淆的类别对。cm confusion_matrix(y_true, y_pred) print(混淆矩阵行真实列预测) print(cm) # 找出错误最集中的位置 import numpy as np np.fill_diagonal(cm, 0) # 把对角线置零后剩下的最大值就是最严重的混淆 worst np.unravel_index(np.argmax(cm), cm.shape) print(f最严重混淆: 真实{target_names[worst[0]]} - 预测{target_names[worst[1]]}, 数量 {cm[worst]})对肾脏三分类数据来说最常见的混淆发生在结节和肿瘤之间因为两者在影像上的特征边界确实模糊。如果混淆矩阵显示 normal 和 stone 之间错误很多那可能是标注本身有问题或者图片采集条件不一致这时候去调模型是徒劳的要回头查数据质量。这一节里做的所有评估动作本质是回答一个问题模型学到了什么又在什么地方学错了。5.3 误判图可视化把错误样本打出来看别再黑匣子调参混淆矩阵告诉你「哪里错了」误判图告诉你「错的是什么样」。把预测错误的样本整理成网格画出来标题同时标注真实类别和预测类别以及置信度分数这一步对医学图像分类尤其有价值。import matplotlib.pyplot as plt misclassified [] model.eval() with torch.no_grad(): for images, labels in test_loader: images, labels images.to(device), labels.to(device) outputs model(images) prob, pred torch.max(torch.softmax(outputs, dim1), 1) for i in range(len(labels)): if pred[i] ! labels[i]: misclassified.append((images[i].cpu(), labels[i].item(), pred[i].item(), prob[i].item())) if len(misclassified) 12: break if len(misclassified) 12: break fig, axes plt.subplots(3, 4, figsize(12, 9)) for idx, (img, true_label, pred_label, conf) in enumerate(misclassified): ax axes[idx // 4][idx % 4] img img.permute(1, 2, 0).numpy() img img * [0.229, 0.224, 0.225] [0.485, 0.456, 0.406] # 反归一化 img img.clip(0, 1) ax.imshow(img) ax.set_title(f真:{target_names[true_label]}\n预测:{target_names[pred_label]} ({conf:.2f})) ax.axis(off) plt.tight_layout() plt.show()这段代码里的反归一化是常见的坑模型训练时做了 Normalize 操作显示图片前必须用相同的 mean 和 std 还原否则看到的是一张偏色的图无法判断问题出在哪。参数说明clip(0, 1) 是为了防止数值越界导致图像出现不自然的色块。每次只取 12 个误判样本人眼能在一张图内快速浏览多了反而不容易看出规律。对误判图的观察要落到具体模式上比如「所有被误判成 tumor 的 stone 图片都有明显的强回声区域」或者「正常样本里有钙化点所以被判成结节」。当误判呈现出明显的视觉规律时说明数据里有干扰特征下一步不是调模型结构而是考虑加预处理步骤屏蔽干扰或者人工清理这些边缘样本。5.4 softmax 置信度的二次排查低置信度样本单独拎出来最后一层 softmax 输出的置信度分布也值得看。医学图像分类的实用场景里我们往往需要模型在拿不准的时候「说出来」而不是硬猜一个结果。统计一下测试集里置信度低于 0.6 的样本数量如果占比很高说明模型对大量样本都缺乏把握部署时要考虑设置置信度阈值低于阈值的样本转人工审核。这个动作的成本很低但对落地价值很大。yolov5 分类模式在推理时会输出每个类别的置信度直接加判断条件即可自己写的模型就是上面代码里那个 prob 变量。二分类里有个概念叫阈值调整三分类同样适用只是要对每个类分别设定。6. 三分类项目收尾技巧把训练配置固化成一张复现清单训练完成、评估做完项目还没结束。医学图像分类模型如果要写进报告或者准备上线最忌讳的是「重启之后不知道这次实验用了什么参数」。我的收尾习惯是把整次实验的可复现信息收敛成一个训练脚本或配置文件下次任何人跑一遍都能得到同一结果。# 训练复现脚本示例每次实验执行前先读取并记录环境状态 export PYTHONHASHSEED0 python classify/train.py \ --model yolov5s-cls.pt \ --data data \ --epochs 100 \ --img 224 \ --batch 64 \ --seed 42 \ --name kidney_final参数说明里有个容易被忽略的PYTHONHASHSEED 控制 Python 字符串哈希的随机种子不固定它即使在代码里调了 random.seed 和 torch.manual_seed某些依赖哈希次序的逻辑比如集合遍历顺序仍可能导致结果不一致。这个环境变量要放在训练命令的同一行 export而不是在 Python 代码里设置因为它在解释器启动前就必须生效。我还会在训练脚本同目录下留一个 data_report.txt把每次实验的数据集分布、类别字典内容、训练和验证的准确率记录在同一份文件里。这样一个月后回来看不用重新打开任何一个权重文件就能快速回忆起当时的数据条件和模型表现那份文档比模型权重本身更能说明问题。分享一个从不例外的习惯不管项目多急最后一轮实验完成后我强制自己用 10 分钟把「数据集版本 划分文件 训练命令 最终指标」四件事写进仓库 README 里。做过的医学图像分类项目里被返工最多的从来不是模型性能不够而是后来者不知道当时的实验是怎么配出来的。这类资源真正的价值是帮你把数据从「看懂」推进到「跑通」——如果这篇拆解帮你少走了弯路希望帮到你。本文还有配套的精品资源点击获取
返回列表