蝴蝶分类数据集20类实战:从数据切分到迁移学习基线
发布时间:2026/10/1 3:04:47来源:尧图网络
简介蝴蝶分类数据集20类.zip 面向机器学习与图像识别方向的开发者、深度学习入门者及生物多样性研究者用于训练和测试蝴蝶物种自动识别模型。压缩包共1870个文件以1866张jpg图像为主体另含2个txt标签文件、1个json字典文件和1个系统隐藏文件整体约60.96MB规模适中便于本地加载与快速实验。其中json字典记录图片路径与物种、属名等元信息txt文件分别提供20个蝴蝶物种名称及所属属名图像按类别组织可直接用于分类任务的数据读取与标签映射。已有118人学习下载适合作为卷积神经网络等模型的训练素材也可支撑物种分布、亲缘关系与保护研究等生物学分析。数据集类别明确、标注结构清晰能帮助读者省去自行采集与整理样本的成本快速搭建图像分类流程并验证模型效果。1. 蝴蝶分类数据集20类从拿到压缩包到跑通第一个基线你从某个渠道拿到一个叫“蝴蝶分类数据集20类.zip”的压缩包解压后大概率是一堆按文件夹分好的蝴蝶图片每个文件夹名对应一个物种。这件事听起来简单但真正动手时第一个翻车点往往不是模型选得不对而是你连数据长什么样都没看清就急着写训练脚本。20类蝴蝶分类属于细粒度图像分类的入门级任务类间差异小、类内差异大背景干扰强这些特性决定了它既适合练手也能暴露你在数据处理上的短板。这篇文章面向的是已经拿到这个压缩包、想把它跑成一个可复现基线的从业者不管你是刚入门的新手还是想快速验证一个想法熟手都能按下面的步骤走通。我会把重点放在目录结构怎么读、数据怎么切、增强怎么做、模型怎么选、训练怎么调、坑怎么避而不是泛泛讲什么卷积神经网络原理。2. 先看清压缩包里的目录结构再动手切分2.1 解压后第一件事统计每个类别的图片数量和尺寸分布拿到压缩包后不要直接喂给框架先写一段脚本把目录结构和图片元信息摸清楚。常见做法是用pathlib遍历所有子目录统计每个类别的文件数、图片宽高、通道模式。这一步能帮你判断是否存在类别不平衡、是否有灰度图混在RGB里、是否有损坏文件。我一般会先跑下面这段代码输出一张类别分布表。from pathlib import Path from PIL import Image from collections import defaultdict root Path(蝴蝶分类数据集20类) # 解压后的根目录 stats defaultdict(lambda: {count: 0, sizes: [], modes: set()}) for cls_dir in sorted(root.iterdir()): if not cls_dir.is_dir(): continue for img_path in cls_dir.glob(*): if img_path.suffix.lower() not in {.jpg, .jpeg, .png, .bmp}: continue try: with Image.open(img_path) as im: stats[cls_dir.name][count] 1 stats[cls_dir.name][sizes].append(im.size) stats[cls_dir.name][modes].add(im.mode) except Exception as e: print(f损坏文件: {img_path}, 错误: {e}) for cls_name, s in stats.items(): if s[sizes]: ws [x[0] for x in s[sizes]] hs [x[1] for x in s[sizes]] print(f{cls_name}: 数量{s[count]}, 宽范围({min(ws)}-{max(ws)}), f高范围({min(hs)}-{max(hs)}), 模式{s[modes]})这段代码的逻辑很直接遍历每个类别文件夹用PIL打开图片读取尺寸和色彩模式最后按类别汇总。参数上你只需要改root指向解压后的实际路径。如果发现某个类别只有几十张而其他类别有几百张后面就要考虑加权采样或重采样如果宽高范围跨度极大说明数据集里混了不同来源的图统一缩放到固定尺寸时要注意长宽比处理方式。2.2 按类别分层切分训练集、验证集和测试集细粒度分类任务里验证集和测试集的划分必须按类别分层否则某个类别可能在验证集里一张都没有导致评估指标剧烈波动。常见做法是每个类别按 7:1.5:1.5 或 8:1:1 的比例切分用sklearn.model_selection.train_test_split的stratify参数就能做到。下面是一个可直接抄的切分脚本切完后生成三个文件夹的软链接或直接复制文件。import shutil from pathlib import Path from sklearn.model_selection import train_test_split root Path(蝴蝶分类数据集20类) out_root Path(split_dataset) train_ratio, val_ratio 0.7, 0.15 all_files, all_labels [], [] for cls_dir in sorted(root.iterdir()): if not cls_dir.is_dir(): continue for img in cls_dir.glob(*): if img.suffix.lower() in {.jpg, .jpeg, .png, .bmp}: all_files.append(img) all_labels.append(cls_dir.name) # 先切出训练集和临时集 train_f, temp_f, train_l, temp_l train_test_split( all_files, all_labels, test_size0.3, stratifyall_labels, random_state42 ) # 临时集再对半切成验证和测试 val_f, test_f, val_l, test_l train_test_split( temp_f, temp_l, test_size0.5, stratifytemp_l, random_state42 ) for split_name, files, labels in [(train, train_f, train_l), (val, val_f, val_l), (test, test_f, test_l)]: for f, l in zip(files, labels): dst out_root / split_name / l dst.mkdir(parentsTrue, exist_okTrue) shutil.copy2(f, dst / f.name) print(切分完成)这里的关键参数是stratify它保证每个类别在三个子集里的比例一致。random_state固定后结果可复现。注意test_size0.3是临时集占总数据的比例后面再对半切最终验证和测试各占 15%。如果你发现某个类别样本数少于 10分层切分可能报错这时要么合并极少数类别要么改用留一法。3. 用迁移学习搭一个能跑通的基线模型3.1 为什么选 ResNet50 而不是从零训练20类蝴蝶数据集通常每类只有几百张图总量在几千到一万出头。从零训练一个卷积网络几乎必然过拟合验证集准确率会在前几个epoch冲高然后暴跌。迁移学习是这类细粒度任务的标配用ImageNet预训练的ResNet50或EfficientNet-B0冻结主干只训练分类头等损失稳定后再解冻最后几个stage做微调。我一般会先用ResNet50跑一个基线因为它的特征提取能力对纹理和形状敏感适合区分蝴蝶翅膀上的斑纹差异。EfficientNet-B0参数更少训练更快但小数据集上ResNet50的稳定性通常更好。3.2 训练脚本的核心参数与数据增强配置下面是一个基于PyTorch的完整训练脚本骨架包含数据加载、增强、模型构建和训练循环。数据增强对细粒度分类尤其重要因为蝴蝶图片的拍摄角度、光照、背景差异很大。我一般会用随机水平翻转、随机旋转15度、颜色抖动和随机裁剪验证集只做中心裁剪和归一化。import torch import torch.nn as nn from torch.utils.data import DataLoader from torchvision import datasets, transforms, models data_dir split_dataset train_tf transforms.Compose([ transforms.RandomResizedCrop(224, scale(0.7, 1.0)), transforms.RandomHorizontalFlip(), transforms.RandomRotation(15), transforms.ColorJitter(brightness0.2, contrast0.2, saturation0.2), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) val_tf transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) train_ds datasets.ImageFolder(f{data_dir}/train, transformtrain_tf) val_ds datasets.ImageFolder(f{data_dir}/val, transformval_tf) train_loader DataLoader(train_ds, batch_size32, shuffleTrue, num_workers4) val_loader DataLoader(val_ds, batch_size32, shuffleFalse, num_workers4) model models.resnet50(weightsmodels.ResNet50_Weights.IMAGENET1K_V2) for param in model.parameters(): param.requires_grad False model.fc nn.Linear(model.fc.in_features, 20) # 20类 criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.fc.parameters(), lr1e-3) scheduler torch.optim.lr_scheduler.StepLR(optimizer, step_size7, gamma0.1) device torch.device(cuda if torch.cuda.is_available() else cpu) model.to(device) for epoch in range(20): model.train() for imgs, labels in train_loader: imgs, labels imgs.to(device), labels.to(device) optimizer.zero_grad() loss criterion(model(imgs), labels) loss.backward() optimizer.step() model.eval() correct, total 0, 0 with torch.no_grad(): for imgs, labels in val_loader: imgs, labels imgs.to(device), labels.to(device) preds model(imgs).argmax(dim1) correct (preds labels).sum().item() total labels.size(0) print(fEpoch {epoch1}, Val Acc: {correct/total:.4f}) scheduler.step()参数说明batch_size32在显存8G以上都能跑如果OOM就降到16。lr1e-3是只训练分类头时的常用值解冻微调时要降到1e-4或更低。StepLR每7个epoch衰减一次适合短周期训练。RandomResizedCrop的scale(0.7,1.0)控制裁剪区域占原图的比例太小会丢失翅膀关键斑纹太大则增强效果弱。验证集准确率如果在前5个epoch就超过90%说明任务比预想简单如果一直在20%左右徘徊检查标签映射是否对齐、归一化参数是否用错。4. 蝴蝶分类数据集20类的避坑与排查清单4.1 类别文件夹名带空格或中文导致ImageFolder报错现象运行datasets.ImageFolder时抛出FileNotFoundError或类别数为0。原因解压后的文件夹名可能包含空格、中文或特殊字符ImageFolder按字典序读取子目录名作为类别但路径拼接时如果编码不一致就会失败。解决先用脚本把所有类别文件夹重命名为英文小写加下划线比如“金斑蝶”改成“golden_birdwing”再重新切分。重命名时保留一份映射表后面推理输出要还原成可读名称。4.2 验证集准确率震荡大每个epoch差十几个点现象训练集损失稳定下降但验证集准确率在70%到85%之间反复横跳。原因验证集样本太少每个类别只有十几张评估结果对个别样本敏感或者数据增强在验证集上误用了随机变换。解决增大验证集比例到20%或者改用K折交叉验证取平均。检查验证集的transform是否只包含Resize、CenterCrop和Normalize任何随机翻转或旋转都会让评估失去意义。4.3 微调时解冻太多层导致灾难性遗忘现象解冻全部层后用较大学习率训练验证集准确率突然从85%掉到40%。原因预训练权重被大梯度破坏浅层学到的通用边缘和纹理特征被覆盖。解决微调时只解冻最后两个stage学习率设为1e-4并用更小的batch size。如果还不行改用差分学习率浅层用1e-5深层用1e-4。我一般会先冻结训练10个epoch再解冻微调10个epoch观察验证集是否还有提升空间。4.4 图片EXIF方向导致训练时图像旋转现象训练时loss正常下降但推理时同一张图预测结果和训练时不一致。原因手机拍摄的JPEG带有EXIF方向标记PIL读取时默认不旋转但某些框架或浏览器会按EXIF旋转导致训练和推理的输入方向不同。解决在数据加载时用ImageOps.exif_transpose统一处理或者在预处理阶段批量把EXIF方向写入像素并清除标记。这个坑很隐蔽血泪经验是训练前先抽几张图用不同工具打开对比方向。4.5 类别不平衡导致少数类召回率极低现象整体准确率有85%但混淆矩阵显示某几个类别的召回率不到50%。原因这些类别样本数只有其他类的一半甚至更少交叉熵损失被多数类主导。解决用加权交叉熵权重设为类别频率的倒数或者用WeightedRandomSampler让每个batch里各类别比例均衡。如果少数类确实太少考虑用数据增强专门为它们生成更多变体但不要用SMOTE这类插值方法处理图像效果通常很差。5. 把基线推到更高精度的三个进阶技巧5.1 用CutMix和MixUp提升细粒度泛化能力细粒度分类里模型容易记住背景或拍摄角度而不是蝴蝶本身的斑纹。CutMix和MixUp通过混合两张图及其标签强迫模型关注更分散的特征。在PyTorch里可以用torchvision.transforms.v2的CutMix和MixUp或者手写一个简单的MixUp。我一般会在训练中期加入概率设为0.5让模型在前几个epoch先学干净样本再学混合样本。实测在蝴蝶数据集上MixUp能把验证集准确率提升2到3个点但训练epoch要相应增加。5.2 用余弦退火和学习率预热替代StepLRStepLR在固定epoch衰减容易错过最优学习率区间。余弦退火让学习率从初始值平滑降到接近0配合前5个epoch的线性预热能显著提升最终精度。把优化器换成torch.optim.AdamW权重衰减设0.05学习率用CosineAnnealingLRT_max设为总epoch数。预热阶段用LambdaLR让学习率从1e-6线性升到1e-3。这套组合在20类蝴蝶数据集上通常比StepLR高1到2个点而且对初始学习率不那么敏感。5.3 用测试时增强和模型集成做最终验证测试时增强是对每张测试图做多次变换如原图、水平翻转、不同裁剪把预测概率平均后取argmax。模型集成则是训练3到5个不同随机种子的模型推理时平均softmax输出。两者叠加通常能再提升1到3个点。下面是一个简单的TTA推理代码片段。def tta_predict(model, img_path, n_crops5): model.eval() img Image.open(img_path).convert(RGB) probs torch.zeros(20).to(device) transforms_list [ transforms.Compose([transforms.Resize(256), transforms.CenterCrop(224)]), transforms.Compose([transforms.Resize(256), transforms.CenterCrop(224), transforms.RandomHorizontalFlip(p1.0)]), transforms.Compose([transforms.Resize(256), transforms.CenterCrop(224), transforms.RandomRotation((10, 10))]), transforms.Compose([transforms.Resize(256), transforms.CenterCrop(224), transforms.RandomRotation((-10, -10))]), transforms.Compose([transforms.Resize(224)]), ] with torch.no_grad(): for tf in transforms_list: tensor tf(img).unsqueeze(0).to(device) tensor transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])(tensor) probs torch.softmax(model(tensor), dim1).squeeze() return probs.argmax().item()这段代码对同一张图做中心裁剪、水平翻转、正负10度旋转和直接缩放五种变换分别推理后累加softmax概率。注意Normalize要放在ToTensor之后这里为了简洁直接对tensor操作。TTA的代价是推理时间翻倍如果线上延迟敏感可以只保留水平翻转和中心裁剪两种。模型集成时每个模型用不同的随机种子训练推理时把概率平均不要平均logits因为不同模型的logits尺度可能不一致。我自己的习惯是每次拿到一个新的分类数据集先花半天把数据切分和基线跑通再花一天做错误分析看混淆矩阵里哪些类别互相误判最多然后针对性地加数据或改增强。蝴蝶分类数据集20类这个任务最容易混淆的是同属不同种的蝴蝶它们的翅膀形状几乎一样只有斑纹颜色和位置有细微差别。遇到这种情况与其盲目加深网络不如把输入分辨率从224提到320或448让模型看到更多纹理细节。另外别忽略标签噪声有些数据集里存在标错的图训练前用置信学习或简单的人工抽查清理一遍往往比调参更有效。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网