广告位 · 页头(后台-模块管理-新建模块后替换此区域)

数据增强为什么反而加重过拟合?随机策略与对抗性增强如何落地

数据增强一直被视作降低过拟合的默认手段,但如果增强策略固定不变或者扰动强度设置过高,模型同样会进入另一种过拟合。它不再单纯记住原始图片的像素,而是开始记忆裁剪、翻转、颜色偏移等增强操作留下的痕迹。当验证集没有完全匹配这些痕迹时,损失曲线会出现训练集一直下降、验证集先降后升的典型分离。真正需要解决的问题不是要不要增强,而是怎样让增强本身也具有随机性和对抗性。下面从成因、随机策略、对抗性增强和组合落地四个部分展开。

数据增强为什么反而加重过拟合?随机策略与对抗性增强如何落地

一、增强过拟合是怎么发生的

过拟合并不只是原始数据记得太死,增强操作本身也可能变成一种新的记忆对象。例如固定使用随机裁剪加水平翻转,模型在训练后期会把图片边缘出现的大量填充像素和特定翻转方向当成类别特征。一旦验证集图片的裁剪位置稍有不同,或者翻转概率发生变化,模型输出就会产生明显波动。这种情况在小数据集上尤其突出,因为同一批增强样本会被反复采样,模型有足够多的迭代次数去拟合这些人工纹理。

另一个容易被忽略的原因是增强强度过大。颜色抖动、旋转角度、遮挡比例一旦超出合理范围,图片的语义信息就会受到破坏。比如把一张猫的照片旋转到接近倒置,再把饱和度调到极低,模型可能不再关注猫的轮廓,而是转向增强后的噪声分布。增强越强,训练分布与真实分布之间的偏移越大,模型在验证集上表现越差,本质上是用一种人为分布替代了原始分布。

要识别增强过拟合,可以观察增强样本是否偏离了人类可识别的正常范围,同时对比训练集准确率和验证集准确率之间的差距。如果模型在增强样本上准确率很高,但在未增强的干净样本上反而下降,就需要调整增强策略,而不是继续加大训练轮数或增加增强种类。

二、随机策略:把增强组合交给概率

随机策略的核心思路是让增强操作在每次迭代中都重新采样,避免同一种增强组合被模型反复看到。传统的固定增强是一个确定流程,比如先裁剪再翻转最后归一化。随机策略会把若干候选操作放进一个操作池,每一步随机选择一部分操作并随机生成幅度。这样同一个原始样本在不同迭代中会生成差异更大的增强结果,模型很难记住某个固定的人工模式。

RandAugment 是随机策略中实现成本较低的一种。它只设置两个参数:操作数量 N 和操作强度 M,每次从预设操作集合中随机选择 N 个变换,并按照强度 M 统一缩放幅度。相比 AutoAugment 需要搜索最优策略,RandAugment 省去了搜索过程,在小模型和小数据集上也能稳定提升泛化能力。下面是一个基于 PyTorch 的简化实现。

import random
import torchvision.transforms as T
from PIL import Image
class SimpleRandAugment:
    def __init__(self, n=2, m=9):
        self.n = n
        self.m = m
        self.ops = [
            T.RandomHorizontalFlip(p=1.0),
            T.RandomRotation(degrees=m),
            T.ColorJitter(brightness=0.1 * m, contrast=0.1 * m),
            T.RandomAffine(degrees=0, translate=(0.05 * m, 0.05 * m)),
        ]
    def __call__(self, img):
        for _ in range(self.n):
            op = random.choice(self.ops)
            img = op(img)
        return img

这段代码每次调用时都会随机选择两个增强操作,操作的强度由 m 控制。由于选择过程和参数变化都发生在训练循环内部,模型无法预判下一批增强样本会是什么样子。实践中可以先把 m 设小一点,观察验证集损失是否更加平滑,再逐步增加强度。随机策略带来的额外计算量通常可以忽略,但训练过程中会引入更多样本多样性,相当于在不增加数据量的情况下扩展了数据空间。

随机策略的另一个好处是便于调节。如果发现验证集仍然过拟合增强痕迹,可以减少 n 或降低 m;如果训练集欠拟合明显,可以适当增加 n。与固定策略相比,这种调节不会破坏整体流程,也不需要重新设计增强管线。

三、对抗性增强:生成模型最难受的样本

对抗性增强与普通随机增强的出发点不同。随机策略是随机产生多样样本,而对抗性增强会利用当前模型的梯度信息,主动寻找让损失变大、让模型判断出错的方向。换句话说,它生成的是当前模型最不想遇到的样本。把这些样本加入训练,相当于逼着模型在决策边界附近修正自己的判断规则。

最常用的对抗性增强方法基于 FGSM 和 PGD。FGSM 只沿梯度方向走一步,计算速度快,但生成的对抗样本强度有限。PGD 会在梯度方向上迭代多步,每一步都重新计算损失和梯度,然后把扰动约束在允许范围内。PGD 生成的样本更难,对模型边界的修正效果更明显,但训练耗时也更高。下面给出一个 PGD 对抗扰动函数。

import torch
import torch.nn as nn
def pgd_attack(model, images, labels, epsilon=0.03, alpha=0.01, steps=5):
    delta = torch.zeros_like(images, requires_grad=True)
    for _ in range(steps):
        loss = nn.CrossEntropyLoss()(model(images + delta), labels)
        loss.backward()
        grad = delta.grad.detach()
        delta.data = (delta + alpha * grad.sign()).clamp(-epsilon, epsilon)
        delta.grad.zero_()
    return (images + delta).detach()

这个函数每次返回的对抗样本都基于当前模型参数计算,因此随着训练推进,对抗样本也会不断变化。这种动态性正是解决增强过拟合的关键。模型不能再依赖某一类固定的人工痕迹,因为每一次扰动都是针对当前模型弱点设计的。需要注意,对抗性增强会显著增加单步训练时间,尤其是 PGD 步数较多时,计算量可能是普通训练的 3 到 5 倍。

对抗性增强并不等同于对抗训练。对抗训练通常把对抗样本作为主要训练输入,目标是提高模型面对攻击时的鲁棒性。而对抗性增强更侧重于扩充训练分布,可以在随机增强之后再加对抗扰动,让模型同时见到随机多样性和梯度难例。这种做法对增强过拟合的抑制效果往往比单独使用对抗训练更明显。

四、实际落地:二者结合与强度调节

随机策略与对抗性增强不是二选一的关系。一个更稳定的流程是:先对原始图片应用随机增强,得到多样化的基础样本;再对这批基础样本计算对抗扰动,生成逼近决策边界的难例;最后把难例送入模型训练。这样模型既不会记忆固定增强模式,也会在困难样本上持续修正边界。

下面是一个简化后的训练循环,展示如何把 RandAugment 与 PGD 组合起来。实际项目中可以根据模型大小调整对抗步数和扰动范围。

for images, labels in train_loader:
    images, labels = images.to(device), labels.to(device)
    # 先做随机增强,增加样本多样性
    images = rand_augment(images)
    # 再生成对抗扰动,形成难例
    images = pgd_attack(model, images, labels, epsilon=0.02, steps=3)
    optimizer.zero_grad()
    outputs = model(images)
    loss = criterion(outputs, labels)
    loss.backward()
    optimizer.step()

在调节强度时,可以先固定对抗步数为 3,逐步提升随机增强的操作数量;等到验证集损失稳定后,再尝试增加 PGD 步数或 epsilon。小模型对于强对抗扰动更敏感,容易从过拟合转向欠拟合,因此小模型建议使用较小的 epsilon 和较少的 PGD 步数。大模型容量足够时,可以适当提高对抗强度,让训练样本更接近真实分布中的困难区域。

部署阶段要关闭对抗分支。推理时输入的是干净图片,不需要再计算梯度,也不应该包含随机增强中的随机裁剪或翻转。如果训练时使用了随机增强和对抗扰动,部署时只保留必要的归一化和缩放操作即可。这样既能享受训练阶段的泛化提升,又不会引入额外的推理延迟。

总的来说,数据增强不是越多越强越好。固定不变或强度过大的增强会让模型学习到人工痕迹,造成另一种形式的过拟合。随机策略通过操作采样打破固定模式,对抗性增强通过梯度难例修正决策边界。二者组合使用,再配合合理的强度调节,可以在不增加标注数据的前提下获得更稳定的验证集表现。

广告位 · 文章底部(后台-模块管理替换此区域)