本文翻译自 Albumentations 官方文档英文版:What Is Image Augmentation?。
很多团队一看到效果不行,第一反应就是换骨干网络(backbone)、堆更大的模型、继续补标数据,或者再调一轮学习率。但问题往往不在模型结构,而在训练分布太窄。模型不是不会识别目标,而是只会识别某一种拍法、某一种光照、某一种设备条件下的目标。
一个在棚拍商品图上训练得很漂亮的模型,用户一上传手机随手拍,就开始大面积翻车。一个在实验室里做到 95% 准确率的医学分类器,部署到另一家使用不同扫描设备的医院后,准确率可能直接掉到 70%。一个在加州夏季道路数据上训练的自动驾驶感知系统,换到欧洲冬季环境里就明显失灵。一个在白天野外视频上表现近乎完美的野生动物监测模型,一旦相机陷阱在黄昏切到红外模式,结果马上不对了。
这些都不是少见的边角案例。只要模型记住了训练数据那一小块狭窄分布,而没有学会底层视觉任务,这就是默认结果。训练集往往只截取了现实世界里的一个切片: 特定光照、特定相机、特定天气、特定取景方式。模型最后学会利用的是这些偶然细节,而不是任务真正关心的语义。
最根本的办法,始终是收集模型真实部署分布里的数据。没有任何东西能替代有代表性的训练数据。但数据采集成本高、速度慢,而且永远不完整: 你不可能提前预见所有部署条件。图像增强就是一个互补工具,用来缩小这道缺口。它通过对已有图像施加不会改变语义的变换,系统性扩展训练分布。模型会看到同一只鹦鹉在几十种光照、方向和画质条件下的样子,于是它会学到: “鹦鹉”主要取决于形状、纹理和姿态,而不是拍下这张图时相机碰巧采用的曝光参数。
你可以把增强理解成一种分布工程: 它不是把图片“搞花”,而是在标签不变的前提下,把模型训练时能见到的世界变宽。很多时候,增强策略调对了,收益比继续盲目换模型更直接,也更便宜。
这篇文章不是那种“列几个常见增强操作,再贴一段代码”的浅层入门,而是沿着一条从原理到生产实践的主线,把这件事讲透:
- 理解什么是增强,以及它为什么有效。
- 设计一个可以直接开训的起步策略。
- 避开那些会悄悄拉低性能的失败模式。
- 用一套可复现的流程做评估和迭代。
- 再往下深挖理论机制和工程约束。
如果你现在只想先记住一句话,那就是:
图像增强不是“把图片搞花”,而是在不破坏标签的前提下,系统性扩大训练分布,让模型少记背景、设备和光照,多学真正能泛化的视觉模式。
先说结论
如果你是奔着实战来的,先记住这 6 条:
- 增强首先是在补训练分布和真实部署分布之间的缺口,不是为了把图做得更花。
- 唯一铁律只有一条: 增强后,人类标注员仍然会给出同一个标签。
- 优先从 分布内增强(in-distribution augmentation) 开始,也就是线上本来就可能出现、只是训练集没采全的变化。
- 分布外增强(out-of-distribution augmentation) 同样重要,它不是为了“模拟真实世界”,而是为了给模型施加更强正则化。
- 增强必须和 无增强基线(baseline) 做对照,而且要按类别、按切片、按失败模式看,不要只盯一个总指标。
- 真正高收益的做法,不是一次性堆满几十个变换,而是小步扩展、持续可视化、单轴消融。
直觉:不改变语义的变换
把一张彩色鹦鹉照片转成灰度图。它还是鹦鹉吗?显然是。真正的语义内容,也就是形状、纹理和姿态,完全还在。颜色本身并不是“它之所以是鹦鹉”的原因。
再把图像做一次水平翻转。它还是鹦鹉。旋转几度,还是鹦鹉。裁得更紧一点,还是鹦鹉。调亮一点,加一点模糊,依然还是鹦鹉。在这些情况下,人类标注员都不会犹豫,都会给出同一个标签。

这就是图像增强最核心的观察: 很多变换会改变图像像素,但不会改变图像“表达的是什么”。技术上,这叫做标签对这些变换是不变的。
这些变换大致可以分成两类:
- 像素级变换只改像素值,不移动几何结构: 亮度、对比度、颜色偏移、模糊、噪声、灰度化。
- 空间变换会改变几何关系: 翻转、旋转、裁剪、缩放、透视形变。
这两类变换在正确使用时都能保持标签不变,而且因为它们作用在相互独立的维度上,所以通常可以自由组合。
为什么增强有效:两个层次
增强大致在两个层次上发挥作用。把这两层分清,是设计有效增强策略的关键,也能解释为什么“只用真实增强”这个建议并不完整。
层次 1:分布内增强(in-distribution),补足那些本来就可能采到但没采到的变化
你可以这样理解分布内增强: 如果你在同样的数据采集条件下无限继续采样,最终会自然出现哪些变化?
比如你在做猫的分类器,数据集里的大多数猫都朝右。但猫当然也会朝左、抬头、以不同角度坐着。只是你现在的数据还没覆盖够。一次水平翻转或者一次小角度旋转,就能生成那些本来数据采集过程迟早会采到、只是这次碰巧没采到的样本。
再比如皮肤科医生用皮肤镜拍病灶图像。设备贴在皮肤上,但在真实操作里,总会有一点点倾斜、轻微旋转、病灶不完全居中。这些变化本来就属于采集过程的一部分,只是没在有限样本里全部出现。小范围仿射变换和裁剪,正好可以把这些空白补上。
每个镜头都会引入轻微的桶形或枕形畸变,现实里的直线在图像里会微微弯曲。不同镜头的畸变特征不同。如果你的训练数据来自某台相机,而线上环境用的是另一台,那么几何畸变分布就会变。OpticalDistortion 模拟的正是这件事: 它会像另一颗镜头那样扭曲图像,生成具有真实光学依据的变化。
自动驾驶数据集如果主要采于夏季,往往就会过多覆盖晴天。但同样的摄像头、同样的道路,在冬天会遇到雨、雾和完全不同的照明条件。亮度、对比度以及天气模拟类增强,可以生成这些仍然来自同一数据生成过程的样本。
分布内增强是最稳妥的一层。你做的事,本质上是把训练分布变得更密,把真实样本之间的空隙用合理变体填起来。这一层真正的风险通常不是太激进,而是太保守。
当训练条件与真实部署条件存在偏移时,这一点尤其有价值。而这种偏移并不是例外,它才是常态。一个在某家医院扫描设备上训练的医学模型,会被部署到另一家使用不同硬件、不同校准方式、不同技师习惯的医院。一个在棚拍商品图上训练的零售分类器,线上接到的是任意光照下的手机图片。一个基于某种卫星传感器训练的模型,最终要在另一套传感器上工作。

分布内增强就是用来跨这道缝的: 亮度和颜色类变换覆盖不同曝光与白平衡,模糊和噪声类变换覆盖不同镜头和传感器质量,几何类变换覆盖不同取景和视角习惯。实践中,增强之所以常常有效,最常见的原因不是训练数据“太差”,而是线上环境天生就比采集环境更不可控。
层次 2:分布外增强(out-of-distribution),用不真实的变换做正则化
现在换一类变换: 无论你把数据采集时间拉多长,它都不会自然出现。把彩色照片转成灰度图,真实彩色相机不会拍出这种结果。施加很强的剪切形变,没有哪颗镜头会自然产生这个效果。随机抹掉几个矩形区域,也不是任何真实物理过程会生成的图像。把红色鹦鹉增强成紫色的极端颜色扰动,也不是现实光照能做到的。
从定义上说,这些就是分布外变换。但图像的语义仍然可能十分清晰。灰度鹦鹉仍然明显是鹦鹉。缺了一块矩形区域的鹦鹉还是鹦鹉。紫色鹦鹉看起来很怪,但它的形状、姿态和纹理仍然足够明确地表达“鹦鹉”。
这类变换的目的不是去模拟真实部署环境,而是强迫网络学习更加鲁棒、更加冗余的特征:
- 灰度化会逼模型只靠形状和纹理识别物体,而不是偷懒依赖颜色。如果你训练一个鸟类分类器,模型学到了“红色就等于鹦鹉”,那它遇到绿色的幼年鹦鹉时就会失效。偶尔把图像转成灰度,就会迫使模型使用结构性特征。病理学里也是一样,用 H&E 染色的组织切片在不同实验室之间染色强度会变,模型不应该依赖精确颜色。
CoarseDropout会迫使模型从目标的多个区域学习,而不是只抓一个最显眼的局部。没有它时,大象检测器可能几乎只靠象鼻这个最显著特征。训练时把象鼻挡住,网络就不得不去学耳朵、腿、体型和皮肤纹理。推理时模型看到的是完整图像,这反而比训练阶段更容易。这种“训练更难、测试更容易”的机制能成立,恰恰因为这些增强图像并不真实。- 弹性形变模拟的不是相机会产生的几何变化,而是某些特定领域真正关心的形变。在医学影像里,显微镜下的组织样本会因为制片和对焦方式不同而发生轻微位移和变形。它不极端,但足够真实,足以让弹性形变学到模型需要的那类几何稳定性。手写字符识别也一样,因为没有两笔手写笔画的几何形状完全相同。
- 强颜色扰动会逼模型对不同光照、不同传感器、不同后处理流程产生的颜色统计差异保持不敏感。野外相机陷阱模型要在清晨、黄昏和树荫下工作。零售模型要在仓库荧光灯和自然日光下工作。超过“真实范围”的颜色增强,常常能教会模型: 物体身份通常不取决于某个精确颜色值。
这是更进阶的玩法。唯一不变的约束仍然是: 经过变换后,标签必须依旧明确。分布外增强用得对时,带来的泛化提升往往会超过单纯的分布内增强;用过头时,如果标签开始含糊,或者模型被迫去学习无关紧要的不变性,反而会伤害性能。
现实里,一条实用的增强策略通常会同时包含这两个层次。分布内变换负责覆盖真实变化,缩小训练到部署的差距;分布外变换通常以更低概率出现,作为额外正则化,逼模型学到冗余特征。无论数据集大小如何,真正有效的训练管线几乎都会同时使用两者。小数据集当然更受益,但即便在数百万图像上训练的大模型,也仍然依赖增强来获得更好的鲁棒性和正则化效果。
唯一规则:标签保持不变
任何增强,不管是哪一种,都必须满足同一个约束:
一个人类标注员在看完增强后的图像后,是否还会给出同一个标签?
如果答案是会,这个变换才有资格进入候选列表。答案是否定的,就要么删掉它,要么把强度收回来,直到答案重新变成“会”。
- 对分类任务来说,意思就是类别身份必须在变换后依然成立。
- 对检测、分割和关键点任务来说,意思就是空间标签必须与图像同步变换。
一旦标签保持被破坏,增强就不再是增强,而是标签噪声。模型收到的是互相冲突的监督信号,性能会下降,而且往往还是静悄悄地下降,因为聚合指标很容易把某些类别上的损伤掩盖掉。
这是绝对规则。本文后面关于该选什么变换、强度应该多大、什么时候可以使用不真实的失真,全部都从这一条推出来。
搭建第一条策略:起步增强管线
你不会去枚举每一种可能的图像变体。真正的做法,是构建一条增强管线: 一组有顺序的变换,每个变换都有自己的触发概率,训练时在线应用。每当数据加载器取出一张图,管线都会即时生成一个新的随机版本。
import albumentations as A
train_transform = A.Compose([
A.RandomResizedCrop(size=(512, 512), scale=(0.8, 1.0), p=1.0),
A.HorizontalFlip(p=0.5),
A.Rotate(limit=10, p=0.3),
A.RandomBrightnessContrast(brightness_limit=0.2, contrast_limit=0.2, p=0.4),
A.GaussianBlur(blur_limit=(3, 5), p=0.1),
A.CoarseDropout(
num_holes_range=(1, 6),
hole_height_range=(0.05, 0.15),
hole_width_range=(0.05, 0.15),
p=0.2,
),
])
这些增强通常在 CPU 侧执行,而 GPU 同时在做前向和反向传播。增强库在速度上已经做了大量优化,相关基准可以看 Image Benchmarks。只要策略设计合理,它通常不会轻易成为 GPU 训练瓶颈。
为什么这里选这些变换:
RandomResizedCrop提供尺度和取景变化,同时保留足够语义内容。HorizontalFlip在大多数自然图像任务里都很安全,能够利用左右对称性。- 小角度
Rotate能覆盖轻微相机倾斜和标注取景变化。 RandomBrightnessContrast覆盖基础曝光变化。- 轻量
GaussianBlur提升对失焦和运动模糊的容忍度。 - 适度
CoarseDropout强迫模型使用多个局部区域,而不是只依赖一个最显眼的小块。

这条策略是刻意保守的。最稳妥的做法,永远是增量式构建: 先从简单版本开始,测一轮;再加一个变换或者一个变换家族,再测一轮;能带来收益的留下,没收益的删掉。比起一开始就把一堆增强全塞进去,再回头排查为什么性能下降,这种方式有效得多。如果你想要一套结构化、一步一步的增强策略构建方法,可以看 Choosing Augmentations。
就算是这样一条简单策略,也已经能生成巨大的多样性。每一个独立的变换方向,都会把有效数据量继续放大:
- 对所有图像都做水平翻转,等价于数据量变成 2 倍
- 以 1 度为步长,从 −15° 转到 +15°,等价于 31 种角度
- 使用 5 种不同的灰度转换方式,等价于再乘 5 倍
这已经是 2 x 31 x 5 = 310 倍的扩展了,而我们还没碰亮度、对比度、缩放、裁剪位置、模糊强度、噪声水平、遮挡程度。Albumentations 提供了几十种像素级变换和几十种空间变换,每种又都有自己的连续或离散参数空间。实际训练时,单张图像的可增强版本空间大到几乎不可能重复,因此哪怕训练几百个 epoch,网络也几乎不会两次看到完全一样的变体。

防止悄悄污染标签:标注同步
一旦任务不只是分类,增强处理的就不再只有图像。目标检测里的边界框必须跟着图像一起移动。语义分割里的掩码必须以完全相同的方式形变。姿态估计里的关键点也必须跟着几何一起变化。
| 任务 | 输入组成 | Albumentations 中的目标类型 |
|---|---|---|
| 分类 | 图像 | image |
| 目标检测 | 图像 + 边界框 | image, bboxes |
| 语义分割 | 图像 + 掩码 | image, mask |
| 关键点检测 / 姿态估计 | 图像 + 关键点 | image, keypoints |
| 实例分割 | 图像 + 掩码 + 边界框 | image, mask, bboxes |
像亮度、对比度、模糊、噪声这类像素级变换不会碰几何,因此标注可以保持原样。翻转、旋转、裁剪、仿射、透视这类空间变换会移动几何,所以所有空间目标都必须和图像严格同步。这里正是手写增强管线最容易出错的地方: 图像转了,框没转;图像裁了,掩码没对齐。训练信号被悄悄污染,但整个系统不会报错。

Albumentations 的多目标调用会自动处理这件事:
result = transform(image=img, mask=mask, bboxes=bboxes, keypoints=keypoints)
注意: 不是每个变换都支持每一种标注目标类型。定稿之前,务必先检查 Supported Targets by Transform。
扩展增强策略:按变换家族逐步加
到这一步,你已经有了一条可工作的基线,标注同步也已经处理正确。接下来要做的,不是“多加点增强试试”,而是一次扩展一个变换家族。每个家族都有自己明确的收益,也有典型的失效方式。这一节给你一张地图;如果你需要完整、逐步的选择流程,可以直接看 Choosing Augmentations。
几何变换
例子: HorizontalFlip, Rotate, Affine, Perspective, OpticalDistortion, SquareSymmetry。
这类变换适合提升视角容忍度、取景变化鲁棒性,以及尺度/位置不变性。HorizontalFlip 在大多数自然图像任务中都安全。对于方向本身不带语义的领域,比如航拍/遥感、显微图像、某些医学扫描,SquareSymmetry 很有价值: 它会在正方形的 8 个精确对称操作里选一个,包括恒等、翻转和 90/180/270 度旋转。这些都是精确操作,不会像任意角度旋转那样引入插值伪影。
失效方式: 变换破坏了场景语义。比如自动驾驶场景做垂直翻转就是胡来;数字识别或文本识别里大角度旋转也常常会破坏标签。永远先问自己: 你引入的几何变化,对这个任务来说到底是不是标签保持的。
光度变换
例子: RandomBrightnessContrast, ColorJitter, PlanckianJitter, PhotoMetricDistort。
这类变换适合处理相机和照明变化、不同设备间的色彩偏差,以及曝光漂移。
失效方式: 生成部署中根本不会出现的颜色分布。比如对灰度医学图像做重度色相扰动就完全不符合物理现实;对高度依赖品牌颜色的零售分类做激进颜色扰动,也可能直接把模型搞迷糊。
模糊与噪声
例子: GaussianBlur, MedianBlur, MotionBlur, GaussNoise。
这类变换适合提升模型对低质量镜头、运动伪影、压缩损失和传感器噪声的鲁棒性。
失效方式: 模糊或噪声太重,把真正定义类别的细节直接抹掉。如果你的任务信号本身就是细小缺陷,比如工业质检或某些医学病灶,强模糊可能会直接擦除目标。
遮挡与 dropout 类增强
例子: CoarseDropout, RandomErasing, GridDropout, ConstrainedCoarseDropout。
dropout 类增强是收益最高的一类增强之一。它会逼网络从目标的多个部位学习,而不是依赖单个主导局部。同时,它还能模拟真实世界里常见、但训练数据往往覆盖不足的局部遮挡。ConstrainedCoarseDropout 更进一步,它不是随机地在整张图上打洞,而是专门在已标注的目标区域内做 dropout,也就是直接对目标本身做更有针对性的遮挡模拟。
失效方式: 洞太大、频率太高,把模型真正需要的核心信号给打没了。想更系统地看这类遮挡策略,可以看 Choosing Augmentations。
颜色削减
例子: ToGray, ChannelDropout。
如果颜色对你的任务不是稳定且可靠的判别特征,这类变换就会迫使模型更多依赖形状、纹理和上下文。ToGray 会移除全部颜色信息,而 ChannelDropout 会随机丢掉部分通道,部分削弱颜色信号。它们都适合作为低概率增强项加入,比如 5% 到 15%,用来降低模型对颜色线索的依赖,尤其是在跨光照条件或跨硬件迁移时。
失效方式: 如果颜色本身就是任务关键,比如成熟/未成熟水果、红绿灯状态,那么这类变换就是在直接污染标签。细节可以看 Choosing Augmentations: Reduce Reliance on Color。
环境模拟
例子: RandomRain, RandomFog, RandomSunFlare, RandomShadow。
这类变换适合户外视觉系统,尤其是天气本身就是线上因素的时候。
失效方式: 合成出来的效果看起来根本不像真实相机拍到的画面。比如一个过于廉价的“雨滴贴图”式增强,线上相机根本不会产生这种视觉模式,那它造成的伤害可能比帮助还大。
高级组合方法
MixUp、CutMix、Mosaic、Copy-Paste 都可能很强,但它们往往不只是单图变换,而是需要和训练循环、标签混合逻辑一起配合。更稳妥的做法是: 先把单图增强基线跑稳,再考虑这些更强的组合方法,尤其当你已经确认需要额外鲁棒性,或者要补少数样本场景时。
系统调参:概率和幅度
每个变换都至少有两个旋钮:
- 概率 (
p): 这个变换在每个样本上被应用的概率。 - 幅度: 变换一旦触发,效果有多强,比如旋转角度、亮度范围、模糊核大小。
大多数增强错误,不是选错了变换,而是把幅度设错了。概率只决定这个变换会不会触发,不决定它触发后会改多大。真正决定图像离原图有多远的是幅度。
幅度怎么设:先从部署现实出发,再往外推
对层次 1 的分布内变换,幅度最好锚定到你真实测到的部署波动:
- 如果线上相机倾斜通常在 ±7 度之内,那旋转范围就从这个量级起步。
- 如果曝光变化只是中等水平,那亮度/对比度边界就先保守一点。
- 如果模糊主要来自轻微运动,那就先用小核。
对层次 2 的分布外变换,幅度本来就该超出现实部署范围,因为它的目标不是模拟,而是正则化。这里真正的约束不是“看起来真实”,而是标签是否仍然清晰。把幅度一路推到标签开始含糊的边界,然后再往回收一点。
为什么叠加效应很重要
变换之间的交互是非线性的。中等颜色偏移单独看没问题,但叠在高对比度和强模糊后,可能就会变成问题。多个激进变换一起叠加时,即便每个单拿出来看都还算合理,组合结果也可能已经远离任何真实相机输出。这也是为什么一定要做单因素消融: 你得把单个变换的贡献和交互影响拆开看。
实用默认值
- 对大多数非核心增强,先把
p设在0.1到0.5之间。 - 如果某些变化是不可避免的,比如
resize/crop,可以保留一两个常开变换。 - 每次只动一个维度: 调概率或者调幅度,不要两者一起改。
- 把增强调参当成受控消融实验,不要把它当成“凭感觉乱试”。
增强强度要匹配模型容量
合适的增强强度,取决于模型容量。小模型容量有限,面对重增强很容易被直接压垮,因为它根本没有能力在强失真条件下还把任务学出来。大模型则相反: 它太容易记住训练集,轻微增强对它的过拟合几乎没什么约束力。
所以一个很实用的策略是:
- 先选你算力预算里能承受的最高容量模型。
- 它在原始数据上大概率会严重过拟合。
- 再逐步加大增强强度,直到过拟合被压住。
对高容量模型来说,单靠分布内增强往往不够。这时层次 2,也就是分布外增强,就不再是“可选项”,而是必要手段。重颜色失真、激进的 dropout 类增强、强几何变换,这些都不真实,但只要标签仍然明确,它们就会成为主力正则化工具。模型有足够能力处理更难任务,而增强的作用就是阻止它走捷径。
所以“只用真实增强”这个建议是不完整的。它对小模型和受限场景可能成立,但对现代大模型来说,很多时候真正区分“只会记忆”和“能泛化”的,恰恰就是那些不真实但标签保持的增强。
还要考虑和其他正则项的交互
增强不是独立开关,而是整体正则预算的一部分。它的效果会受模型容量、标签噪声、优化器、学习率计划,以及其他正则项,比如权重衰减(weight decay)、dropout、标签平滑(label smoothing)、随机深度(stochastic depth)等因素的共同影响。
实践里常见的交互规律:
- 增强显著变强后,往往需要更长训练,或者调整学习率计划。
- 强增强叠加强 label smoothing,可能直接导致欠拟合。
- 在标签本身噪声就很重的数据上,重增强可能放大优化难度,而不是帮你。
- 增大模型容量和增大增强强度通常需要一起调,它们是耦合旋钮,不是互相独立的两个参数。
真正上线前,先把这些失败模式看明白
过度增强是真实存在的。最常见的三种死法:
- 标签污染: 几何变换破坏标签语义,比如翻转文本、旋转单向场景;裁剪直接裁掉目标;颜色变换破坏了任务关键颜色信息,比如成熟/未成熟水果、红绿灯状态。
- 容量浪费: 模型把容量浪费在处理那些对真实任务毫无泛化收益的变化上,也就是学习了一堆和任务无关的不变性。
- 只堆强度,不做测量: 一口气叠很多激进变换,却不验证每个变换单独是否有帮助。由于变换间是非线性交互,组合后很容易把样本推过标签保持边界,即便每个变换单独看似乎都还行。
过度增强的常见症状:
- 训练 loss 长时间停在异常高的位置
- 验证指标上下乱跳,没有清晰趋势
- 即使总精度看起来稳定,校准却变差
- 某些类别明显回退,但聚合指标把问题掩盖了

关键判断标准: 真正的问题不是“这张图看起来真不真实”,而是“标签是不是仍然显而易见地正确”。不真实但标签清晰的图,往往是很强的正则项;看起来很真实、但标签被搞坏的图,则是毒药。
按任务定制,再对弱点做定向增强
不同任务的敏感点不同,不同失败模式也需要不同增强策略。同一套对分类有效的策略,如果不加控制地拿去做检测或分割,完全可能直接污染标签。这一节分成两个层次: 第一层是任务类型差异,也就是分类、检测、分割到底哪里不一样;第二层是更细粒度的定向增强,也就是如何针对具体类别、难例和领域做精确打击。前提都是: 你的通用基线已经跑稳。
分类
分类任务的核心风险是语义被破坏。对很多物体类别来说,适度几何和颜色变换都很安全。但对方向敏感的类别,比如数字、箭头、文字方向,翻转和大角度旋转可能会直接让标签失效。
目标检测
检测任务对裁剪和尺度策略高度敏感:
- 激进裁剪会把小目标直接裁没,悄悄丢掉训练样本。
- 图像边缘附近的框,在空间变换后需要被谨慎处理。
- 裁剪或旋转后的框过滤规则,可能在不知不觉中删掉你最需要的难例。
- 尺度策略对小目标召回的影响,往往比整体 mAP 更大。
- 长宽比失真可能会干扰 anchor 或者样本分配行为,具体取决于架构。
不要只看总 mAP,至少要把按尺寸分桶的小/中/大目标指标拆开看。
语义分割
掩码的完整性是底线:
- 掩码必须使用最近邻插值,避免引入非法类别值。
- 细边界目标,比如电线、血管、裂缝,对插值和激进
resize非常脆弱。 - 小连通区域会在激进裁剪下直接消失。
对边界敏感任务,不要只看全局 IoU,最好也看 boundary F1 或轮廓类指标。类别分布不平衡时,per-class IoU 往往比 mean IoU 更重要。
关键点和姿态估计
关键点管线会以一些很隐蔽的方式出错:
- 裁剪或旋转后,可见性处理可能会意外丢点。
- 过强透视变换可能生成解剖结构上根本不可能的骨架几何。
这里最常见、也最容易被忽略的问题,是翻转之后标签语义变了。比如你水平翻转一张人脸图像,原来的左眼像素位置会移动到右侧。坐标当然更新对了,但标签本身错了。数组里的索引 36 还写着“左眼”,但翻转后它在解剖学意义上其实已经是图中人物的右眼。只要你的关键点标签是通过数组索引承载语义的,比如人脸 landmark、人体姿态、手部关键点,这个问题就会悄悄污染训练。
Albumentations 用 label_mapping 解决这个问题。你可以告诉管线: 在某些特定变换下,关键点标签应该如何重映射和重排。
import albumentations as A
FACE_68_HFLIP_MAPPING = {
# Eyes: left (36-41) ↔ right (42-47)
36: 45, 37: 44, 38: 43, 39: 42, 40: 47, 41: 46,
45: 36, 44: 37, 43: 38, 42: 39, 47: 40, 46: 41,
# Mouth: left ↔ right
48: 54, 49: 53, 50: 52, 51: 51,
54: 48, 53: 49, 52: 50,
# ... (full 68-point mapping omitted for brevity)
}
transform = A.Compose([
A.Resize(256, 256),
A.HorizontalFlip(p=0.5),
A.Affine(scale=(0.8, 1.2), rotate=(-20, 20), p=0.7),
], keypoint_params=A.KeypointParams(
format='xy',
label_fields=['keypoint_labels'],
label_mapping={'HorizontalFlip': {'keypoint_labels': FACE_68_HFLIP_MAPPING}},
))
做完翻转后,管线不仅会更新坐标,还会交换标签、重排关键点数组,从而保证索引 36 仍然表示“左眼”,并继续和翻转后的图像解剖结构一致。
如果你想看包含训练流程的完整例子,可以参考 Face Landmark Detection with Keypoint Label Swapping。
真正开训前,务必检查翻转前后关键点数量是否一致、翻转后的标签是否被正确交换,并先把增强样本可视化一遍。
医学影像
这个领域对“领域有效性”要求非常严格。很多模态本来就是灰度图,对它们做激进颜色变换没有任何物理意义。空间变换也必须符合解剖合理性和成像几何。最好的起点永远是: 先从你已知存在的扫描仪和采集波动出发,再把这些波动显式编码成增强。
OCR 与文档视觉
旋转、透视、模糊、压缩通常都很有用。垂直翻转几乎总是无效。色相变化是否有意义,取决于扫描仪/相机流水线,有时无关紧要,有时反而有害。
遥感与航拍
旋转不变性经常有价值,但并不总是意味着完整的 360 度不变性。如果 north-up 约定或者采集几何对标签语义有影响,无约束旋转就会污染标签。
工业质检
细小缺陷在模糊或下采样下会直接消失。如果部署图像质量没有同等退化,就不要轻易破坏微观结构。增强应该匹配真实传感器和光照变化,而不是机械地套通用图像变换。
迁移学习与微调
微调预训练模型时,增强策略也得跟着变。模型已经在预训练阶段学到了一套很强的边缘、纹理、形状表征,它并不需要像从零训练那样重新学这些底层模式。因此,对从零训练合适的重增强,在微调阶段可能反而过头,尤其是在小目标数据集上。模型会把容量浪费在重新适应那些本来就没必要重新学习的失真上。
从比从零训练(training from scratch)更轻的增强开始通常更合理: 保守裁剪、轻微亮度和颜色变化、在合适任务上加水平翻转。随着微调 epoch 增多,或者你解冻更多层,再逐步加大增强强度。如果你只是在冻结骨干网络的情况下微调分类头,增强的重要性会下降,因为特征提取器本身不动了。这时更应该关注“哪些增强能补部署分布差距”,而不是“哪些增强能带来更强正则化”。
它和学习率也有交互。微调通常用更低学习率。如果你一边上重增强,一边用很低学习率,模型每步看到的都是重失真样本,却只能做极小参数更新,结果往往就是收敛变慢、算力白烧。
精确打击:针对具体弱点做增强
当你已经有一条按任务区分的稳定基线后,下一步就该开始做“定点打击”。和权重衰减、dropout、标签平滑这种对所有样本、所有类别、所有错误模式都一视同仁的正则项不同,增强是一种有结构的正则化,你可以把它瞄准模型真正薄弱的地方。
按类别定制增强。 你可以给不同类别或图像子类型配不同策略。比如野生动物监测系统,对林地物种可能需要很强的颜色扰动,因为林冠下光照变化大;但对沙漠物种,颜色增强可能反而应该很弱,因为光照更稳定。医学影像里,软组织模态可以加弹性形变,但骨骼成像可能必须保持刚性。自动驾驶系统甚至可以只对高速公路场景做天气增强,而对隧道视频保持原样。
通过增强去打难例。 如果模型总在某个子集上翻车,比如小目标、遮挡目标、特殊视角,那就对这些难例单独加更强的增强。你也可以把它理解成: 通过数据管线实现更偏定向的难例挖掘,而不是只靠损失函数去兜底:
- 对那些主要失败模式是遮挡的类别,施加更强的
ConstrainedCoarseDropout。它会直接在已标注目标区域内部打洞,而不是只对背景随机打洞。 - 对那些模型明显过拟合标准姿态的类别,增加更强几何变换。
- 对那些在低质输入上容易翻车、但在高质量输入上表现良好的类别,提高模糊和噪声强度。
这通常比全局一刀切地把增强强度全拉高更有效。后者确实可能救下难例,但同时也可能把原本简单的样本一起拖垮。
按领域拆策略。 在多域数据集里,比如室内 + 室外、白天 + 夜晚、不同传感器混合,单一增强策略几乎总是次优。对户外有帮助的增强,比如天气模拟、强亮度变化,拿去用在稳定照明的室内场景上可能反而伤害性能。按域拆策略,或者根据元数据做条件增强,通常会明显优于“一套策略打天下”。
没有别的正则化手段能给你这种控制力。权重衰减不能按类别调,dropout 不能针对特定失败模式,增强可以。
用可复现的实验协议评估增强
增强不是“设完就忘”的东西。一套纪律化的评估流程,能帮你省掉几周随机试错。
第 1 步:无增强基线
先训练一个完全不做增强的基线。没有这个基线,你后面的所有实验都只是在对比一个不断变化的参照物,根本无法知道净收益是什么。
第 2 步:保守起步策略
上一条中等强度的基线增强策略,比如前面那条,完整训练,然后记录:
- 总指标,比如准确率(accuracy)、mAP、IoU
- 每类指标
- 子群指标,比如昼/夜、相机类型、地理位置、目标尺度
- 如果相关的话,也记录校准指标
第 3 步:单轴消融
每次只改一个因素:
- 提高或降低一个变换的触发概率
- 放大或缩小一个幅度范围
- 新增或移除一个变换家族
第 4 步:合成压力测试
增强不只是训练工具,它也是评估模型鲁棒性的利器。你可以额外构建一些验证管线,在标准 resize + normalize 之上,再叠加你关心的定向变换,然后把结果和干净验证集对比。如果模型在一次简单的水平翻转下准确率就明显下降,说明它根本没有学到你以为它已经学到的不变性;如果它在中等程度降亮度下指标直接崩盘,那你就知道下一步训练策略该补什么了。相关代码可以看 Using Augmentations to Test Model Robustness。
第 5 步:在真实失败切片上评估
合成压力测试只能单独探测某种不变性。真正完整的图景,还得靠真实世界失败分析。把那些困难子集单独抽出来测,比如低照度、模糊、天气、重遮挡、相机/域偏移,再把每种失败模式映射回对应的增强家族:
- 光照失败 -> brightness、gamma、shadow
- 运动/焦点失败 -> motion blur、gaussian blur
- 视角失败 -> rotate、affine、perspective
- 部分可见失败 -> 粗粒度遮挡(coarse dropout)、激进裁剪
- 传感器噪声失败 -> gaussian noise、压缩伪影
如果你的增强策略里某个变换并没有映射到任何真实失败模式上,那它大概率只是在增加算力消耗,而没有创造价值。
第 6 步:在大改架构前先锁策略
不要一边大改模型架构,一边同时重调增强。混杂实验只会浪费时间,最后得出一堆不可靠结论。
诚实地读指标
总指标很擅长掩盖增强策略造成的损伤。尤其要盯这些信号:
- 某些类别明显回退,但被头部类别掩盖
- 置信度校准恶化
- 简单切片上变好,但关键尾部场景变差
- 重增强策略在不同随机种子下波动很大
最终候选策略至少跑两个随机种子。重增强往往会提升结果方差。
进阶:这些经验法则为什么成立
如果你已经把增强管线跑起来了,这一节讲的是上面那些经验法则背后的机制。第一次读可以先跳过,等你需要更形式化地思考策略设计时再回来。
增强到底对优化做了什么
增强本质上是一种按语义结构注入的正则化。它不像权重衰减或 dropout 那样给参数或激活直接加通用噪声,而是给输入空间加带领域结构的噪声:
- 它在输入空间里引入随机性,降低模型死记训练集的压力。
- 它让决策边界在训练样本附近变得更平滑。
- 它鼓励模型对干扰因素(nuisance factors)学到不变性,对空间目标学到等变性。
- 它还能通过减少对狭窄模式的过度自信拟合,改善校准。
不变性 vs 等变性
这两个概念,基本就能把增强到底在教模型什么说清楚:
- 不变性: 变换之后,预测不该变。比如“鹦鹉”在轻微旋转后还是“鹦鹉”。
- 等变性: 变换之后,预测也应该以可预测方式跟着变。比如目标框坐标应该随着图像一起旋转。
很多训练错误,本质上就是把应该等变的目标错当成了不变目标。最典型的例子,就是做了检测图像增强,却没同步变换框。
对称性:用数据编码,还是用架构编码
编码不变性大致有两种路子:
- 增强,数据层面: 给模型看各种变化后的输入,让它自己学会不变性/等变性。
- 架构设计: 直接在网络层里把某种对称性硬编码进去,比如等变网络、几何深度学习。
在架构层面编码对称性当然很强,但适用面其实很窄。它更适合那种干净、数学上定义明确的对称群,比如旋转群、反射群、平移等变性。如果你的数据真的有这种清晰对称结构,比如显微图像里的旋转不变性,或者卷积里天然的平移等变性,那把它写进架构会很优雅,也更省样本。
但现实世界里大多数重要不变性并不是这种“干净对称性”。你想让模型对雨、雾、镜头畸变、JPEG 压缩、传感器噪声、光照变化鲁棒,这些都没有一个简洁的群论表示。不存在什么“weather-equivariant convolution”。想教会模型这些东西,最实用的办法还是增强。
实践里,增强通常是第一选择,因为它接入成本低、与架构无关、既能覆盖数学上的干净对称性,也能覆盖现实世界里那种脏而复杂的变化,而且非常容易做消融。架构先验可以作为补充,把那些干净对称性硬编码进去,从而降低数据管线负担;但它替代不了增强,因为现实里最重要的很多不变性,根本不是代数结构能优雅表达的。
流形视角
还有一个很好用的几何视角,可以解释增强为什么有效,也解释它为什么会失效。
高维图像空间里,大部分区域都是空的。自然图像实际上只占据像素空间中的一个低维流形,也就是一张弯曲的“表面”,这张表面上的点才看起来像真实场景照片。随机像素噪声不在这张流形上,对抗扰动通常也不在。你的训练样本只是这张流形上很稀疏的一些点,而模型必须从这些稀疏点里学会整张流形的结构。
增强做的事,就是在这张流形上生成新点。当一个变换既保持标签,又生成视觉上仍然合理的图像时,增强样本其实还在原图所在的同一张流形上,只是落在另一个邻近区域。它是在做 densification,也就是把稀疏训练样本之间的空隙,用沿流形表面的合理插值填起来。
失效方式也就很清楚了: 如果某个变换把样本推离了流形,推到那些真实相机不可能产生、人类也难以识别的区域,模型就会把容量浪费在处理“不可能输入”上。这也是为什么极端参数即便技术上还没破坏标签,也可能伤害性能。比如一只旋转 175 度、颜色反相、像素化很重的鹦鹉,你也许勉强还能认出来,但它已经离任何部署时会出现的自然图像流形区域太远了。
所以一个很实用的启发式规则就出来了: 增强样本应该留在流形上,或者至少非常靠近流形。分布内增强严格留在流形上;分布外增强可以往边界靠,但不应该真正跨到明显不自然的区域。“一个人类是否还会给同一标签”这个测试,本质上就是“它是否仍然在一个可识别图像流形上”的近似判断。
不只是监督训练:增强在其他场景里也很重要
前面讨论的是最常见的场景,也就是监督训练中的单图增强。但增强的作用远不止这里。在某些设定里,它直接定义了学习信号;在另一些设定里,它会在推理阶段提升预测质量;在基于仿真的训练里,它甚至是跨越现实落差的核心工具。底层原则其实没变,还是标签保持、受控多样性、增强要匹配任务,只是每种场景下的设计约束不同。
自监督和对比学习里的增强
在监督学习里,增强的作用是通过扩展训练分布来提升泛化。在自监督学习里,增强不只是有帮助,它本身就是学习信号的一部分。
SimCLR、MoCo、BYOL、DINO 这一类对比学习方法,会对同一张图生成多个增强视图,然后训练模型识别它们共享相同语义内容。核心损失函数会把同一张图的不同增强视图表征拉近,同时把不同图像的表征拉远。没有增强,整个学习信号就不存在。
这里的设计约束也跟监督学习不同。监督学习里,你希望增强既保持标签,又增加多样性。对比学习里,你希望增强去掉模型不该依赖的低层细节,比如精确裁剪位置、颜色统计、模糊水平,同时保留模型真正该编码的高层语义内容。换句话说,增强策略直接定义了模型会对哪些特征学到不变性。
这带来的直接后果是: 对比预训练的增强策略,通常会比同一数据上的监督微调增强策略激进得多。强颜色失真、强裁剪、重模糊,在对比学习里都很常见。只要语义还在,模型就会学到可以跨这些干扰变化迁移的表征。
这也解释了为什么自监督阶段的增强策略会影响下游任务表现。如果你在预训练时大量使用颜色增强,那么学出来的特征就会更偏颜色不变性。这对一般物体分类可能是好事,但对颜色带语义的任务,比如花卉物种识别、红绿灯状态识别,就可能是坏事。预训练阶段的增强策略,会直接决定哪些不变性被焊死进表征里。
测试时增强(TTA)
增强主要发生在训练阶段,但推理阶段也有一个相关技巧。
测试时增强(TTA) 的做法是: 不是对测试图像只预测一次,而是先做几个增强版本,比如水平翻转、多个裁剪,然后分别预测,最后把结果聚合起来,通常是概率平均或者投票。多个增强视角组成的小集成,常常比任何单一视角都更稳。
TTA 特别适合下面这些情况:
- 模型训练时使用过增强,但测试样本本身模糊、边界、难判。
- 测试分布里存在训练时覆盖不够充分的变化。
- 精度比推理时延更重要,比如医学诊断或者竞赛提交。
最常见的 TTA 变换包括水平翻转,几乎总是有帮助;多尺度推理,也就是多分辨率预测后再平均;多裁剪,也就是从图像不同区域取多个 crop。更激进的旋转或颜色变化在某些领域也可能有效,但如果训练阶段已经让模型学到了很强先验,它们也可能带来反效果。
代价也很直接: TTA 的推理成本会随着增强版本数线性增长。5-fold TTA 就意味着 5 次前向。对延迟敏感的应用,这通常不可接受;但在离线批处理或高风险决策里,它是一个不用重训就能继续榨出一些精度的可靠办法。实现细节可以看 Test Time Augmentation。
域随机化:从模拟走向现实
增强还有一个非常典型的高级应用场景: 机器人和仿真训练。
当你用合成数据训练感知模型,比如游戏引擎或物理模拟器生成的数据,合成图像和真实图像之间通常会有系统性差异,比如纹理、光照、渲染伪影都不一样。只在合成数据上训练的模型,到了真实世界往往会灾难性失败。
域随机化 的思路是: 不去努力把合成数据做得更真实,而是在训练时对合成图做极端随机增强。逻辑和前面讲的“拓宽分布”完全一致: 与其把合成图往真实图上贴,不如让它变得极其多样。把纹理、颜色、光照、相机参数、物体位置全部大幅随机化,甚至远超现实范围。只要训练分布够宽,真实世界图像就会只是其中一种普通变体,自然落进模型已经学会处理的范围里。
这其实就是把层次 2,也就是分布外增强,用到了极端。它之所以能成立,依然是因为标签没变: 一个模拟机械臂不管是镀铬、木纹,还是迷幻彩虹纹理,它还是机械臂。模型学到的是跨所有外观变化都稳定的特征,而真实世界物体外观,只是其中一小种。这个原则其实不只适用于机器人,它对很多增强策略决策都成立: 只要训练分布足够宽,目标域就会被吸进去,而不必显式建模它。
生产环境里的几个现实问题
永远不要增强验证集和测试集
最常见、也最接近线上事故的问题,就是不小心把增强带进了评估数据。训练增强必须和验证/推理预处理严格隔离。验证和测试阶段只应该有确定性变换: resize、pad、normalize,不要有任何随机项。
听起来很显然,但它经常以非常隐蔽的方式发生:
- 训练和验证复用了同一个
transform变量。 - 某个配置开关默认值是
True,评估阶段没显式覆盖。 - 服务端推理管线直接复制了训练预处理,把增强也一起带进去了。
如果你发现验证指标在相同数据和相同模型检查点(checkpoint)下每次跑都抖得很厉害,先检查是不是增强泄露到评估链路了。一个快速诊断办法是: 用同一份数据连续跑两次验证。如果结果不同,那管线里一定还有随机操作。
训练前先把增强可视化检查一遍
增强相关的错误很少会直接报异常。旋转范围设错、掩码插值方式错、边界框没跟着翻转,这些都能生成“合法输出”,只是会静悄悄地把训练污染掉。唯一靠谱的检查方式就是肉眼看。
在正式投入训练之前,先渲染 20 到 50 个增强样本,把所有目标叠上去一起看,重点检查:
- 掩码有没有发生错位,或者和图像的形变方式不一致
- 边界框还包不包得住目标
- 关键点有没有跑到图像外,或者明显跑错位置
- 图像是否已经被扭曲到标签开始含糊
- 旋转或透视后有没有明显边缘伪影,比如黑边、重复像素
这 10 分钟检查,往往能帮你省掉几天的无效训练。你如果只是想先单独看看某个变换长什么样、参数变化会带来什么效果,也可以直接用 Explore Transforms 这个交互工具,在自己图像上先试完,再写管线代码。
吞吐量
增强不是没有代价的。重 CPU 侧变换很可能把数据管线卡住:
- GPU 在等待数据加载 worker 处理图像
- 每个 epoch 时间明显拉长,实验变慢
- 复杂随机变换也会让复现性更难控制
缓解办法是尽早分析数据加载吞吐。看 GPU 利用率,如果明显上不去,瓶颈就在数据管线。像弹性形变、透视扭曲这种昂贵变换,尽量用更低概率。把解码、基础 resize 这类确定性预处理缓存起来,只把随机增强放在后面。再根据硬件调 worker 数量和预取缓冲(prefetch buffer)。如果某个变换明显吃掉了大部分管线时间,就问自己: 有没有更便宜的替代方案,也能学到同样的不变性?
可复现性
- 该设种子就设种子,但也要接受某些底层算子在不同硬件或不同库版本上仍然可能非确定。
- 增强策略要做版本管理,放进配置文件,而不是只散落在训练脚本里。
- 把增强策略和模型产物一起追踪,这样当分布漂移出现时,你才能真正回滚。
模型上线时,训练它用的增强策略应该是产物元数据的一部分,就像架构、超参数和数据集版本一样。
团队里的策略治理
如果一个项目里有多个人在训练模型,没有被跟踪的增强策略改动,就会制造那种几个月后才爆出来的“神秘回归”。有人加了一个变换,没有做消融验证,性能变了,但大家直到下一次大评测才意识到这两个事件有关。
所以增强策略应该被当成受治理的配置项来管理: 做版本、写变更记录、重大改动要求附带消融证据、每个发布模型都绑定对应策略版本。对增强策略的代码评审,严格程度应该接近对模型结构改动的评审,因为它们对性能的影响是同等级的。
什么时候要重审已有策略
一条曾经有效的增强策略,会在下面这些变化出现时变得不再正确:
- 相机栈变了,比如新传感器、新分辨率、新镜头
- 标注规范变了,比如类别定义变了、框更紧了
- 数据源在地理或人群维度上发生变化
- 服务侧预处理改了,比如
resize逻辑或normalize逻辑改了 - 产品约束变了,比如时延要求、目标分辨率要求改变
所以增强策略复查应该成为数据或产品大改时的标准步骤,而不是等指标掉了才去补救。等指标真的掉下来时,退化模型往往已经上线了。
总结
图像增强是计算机视觉里投入产出比最高的工具之一。它本质上不是“图像特效”,而是分布工程 + 正则化设计。一类增强在补真实部署变化,也就是分布内增强;另一类增强在主动制造更难但标签仍然清晰的训练样本,也就是分布外增强。两者共享同一个不可谈判的前提: 变换之后,标签必须依然明确。
如果把全文再压缩成真正能落地的版本,可以记成下面几条:
- 先从那些和真实部署变化匹配、并且保持标签的分布内变换开始。
- 永远和无增强基线做对比,不要凭感觉判断增强是否有效。
- 再逐步加入分布外变换。它们不是“默认危险”,但必须经过验证。
- 让增强强度匹配模型容量: 模型越大,通常越需要、也越能承受更强增强。
- 只保留那些确实改善你真正关心指标的变换,而且要按类别、按切片去测。
- 随着数据、模型和部署条件变化,对增强策略做版本管理和持续复查。
延伸阅读与工具
- 安装 Albumentations: 先把库装起来。
- 核心概念: 了解变换、管线、概率和目标类型。
- 如何选择增强: 一套更实战的策略选择框架。
- 基础用法示例: 分类、检测、分割、关键点的实际写法。
- Supported Targets by Transform: 查兼容性。
- Explore Transforms Visually: 可视化交互式试变换。
如果你准备把这篇文章里的内容今天就落到项目里,最值得先做的不是继续换骨干网络,而是先补齐这三件事:
- 跑一个无增强基线。
- 加一条保守起步策略,只改一个因素做消融。
- 把增强后的样本可视化,确认标签没有被你自己悄悄搞坏。
很多时候,这三步带来的收益,会比盲目叠更多“高级技巧”更直接。
如果你愿意再往前走一步,那第四件最值得做的事是: 把线上失败样本专门收集出来,按“低照度、模糊、遮挡、视角偏、设备偏移”这些维度做切片,然后反过来驱动增强策略。增强最怕的不是不够复杂,而是和真实失败模式没有对应关系。
一句话收尾:
不要把增强当成训练脚本末尾顺手加的调味料。它更像是你对真实世界分布做的一次显式建模。