图像数据增强实战:从翻转到 MixUp/CutMix——免费的涨点神器

· 约 5 分钟 ·
本文目录(8 节)

训练图片少时,可以通过变换增加输入的变化,但“看起来像新图片”不等于“标签仍然正确”。翻转猫的照片通常不改变类别,翻转文字、交通标志或医学图像却可能改变任务含义。选增强策略之前,先写清哪些变化应该被模型忽略。

前置阅读:建议先读 CNN 详解、过拟合与正则化、OpenCV 基础。

基础盘:几何 + 光度,两类先分清

几何变换(动像素位置):翻转、旋转、缩放、裁剪、平移。核心问题:变换后标签还成立吗——水平翻转猫还是猫,但翻转「6」就成了「9」;OCR 场景翻转文字是灾难。增强必须尊重任务的语义不变性,这是第一条纪律。

光度变换(动像素值):亮度、对比度、饱和度、噪声、模糊。相对安全,但医学影像/工业检测里像素值本身可能承载信息(CT 值、缺陷灰度特征),乱动会把信号洗掉。

import albumentations as A

train_tf = A.Compose([
    A.RandomResizedCrop(size=(224, 224), scale=(0.6, 1.0)),
    A.HorizontalFlip(p=0.5),
    A.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, p=0.5),
    A.GaussNoise(std_range=(0.01, 0.05), p=0.3),
    A.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),
])

为什么用 Albumentations 而不是 torchvision.transforms:快(OpenCV 实现)、检测/分割的标签同步开箱即用(bbox/keypoint/mask 跟着图像一起变)。

样本级增强:MixUp 与 CutMix

这两个是近十年最有影响力的增强思想——不增强单张图,而是「混合」两张图:

MixUp:像素级线性叠加,标签也线性混合:img = λ·A + (1−λ)·B, label = λ·yA + (1−λ)·yB。模型学到的决策边界更平滑,泛化和对抗鲁棒性都提升。

CutMix:把 B 的一块矩形区域剪切贴到 A 上,标签按面积比例混合。比 MixUp 更符合视觉习惯(局部区域完整保留)。

import numpy as np

def cutmix(img_a, img_b, label_a, label_b, alpha=1.0):
    lam = np.random.beta(alpha, alpha)
    H, W = img_a.shape[:2]
    # 按 lam 算裁剪框
    rh, rw = int(H * np.sqrt(1 - lam)), int(W * np.sqrt(1 - lam))
    cy, cx = np.random.randint(H), np.random.randint(W)
    y1, y2 = max(cy - rh//2, 0), min(cy + rh//2, H)
    x1, x2 = max(cx - rw//2, 0), min(cx + rw//2, W)
    img_a[y1:y2, x1:x2] = img_b[y1:y2, x1:x2]
    lam_adj = 1 - (y2-y1)*(x2-x1) / (H*W)   # 按实际面积修正
    return img_a, lam_adj * label_a + (1 - lam_adj) * label_b

注意标签变成软标签后,损失函数要用 BCE 或软标签交叉熵,不能用硬标签的 sparse CE。训练技巧:MixUp/CutMix 通常在最后一个 epoch 关掉(fine-tune 干净数据),收敛更稳。

检测与分割:标签必须跟着变

分类增强只动图,检测/分割的增强要动标签——bbox 坐标随裁剪平移变换、mask 同步形变。这是 Albumentations 的主场:

det_tf = A.Compose([
    A.RandomResizedCrop(size=(640, 640), scale=(0.7, 1.0)),
    A.HorizontalFlip(p=0.5),
    A.RandomBrightnessContrast(p=0.4),
], bbox_params=A.BboxParams(format="yolo", label_fields=["class_labels"],
                            min_visibility=0.3))   # 裁剪后可见度<30%的框丢弃

out = det_tf(image=img, bboxes=bboxes, class_labels=labels)

min_visibility 是关键细节:裁剪把目标切得只剩一个角时,这个框该丢——否则模型被迫学「一个角 → 整个物体」的伪标签。YOLO 系列的训练管线(Mosaic 四图拼接)也是样本级增强的思想,YOLO 篇有展开。

增强强度的调优:不是越强越好

增强是正则,同样遵循 偏差-方差权衡:

  • 数据少 → 强增强(2000 张:MixUp + RandAugment 全套)
  • 数据多 → 弱增强(100 万张:翻转 + 轻裁剪即可,过强反而拖慢收敛)
  • 分布对齐原则:增强生成的样本别跑出真实部署分布——线上图都是白天拍的,狂加黑夜增强是浪费容量
  • 自动搜索:RandAugment(随机抽 N 种操作统一强度)和 TrivialAugment(每次随机一种随机强度)已经替代手工配方成为默认起点,AutoAugment 的强化学习搜索成本太高不值得复现

我的默认配方(中等数据量分类任务):RandomResizedCrop + Flip + ColorJitter + RandAugment(n=2) + 最后几 epoch 关强增强。从小往强调,过拟合消失就停。

踩坑排查

现象原因解法
加增强后训练准确率暴跌强度太大/语义被破(如文字翻转)逐种加,每加一种看曲线
检测训练 loss 异常裁剪后 bbox 出界/丢空min_visibility 过滤 + 空标签样本处理
MixUp 后不收敛用了硬标签损失换软标签 BCE/KL
验证集也被增强了transform 写错位置训练/验证两套 transform,验证只做 Resize+Normalize
增强后比不增强还差增强分布偏离真实分布对齐部署场景,砍掉无关变换

练习

  1. 在 CIFAR-10 子集(2000 张)上对比:无增强 / 基础增强 / 基础+CutMix 三条训练曲线。
  2. 用 Albumentations 实现一个检测增强管道,可视化 8 个增强后的样本和框,检查 min_visibility 的作用。
  3. 实验「增强强度」:RandAugment 的 n 从 1 到 4 各训一次,画「n vs 验证准确率」曲线。
  4. 故意把验证集也加上随机增强,观察验证指标的噪声——理解为什么验证集必须确定性。

面试常问

Q:MixUp 为什么能提升泛化? 三个解释:① 决策边界平滑——两张图之间的线性插值迫使模型在样本间隙也表现良好(vicinal risk 最小化);② 软标签自带 标签平滑效果,抑制过自信;③ 破坏了「死记硬背单一样本」的可能。代价:训练收敛变慢,校准性变差(概率不再锐利),所以最后几 epoch 常关掉。

Q:检测任务增强的特殊考虑? 标签几何同步(bbox/mask 随图像变换)、目标完整性(min_visibility 丢弃残缺框)、小目标风险(RandomResizedCrop 可能把小目标裁没——小目标多的场景慎用)、Mosaic 等多图拼接类增强的批量统计量偏移(BN 在拼接图上的分布和单图不同)。

Q:RandAugment 相对手工配方的优势? 把「选哪几种操作、各多强」的超参搜索压缩成两个数:N(每图随机 N 种)+ M(统一强度)。论文证明这个简化空间就够摸到手工精调的水平,且跨数据集迁移性好。TrivialAugment 更进一步——连 M 都随机,零超参。工程结论:别再手调增强配方,从 TrivialAugment 起步。

Q:测试时增强(TTA)值得用吗? 推理时对同一图做多增强版本(翻转/多尺度),预测取平均——白捡 0.5~1 个点。代价:推理时间 ×N。竞赛里标配,生产里看延迟预算。注意 TTA 要和训练增强分布一致,别引入训练没见过的变换。

Q:生成模型(Diffusion)合成数据算增强吗? 算「数据合成」的新范式:用扩散模型生成稀有类别的样本补充训练。2024 后实证有效(尤其长尾类),但注意:合成数据是模型分布的近似,喂太多会让模型「自食其尾」(model collapse 风险);合成:真实 比例控制在 1:1 以内,且合成图别进验证集。

相关阅读

数据增强的哲学:把你对问题的先验知识(什么变换不改语义)免费地注入模型。它是少数「理解业务就能提升指标」的技术,和特征工程一样,是领域知识的变现渠道。

💬 评论

这套方案在哪一步卡住了?也欢迎补充你的实现方式或踩坑经验。

正在连接评论服务,首次访问可能需要几秒…