肺炎胸片4分类实战:从数据处理到迁移学习与模型评估

发布时间:2026/10/1 3:16:14
肺炎胸片4分类实战:从数据处理到迁移学习与模型评估
简介面向医学图像分类任务的肺炎胸片四分类数据集适合深度学习初学者与医疗影像研究人员直接用于模型训练与验证。数据涵盖COVID新型冠状肺炎、Lung_Opacity肺部浑浊、Normal正常、Viral_Pneumonia病毒性肺炎四个类别共21165张胸片图像其中训练集16933张、测试集4232张已按类别分目录存放无需额外清洗即可输入深度学习框架。资源包总体积约743MB共2000个文件其中1998张PNG图片按训练集与测试集分目录存放另附JSON类别字典与Python可视化脚本后者可随机展示样本并保存预览图便于快速核对数据分布。解压后data-train与data-test目录结构清晰子文件夹名即类别名配合类别映射文件能方便地完成多分类训练与结果分析。目前已有698人下载学习是一套适合肺炎图像识别、医学影像分类等教学与实验场景的入门级实践数据。1. 医学图像分类数据集实测肺炎胸片4分类先看懂再动手一份肺炎胸片4分类的医学图像分类数据集拿到手第一步不是开训练而是先把标签、划分和预处理逐项摸清。这个数据集按正常、细菌性肺炎、病毒性肺炎和COVID-19四类组织胸片图像是典型的医学图像识别与深度学习分类练习样本也是研究图像识别算法在医疗场景下真实表现的常用载体。新手可以用它把整个图像分类流程从数据读取到指标评估完整跑一遍熟手则建议重点看它的类别边界、患者级划分和类别不平衡处理因为这三件事直接决定训练出来的模型能不能落地。我很少拿到数据集就直接开训先花半小时把目录结构、文件名规则和标签分布列出来能少走一整个晚上的弯路。后面几章就按照我平时拆数据集的顺序来写先讲标签和预处理给出可直接运行的PyTorch训练骨架再对比三个常用分类骨干网络最后把真正容易翻车的地方集中成一份排查清单。2. 拆解4分类数据集目录结构、标签映射与预处理管线2.1 四类标签怎么划定边界不只在“有没有病”决定四分类边界的是影像学上的可见差异。正常胸片没有明显局灶或间质病变细菌性肺炎最常见表现为肺叶或肺段的实变伴随空气支气管征病毒性肺炎多表现为间质纹理增粗、磨玻璃影和网状影COVID-19的特征则集中在双肺外周和胸膜下分布为主的磨玻璃影与实变。模型要学的不是“这是不是病”而是这四类之间的影像学差异。很多新手直接把普通二分类网络改成四分类就跑结果混淆矩阵里viral和covid互相错得离谱原因就是没有先确认类别边界定义。拿到数据集第一件事是看标签文件名和自带标签文件确认四类具体是哪四类。有些版本的4分类把“其他肺部疾病”作为第四类COVID-19单列另一些则把COVID-19并入病毒性肺炎。标签定义不同训练目标和评价口径完全不同。如果数据集给出的类别名不是normal、bacterial、viral、covid就按实际目录名重写后面代码里的class_to_idx映射其余流程不用变。处理数据集的顺序永远是先摸清语义再写代码。2.2 目录结构train/val/test怎么分这类胸片分类数据集的常见组织方式是图像按类别分子目录train、val、test三套目录互相独立data/ ├── train/ │ ├── normal/ # 正常胸片 │ ├── bacterial/ # 细菌性肺炎 │ ├── viral/ # 病毒性肺炎 │ └── covid/ # COVID-19 ├── val/ │ ├── normal/ │ ├── bacterial/ │ ├── viral/ │ └── covid/ └── test/ ├── normal/ ├── bacterial/ ├── viral/ └── covid/划分比例常见的有8:1:1和6:2:2两种。如果是自己切数据我一般用6:2:2因为医学数据集本身噪声大验证集太薄会导致早停频次和最终指标的方差变大20%的验证数据能让每个epoch的评估结果稳定不少。更重要的是划分方式不要按图片随机划分要按患者划分。同一个患者可能有多张胸片如果一张进了train、另一张进了val模型在val上相当于提前见过了这个人指标虚高且很难在事后通过调参消除。后面第5章会专门讲怎么用患者ID重新划分这里先记住结论。2.3 预处理灰度转RGB与归一化胸片在文件层面是灰度图但torchvision里常用的预训练模型输入是三通道。处理这个矛盾的常见做法是把灰度图复制成三通道再用ImageNet的均值方差归一化from PIL import Image import numpy as np # 方式APIL直接转换 img Image.open(chest_xray.jpg) img_gray img.convert(L) # 强制转成单通道灰度 img_rgb img_gray.convert(RGB) # 单通道复制成三通道 # 方式Bnumpy复制通道适合做像素级分析时使用 arr np.array(img_gray) # shape: (H, W) arr_rgb np.stack([arr, arr, arr], axis-1) # shape: (H, W, 3)逻辑说明convert(L)先把图片转成严格灰度避免某些JPEG里残留的彩色通道信息干扰后续分布统计convert(RGB)再把灰度值复制到三个通道让输入shape满足预训练模型的3通道要求。方式B适合在需要自己写归一化逻辑、或需要做灰度直方图统计的时候使用。参数说明复制三通道只解决shape匹配没有解决分布匹配。ImageNet预训练权重看到的是自然图像的统计特征胸片是三通道同值的灰度分布二者不完全一致。所以后面微调时训练轮数不能太少一般会跑15到30个epoch给底层卷积核足够时间适应胸片的灰度分布而不是指望第一个epoch就收敛。2.4 数据增强哪些能用、哪些别碰医学图像增强和自然图像增强有明显区别。胸片上病灶可能只占整张图像的百分之几RandomResizedCrop这种先随机裁剪再缩放的增强很容易把病灶区域裁掉模型学到的全是正常组织纹理。ColorJitter里的大范围亮度、对比度扰动也可能把磨玻璃影和正常灰度之间的微小差异抹平。在自然图像上越强的增强在胸片上越可能是负优化。我在这类数据集上常用的增强配置如下from torchvision import transforms IMG_SIZE 224 train_transform transforms.Compose([ transforms.Resize((IMG_SIZE, IMG_SIZE)), transforms.RandomRotation(degrees10), transforms.RandomHorizontalFlip(p0.5), transforms.RandomAffine(degrees0, translate(0.05, 0.05), scale(0.9, 1.1)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) val_transform transforms.Compose([ transforms.Resize((IMG_SIZE, IMG_SIZE)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])参数说明RandomRotation的10度是有意限制的胸片有明确的上下方向大的旋转会让模型学到错误的解剖方向这在解剖结构强相关的医学图像里是常见坑。RandomAffine里的scale范围0.9到1.1模拟不同设备的放大倍率差异translate的精度0.05模拟轻微的拍摄偏移。验证集上不做任何随机增强只做Resize和Normalize这是为了让每次验证指标完全可复现比较不同实验时才有意义。3. 用PyTorch训练肺炎胸片4分类模型从数据加载到指标评估3.1 自定义Dataset读目录、建标签映射有了上面的目录结构用torch.utils.data.Dataset写一个能同时用于train和val的类。关键点在于类别顺序要稳定读取时要过滤非图片文件路径和标签要一一对齐。import os from PIL import Image from torch.utils.data import Dataset class PneumoniaDataset(Dataset): def __init__(self, root_dir, transformNone): self.root_dir root_dir self.transform transform # 对类别名排序保证每次运行标签顺序一致 self.classes sorted(os.listdir(root_dir)) self.class_to_idx {name: idx for idx, name in enumerate(self.classes)} self.samples [] for cls in self.classes: cls_dir os.path.join(root_dir, cls) if not os.path.isdir(cls_dir): continue for fname in os.listdir(cls_dir): if fname.lower().endswith((.jpg, .jpeg, .png)): path os.path.join(cls_dir, fname) self.samples.append((path, self.class_to_idx[cls])) def __len__(self): return len(self.samples) def __getitem__(self, idx): path, label self.samples[idx] image Image.open(path).convert(RGB) if self.transform: image self.transform(image) return image, label逻辑说明__init__里把类别目录名按字母序排序normal、bacterial、viral、covid对应的标签索引依序是0、1、2、3。这只在目录名恰好是这四个单词时成立如果数据集目录命名不同排序后索引也会跟着变训练前先打印train_ds.classes确认一下否则后续评估的target_names全盘错位。参数说明过滤后缀是必须的有些数据集目录里混着Thumbs.db或者其他非图片文件不过滤会在训练一开始就报解码错误。convert(RGB)统一转三通道避免某些单通道PNG直接进入模型时shape对不上。__getitem__里每次重新打开图片瓶颈在磁盘IOWindows上用num_workers0或1更稳Linux可以开到4以上。3.2 数据增强流水线与DataLoader参数加载部分把第2章的transform接进Dataset再设置batch size和num_workers。batch size不是越大越好胸片数据集总量不大batch太大一个epoch就几百步BN统计量不够平滑batch太小梯度抖动又太大。from torch.utils.data import DataLoader train_ds PneumoniaDataset(data/train, transformtrain_transform) val_ds PneumoniaDataset(data/val, transformval_transform) train_loader DataLoader(train_ds, batch_size32, shuffleTrue, num_workers4) val_loader DataLoader(val_ds, batch_size32, shuffleFalse, num_workers4)参数说明shuffleTrue只用于训练集val_loader固定顺序目的是让每个epoch的验证顺序一致避免随机顺序干扰early stopping判断。batch_size选32是兼顾显存和梯度稳定性的折中显存不够就降到16每类样本数较多时也可以升到64但要重新确认val指标没有明显变差。num_workers设置为4时如果机器内存紧张或Windows系统容易报DataLoader worker错误改成0最省事代价是训练速度会慢一些。3.3 类别不平衡先用加权损失胸片数据集的另一个常见问题是类别数量不均衡。正常胸片数量通常远多于细菌性、病毒性如果直接CrossEntropyLoss模型会倾向于把一切预测成正常类整体准确率看着还行实际对肺炎类别几乎没有判别力。处理方式是在损失函数里按类别样本数反比加权import numpy as np import torch import torch.nn as nn from sklearn.utils.class_weight import compute_class_weight # device 在训练脚本里用 torch.device(cuda if torch.cuda.is_available() else cpu) 得到 labels [label for _, label in train_ds.samples] classes np.array([0, 1, 2, 3]) weights compute_class_weight(balanced, classesclasses, ynp.array(labels)) weights torch.tensor(weights, dtypetorch.float).to(device) criterion nn.CrossEntropyLoss(weightweights)逻辑说明compute_class_weight的balanced模式计算公式是n_samples除以类别数和类别频数的乘积少数类样本少权重就大。CrossEntropyLoss的weight参数会作用在归一化后的每个样本loss上权重大的类别在反向传播里梯度放大逼着模型在少数类上多花学习容量。参数说明如果训练后发现少数类recall还是偏低例如viral类始终只有30%左右可以把对应权重再乘一个1.5的缩放因子把类别权重张量里的viral索引位单独放大。这种调整要配合验证集混淆矩阵来观察别只盯着训练loss数值。3.4 训练主循环与学习率设定接下来是标准训练循环优化器用AdamW配合L2正则。医学小数据集上weight_decay太大会压制模型容量太小又控制不住噪声我用1e-4起步。模型构建函数build_model在第4章给出完整实现这里先按同一接口调用。def train_one_epoch(model, loader, criterion, optimizer, device): model.train() total_loss, total_correct, total_num 0.0, 0, 0 for images, labels in loader: images images.to(device) labels labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() total_loss loss.item() * images.size(0) total_correct (outputs.argmax(dim1) labels).sum().item() total_num images.size(0) return total_loss / total_num, total_correct / total_num model build_model(backboneefficientnet_b0, num_classes4) optimizer torch.optim.AdamW(model.parameters(), lr1e-4, weight_decay1e-4) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max30)逻辑说明outputs.argmax(dim1)在四分类输出上取最大概率对应的类作为预测结果total_correct统计当前epoch内预测正确的图片数。最后返回平均loss和训练集准确率方便打印日志和判断是否过拟合。参数说明lr1e-4是微调预训练模型时的常见起点太大容易在第一个epoch就把预训练权重冲坏太小则需要更多轮数才能看到loss明显下降。CosineAnnealingLR的T_max设为30对应计划训练30个epoch学习率从1e-4余弦降到接近0省去手动分段调整学习率的麻烦。如果训练到一半发现验证loss反弹就提前停止不要等完整30轮。3.5 评估准确率只是起点要看每类召回率四分类医学任务里accuracy容易被类别不平衡掩盖必须同时看分类报告和混淆矩阵。这一步输出比训练循环本身更能说明模型问题from sklearn.metrics import classification_report, confusion_matrix def evaluate(model, loader, device): model.eval() all_preds, all_labels [], [] with torch.no_grad(): for images, labels in loader: images images.to(device) outputs model(images) all_preds.extend(outputs.argmax(dim1).cpu().numpy()) all_labels.extend(labels.numpy()) print(classification_report(all_labels, all_preds, target_names[normal, bacterial, viral, covid])) print(confusion_matrix(all_labels, all_preds))逻辑说明with torch.no_grad()关闭梯度计算评估阶段推理提速并省显存。classification_report输出每个类别的precision、recall、f1confusion_matrix输出4乘4的矩阵能直接看到哪两类互相混淆。这里返回的all_labels和all_preds在项目收尾时还会再用来画图。参数说明target_names要与Dataset里的类别索引顺序一致如果顺序错了报告里的类名和数字对不上比不打印还容易误导人。对胸片四分类重点看bacterial和viral的recall是否明显低于normal而不是先看整体accuracy。这一点在医学图像识别场景里几乎是默认共识。4. 迁移学习选型ResNet、DenseNet、EfficientNet怎么选4.1 为什么小样本医学分类必须迁移学习胸片分类数据集的规模通常只有几千到几万张四分类情况下每类可能只有几百到几千张从头训练一个CNN很容易过拟合而且训练时间成本高。预训练模型在ImageNet上已经学到了边缘、纹理、形状这些通用视觉特征迁移学习的本质是把这部分通用特征直接拿过来只需要在胸片数据上微调高层语义特征。对于肺炎胸片识别这是目前最稳的做法比从零搭网络效果好很多收敛速度也能快一半以上。4.2 三个backbone的参数对比三个模型在torchvision里都能用一行代码加载预训练权重但各自特点差别不小backbone参数量输入分辨率训练稳定性适合场景ResNet50约25.6M224×224很稳定显存充裕、追求稳妥基线DenseNet121约8M224×224稳定小数据、小显存、特征复用EfficientNet-B0约5.3M224×224对学习率敏感资源受限、快速实验DenseNet121的Dense Block把每层输出都拼接到后续层特征复用让它在参数更少的情况下保留足够表达能力我在小规模胸片数据集上更常用它。EfficientNet-B0理论FLOPs最低但它的MBConv结构对学习率和batch size更敏感训练时如果发现loss来回震荡通常不是网络设计问题而是学习率没调对需要配合warmup或更低的学习率才能稳住。4.3 可切换backbone的模型构建代码写一个build_model函数用字符串参数切换三个骨架避免每个实验单独改模型定义import torch.nn as nn from torchvision import models def build_model(backboneefficientnet_b0, num_classes4): if backbone resnet50: model models.resnet50(weightsmodels.ResNet50_Weights.IMAGENET1K_V2) model.fc nn.Linear(model.fc.in_features, num_classes) elif backbone densenet121: model models.densenet121(weightsmodels.DenseNet121_Weights.IMAGENET1K_V1) model.classifier nn.Linear(model.classifier.in_features, num_classes) else: model models.efficientnet_b0(weightsmodels.EfficientNet_B0_Weights.IMAGENET1K_V1) model.classifier[1] nn.Linear(model.classifier[1].in_features, num_classes) return model逻辑说明三个模型改的是不同位置的分类头。ResNet50最后是fc层DenseNet121是classifier线性层EfficientNet-B0的classifier是一个Sequential容器最后一个元素才是全连接层所以要改classifier[1]而不是classifier。这是用同一个函数切换backbone时最容易写错的地方。参数说明weightsmodels.xxx是torchvision新版本推荐写法旧版本的pretrainedTrue已经弃用。num_classes设为4时新分类头的输出维度是4与前面Dataset的标签索引范围一致。如果数据集类别改名了这里num_classes要同步调整。4.4 两段式微调与BN冻结用预训练权重微调时我习惯分两步走先冻结骨干只训练刚替换的分类头跑5个epoch让分类头先收敛到大致可用的状态再解冻全部参数用更小的学习率微调整个网络。这样做的原因是新分类头随机初始化如果一开始就直接全量微调大梯度会反向传播到骨干网络把预训练权重破坏掉。def set_bn_eval(model): for module in model.modules(): if isinstance(module, nn.BatchNorm2d): module.eval()逻辑说明冻结骨干后torchvision预训练模型里的BatchNorm层仍然会更新running mean和running var。如果batch size比较小BN统计量波动大反而把已经稳定的底层特征搞乱。set_bn_eval让所有BN层进入eval模式保持预训练统计量不变只让分类头和少数残差分支的参数参与更新。参数说明第一段微调用lr1e-3只更新分类头参数优化器里需要传入filter(lambda p: p.requires_grad, model.parameters())来排除冻结参数第二段解冻骨干后把lr降到1e-4。如果机器显存允许建议第一阶段就用4到8的batch size跑第二阶段再切回32因为冻结阶段参数量少小batch也能快速收敛。5. 肺炎胸片分类的避坑与排查五个血泪经验5.1 数据泄漏同一患者出现在train和val现象在默认划分的val集上准确率有0.95但一换到独立的test集只有0.85或者训练时val曲线一直很漂亮打印混淆矩阵却发现问题样本集中在某几个患者ID前缀下。原因数据集的图像文件是按图片随机划分的同一患者的多张随访胸片被拆进了train和val。这类胸片常有连续拍摄的序列相邻帧差异极小模型实际记住了患者ID而不只是病灶特征。解决拿到数据先提取文件名中的患者ID按患者分组重划。没有现成患者ID字段时预处理阶段直接把文件路径切出一段作为ID。用GroupShuffleSplit实现import pandas as pd from sklearn.model_selection import GroupShuffleSplit df pd.DataFrame({path: paths, label: labels, patient_id: patient_ids}) gss GroupShuffleSplit(n_splits1, test_size0.2, random_state42) train_idx, val_idx next(gss.split(df, groupsdf[patient_id]))逻辑说明GroupShuffleSplit的groups参数接收患者ID数组划分时保证同一个patient_id的所有行被分到同一侧。n_splits1表示只需要切一次test_size0.2对应20%进验证集。这里直接next取出唯一的划分结果比for循环遍历更简洁。参数说明patient_id需要从文件名里提取比如文件名Rx_0234_20240105.jpg里的0234就是患者号正则表达式提取后先做唯一性检查确认没有重复才能当group字段用。如果文件名里没有患者号就把完整文件名当group这样至少避免同一文件被复制到不同目录时造成泄漏。5.2 灰度图复制三通道的域偏移现象训练前几个epoch的loss下降很慢最终准确率卡在一个不高不低的位置怎么调学习率都突破不了。原因ImageNet预训练权重是在彩色自然图像上训练的底层卷积核学习的是彩色边缘和纹理组合。胸片复制三通道后三个通道完全一样输入分布的统计特性和预训练分布不匹配需要额外几个epoch才能让底层适应。解决默认接受复制三通道但要给足微调轮数我用30轮起步。如果实验环境允许也可以使用在医学图像上预训练的权重或在自己收集的大批量无标签胸片上做自监督预训练再微调。后者成本高通常只有研究场景才值得做。5.3 类别不平衡让模型只输出正常类现象整体准确率不低打印classification_report后却发现normal的recall很高bacterial、viral的recall只有30%左右模型把大部分胸片都判成正常。原因CrossEntropyLoss对每个样本同等看待大多数样本是正常类梯度被正常类主导少数类的错误对loss影响太小模型选择躺平。解决用第3.3节的加权CrossEntropy。如果加权后少数类recall还是低考虑Focal Loss它按样本难易程度给权重gamma2、alpha0.25是常见起手参数。Focal Loss核心实现如下import torch import torch.nn as nn import torch.nn.functional as F class FocalLoss(nn.Module): def __init__(self, gamma2.0, alphaNone): super().__init__() self.gamma gamma self.alpha alpha def forward(self, logits, targets): ce F.cross_entropy(logits, targets, reductionnone) p torch.exp(-ce) loss (1 - p) ** self.gamma * ce if self.alpha is not None: alpha self.alpha[targets] loss alpha * loss return loss.mean()逻辑说明p是模型对正确类别的置信度p越接近1说明样本越容易被分类系数(1-p)^gamma越小难样本的loss权重越大。alpha按类别加权处理样本数量差异。两个机制叠加就是Focal Loss的核心。参数说明gamma增大会让难样本权重更明显一般从2.0开始调验证集viral的recall上不去就适当加大alpha需要传入和类别数等长的张量比如torch.tensor([0.4, 0.8, 1.0, 0.8])这里torch.tensor的dtype默认float32直接乘到loss上没有问题。5.4 数据增强过猛把病灶抹平现象用了RandomResizedCrop和较强ColorJitter后训练集loss下降变慢val指标波动反而变大偶尔出现val准确率比train还高的情况。原因胸片病灶通常只占图像几个百分点RandomResizedCrop的随机裁剪可能把病灶切掉模型只能学正常纹理。ColorJitter的高对比度扰动又可能让磨玻璃影和周围组织灰度差被压缩。这类增强在自然图像上是常规操作在医学图像上属于负优化。解决按第2.4节的轻量增强配置执行。旋转不超过10度scale范围0.9到1.1translate控制在0.05以内别用RandomResizedCrop。如果发现val指标波动大先检查增强强度是不是太大再考虑换模型多半是增强配置的问题而不是网络容量的锅。5.5 标签噪音与跨设备域差异现象训练曲线正常但混淆矩阵里normal和viral互混特别多且错误样本集中在某几个文件名前缀相同的批次里。原因数据集可能由多个来源拼接不同医院或不同DR设备的胸片灰度分布差异很大部分标签也可能在人工标注环节标错。这属于标签噪音和域差异的叠加模型可能学到的是设备特征而不是病理特征。解决用训练好的模型在测试集上找置信度高的错误预测逐个人工复核画出各来源样本的灰度直方图观察分布是否明显分群。如果确认存在域差异按来源做分层划分避免某个来源的样本只在train或val里单独出现。处理数据集时的这一层排查往往比换模型结构带来的收益更明显。6. 验证模型的最后一公里混淆矩阵与患者级分区检查训练结束不等于项目收工。对胸片4分类这种医学任务我最后一步永远是用患者级分区重新验证一遍。做法很简单从文件名中提取患者ID用GroupShuffleSplit按患者重新切一次训练验证集再训练同一个模型、看同一组指标。如果按患者划分的验证准确率比默认随机划分低2到3个百分点说明原划分里存在数据泄漏如果两个指标几乎一致说明模型学的确实是病灶特征而不是患者特征。这个验证只需要半天时间但能避免拿到一个看起来漂亮、实际上靠记忆患者ID撑起来的模型。验证时只打印准确率不够我把混淆矩阵直接画出来存图。第3.5节evaluate函数返回的all_labels和all_preds在这里直接复用import matplotlib.pyplot as plt from sklearn.metrics import ConfusionMatrixDisplay cm confusion_matrix(all_labels, all_preds) disp ConfusionMatrixDisplay(confusion_matrixcm, display_labels[normal, bacterial, viral, covid]) disp.plot(cmapBlues) plt.savefig(confusion_matrix_patient_level.png, dpi300)逻辑说明ConfusionMatrixDisplay把sklearn的混淆矩阵包装成图display_labels传入类别名plot函数自动填充色块和数字。dpi300保证后续复用清晰度日常快速验证用150就够。参数说明如果四类类别名和实际索引顺序不符这张图会误导后续判断。保存前先确认train_ds.classes的顺序再决定display_labels怎么写。另外图上数字的排列能直接暴露类间混淆模式比如normal被大量错分成viral就要回到第5.5节的域差异排查而不是继续调参。从那以后我每次拿到医学图像分类数据集都强制走一遍患者ID分组、灰度直方图核对、轻量增强、加权损失、患者级验证这五步。肺炎胸片4分类这个场景尤其如此因为正常类和疾病类样本量悬殊多设备来源又让域差异明显跳过任何一步都可能把数据处理的痕迹误判成模型能力。数据集的目录结构、标签定义和划分方式拿到手先验一遍再往训练阶段走这是最省时间的顺序。希望帮到你。本文还有配套的精品资源点击获取