ARTICLE DETAIL

资讯详情

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

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

动物图片数据集实战:10类28K图像分类与迁移学习指南 简介这份动物图片数据集面向计算机视觉初学者、深度学习课程学习者以及需要练手图像分类项目的开发者覆盖狗、猫、马、spyder、蝴蝶、鸡、羊、牛、松鼠、大象共10个类别可用于训练与验证卷积神经网络分类模型、迁移学习实验或数据增强练习。资源包共约2000个文件以jpeg与jpg图像为主体另有少量png图片及1个py脚本压缩包整体约586.39MB主目录按类别分文件夹存放每类图像数量在2K至5K之间结构清晰便于直接读取与划分训练集、验证集。目前已有790人学习下载适合作为课程作业、毕业设计或算法对比实验的基础数据。中等质量的图像规模兼顾了训练效率与类别多样性读者可据此快速搭建baseline、测试模型泛化能力并在此基础上尝试数据清洗、类别平衡与预处理流程。1. 动物图片数据集 JPG10 类 28K 图像到底能拿来干什么如果你正在找一个能直接跑图像分类、迁移学习或者做数据增强实验的现成图片集这个 10 类、约 28K 张的动物图片数据集 JPG 版本值得先看一眼。它的目录结构非常朴素主目录下按类别分文件夹狗、猫、马、spyder、蝴蝶、鸡、羊、牛、松鼠、大象各一个文件夹每个类别大约 2K 到 5K 张不等。图片格式统一为 JPG中等质量不是那种动辄几 MB 一张的原图对磁盘和显存都相对友好。适合谁刚入门深度学习想跑通第一个分类模型的人、需要快速验证某个 backbone 效果的人、以及做教学演示或数据清洗练习的人。它不解决 SOTA 精度问题但能让你在半小时内把数据加载、划分、训练、评估这条链路完整走一遍。2. 拆开目录看结构10 个类别的文件分布与加载逻辑2.1 类别文件夹命名与数量分布拿到压缩包解压后你会看到类似这样的结构animal_dataset/ ├── dog/ │ ├── 0001.jpg │ ├── 0002.jpg │ └── ... ├── cat/ ├── horse/ ├── spyder/ ├── butterfly/ ├── chicken/ ├── sheep/ ├── cow/ ├── squirrel/ └── elephant/每个文件夹内的图片命名不统一有OIP-xxx.jpeg这种从网络抓取留下的原始名也有157.jpeg、1121.jpeg这类纯数字名。这不影响加载但如果你后续要做可复现实验建议先统一重命名。类别数量上狗和猫通常偏多接近 5Kspyder、蝴蝶、松鼠这类相对少一些2K 出头。这种不均衡在真实数据集里很常见训练时要么做重采样要么在 loss 里加类别权重。2.2 用 Python 快速统计与校验在动手训练之前我一般会先跑一段脚本确认三件事每个类别实际有多少张、有没有损坏文件、图片尺寸分布大概是什么范围。import os from PIL import Image from collections import defaultdict root animal_dataset stats defaultdict(int) bad_files [] sizes [] for cls in os.listdir(root): cls_dir os.path.join(root, cls) if not os.path.isdir(cls_dir): continue for fname in os.listdir(cls_dir): fpath os.path.join(cls_dir, fname) try: with Image.open(fpath) as im: im.verify() # 校验文件完整性 stats[cls] 1 sizes.append(im.size) except Exception as e: bad_files.append((fpath, str(e))) print(类别统计, dict(stats)) print(损坏文件数, len(bad_files)) print(尺寸样本, sizes[:5])这段代码的逻辑很直接遍历每个类别文件夹用 PIL 打开并 verify能通过的就计数抛异常的就记下来。im.verify()不会真正解码像素所以速度很快适合几万张级别的快速筛查。如果你发现某个类别数量明显偏少或者损坏文件超过几十张建议先手动清理再进入训练流程。尺寸样本可以帮你判断是否需要统一 resize比如大部分是 300x300 左右那统一到 224x224 或 256x256 都合理。2.3 划分训练集与验证集的常见做法这个数据集没有官方划分所以你得自己切。常见做法是按 8:2 或 7:3 分层抽样保证每个类别在训练集和验证集里的比例一致。import random from sklearn.model_selection import train_test_split all_files [] all_labels [] for cls in os.listdir(root): cls_dir os.path.join(root, cls) if not os.path.isdir(cls_dir): continue for fname in os.listdir(cls_dir): all_files.append(os.path.join(cls_dir, fname)) all_labels.append(cls) train_files, val_files, train_labels, val_labels train_test_split( all_files, all_labels, test_size0.2, stratifyall_labels, random_state42 )stratifyall_labels是关键参数它保证切分后每个类别的比例与原始一致。random_state42是为了可复现你换成别的数字也行但一旦定了就别改否则每次跑的结果都对不上。切完之后建议把文件路径和标签写成一个 CSV后续用 Dataset 类读取会方便很多。3. 从 JPG 到 Tensor构建 PyTorch 数据管道的完整步骤3.1 自定义 Dataset 类的写法与参数说明PyTorch 里最稳妥的方式是继承torch.utils.data.Dataset把文件路径列表和标签列表传进去。import torch from torch.utils.data import Dataset, DataLoader from torchvision import transforms from PIL import Image class AnimalDataset(Dataset): def __init__(self, file_list, label_list, transformNone): self.file_list file_list self.label_list label_list self.transform transform self.class_to_idx {cls: i for i, cls in enumerate(sorted(set(label_list)))} def __len__(self): return len(self.file_list) def __getitem__(self, idx): img Image.open(self.file_list[idx]).convert(RGB) label self.class_to_idx[self.label_list[idx]] if self.transform: img self.transform(img) return img, labelconvert(RGB)这一步不能省因为有些 JPG 可能是灰度图或带 alpha 通道不统一转成三通道后面会报错。class_to_idx用sorted是为了让类别索引固定不然每次实例化顺序可能不一样导致标签错位。transform 传进来之前定义好训练和验证要用不同的增强策略。3.2 训练与验证的 transform 配置差异train_tf transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomCrop(224), transforms.RandomHorizontalFlip(), transforms.ColorJitter(brightness0.2, contrast0.2), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) val_tf transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])训练时用RandomCrop和RandomHorizontalFlip做增强验证时只做 resize 和归一化。Normalize的均值和方差是 ImageNet 的统计值如果你从零训练可以改成这个数据集自己的均值方差但用预训练权重的话最好保持一致。ColorJitter对这个数据集比较有用因为很多图片来自网络光照和色调差异大适度抖动能让模型更鲁棒。3.3 DataLoader 的 batch 与 worker 设置train_ds AnimalDataset(train_files, train_labels, transformtrain_tf) val_ds AnimalDataset(val_files, val_labels, transformval_tf) train_loader DataLoader(train_ds, batch_size64, shuffleTrue, num_workers4, pin_memoryTrue) val_loader DataLoader(val_ds, batch_size64, shuffleFalse, num_workers4, pin_memoryTrue)batch_size64在 8GB 显存上跑 ResNet50 基本够用如果 OOM 就降到 32。num_workers4是常见的起步值Windows 下如果报错就改成 0。pin_memoryTrue在 GPU 训练时能加速数据传输但如果你用的是 CPU 训练这个参数没意义。shuffleTrue只给训练集开验证集必须关掉否则评估指标会波动。4. 避坑与排查这份数据集最容易翻车的五个地方4.1 类别名拼写不一致导致标签错乱现象训练时 loss 正常下降但验证准确率始终在 10% 左右跟随机猜差不多。原因spyder这个类别名不是标准英文拼写标准写法是spider。如果你在代码里手写了spider去匹配文件夹就会漏掉整个类别导致标签和图片对不上。解决统一用os.listdir动态读取文件夹名不要手写类别列表。或者在数据准备阶段就把spyder重命名为spider后续所有代码都用同一套命名。4.2 图片损坏或截断导致训练中断现象训练跑了几百个 step 后突然报PIL.UnidentifiedImageError或OSError: image file is truncated。原因网络抓取的 JPG 里混入了下载不完整的文件PIL 在解码时才会暴露。解决在 Dataset 的__getitem__里加 try-except遇到坏图就返回一个全零张量或者随机换一张。更彻底的做法是训练前跑一遍 2.2 节的校验脚本把坏文件清单导出来直接删掉。4.3 内存暴涨与 worker 死锁现象训练开始后系统内存迅速被占满或者程序卡在DataLoader初始化不动。原因num_workers设得太大每个 worker 都会复制一份数据索引或者 Windows 下多进程 spawn 方式与某些库不兼容。解决把num_workers降到 2 或 0观察内存变化。如果用的是 Jupyter Notebook建议把 DataLoader 的创建放在if __name__ __main__:里面避免多进程递归启动。4.4 验证集准确率虚高现象验证集准确率比训练集还高或者高得离谱。原因划分数据时没有用stratify导致验证集里某几个类别占比过大或者验证集的 transform 里误加了随机增强。解决检查train_test_split是否带了stratifyall_labels检查val_tf里有没有RandomHorizontalFlip之类的操作。验证集必须只做确定性的 resize 和归一化。4.5 图片尺寸差异过大导致 resize 变形现象训练出来的模型在测试时对某些类别的图片识别率特别低。原因这个数据集里图片长宽比差异很大直接Resize((224, 224))会把某些图压扁或拉长丢失关键特征。解决改用Resize(256)加CenterCrop(224)的组合先等比缩放到短边 256再从中心裁 224。这样能保留长宽比对蝴蝶、spyder 这类形状敏感的类别更友好。5. 进阶技巧用预训练权重和混淆矩阵把 28K 图片榨干如果你不想从零训练最省事的路径是拿一个 ImageNet 预训练模型做微调。我一般会先用 ResNet18 跑一轮 baseline确认数据管道没问题再换 ResNet50 或 EfficientNet 看能涨多少。下面这段代码展示了如何只训练最后的全连接层冻结前面的卷积层。import torch.nn as nn from torchvision import models model models.resnet18(pretrainedTrue) for param in model.parameters(): param.requires_grad False # 冻结卷积层 num_features model.fc.in_features model.fc nn.Linear(num_features, 10) # 10 个类别 model model.cuda() criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.fc.parameters(), lr1e-3)冻结卷积层后只有fc层的参数会被更新训练速度非常快几分钟就能跑完一个 epoch。等 loss 稳定后可以解冻最后几个 block 做更细粒度的微调学习率调到 1e-4 左右。CrossEntropyLoss默认不做类别加权如果你发现某个类别 recall 特别低可以传入weight参数把少样本类别的权重调高。训练完成后别只看一个总体的 accuracy。用混淆矩阵看看哪些类别容易被搞混比如狗和猫、羊和牛这些在低分辨率下确实容易出错。from sklearn.metrics import confusion_matrix import seaborn as sns import matplotlib.pyplot as plt model.eval() all_preds [] all_trues [] with torch.no_grad(): for imgs, labels in val_loader: imgs imgs.cuda() outputs model(imgs) preds outputs.argmax(dim1).cpu().numpy() all_preds.extend(preds) all_trues.extend(labels.numpy()) cm confusion_matrix(all_trues, all_preds) sns.heatmap(cm, annotTrue, fmtd, cmapBlues) plt.xlabel(Predicted) plt.ylabel(True) plt.show()混淆矩阵能告诉你模型到底在哪些类别上翻车。如果狗和猫的交叉项特别大说明特征区分度不够可以考虑换更大的输入尺寸或者用更强的 backbone。如果某个类别几乎全错回去检查那个类别的文件夹里是不是混进了别的图片——这个数据集来自网络抓取标签噪声是真实存在的。从那以后我每次拿到新数据集都会先跑一遍类别统计和坏图筛查再切分、再训练。这个习惯帮我省下了大量排查玄学问题的时间。希望帮到你。本文还有配套的精品资源点击获取
返回列表