首页 / 资讯中心 / 文章详情

动物图片数据集JPG实战:10类28K图像分类baseline与迁移学习

动物图片数据集JPG实战:10类28K图像分类baseline与迁移学习 ★ FEATURED ARTICLE
简介这份动物图片数据集面向计算机视觉初学者、深度学习课程实验者及图像分类模型训练人员用于解决动物识别任务中样本不足、类别不均衡的问题。数据集覆盖狗、猫、马、蜘蛛、蝴蝶、鸡、羊、牛、松鼠、大象共10个类别主目录按类别分文件夹存放每类图像数量在2K至5K之间总量约28K张中等质量图片便于直接构建训练集与验证集。资源包共2000个文件以jpeg和jpg图像为主另有少量png及1个py脚本压缩包约586.39MB目录结构清晰适合快速加载与批量预处理。目前已有790人学习下载读者可借助该数据集完成图像分类模型的训练、迁移学习微调、数据增强实验及类别分布分析也可用于课堂演示与个人练手项目省去自行爬取与清洗图片的时间成本。1. 动物图片数据集 JPG10 类 28K 图像到底能拿来干什么上周有个做宠物用品推荐的朋友找我说想训一个「猫狗识别」的小模型跑在门店的边缘盒子上用来统计进店顾客带的是猫还是狗。他第一反应是去爬电商评论区的图结果爬了两千张就发现猫狗混在一起、背景全是商品图、标签还得自己打干了两天直接放弃。我让他先别急着造轮子直接拿现成的动物图片数据集 JPG 跑一版 baseline10 类、28K 张、按类别分文件夹当天下午就把分类器训出来了。这份数据集的核心价值就一句话它把「找图、清洗、打标签、分目录」这四件最耗时的脏活提前干完了。主目录下每个类别一个文件夹狗、猫、马、spyder蜘蛛、蝴蝶、鸡、羊、牛、松鼠、大象单类 2K 到 5K 张不等总量约 28K格式统一是 JPG。适合谁做图像分类入门练手的、要给目标检测做预训练底料的、想快速验证某个 backbone 效果的、以及像我朋友这种「业务侧要个能跑的东西、不想在数据上耗一周」的工程师。不适合谁追求 SOTA 精度、需要 COCO 级别标注框和分割掩码的这份数据给不了你它就是个中等质量的分类数据集定位要摆正。2. 拆开目录看结构10 类 28K 张 JPG 的组织方式与选型理由2.1 目录树长什么样先跑一遍统计再动手拿到任何数据集我的习惯是先别写模型先写个脚本把家底摸清楚。这份数据是典型的ImageFolder风格——一个根目录下面每个子文件夹名就是类别名文件夹里全是 JPG。这种结构最大的好处是 PyTorch 的torchvision.datasets.ImageFolder和 TensorFlow 的image_dataset_from_directory都能零配置直接读不用写自定义 Dataset。先跑统计脚本确认类别数、每类数量、有没有损坏文件import os from pathlib import Path from PIL import Image root Path(./animal_dataset) # 换成你的实际路径 total, bad 0, [] for cls_dir in sorted(root.iterdir()): if not cls_dir.is_dir(): continue imgs list(cls_dir.glob(*.jpg)) list(cls_dir.glob(*.jpeg)) print(f{cls_dir.name:12s} {len(imgs):6d}) for p in imgs: total 1 try: with Image.open(p) as im: im.verify() # 只校验文件头不解码全图快 except Exception as e: bad.append((str(p), str(e))) print(ftotal{total}, bad{len(bad)}) for b in bad[:10]: print(b)逻辑说明iterdir()遍历一级子目录glob同时匹配.jpg和.jpeg两种后缀——这份数据里两种后缀混着出现只匹配.jpg会漏掉一批。Image.verify()是轻量校验只读文件头判断是不是合法图像比load()快得多28K 张图几十秒能跑完。参数上root指向你解压后的根目录如果统计出来的bad不为 0先别删单独挪到一个_quarantine文件夹后面训练时用try/except跳过即可别让几张坏图卡住整个流程。2.2 为什么选 ImageFolder 而不是自己写 Dataset很多人一上来就想写自定义 Dataset觉得灵活。但在这份数据上自定义 Dataset 是负收益。原因有三第一目录结构天然就是「文件夹名标签」ImageFolder会自动生成classes列表和class_to_idx映射你手写反而要维护一份 label map第二ImageFolder和DataLoader的num_workers配合成熟多进程读图不会踩到文件句柄泄漏的坑第三做迁移学习时ImageFolder的target_transform能直接接OneHot或LabelSmoothing省事。常见做法是先用ImageFolder跑通全流程等确认数据没问题、模型能收敛了再考虑要不要换成自定义 Dataset 做难例挖掘或在线增强。顺序反了你会在「到底是数据问题还是代码问题」上浪费大量时间。2.3 类别不平衡怎么处理2K 到 5K 的差距不能装看不见摘要里写得很清楚每类 2K 到 5K 不等这意味着最大类和最小类差了 2.5 倍。直接训模型会偏向样本多的类。我一般用两种手段组合一是WeightedRandomSampler给每类按样本数倒数加权二是损失函数上CrossEntropyLoss(weight...)权重取类别频率的倒数归一化。import torch from torch.utils.data import DataLoader, WeightedRandomSampler from torchvision import datasets, transforms tf transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), ]) ds datasets.ImageFolder(./animal_dataset, transformtf) targets [s[1] for s in ds.samples] class_count torch.bincount(torch.tensor(targets)) class_weight 1.0 / class_count.float() sample_weight class_weight[torch.tensor(targets)] sampler WeightedRandomSampler( weightssample_weight, num_sampleslen(sample_weight), replacementTrue, ) loader DataLoader(ds, batch_size64, samplersampler, num_workers8)逻辑说明class_count统计每类样本数class_weight取倒数样本少的类权重高。WeightedRandomSampler按样本权重采样replacementTrue表示有放回保证每个 epoch 抽到的总数等于数据集大小。参数上num_samples一般设成len(dataset)想训得更久可以设成 2 倍num_workers在 Linux 上设 8 左右Windows 上设 0 或 2否则容易卡在共享内存上。注意用了 sampler 就不要再设shuffleTrue两者冲突。3. 从零跑通一版分类 baselineResNet18 迁移学习全流程3.1 数据划分别用随机 split按类别分层28K 张图如果直接random_split小类别可能被分得七零八落验证集里某类只剩几十张指标波动大。正确做法是分层抽样sklearn的train_test_split带stratify参数就能干。from sklearn.model_selection import train_test_split from torch.utils.data import Subset targets [s[1] for s in ds.samples] idx list(range(len(targets))) train_idx, val_idx train_test_split( idx, test_size0.2, stratifytargets, random_state42 ) train_ds Subset(ds, train_idx) val_ds Subset(ds, val_idx)逻辑说明stratifytargets保证训练集和验证集里每类的比例与原始一致。random_state42固定随机种子方便复现。test_size0.2是常规选择数据量再小可以调到 0.3。注意Subset只是索引视图底层还是同一份ds所以 transform 是共享的——如果你要给训练集加增强、验证集不加得建两个ImageFolder实例别偷懒。3.2 训练循环冻结 backbone 先训分类头迁移学习的标准套路先冻结ResNet18的卷积层只训最后的fc等 loss 降稳了再解冻微调。这样能避免随机初始化的分类头把预训练权重带偏。import torch.nn as nn from torchvision import models model models.resnet18(weightsmodels.ResNet18_Weights.DEFAULT) for p in model.parameters(): p.requires_grad False model.fc nn.Linear(model.fc.in_features, 10) # 10 类 criterion nn.CrossEntropyLoss(weightclass_weight) optimizer torch.optim.Adam(model.fc.parameters(), lr1e-3) for epoch in range(5): model.train() for x, y in loader: optimizer.zero_grad() loss criterion(model(x), y) loss.backward() optimizer.step() print(fepoch {epoch}, loss{loss.item():.4f})逻辑说明weightsResNet18_Weights.DEFAULT加载 ImageNet 预训练权重这是迁移学习能 work 的前提。冻结参数后只优化fclr可以设大一点1e-3因为从头训的层需要快速下降。class_weight传进CrossEntropyLoss处理不平衡。参数上batch_size64 在 8G 显存上跑 224×224 没问题显存小就降到 32 或把输入 resize 到 128。5 个 epoch 后 loss 通常能降到 0.5 以下这时候再解冻全部参数lr调到 1e-4 微调 10 个 epoch验证集准确率一般能到 90% 以上。3.3 验证与指标别只看 accuracy10 类不平衡数据accuracy 会骗人。必须看每类的 precision/recall 和混淆矩阵。from sklearn.metrics import classification_report, confusion_matrix model.eval() preds, gts [], [] with torch.no_grad(): for x, y in DataLoader(val_ds, batch_size64, num_workers8): preds.extend(model(x).argmax(1).tolist()) gts.extend(y.tolist()) print(classification_report(gts, preds, target_namesds.classes)) print(confusion_matrix(gts, preds))逻辑说明model.eval()关掉 dropout 和 batchnorm 的训练行为torch.no_grad()省显存。classification_report会输出每类的 precision、recall、f1重点看样本少的类 recall 是不是明显低。混淆矩阵能告诉你哪两类容易混——这份数据里猫和狗、羊和牛、蝴蝶和蜘蛛如果蜘蛛图里有类似纹理是常见混淆对。如果某类 recall 低于 0.7回去检查该类样本是不是有大量灰度图或低分辨率图。4. 避坑与排查这份数据集上手时最容易翻车的五件事4.1 现象训练 loss 不降accuracy 卡在 10% 左右原因最常见的是标签映射错位。ImageFolder按文件夹名的字母序生成class_to_idx如果你自己另写了一份 label map 且顺序不一致模型学的和评估用的就对不上。另一个原因是 transform 里忘了ToTensor()输入还是 PIL 对象模型直接报错或输出垃圾。解决打印ds.class_to_idx确认映射评估时统一用ds.classes的顺序。transform 链最后必须是ToTensor()或Normalize且Normalize的 mean/std 要和预训练权重匹配ImageNet 是[0.485,0.456,0.406]/[0.229,0.224,0.225]。4.2 现象DataLoader 报OSError: image file is truncated原因这份数据是中等质量部分 JPG 文件在传输或压缩时被截断PIL 默认遇到截断图直接抛异常。解决在 Dataset 层面加容错或者全局设置ImageFile.LOAD_TRUNCATED_IMAGES True。我一般用前者因为后者会静默加载半张图反而污染训练。from PIL import ImageFile ImageFile.LOAD_TRUNCATED_IMAGES True # 应急用不推荐长期更稳的做法是继承ImageFolder重写__getitem__try/except包住Image.open失败就返回一个全黑图并打日志训练不中断。4.3 现象验证集准确率比训练集高很多原因不是模型泛化好是验证集的 transform 太弱或数据泄漏。常见于你把增强只加在训练集验证集用了原图 resize而训练集用了随机裁剪导致两者分布不一致。另一个隐蔽原因是分层抽样时random_state没固定每次跑划分不同指标不可比。解决验证集 transform 只用Resize CenterCrop ToTensor Normalize不加随机翻转和颜色抖动。固定random_state把划分索引存成 npy 文件后续所有实验复用同一份。4.4 现象num_workers设大了反而变慢甚至卡死原因Windows 上多进程 spawn 开销大且ImageFolder每个 worker 都会复制一份数据集索引内存翻倍。Linux 上如果磁盘是机械盘8 个 worker 同时随机读 28K 张小文件IO 寻道直接打满。解决Windows 设num_workers0或 2Linux 上先设 4用nvidia-smi和iostat看 GPU 利用率和磁盘 IOGPU 利用率低于 60% 再往上加。更彻底的办法是先把所有图 resize 到 256×256 存成ImageFolder的缓存版或者打包成 LMDB读图速度能快 5 到 10 倍。4.5 现象蜘蛛spyder类样本里混进了无关图原因这份数据是网络爬取后人工粗筛的spyder这个拼写本身就不标准正常是 spider爬虫可能把带 spyder 关键词的跑车图、软件截图也抓进来了。中等质量数据集的通病。解决训练前对每个类随机抽 50 张可视化肉眼过一遍。发现脏类就用cleanlab或简单的置信度过滤先用一版模型预测把 loss 最高的 5% 样本挑出来人工复核。这一步花半小时能省掉后面调参两天的冤枉路。5. 进阶玩法把 28K 分类数据榨出检测和自监督的价值跑通分类只是起点。这份数据真正的性价比在于它能当「预训练底料」和「弱监督种子」。我一般会做两件事。第一件用它做自监督预训练。28K 张无标注图分类标签先放一边跑一版 SimCLR 或 MoCo v2学到的特征再迁移到只有几百张标注的下游任务上小样本场景下比直接 ImageNet 预训练还稳。具体做法把ImageFolder的transform换成两个随机增强视图batch_size拉到 256用梯度累积训 100 个 epoch。这份数据类别内差异大比如狗包含各种品种正好适合对比学习。第二件把它当检测数据集的「负样本库」或「分类头初始化」。如果你手上有 COCO 或 VOC 格式的检测数据但某些类样本少可以从这份数据里挑对应类的图用现成的检测模型跑一遍伪标注人工修一修扩充检测训练集。我试过用这份数据的「狗」类给一个车辆检测模型做背景负样本误检率降了 3 个点。验证自监督效果的方法很简单冻结 backbone只训一个线性分类头看 10 类分类准确率。如果线性探测能到 70% 以上说明特征学得不错低于 50%回去检查增强强度是不是太大或太小。# 线性探测冻结 backbone只训 fc backbone model # 自监督训好的 encoder for p in backbone.parameters(): p.requires_grad False backbone.fc nn.Linear(backbone.fc.in_features, 10) opt torch.optim.SGD(backbone.fc.parameters(), lr0.1, momentum0.9) # 后续训练循环同 3.2参数上线性探测的lr要比微调大一个量级0.1 vs 1e-4因为只训一层需要快速收敛。weight_decay设 0线性探测不加正则更能反映特征质量。从那以后我每次拿到新数据集都强制先跑一遍「统计脚本 50 张可视化 分层划分」这三步再动模型。这份动物图片数据集 JPG 不算完美蜘蛛类有噪声、类别不平衡、图像质量参差但它的目录结构和体量决定了它是一个合格的 baseline 起点——28K 张、10 类、开箱即用的 JPG省下的数据清洗时间够你把模型迭代三轮。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站