
简介这是一份面向深度学习初学者的数据集扩充方法PDF资料重点解决图像样本数量不足导致模型训练精度下降的问题。作者从自身1406张、4分类图像的实际项目出发通过亮度增强、对比度增强、水平翻转和随机方向旋转四种手段将数据量扩充至7030张并附带了完整的Python变换程序与调用示例。资料共1个PDF文件压缩包大小35KB内容简洁直接适合正在处理小样本图像分类任务、希望快速上手数据增强的开发者参考。目前已有12659人学习实用性和热度都很高。读者从中不仅能获得可直接改造运行的扩充代码还能理解增强系数取值范围、旋转角度随机生成等关键细节有效提升模型泛化能力。1. 数据集太少是常态先别急着找数据先想清楚缺的是哪种“少”做深度学习项目第一脚踢到的铁板往往不是模型选型而是数据集少得可怜——手头只有两三百张图、几千条样本连验证集都分得心惊胆战。这个标题问的是“数据集太少怎么办”很多人的第一反应是去网上找更多数据但一线做法其实是先做一次“数据缺口判断”你的少是样本量少、类别不均衡、还是场景覆盖不够三种情况对应的扩充路线完全不同硬套数据增强反而会把模型训偏。本文要讲的不是某一份开源代码的用法而是一条从“分析现状”到“离线增强 在线增强 外部数据补充”的完整落地路径适合正在用 YOLO、ResNet、Transformer 这类常见框架跑检测或分类任务的同学。先给一个反直觉的结论对很多中小数据集来说先把在线增强参数调对比费劲找一万张外部图更划算数据扩充的本质不是“变多”而是“把已有的分布边界撑开”。我们一步步把这个事拆开做。2. 先做数据盘点从文件数量到分布偏差十分钟摸清家底2.1 统计样本量与类别分布用脚本代替肉眼拿到数据集第一件事不是写增强代码而是统计。常见做法是写一个几行的遍历脚本把每张图、每个标注都过一遍输出总样本数、每类数量、图像尺寸分布和标注框宽高比。很多人会跳过这步但恰恰是这里能看出增强策略该怎么定如果某类只有 30 个样本在线增强要多给一点亮度扰动如果标注框普遍偏小就不能用大幅随机裁剪否则目标直接裁没了。import os from collections import Counter from PIL import Image img_dir datasets/images label_dir datasets/labels img_files [f for f in os.listdir(img_dir) if f.endswith(.jpg)] class_counter Counter() size_list [] for img_file in img_files: img Image.open(os.path.join(img_dir, img_file)) size_list.append(img.size) label_path os.path.join(label_dir, img_file.replace(.jpg, .txt)) if not os.path.exists(label_path): continue with open(label_path, r) as f: for line in f.readlines(): cls_id int(line.split()[0]) class_counter[cls_id] 1 print(总图片数:, len(img_files)) print(类别样本数:, dict(class_counter)) print(宽高范围:, min(s for s,_ in size_list), -, max(s for s,_ in size_list))这段脚本统计的是 YOLO 格式的标注核心逻辑是走到哪一步都要把“数字”打出来。总图数不等于总样本数一张图里可能有 5 个目标这决定了你后续用图级增强还是目标级增强。图像尺寸范围也很关键如果尺寸差异极大后面做 resize 时小图会被放大得模糊需要考虑按长边等比缩放而不是直接拉伸。2.2 从“少”到“缺”判断你的数据到底缺在哪统计完数量之后要回答一个更关键的问题模型目前表现差是因为见过的样本少还是因为没见过某些场景判断方法很朴素拿现在的手头数据训练一个基线模型再用这个模型去跑测试集把所有预测错误或漏检的样本单独抽出来看。如果错例集中在晚上、强光、遮挡这类场景说明属于“场景覆盖缺失”如果各类错误分布均匀说明就是“量不足”。我一般会把这批坏样例单独建一个文件夹人为给它们打上“场景标签”比如 night、rain、blur、truncated。这一步动作不大但决定了之后是去做有针对性的数据采集还是可以靠通用增强把量撑起来。如果是场景缺失先试增强增强后模型在这些标签上仍无明显提升再考虑去开源数据集或自己补采。这个“台账”习惯能让你在项目汇报时讲得清效果差异的来源不是说一句“加了增强所以涨点”就完了。2.3 分清正样本与负样本的比例扩充不总是加正样本在目标检测和二分类任务里很多“少”其实是正样本少负样本反而泛滥。比如工业质检场景良品图几千张次品只有几十张。这时候扩充的重点不是把所有图都做增强而是对少数的次品做更重的增强同时在训练时通过采样策略让每个 batch 里正负样本比例维持在 1:1 到 1:3 之间避免模型学成“永远预测负类”。实现上常见做法是写一个自定义 Dataset 类在getitem里按类别索引做重采样。这比在数据文件里重复复制图片更省内存也更灵活——你可以随时调整采样权重不用改文件。很多框架自带的 sampler 也能实现类似效果但自己控制索引更直观方便排查漏了哪个类别。class BalancedDataset(torch.utils.data.Dataset): def __init__(self, img_paths, labels, target_cls0, pos_weight3): self.img_paths img_paths self.labels labels self.pos_idx [i for i, l in enumerate(labels) if l target_cls] self.neg_idx [i for i, l in enumerate(labels) if l ! target_cls] self.pos_weight pos_weight def __len__(self): return len(self.pos_idx) * self.pos_weight len(self.neg_idx) def __getitem__(self, idx): if idx len(self.pos_idx) * self.pos_weight: i self.pos_idx[idx % len(self.pos_idx)] else: i self.neg_idx[idx % len(self.neg_idx)] return self.img_paths[i], self.labels[i]这段代码的核心是让采样器多“路”过正样本几遍从而起到类似复制的效果但保留随机性。注意 pos_weight 不要设得过大一般 35 之间。权重太大模型每轮都反复看同样那几张正样本反而会过拟合到噪声上。3. 在线增强不改文件也能扩充PyTorch 里的三板斧3.1 基础几何与色彩随机裁剪、翻转、亮度饱和度的真实作用在线增强指的是在数据加载进模型之前即时做变换训练每个 epoch 时看到的是经过不同随机变换的版本相当于变相扩充了整体数据量同时又避免了占满磁盘空间。很多资料简单说增强就是翻转加裁剪就到此为止了。但一线项目的经验是不同的任务适合不同的基础组合目标检测尽量用随机翻转、随机尺度变化、随机裁剪少用旋转——因为检测框的方向一变标注框也要跟着旋转处理起来容易出坐标错误分类任务则可以放心用旋转幅度控制在正负 15 度以内即可。from torchvision import transforms train_transform transforms.Compose([ transforms.RandomResizedCrop(size(224, 224), scale(0.6, 1.0)), transforms.RandomHorizontalFlip(p0.5), transforms.ColorJitter(brightness0.4, contrast0.4, saturation0.4, hue0.1), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])上面这段是分类任务里比较标准的一组分关键参数是 scale 和 brightness。RandomResizedCrop 的 scale 设到 0.6 以上是保证裁剪后主体还能看清值设到 0.2 会造成大量语义信息丢失等于制造噪声。brightness 和 contrast 分别加 0.30.4 在光照不稳定的数据上效果很好但如果你的数据本身采集条件统一就没有必要加这么高否则会强迫模型学会“忽略色彩”反而不利收敛。3.2 Mosaic 与 MixUp让模型同时看多张图提升泛化能力Mosaic 和 MixUp 借助多张图拼接让模型尽量少依赖背景、多依赖目标本身的特征。这类方法的出发点跟 “复制粘贴式扩充”完全不同不是多给几张类似的图而是让单张样本的信息量变大迫使模型学会在复杂背景下认出目标。YOLOv4 用过 Mosaic 之后小目标检测效果提升明显MixUp 则常用于分类任务是对抗噪声标注的有效手段。# MixUp 的简易实现两张图按权重叠加 def mixup_data(x, y, alpha1.0): lam np.random.beta(alpha, alpha) batch_size x.size(0) index torch.randperm(batch_size) mixed_x lam * x (1 - lam) * x[index] mixed_y (lam * y (1 - lam) * y[index]) return mixed_x, mixed_y这个实现的用意是每张图都与同一 batch 里的另一张图按比例融合、对应标签也按同样比例混合。参数 alpha 控制混合强度alpha1.0 时 lam 在 0.180.82 之间波动混合得比较“狠”如果数据本身类别差异大alpha 调到 0.2 会更安全。Mosaic 的实现则更重一点因为要同时读取四张图、做四份变换再拼成一张建议放到数据加载环节里实现而不是预先生成所有图否则会占用额外空间。对检测任务而言拼接时要注意把四个子图的标注框坐标换算到同一坐标系里。3.3 增强强度怎么调先看验证集loss别凭感觉堆强度很多刚接触增强的人容易进入误区把色彩抖动、旋转、模糊、擦除全部叠加在一起觉得“增强得多 泛化好”结果训练 loss 一直降不下去。写到这里就必须说增强是在制造训练难度难度过大时模型根本学不动。我调试增强强度的习惯是先用一套基础增强翻转、裁剪、小幅度色彩抖动跑 20 个 epoch 当基线记录验证集 loss然后逐个加上某种增强控制变量看验证 loss 是下降还是上升。涨了就留着跌了就拆掉或者降低幅度。需要特别小心的是测试阶段别用增强只有训练时用。很多人在推理时也习惯地把 transform 写在代码里导致预测结果忽好忽坏问题就出在这里——推理阶段只做 resize 和归一化不引入随机变换。这里的频率也值得一提裁剪、翻转这类几何增强可以每次加载都做mixup 这类样本混合增强则适合隔几个 epoch 再启用避免前期训练不稳定。3.4 在线增强做不到的事它不会产生“没见过”的信息在线增强本质上是在已有数据分布内部做扰动所以它没办法为你补充真正没见过的视觉信息。拿一个只有白天街景的车辆检测数据集来举例怎么做亮度、对比度调整里面也不会凭空跑出一辆夜间开大灯的卡车色温扭曲得厉害反而让模型把夜色学成“偏蓝的白天”。所以识别出在线增强能力的边界很重要这也是为什么下一章我们要讲离线生成与外部补充——那才是把分布边界往外推的真正手段。4. 离线扩充与外部数据从生成图片到开源数据集4.1 离线增强的适用场景存储够、训练慢、需要统一增强结果时如果你做的是医学影像、卫星遥感、工业质检这类对可复现性要求比较高的任务在线增强的随机性有时会让实验对比产生偏差。这时使用离线增强是普遍的做法运行一次脚本把每张原图制作成若干张增强后的图片直接把这些图片合流到数据集里之后所有实验都复用同一份增强结果。这样做的好处是实验结果可以追溯团队之间也能共享不至于出现“你跑的和我跑的增强参数不一样”的尴尬局面。常见做法是按下表组织一份增强配置用脚本批量执行。文件命名建议把增强类型和强度写进文件名比如“flip_h_001.jpg”便于以后筛选。增强类型参数设置输出文件命名水平翻转p0.5flip_h_001.jpg亮度调整factor0.8/1.2bright_08_001.jpg高斯模糊kernel3, sigma1.0blur_g_001.jpg随机裁剪crop0.7crop_07_001.jpg4.2 用预训练模型做伪标注半自动扩充的可行边界如果手里有大量无标注的图片而标注人力又不足一个常见做法是用已经训练好的模型先做一次伪标注然后人工抽检修正。这条路线在自动驾驶场景里很常见BDD100K、Cityscapes 这类数据集里的部分标注就是用模型辅助生成的再配合人工清洗。对个人项目来说操作路径是用现有模型跑一批无标注图把置信度高于 0.9 的检测框直接当成真值0.60.9 之间的人工过一遍低于 0.6 的直接丢弃。这里要特别提醒“伪标注”的准确性完全取决于模型在目标场景上的表现如果你的模型本身就欠拟合那扩充的图越多错误越多等于把噪声喂给模型放大。我见过太多人栽在这上面。所以伪标注只适合填补“标注数量少但模型已经基本可用”的缺口不适合标注质量很差或者类别没见过的阶段。4.3 从开源数据集中挑数据不要盲目全量下载开源数据集是扩充数据的主要外部来源。像 COCO2017、ImageNet-1K 这些大型数据集的质量有保障但直接全部下载既费时又没必要而且类别体系不匹配会导致你需要重新标注。高效做法是按类别筛选下载对应类别的子集再把标注转成你自己的格式。这个环节“格式转换”是最花时间的坑。以车辆检测为例如果你是在 COCO 里挑“car”和“truck”两个类需要把 COCO 的 JSON 标注转成 YOLO 的 txt 坐标格式。转换脚本里有两个特别容易出错的点一是 COCO 的 bbox 是 xywh左上角坐标 宽高而 YOLO 需要的是中心点 xywh且要除以图片宽高归一化二是 COCO 的类别索引和你的类别索引不一定一致必须重新映射否则训练时会发现 box 对不上号。这类错误不报错只会让指标轻则闪崩、重则一直不涨。# COCO JSON 转 YOLO txt 的简化版 import json, os with open(annotations/instances_train2017.json) as f: coco json.load(f) # 只挑 car(2) 和 truck(7) 两个类别 keep_cats {2: 0, 7: 1} cat_id_to_name {c[id]: c[name] for c in coco[categories]} img_id_to_info {img[id]: img for img in coco[images]} for ann in coco[annotations]: cat_id ann[category_id] if cat_id not in keep_cats: continue img img_id_to_info[ann[image_id]] w, h img[width], img[height] x, y, bw, bh ann[bbox] cx (x bw / 2) / w cy (y bh / 2) / h bw / w bh / h line f{keep_cats[cat_id]} {cx:.4f} {cy:.4f} {bw:.4f} {bh:.4f} out_path os.path.join(yolo_labels, img[file_name].replace(.jpg, .txt)) with open(out_path, a) as f: f.write(line \n)这段脚本最需要注意的地方就是坐标换算。COCO 的 bbox 里 x、y 是左上角坐标改成中心点是一步同时整张图要先除以宽高做归一化是另一步。这两个操作出错中途连报错都没有。跑完脚本后要随机抽几张小图把标注框画出来看一眼确认坐标是贴合目标位置的不要直接拿去开训练。4.4 特定领域的数据集哪里找遥感、车辆、工业场景的可用选项针对不同垂直领域业界沉淀了一批已经被广泛使用的公开数据集技术方向选型时通常直接拿来做扩充或预训练。遥感旋转检测方向DOTA、HRSC2016 这两份数据在场景文本与船只检测里很常用做旋转目标检测的人应该都知道 mmrotate 仓库对 DOTA 的预处理很有参考价值。车辆与自动驾驶方向BDD100K、CCPD中国车牌是经常用到的其中 CCPD 对车牌检测来说是一项很好的补充能做“应付复杂背景”的专项训练。工业故障诊断方向行星齿轮箱数据集、PHM2012 这类在剩余寿命预测任务中比较常见在做风力发电、工业设备预测性维护的项目里直接拿来做基础训练的概率很高。医学影像方向PlantDoc 是植物病害检测的常用数据集很多农业 AI 项目会先去它那取一批叶子病害图做预训练再配合自己的田间数据微调。多模态方向如果用到了自动驾驶的长尾场景可以关注 BDD100K 的语义分割与跟踪标注它比单纯的检测框提供更多信息量。需要强调一个判断原则扩充分支时新数据的图像风格、光照分布、分辨率跟原数据不能差得太大。让模型同时学习监控视频截图和航拍影像它会在低级特征上左右为难。你需要的不是“数据集多多益善”而是“数据分布补全”。5. 数据扩充的避坑指南5 个最常见的翻车现场5.1 翻车现场一标签同步丢失训练集凭空少了 20%现象增强脚本跑完发现 train.txt 里引用的图片有 500 张但对应的标注文件只有 400 份训练过程在某一轮突然结束报错找不到 label。原因增强流程里对图片做翻转只需要水平翻转即可但如果同时也对标注框做了变换漏掉了那一部分的输出就会出现“图有但标没有”的情况。常见于写了图像增强代码但忘了把标注部分一起补上尤其是用随机数生成后没有统一 rand 数的问题。解决在做几何增强的时候最好用“同一个随机状态”同时作用于图和框。具体做法是在增强前固定住一个是全局的随机种子然后按固定顺序对图和框做同一参数翻转。代码里加一行断言检查最好增强完 count(图片) count(标注)在脚本开始和结束各打一次保证数量一致再进入下一步训练。5.2 翻车现场二增强后类别失衡加剧小类直接消失现象做完增强大类样本量翻倍小类还是原来那几张训练完模型在小类上完全丧失预测能力甚至直接不预测这一类。原因很多增强策略是图级统一执行的每张图都增强。对大类是锦上添花对小类却在重复造同样内容没有把小类样本真正利用到。同时如果一个数据集的类别极度不平衡对整张图做随机裁剪时小类别样本容易因为尺度太小而被裁掉间接等于抹除了它。解决对小类目标使用目标级裁剪增强先裁剪出小目标区域再放大增强并拼接回去保留它们在场景中的存在比例。同时控制总体的正负增强倍数匹配到合理区间。5.3 翻车现场三测试集没做同样预处理线上指标崩掉现象训练集上指标很好看一部署到线上召回率全面下滑甚至比训练前还不如。原因很可能是因为训练时对输入做了较强的增强变换比如 mixup、random erasing而线上数据是原图模型没有见过“干净”的输入。更常见的还有一种你在数据划分时没有把增强后的样本与原始样本分开导致同一个原始图既出现在训练集里又因为离线增强版混进测试集造成数据泄漏训练指标虚高。解决离线增强时必须在划分训练集之前就把增强样本做好或者在划分之后不要动测试集。保底原则测试集永远只用原始图像或只做标准化不做随机裁剪、翻转、混合这一类变换。上面那句话写成一条检查纪律划分数据后关掉测试集的随机增强只保留必要预处理。5.4 翻车现场四坐标变换折腾反了模型学了一堆“偏移框”现象模型 loss 能降但 mAP 一直在 0.3 上下徘徊画出检测框来发现目标框总是往右上偏一圈。原因检测任务里对图像做了翻转、裁剪标注框坐标没有跟随图像坐标系一起变换。解决把“图像变换”和“框变换”的代码放在同一处用同一把随机数。像 mmmdetection、detectron2 都封装了对应的 bbox 变换接口如果手写增强脚本那就遵循一个准则增强代码和标注坐标变换代码必须放在同一个函数里禁止分开写。这里其实适合单独写一个断言翻转之后重新把框画在图上保存下来肉眼抽验几个样例只花五分钟就能拦住最大的坑。6. 做数据集扩充的正确姿势记流水账、分阶段验证一个很实用的技巧做完任何一个扩充动作顺手把“增强了什么数据、加了哪几类、用了什么参数训练结果对比差多少”记在一个文本文件里。这件事坚持下来比任何增强库都值钱。因为数据扩充的效果极难线性推导你以为有用的增强方式可能在下一次换模型时全部失效这种时候能翻的只有你的实验记录本。我自己的习惯是拿着一个固定表格记录文件路径、原样本量、扩增量、参数、验证集指标以及“这个手段有没有用”的主观判断。每次只做一处变化先跑固定 epoch对比 mAP 或 acc再做下一处变化。这比把十种增强一次性全开后瞎猜要高效得多。分阶段验证的路线可以这样安排先只用基础增强做基线发现模型过拟合明显时再加入更强的增强方式观察到小类指标低单独加大该类增强强度外部数据用在最后验证分布靠近后再合流。每过一阶段模型和验证集的诊断比结论更重要每次都把 train loss 与 val loss 的差距记下来是阻挡过拟合最直观的刻度尺。这整套流程最终会让你对“数据集太少”这件事脱敏因为已经能准确判断少的类型、知道对应增强手段与边界你接下来做的只是调参数和看曲线而已。希望今天这套做法可以帮你把扩充数据的这条路走顺少踩几个我当年踩过的坑。本文还有配套的精品资源点击获取