
简介蝴蝶分类数据集20类.zip是一份面向机器学习与图像识别场景的标注图像资源其中包含二十个不同蝴蝶物种的样本图片可供研究者训练分类模型、验证物种识别算法也可用于生物多样性与物种亲缘关系的对比研究。压缩包共收录1870个文件主要素材是1866张JPG格式的蝴蝶图像这些图像按类别存放在独立子目录中便于批量读取与多角度观察此外还配有2个TXT清单分别记录全部物种名称及其所属属名方便标签核对与分类学梳理另有1个JSON元数据字典用于存储图片路径、物种归属、描述性字段等结构化信息便于程序化解析。整份资源体积约60.96MB规模紧凑下载后可直接开展图像增强、标准化等预处理工作。目前已有119人学习下载数据集标注完整、组织清晰既适合作为卷积神经网络入门训练的基准数据也能为昆虫分布、进化关系及物种保护等科研问题提供基础数据支撑实用价值突出。1. 蝴蝶分类数据集20类.zip一个解压就能开始练手的小型分类数据包如果你的工作台或下载目录里恰好放着这个叫“蝴蝶分类数据集20类.zip”的压缩包大概率是准备做图像分类的入门练习或课程设计。它的大小通常在几十到几百MB解压后是20个按蝴蝶类别命名的文件夹每类图片少则几十张、多则几百张。这个体量恰好能跑通一整套图像分类流程解压、数据体检、训练集/验证集划分、迁移学习训练、分类评估一个环节都不缺。它最适合两类人一是想快速验证baseline、不想一上来就啃几十GB大模型库的初学者二是需要做一个可复现对比实验的进阶学习者。它的核心价值在于让你在半天之内完整经历从“拿到zip”到“训练出可用的分类器”的全过程数据规模适中、类别粒度真实。2. zip解压与数据体检先搞清楚20类到底装了什么再训练拿到压缩包后的第一件事不是写模型而是把zip解压干净弄清楚里面到底是什么布局。这个步骤决定了你后续所有处理路径是否顺畅也是新手最容易跳过的坑。先看目录再体检再判断分布三步走完再碰训练代码。2.1 解压zip与目录结构先处理“套娃”和隐藏的二级压缩包最常见的操作是在Linux或macOS下用unzip解压Windows下则用解压软件但命令行的方式更可控、更适合脚本化mkdir -p butterfly_data unzip 蝴蝶分类数据集20类.zip -d butterfly_data cd butterfly_data find . -maxdepth 3 -type d | sort | head -50参数说明-d指定解压目标目录避免zip内容直接散落在当前目录find加maxdepth 3只列出三层以内的目录避免把类里几百个图片路径也刷到屏幕上。执行后你就能一眼看清根目录下到底有多少个文件夹、每个文件夹叫什么名字。解压完成后要重点检查两种结构问题。第一种是套娃外层zip解压出来里面还套着一个zip或者再包一层同名目录这种情况非常常见。比如“蝴蝶分类数据集20类.zip”解压后里面可能是一个叫“butterfly”的文件夹再往下一层才是20个类别那么你在组织训练目录时就必须先把这个中间层剥掉。第二种是目录层级不统一有的类直接平铺在根目录下有的类下面又按科属分了两层。如果你要做的是“20类平级分类”遇到层级结构就必须把所有物种目录提升到同一层否则后面用到ImageFolder时它会自动把第一层目录当作类别结果数出来可能只有10类或30类而不是预期的20类。另外值得确认的是压缩包内是否附带README或labels.txt。这类文件通常记录了类别全名、数据来源或标注口径但由于它不是图片很容易被忽略。我见过不止一个数据集解压后实际内容比zip名字多出或缺少一类这种偏差会直接影响后续分类评估的可信度所以目录层面的核对是第一步。2.2 数据体检脚本图片数量、尺寸、通道与损坏检测目录结构确认后用体检脚本把每个类的底数摸清楚。这一步能避免训练过程在中途的某次迭代中突然报错也能提前发现类别数量上的隐患。核心脚本用PIL和os做一次全量扫描import os from PIL import Image root butterfly_data cls_dirs sorted([d for d in os.listdir(root) if os.path.isdir(os.path.join(root, d))]) print(fnumber of classes: {len(cls_dirs)}) for cls in cls_dirs: cls_path os.path.join(root, cls) files os.listdir(cls_path) valid, bad, n 0, 0, 0 size_counter {} typical_size None for f in files: if not f.lower().endswith((.jpg, .jpeg, .png, .bmp)): continue p os.path.join(cls_path, f) try: with Image.open(p) as im: im.verify() size_counter[im.size] size_counter.get(im.size, 0) 1 valid 1 typical_size im.size except Exception: bad 1 n 1 print(f{cls}: files{n}, valid{valid}, bad{bad}, fdistinct_sizes{len(size_counter)}, typical{typical_size})逻辑说明im.verify()只校验文件头是否完整不会真正解码整张图所以速度快、适合全量扫描。扫描后每类打印五个数值文件总数、能正常读取数、损坏数、尺寸种类数、最后一张图的尺寸。如果bad不为0说明图有问题或格式伪装如果distinct_sizes很大说明这个数据集的图片没有被统一缩放训练时需要在transforms里统一Resize。这个环节最容易被跳过但以我的经验数据集里图片损坏的比例完全没有规律可能一个类只有一两张坏图另一个类却藏着十几个扩展名为jpg、实际已截断的文件。如果不提前清理掉DataLoader很可能在第3个epoch或第7个epoch时突然抛UnidentifiedImageError而且何时触发与数据顺序相关排查起来非常消耗时间。体检脚本跑一次只要几分钟能省下后面几小时的排障时间。2.3 类别分布初判哪些类会拖后腿一眼就能看出来体检脚本的输出就是一张类别数量表接着就可以做第一轮分布判断。一个理想的“20类”数据集每类最好有100到500张图片。但如果某些类只有二三十张而另一些类有近千张这就需要为后续的数据划分和分类评估提前做准备。蝴蝶分类数据集常见的来源包括野外拍摄和馆藏标本扫描两种来源的图片风格差异很大野外拍摄背景复杂、蝴蝶姿态多变、光线不匀标本扫描则背景干净、角度固定、光照均匀。如果zip里两种来源的图片都混在一起训练出的模型很容易学会“绿色背景就是某类”“白底图片就是某类”这种伪规律。体检阶段就应该随机抽几类图片看看背景差异如果发现出现两种明显的背景风格一个稳妥做法是训练前先做一次来源层面的分组在训练集和验证集中同时保留两种风格避免验证集只在单一风格上评估。对于数量特别少的类我会在体检表上单独标注并在后续训练脚本里为它们增加权重。一个常见的做法是把类别权重写进一个字典class_weights { 某类英文代号: 1.5, 另一个少数类代号: 1.2, # 数值越大训练时该类loss的放大倍数越高 }这里要说明权重只是在样本数量不足时的补救措施并不能凭空创造数据。如果一个类有效图片不到40张那么无论权重怎么调这个类的泛化能力都有限。比较务实的底线是保证每个类至少40张可用图低于这个数优先考虑补充数据而不是急着训练。3. 把原始目录整理成训练可用格式训练集/验证集划分与标签映射zip解压后的目录结构再规整也不能直接作为PyTorch训练输入。中间需要做一次数据整理按比例拆出训练集和验证集把目录名映射成稳定的语义标签并保存一份可复查的映射文件。这一节做的事情虽然琐碎但直接影响训练代码的复杂度和后续实验的可复现性。3.1 为什么标准目录结构比自定义Dataset更适合起步torchvision提供了一个叫ImageFolder的数据集类它规定根目录下的第一层子文件夹就是类别名每一类图片分别放在各自的子文件夹里label由文件夹名自动生成。对于一个目录名已经是类别名的数据集这是最合适的数据装载方式。你自己写一个Dataset类当然也可以但问题在于它把“路径到标签”的映射逻辑写死在代码里当类别数或目录结构变动时需要改代码并重新匹配字符串排错成本高。ImageFolder的最大优势是把映射关系打开在属性里train_ds.classes直接给出类别列表train_ds.class_to_idx给出从类名到整数的字典。训练时只要打印这两项就能立刻确认20类是不是真的是20类、顺序是否符合预期。这对训练流程的稳定性和后续分类评估都有好处因此我一般会先整理目录让数据本身贴合ImageFolder的约定。3.2 数据划分脚本按比例拆train/val类标签自动生成划分脚本要做的事情很简单在每个类别内随机抽样按比例拆成train和val两份。但有几个细节不能省固定随机种子保证实验可复现先shuffle再切割避免直接取前缀。很多数据集的图片列表本身就按采集时间排序如果直接取前80%验证集和训练集的采样来源就不够独立。import os, random, shutil random.seed(42) root butterfly_data out_root butterfly_split split_ratio 0.8 for cls in sorted(os.listdir(root)): src_dir os.path.join(root, cls) files [f for f in os.listdir(src_dir) if f.lower().endswith((.jpg, .jpeg, .png, .bmp))] random.shuffle(files) n_train int(len(files) * split_ratio) for phase, subset in [(train, files[:n_train]), (val, files[n_train:])]: dst_dir os.path.join(out_root, phase, cls) os.makedirs(dst_dir, exist_okTrue) for f in subset: src os.path.join(src_dir, f) dst os.path.join(dst_dir, f) shutil.copyfile(src, dst) print(f{cls}: train{n_train}, val{len(files) - n_train})参数说明split_ratio0.8是训练集占比对中小规模数据集0.8/0.2的划分比较常用如果你担心验证集太小导致评估噪声大可以改为0.85/0.15。脚本中copyfile是复制而非移动这样原始解压目录被完整保留之后做交叉验证或重新划分时不需要再次解压。按类别内切分还天然保持了分层比例不需要引入额外的分层采样库。一个容易被忽略的问题不要把训练集和验证集切分后再做整目录shuffle那样会导致同一张图片跨集合出现造成数据泄漏。按类内顺序切分并保留原始目录是避免重复图片混入的最简单做法。3.3 标签映射落地中文目录名转英文代号写一份class_to_idx.json目录名如果是中文或带空格在Linux与Windows之间搬运时偶尔会遇到编码和路径解析问题。稳妥的做法是准备一份类别映射表把原始目录名转换为语义清晰的英文代号同时生成class_to_idx.json让训练、评估和推理阶段都读取这份JSON而不是在代码里手写标签顺序。import json with open(raw_cls_names.txt, r, encodingutf-8) as f: raw_names [line.strip() for line in f if line.strip()] mapping { 某蝴蝶原始名: Butterfly_01, # 在这里维护你手工整理的同义映射 } class_to_idx {} for i, raw in enumerate(raw_names): code mapping.get(raw, fbutterfly_{i:02d}) class_to_idx[code] i with open(class_to_idx.json, w, encodingutf-8) as f: json.dump(class_to_idx, f, ensure_asciiFalse, indent2) print(class_to_idx)这段代码的核心价值是固化“目录名到整数标签”的关系。后续用ImageFolder或自定义Dataset时都从这个JSON读取映射避免多次运行数据管线时标签顺序不一致导致模型权重和标签语义错位。分类评估打印混淆矩阵时也需要用这份JSON把整数索引还原成可读的类名。我在实际项目中见过太多次“训练时用0到19的索引当标签评估时忘了写映射最后只能对着混淆矩阵猜第7类是哪个蝶”的翻车经历所以花两分钟做映射是值得的。4. 用迁移学习训练20类蝴蝶分类器ImageNet预训练权重是省时间的关键目录和标签都准备好之后就进入模型训练环节。蝴蝶分类属于典型的细粒度图像分类类间差异往往只体现在翅膀花纹和色斑分布上这对模型的特征提取能力提出了较高要求。在这个数据规模下迁移学习是性价比最高的选择。4.1 选型理由20类蝴蝶为什么天然适合迁移学习细粒度分类的一个经典类比是flower102花卉按类型分类类别总数不算多但类间外观极其相似。蝴蝶和花卉、鸟类都属于同一类问题区别在于蝴蝶的类间差异更细微很多品种只靠翅膀背面花纹来区分。如果从头随机初始化一个ResNet来训练以每类几十到几百张的样本量模型很难学出足够有判别力的层次化特征大概率会过拟合训练集表现很好、验证集却一塌糊涂。更务实的做法是直接利用在ImageNet-1K上预训练过的权重。这里要澄清一个常见误区很多人以为“先用ImageNet-1K数据集下载并预训练模型”才能继续微调其实不需要。torchvision中的weights参数可以直接加载官方发布的预训练权重你不需要下载原始ImageNet图片只需要联网让torchvision自动下载权重文件即可。以预训练权重为起点模型已经具备识别边缘、纹理、色块等通用视觉特征的能力你只需要替换最后的分类头并微调少数层的参数就能在几百张图的小数据集上获得可用的分类器。4.2 数据加载与增强transforms配置与关键参数数据加载的核心是transforms配置。由于蝴蝶数据集图片尺寸不统一、背景复杂度高训练变换要兼顾尺度归一和过拟合抑制。基本配置是先将图缩放到256再随机裁剪出224的区域配合随机水平翻转与颜色抖动。颜色抖动幅度要控制得当我一般给0.2到0.3的亮度/对比度扰动因为蝴蝶翅膀的色泽本身就是重要判别特征扰动过大会改变真实外观扰动过小又起不到增强效果。from torchvision import datasets, transforms from torch.utils.data import DataLoader train_tf transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomCrop(224), transforms.RandomHorizontalFlip(p0.5), transforms.ColorJitter(brightness0.3, contrast0.3, saturation0.3, hue0.02), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), ]) val_tf transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), ]) train_ds datasets.ImageFolder(butterfly_split/train, transformtrain_tf) val_ds datasets.ImageFolder(butterfly_split/val, transformval_tf) train_loader DataLoader(train_ds, batch_size32, shuffleTrue, num_workers4) val_loader DataLoader(val_ds, batch_size32, shuffleFalse, num_workers4)参数说明Resize到256再RandomCrop到224是为了让同一张图在训练时产生多个局部视角提升模型对位移和尺度变化的鲁棒性。验证集不做随机裁剪只用Resize到224确保每次评估结果可复现。Normalize的三组数值必须与ImageNet预训练权重保持一致不能随意修改否则输入分布与预训练分布不匹配微调效果会明显下降。num_workers4在主流台式机上够用如果CPU核心数多可以提高到8但在Windows下需要注意把数据加载代码放在if __name__ __main__保护块中避免多进程重复执行脚本。4.3 训练循环resnet18微调的核心代码与超参用ResNet18作为baseline是这类中小型图像数据集的稳妥选择。ResNet34或ResNet50当然也能用但细粒度分类任务里小模型配合合理的正则化往往比大模型更稳定推理速度也更快。替换分类头时只需要把最后的fc层改为输出20维的线性层import torch import torchvision model torchvision.models.resnet18( weightstorchvision.models.ResNet18_Weights.IMAGENET1K_V1 ) model.fc torch.nn.Linear(model.fc.in_features, 20)超参设置上因为整个网络初始化自预训练权重全量微调用Adam时我一般从1e-4学习率起步而不是常见的3e-4这个量级对预训练权重的扰动更小收敛也更稳定。损失函数用CrossEntropyLoss但如果某些类样本明显偏少可以传入类权重向量。优化器配合余弦退火调度训练20到30个epoch足以看到清晰的收敛趋势。device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device) criterion torch.nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.parameters(), lr1e-4) scheduler torch.optim.lr_scheduler.CosineAnnealingLR( optimizer, T_max25, eta_min1e-6) for epoch in range(25): model.train() train_loss 0.0 for x, y in train_loader: x, y x.to(device), y.to(device) optimizer.zero_grad() loss criterion(model(x), y) loss.backward() optimizer.step() train_loss loss.item() * x.size(0) scheduler.step() model.eval() correct total 0 with torch.no_grad(): for x, y in val_loader: x, y x.to(device), y.to(device) pred model(x).argmax(dim1) correct (pred y).sum().item() total y.size(0) print(fepoch {epoch:02d} | loss{train_loss/len(train_ds):.4f} f| val_acc{correct/total:.4f})参数说明CosineAnnealingLR的T_max25要和总epoch数对齐它表示学习率从初始值下降到eta_min的周期长度最后几个epoch学习率已经很小val_acc出现小幅波动是正常的不代表模型退化。这里我没有写混合精度因为batch_size和数据集规模都不大FP32全精度完全能跑硬加AMP反而会引入设备兼容性上的麻烦。如果希望更精细地微调可以冻结前几层卷积只微调最后几个残差块和分类头。这种做法收敛稍慢但对预训练特征的破坏更小适合想要更保守的迁移策略时使用。蝴蝶图像与自然图像的分布差异不大全量微调优先级更高如果时间充裕可以两者都试。4.4 分类评估除了准确率还要看per-class recall和混淆矩阵训练结束时不能只盯着val_acc这一个指标。20类蝴蝶数据集里整体准确率98%和所有类别平均准确率98%含义完全不同如果有两个近缘种被系统性地互相混淆全局准确率可能只掉两三个点但这两类在真实场景中的表现会非常差。所以分类评估要同时覆盖整体指标和细粒度错误。from sklearn.metrics import classification_report, confusion_matrix import numpy as np model.eval() all_preds, all_labels [], [] with torch.no_grad(): for x, y in val_loader: x, y x.to(device), y.to(device) pred model(x).argmax(dim1).cpu().numpy() all_preds.extend(pred) all_labels.extend(y.cpu().numpy()) target_names [cls for cls in sorted(os.listdir(butterfly_split/train))] print(classification_report(all_labels, all_preds, target_namestarget_names)) cm confusion_matrix(all_labels, all_preds) np.save(confusion_matrix.npy, cm)这段代码会把每个类的precision、recall、f1-score和support打印出来同时把混淆矩阵保存为npy文件方便后续加载绘图。判断一个类是否健康的关键指标不是precision而是recall是否低于整体平均水平10个百分点以上。如果某个类recall明显偏低说明它经常被识别成其他类别需要重点分析类和类之间的混淆模式这个分析是最后一章要讲的内容。5. 蝴蝶数据集避坑笔记四条真实的踩坑记录数据类项目最容易出问题的环节往往不在模型结构而在数据本身的细节。这一章记录我处理这类分类数据集时踩过的四个真实问题按“现象 → 原因 → 解决”展开希望能帮你少走弯路。5.1 图片损坏或格式混杂训练中途报错、loss变成NaN现象训练到第几个epoch时DataLoader突然抛PIL.UnidentifiedImageError: cannot identify image file xxx.jpg或者loss在某个batch变成NaN后一路不恢复之前的训练成果全部作废。原因这类数据集多由人工采集或半自动下载整理而来图片并未做完整体校验。常见情况包括jpg扩展名但文件已截断、PNG带透明通道被误改后缀、扫描标本图片夹杂了损坏的ICC色彩配置文件等。这些文件在体检阶段不显眼但会在数据加载时随机触发异常。解决在第二章的体检阶段就把损坏图片找出来。我习惯维护两份清单一份corrupted.txt记录所有损坏文件的路径一份removed.txt记录已剔除的文件而不是直接删掉。这样处理的好处是如果某类图片本来就很少还能根据清单判断是数据源缺失还是图片质量问题。若某个类坏图比例超过10%优先考虑从其他渠道补充该类的有效数据而不是简单删掉了事。5.2 类别不均衡少数类被多数类“吃掉”现象训练曲线很漂亮验证集整体准确率接近98%但按类展开看某两三个类的recall只有0.4甚至更低其余类都接近1.0。原因20类的数量分布并不均匀有的类上百张有的类只有几十张。模型在多数类上积累的梯度更多分类边界会偏向样本量大的类少数类被挤压到特征空间边缘泛化能力自然下降。解决第一种做法是用WeightedRandomSampler按类别样本数的倒数调整采样权重让少数类在同一个epoch中被采样多次第二种做法是在CrossEntropyLoss中传入按样本数计算出的类别权重第三种做法是对少数类单独做更重度的数据增强比如旋转、透视变换。三个办法叠加时要注意幅度不能对少数类做过于夸张的变换否则模型学到了异常形变当作正常样式验证集指标反而会下降。最保守的起点是只加类权重观察少数类recall是否回升再决定是否叠加其他手段。5.3 背景过拟合模型靠叶子认蝴蝶而不是靠翅膀现象训练集和验证集来自同一个数据源时准确率很高但换一批新背景图片比如从标本照换成野外逆光照准确率下降明显甚至跌破60%。原因蝴蝶分类数据集中图片的背景与类别之间常常存在强相关例如某类蝴蝶总出现在黄色花丛中另一类标本总贴在白底卡纸上。卷积网络会优先学到最容易区分的局部特征当背景可作为分类线索时模型会偷懒把背景和类别绑定起来而不是真正聚焦在翅膀结构上。解决在训练transforms中加入RandomErasing或Cutout随机遮住图像的一部分区域强制模型从多个区域提取特征。另一种有效做法是在训练集里混入不同背景风格的数据哪怕只是9:1的比例也能显著降低模型对单一背景的依赖。我通常还会在训练后准备一批“自然场景负样本”做验证如果真实场景准确率比训练时的验证准确率低超过5个百分点就优先怀疑背景过拟合而不是数据标注问题。5.4 zip文件名中文乱码与路径分隔符问题现象在Windows下解压zip时文件名显示正常但把同一个zip拷到Linux服务器解压后目录名变成乱码或出现?更隐蔽的情况是Windows下训练脚本报错找不到形如butterfly_data/某蝶种/xxx.jpg的路径。原因zip在中文Windows环境下打包时文件名编码通常为GBK或CP936而Linux的unzip默认按UTF-8解码编码不匹配就会产生乱码。另一个常见原因是最初打包时使用了反斜杠作为路径分隔符Windows能识别但Linux不行。解决Linux下解压时带上编码参数unzip -O gbk 蝴蝶分类数据集20类.zip -d butterfly_data如果当前系统的unzip版本不支持-O参数可以先解压再批量重命名或者直接把所有乱码目录改成英文代号顺便把第三章的class_to_idx.json映射表建好。还有一个从源头避免问题的方式在Windows下用7-Zip把这些数据重新打包为英文文件名再分发。路径分隔符问题则建议统一使用os.path.join或pathlib来处理不要手拼字符串路径。6. 从“能分类”到“分得准”混淆矩阵、测试时增强与模型存根训练完成只是第一步让模型在真实场景中稳定可靠才是目的。这一章分享三个我每次做细粒度分类实验都会用到的技巧。6.1 训练完先画混淆矩阵别急着部署我每次训练完第一件事不是看acc而是把混淆矩阵加载出来逐行看确认哪些类互相打架。对于蝴蝶这种细粒度分类类间混淆集中在翅膀花纹相似的品种上非常常见这类问题属于“数据本身区分度不足”不是单纯调参能解决的。常做的处理是找到这两个品种的图像差异点分析模型到底在什么背景下出错。如果训练集里甲种蝴蝶频繁出现在乙种常见的花朵背景中模型学到背景关联后就会把甲误判为乙这时真正要调整的是训练集分布而不是增加网络深度。如果你想沿这个方向扩展成目标检测任务可以参照“用yolov8训练自己的数据集”的流程重新制作检测标注但注意分类用途的文件夹结构不能直接当作检测标注来源。6.2 测试时增强推理时也做随机裁剪白捡零点几个点测试时增强TTA是一个性价比很高的推理技巧。验证或推理时对同一张图做10次随机裁剪把10次softmax结果平均后再取最大值这样可以显著降低单次随机裁剪带来的方差通常能提升0.3到1个百分点的准确率。代码改动很小tta_transform transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomCrop(224), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), ]) probs [] with torch.no_grad(): for _ in range(10): x tta_transform(img).unsqueeze(0).to(device) probs.append(torch.softmax(model(x), dim1)) probs torch.mean(torch.cat(probs, dim0), dim0) pred_idx probs.argmax().item()这里要说明的是TTA提升的是单图推理的稳定性而不是模型的真实泛化能力。如果模型本身已经严重过拟合TTA只能把准确率从0.92抬到0.93解决不了根本问题。更有效的做法是先把验证集上的混淆矩阵厘清再考虑要不要用TTA。6.3 一个数据管理习惯每次实验都存一份数据hash最后分享一个让我省下很多对比时间的小习惯把数据集目录连同划分方式一起做SHA256校验并记录下来。20类蝴蝶数据集这种规模的中小数据常被人反复拷贝和重新整理。别人发来一个“补数据”的zip你直接合并进原目录后某类图片数量可能翻倍另一类则没有变化训练出的模型与之前的对比就失去了意义。每次实验结束后把butterfly_split目录的hash、划分脚本的版本、训练命令一起写入实验log当模型指标异常时可以快速定位是数据变了还是权重变了。这一条不涉及任何模型技巧却能规避大量重复性排障工作。这次在蝴蝶分类数据集上走完整个流程后我最深的体会是数据体检和标签映射消耗的时间往往比训练本身还要长。模型能力的上限由数据质量决定迁移学习只是帮你更接近这个上限而已。希望这篇笔记能帮你把前半段路径走顺少踩几个无谓的坑。本文还有配套的精品资源点击获取