Day 26:数据增强技术 — 让模型更鲁棒

今日目标 :掌握从基础几何变换到MixUp/CutMix的完整数据增强体系,学会用数据增强白嫖准确率

预计阅读 :10分钟 | 动手操作:40分钟


一、数据增强:最便宜的精度提升手段

复制代码
数据增强 = 在不增加真实数据的情况下,让模型"看到"更多样本

一句话总结:
  你花一周收集的1000张数据,通过数据增强能变成10万张
  而且这10万张都比原始数据稍微不同,模型学到的特征更鲁棒!

端侧AI特别重要:
  端侧场景光照、角度、遮挡变化大
  好的数据增强 = 模型在端侧鲁棒性提升10-20%
python 复制代码
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision.transforms as T
import torchvision.transforms.functional as TF
from torch.utils.data import DataLoader, Dataset
from torchvision import datasets
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt

torch.manual_seed(42)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

二、核心知识点

2.1 基础增强:几何变换

python 复制代码
# ===== 几何变换:改变图像的空间结构 =====

geometric_transforms = {
    # 1. 随机裁剪(最常用的增强!)
    'RandomCrop(32, padding=4)': T.RandomCrop(32, padding=4),
    # 先padding到40×40,再随机裁32×32 → 引入了平移不变性
    
    # 2. 随机水平翻转
    'RandomHorizontalFlip(p=0.5)': T.RandomHorizontalFlip(p=0.5),
    # 50%概率翻转,对于左右对称的类别(如猫狗)特别有效
    # 注意:数字识别不要用!6翻转后变成什么?
    
    # 3. 随机垂直翻转
    'RandomVerticalFlip(p=0.3)': T.RandomVerticalFlip(p=0.3),
    # 适用于卫星图像、医学图像等
    # 自然场景少用(天空不会在下面)
    
    # 4. 随机旋转
    'RandomRotation(degrees=15)': T.RandomRotation(degrees=15),
    # 旋转±15度,引入旋转不变性
    
    # 5. 随机仿射变换
    'RandomAffine(degrees=10, translate=(0.1,0.1))': T.RandomAffine(
        degrees=10, translate=(0.1, 0.1), scale=(0.9, 1.1)
    ),
    # 组合:旋转+平移+缩放
    
    # 6. 随机透视变换
    'RandomPerspective(distortion_scale=0.2, p=0.5)': T.RandomPerspective(
        distortion_scale=0.2, p=0.5
    ),
    # 模拟不同视角
    
    # 7. Resize + 中心裁剪
    'Resize(40)+CenterCrop(32)': T.Compose([
        T.Resize(40),
        T.CenterCrop(32),
    ]),
}

# 几何变换选型指南
print("几何变换选型指南:")
print("  分类任务: RandomCrop + RandomHorizontalFlip → 标配")
print("  检测任务: Mosaic + RandomAffine → YOLO标配")
print("  分割任务: RandomRotation + RandomCrop → 保持标注一致性")
print("  人脸识别: RandomHorizontalFlip → 人脸对称")
print("  文字识别: 不要用翻转!6和9会混淆")

2.2 基础增强:颜色变换

python 复制代码
# ===== 颜色变换:改变图像的色彩和光照 =====

color_transforms = {
    # 1. 颜色抖动(最常用!)
    'ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1)': 
        T.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1),
    
    # 2. 灰度化
    'RandomGrayscale(p=0.1)': T.RandomGrayscale(p=0.1),
    # 10%概率转灰度,让模型不依赖颜色
    
    # 3. 高斯模糊
    'GaussianBlur(5, sigma=(0.1, 2.0))': T.GaussianBlur(5, sigma=(0.1, 2.0)),
    # 模拟运动模糊或对焦不准
    
    # 4. 随机反转
    'RandomInvert(p=0.2)': T.RandomInvert(p=0.2),
    # 模拟负片效果
    
    # 5. 随机调整锐度
    'RandomAdjustSharpness(sharpness_factor=2, p=0.3)': 
        T.RandomAdjustSharpness(sharpness_factor=2, p=0.3),
    
    # 6. 随机色调分离
    'RandomPosterize(bits=4, p=0.3)': T.RandomPosterize(bits=4, p=0.3),
    
    # 7. 随机均衡化
    'RandomEqualize(p=0.3)': T.RandomEqualize(p=0.3),
    # 直方图均衡化,增强对比度
}

# 颜色变换选型指南
print("\n颜色变换选型指南:")
print("  室外场景: ColorJitter + RandomGrayscale → 光照变化")
print("  监控场景: ColorJitter(亮度为主) → 夜间/白天")
print("  医学图像: 谨慎使用 → 颜色可能有诊断意义")
print("  文档图像: 不使用 → 文字颜色是重要特征")

2.3 高级增强:CutOut、MixUp、CutMix

python 复制代码
# ===== 1. CutOut:随机遮挡 =====
class CutOut:
    """
    随机遮挡图像的一部分
    
    原理:强迫模型学习全局特征,不能只依赖局部
    效果:在CIFAR-10上提升1-2%准确率
    
    端侧应用:模拟遮挡场景(树枝遮挡、人群遮挡)
    """
    def __init__(self, n_holes=1, length=16):
        self.n_holes = n_holes
        self.length = length
    
    def __call__(self, img):
        h, w = img.shape[1], img.shape[2]  # img: (C, H, W)
        mask = torch.ones_like(img)
        
        for _ in range(self.n_holes):
            y = np.random.randint(h)
            x = np.random.randint(w)
            
            y1 = max(0, y - self.length // 2)
            y2 = min(h, y + self.length // 2)
            x1 = max(0, x - self.length // 2)
            x2 = min(w, x + self.length // 2)
            
            mask[:, y1:y2, x1:x2] = 0
        
        return img * mask

# ===== 2. MixUp:两张图混合 =====
def mixup_data(x, y, alpha=1.0):
    """
    MixUp:两张图像按比例混合,标签也按比例混合
    
    x̃ = λ·x_i + (1-λ)·x_j
    ỹ = λ·y_i + (1-λ)·y_j
    
    效果:CIFAR-10提升2-3%,ImageNet提升1-2%
    论文:https://arxiv.org/abs/1710.09412
    """
    if alpha > 0:
        lam = np.random.beta(alpha, alpha)
    else:
        lam = 1
    
    batch_size = x.size(0)
    index = torch.randperm(batch_size)
    
    mixed_x = lam * x + (1 - lam) * x[index]
    y_a, y_b = y, y[index]
    
    return mixed_x, y_a, y_b, lam


def mixup_criterion(criterion, pred, y_a, y_b, lam):
    """
    MixUp的损失函数
    loss = lam * loss(pred, y_a) + (1-lam) * loss(pred, y_b)
    """
    return lam * criterion(pred, y_a) + (1 - lam) * criterion(pred, y_b)


# ===== 3. CutMix:CutOut + MixUp =====
def cutmix_data(x, y, alpha=1.0):
    """
    CutMix:从一张图切一块,贴到另一张图上
    
    比CutOut更有效:遮挡区域不是黑的,而是另一张图的内容
    比MixUp更自然:不是混合,而是真实的区域替换
    
    效果:CIFAR-10提升2-4%,ImageNet提升1-3%
    论文:https://arxiv.org/abs/1905.04899
    """
    if alpha > 0:
        lam = np.random.beta(alpha, alpha)
    else:
        lam = 1
    
    batch_size = x.size(0)
    index = torch.randperm(batch_size)
    
    _, _, H, W = x.shape
    cx = np.random.randint(W)
    cy = np.random.randint(H)
    
    # 裁剪区域大小
    cut_w = int(W * np.sqrt(1 - lam))
    cut_h = int(H * np.sqrt(1 - lam))
    
    x1 = max(0, cx - cut_w // 2)
    x2 = min(W, cx + cut_w // 2)
    y1 = max(0, cy - cut_h // 2)
    y2 = min(H, cy + cut_h // 2)
    
    # 实际混合比例
    lam = 1 - ((x2 - x1) * (y2 - y1)) / (H * W)
    
    # 把另一张图的区域贴过来
    mixed_x = x.clone()
    mixed_x[:, :, y1:y2, x1:x2] = x[index, :, y1:y2, x1:x2]
    
    return mixed_x, y, y[index], lam


# 对比三种方法
print("\n三种高级增强对比:")
print("┌──────────┬──────────────┬──────────────┬──────────────────────┐")
print("│  方法    │   原理       │   标签处理    │   端侧AI适用场景      │")
print("├──────────┼──────────────┼──────────────┼──────────────────────┤")
print("│ CutOut   │ 随机遮挡     │ 标签不变      │ 遮挡场景(安防/交通) │")
print("│ MixUp    │ 线性混合     │ 线性混合      │ 通用分类任务          │")
print("│ CutMix   │ 区域替换     │ 面积比例混合  │ 分类+检测(最推荐)   │")
print("└──────────┴──────────────┴──────────────┴──────────────────────┘")

2.4 自动增强:AutoAugment、RandAugment、TrivialAugment

python 复制代码
# ===== 自动搜索最优增强策略 =====

"""
AutoAugment:用强化学习搜索最优增强策略
  - 优点:效果好,策略针对特定数据集优化
  - 缺点:搜索成本高(需要训练很多次)

RandAugment:简化版,随机选择N种增强,每种增强强度为M
  - 优点:不需要搜索,只需调两个参数(N, M)
  - 推荐:N=2, M=9 是常用配置

TrivialAugment:最简单的自动增强
  - 随机选一种增强 + 随机强度
  - 优点:一个参数都不需要调!
  - 推荐:快速实验时使用
"""

# RandAugment
from torchvision.transforms import RandAugment
randaug = RandAugment(num_ops=2, magnitude=9)

# TrivialAugment
from torchvision.transforms import TrivialAugmentWide
trivialaug = TrivialAugmentWide()

# AutoAugment (CIFAR-10预搜索策略)
from torchvision.transforms import AutoAugment, AutoAugmentPolicy
autoaug = AutoAugment(AutoAugmentPolicy.CIFAR10)

print("自动增强选型:")
print("  追求最好效果: AutoAugment(策略已预搜索好)")
print("  一般训练: RandAugment(N=2, M=9)")
print("  快速实验: TrivialAugment(零参数)")

2.5 Mosaic增强:YOLO的杀手锏

python 复制代码
def mosaic_augment(images, labels, size=640):
    """
    Mosaic增强:把4张图拼成1张
    
    这是YOLOv4/v5/v8的标准增强!
    特别适合小目标检测场景
    
    原理:
    1. 随机选4张图
    2. 每张图随机缩放和裁剪
    3. 拼成一张大图
    4. 标签也跟着调整
    
    效果:
    - 一次训练看到4张图的内容
    - 丰富小目标样本
    - 相当于batch_size×4
    """
    # 简化实现
    mosaic_img = torch.zeros(3, size, size)
    mosaic_labels = []
    
    # 随机分割点
    cut_x = np.random.randint(size // 3, 2 * size // 3)
    cut_y = np.random.randint(size // 3, 2 * size // 3)
    
    positions = [
        (0, 0, cut_y, cut_x),           # 左上
        (0, cut_x, cut_y, size),          # 右上
        (cut_y, 0, size, cut_x),          # 左下
        (cut_y, cut_x, size, size),       # 右下
    ]
    
    print("Mosaic增强示意图:")
    print(f"  分割点: ({cut_x}, {cut_y})")
    print(f"  ┌────────────┬────────────┐")
    print(f"  │  图1(左上)  │  图2(右上)  │")
    print(f"  │  {cut_y}×{cut_x}  │  {cut_y}×{size-cut_x}  │")
    print(f"  ├────────────┼────────────┤")
    print(f"  │  图3(左下)  │  图4(右下)  │")
    print(f"  │  {size-cut_y}×{cut_x}  │  {size-cut_y}×{size-cut_x} │")
    print(f"  └────────────┴────────────┘")
    print(f"\n  端侧AI应用:检测模型训练标配,提升小目标检测能力")

mosaic_augment([], [])

三、动手实践

3.1 实战:CIFAR-10增强对比实验

python 复制代码
def compare_augmentations():
    """对比不同增强策略对CIFAR-10准确率的影响"""
    
    # 定义不同的增强策略
    augmentations = {
        'Baseline (无增强)': T.Compose([
            T.ToTensor(),
            T.Normalize((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616)),
        ]),
        '基础增强 (几何+颜色)': T.Compose([
            T.RandomCrop(32, padding=4),
            T.RandomHorizontalFlip(),
            T.ColorJitter(0.1, 0.1, 0.1, 0.05),
            T.ToTensor(),
            T.Normalize((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616)),
        ]),
        '强增强 (RandAugment)': T.Compose([
            T.RandomCrop(32, padding=4),
            T.RandomHorizontalFlip(),
            RandAugment(num_ops=2, magnitude=9),
            T.ToTensor(),
            T.Normalize((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616)),
        ]),
        '最强增强 (AutoAugment)': T.Compose([
            AutoAugment(AutoAugmentPolicy.CIFAR10),
            T.ToTensor(),
            T.Normalize((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616)),
        ]),
    }
    
    print("增强策略对比(预期CIFAR-10准确率):")
    print(f"{'策略':<25s} {'预期准确率':>10s} {'提升':>8s}")
    print('-' * 48)
    
    baselines = {
        'Baseline (无增强)': '86%',
        '基础增强 (几何+颜色)': '90%',
        '+ CutOut': '91%',
        '+ MixUp': '92%',
        '+ CutMix': '93%',
        '强增强 (RandAugment)': '93%',
        '最强增强 (AutoAugment)': '95%',
    }
    
    baseline_acc = 86.0
    for name, acc_str in baselines.items():
        acc = float(acc_str.replace('%', ''))
        improvement = acc - baseline_acc
        bar = '█' * int(improvement * 2)
        print(f"{name:<25s} {acc_str:>10s} {improvement:>+7.1f}% {bar}")

compare_augmentations()

3.2 实战:MixUp训练完整实现

python 复制代码
def train_with_mixup(model, train_loader, val_loader, epochs=50, alpha=1.0):
    """
    完整的MixUp训练流程
    
    MixUp的关键:
    1. 训练时mixup数据,验证时不mixup
    2. 损失函数用mixup_criterion
    3. 学习率可能需要调大一点(因为标签变软了)
    """
    model = model.to(device)
    optimizer = torch.optim.AdamW(model.parameters(), lr=0.001, weight_decay=1e-4)
    scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=epochs)
    criterion = nn.CrossEntropyLoss()
    
    best_acc = 0
    
    for epoch in range(epochs):
        # 训练
        model.train()
        train_loss, train_correct, train_total = 0, 0, 0
        
        for x, y in train_loader:
            x, y = x.to(device), y.to(device)
            
            # MixUp
            mixed_x, y_a, y_b, lam = mixup_data(x, y, alpha)
            
            optimizer.zero_grad()
            output = model(mixed_x)
            loss = mixup_criterion(criterion, output, y_a, y_b, lam)
            loss.backward()
            optimizer.step()
            
            train_loss += loss.item() * x.size(0)
            # 注意:MixUp的准确率计算不准确,这里简化
            pred = output.argmax(1)
            train_correct += (lam * pred.eq(y_a).float() + 
                             (1 - lam) * pred.eq(y_b).float()).sum().item()
            train_total += y.size(0)
        
        scheduler.step()
        
        # 验证(不mixup)
        model.eval()
        val_correct = 0
        with torch.no_grad():
            for x, y in val_loader:
                x, y = x.to(device), y.to(device)
                val_correct += model(x).argmax(1).eq(y).sum().item()
        val_acc = val_correct / len(val_loader.dataset)
        
        if val_acc > best_acc:
            best_acc = val_acc
        
        if epoch % 10 == 0:
            print(f"Epoch {epoch:3d}: Train Loss={train_loss/train_total:.4f}, "
                  f"Val Acc={val_acc:.2%}")
    
    print(f"最佳准确率: {best_acc:.2%}")
    return model

print("\nMixUp训练要点:")
print("  1. alpha=1.0 是常用值(Beta分布均匀)")
print("  2. 训练时用mixup,验证时不用")
print("  3. mixup让标签变软 → 训练loss看起来更高是正常的")
print("  4. 可能需要更多epoch才能收敛")

3.3 实战:端侧AI场景的增强策略

python 复制代码
def edge_ai_augmentation_guide():
    """
    端侧AI不同场景的推荐增强策略
    """
    scenarios = {
        '安防监控(行人/车辆)': {
            '几何': 'RandomCrop + RandomHorizontalFlip + RandomAffine(小角度)',
            '颜色': 'ColorJitter(brightness=0.3) + RandomGrayscale(0.1)',
            '高级': 'Mosaic + CutMix',
            '注意': '夜间场景多 → 增强亮度和噪声',
        },
        '工业缺陷检测': {
            '几何': 'RandomRotation(±5°) + RandomAffine(小位移)',
            '颜色': 'ColorJitter(contrast=0.2) + GaussianBlur',
            '高级': 'CutOut(模拟遮挡)',
            '注意': '缺陷可能很小 → 不要过度裁剪',
        },
        '无人机/航拍': {
            '几何': 'RandomRotation(±180°) + RandomVerticalFlip',
            '颜色': 'ColorJitter(brightness=0.3, saturation=0.2)',
            '高级': 'Mosaic + MixUp',
            '注意': '角度任意 → 旋转范围可以大',
        },
        '人脸识别': {
            '几何': 'RandomHorizontalFlip + RandomAffine(小角度)',
            '颜色': 'ColorJitter(brightness=0.2) + RandomGrayscale(0.05)',
            '高级': 'CutOut',
            '注意': '不要垂直翻转!不要大角度旋转!',
        },
        '车牌/文字识别': {
            '几何': 'RandomAffine(小角度) + RandomPerspective',
            '颜色': 'ColorJitter(brightness=0.2, contrast=0.2)',
            '高级': 'GaussianBlur + GaussianNoise',
            '注意': '不要翻转!6和9会混淆',
        },
    }
    
    print("端侧AI场景增强策略:")
    for scenario, strategy in scenarios.items():
        print(f"\n  [{scenario}]")
        for k, v in strategy.items():
            print(f"    {k}: {v}")

edge_ai_augmentation_guide()

四、常见坑点

坑1:验证集也用了增强

python 复制代码
# ❌ 错误:验证集用了RandomCrop和RandomHorizontalFlip
# val_transform = T.Compose([T.RandomCrop(32, padding=4), T.RandomHorizontalFlip(), ...])

# ✅ 正确:验证集只做必要的预处理
# val_transform = T.Compose([T.ToTensor(), T.Normalize(mean, std)])

坑2:增强太强反而降低准确率

python 复制代码
# 增强强度的黄金法则:人眼还能认出原图是什么
# 如果增强后连你自己都认不出来,模型也学不到东西

# 增强强度建议:
# 小数据集(<1k):强增强,但augmentation probability < 0.5
# 中数据集(1k-10k):标准增强强度
# 大数据集(>10k):轻量增强,甚至可以不用高级增强

坑3:MixUp/CutMix训练时准确率看起来低

python 复制代码
# MixUp训练时,准确率计算不准(因为标签是混合的)
# 不要用训练集准确率判断模型好坏
# 看验证集准确率!
# 而且MixUp通常需要更多epoch才能收敛

坑4:检测/分割任务的增强要保持label一致性

python 复制代码
# 检测任务:bbox要跟着图像变换一起调整
# 分割任务:mask要跟着图像变换一起调整
# 用专门的库(如albumentations)处理,不要自己写

五、今日作业

  1. 增强对比:在CIFAR-10上对比"无增强 vs 基础增强 vs RandAugment",记录准确率差异
  2. MixUp实战:实现MixUp训练,观察训练loss和验证准确率的关系
  3. 端侧策略:根据你的端侧AI场景,设计一套数据增强策略,写下来
  4. 打卡 :评论区发你的增强对比结果,格式:"Day 26/100 打卡:数据增强全部掌握!"

今日小结

复制代码
今天你学会了:
✅ 几何变换:RandomCrop/Flip/Rotation/Affine/Perspective
✅ 颜色变换:ColorJitter/Grayscale/Blur/Invert
✅ CutOut:随机遮挡,强迫学习全局特征
✅ MixUp:两张图线性混合,标签也混合
✅ CutMix:区域替换,CutOut+MixUp的结合
✅ AutoAugment/RandAugment/TrivialAugment:自动搜索最优增强
✅ Mosaic:YOLO的4图拼接增强
✅ 端侧AI各场景的增强策略
✅ 4个经典坑点

明日预告

Day 27:迁移学习与微调 --- 站在巨人的肩膀上

预训练模型、特征提取vs微调、冻结策略、ImageNet预训练、领域自适应


🔥 关注我,每天解锁一个端侧AI技能!

微信公众号:xxx | 小红书:xxx | CSDN:xxx

评论区打卡,一起坚持100天!


附:小红书图文版

封面标题建议:数据增强大全 | 不增加数据,让模型准确率提升10% 📈

P1 --- 封面

标题:数据增强技术

副标题:几何 / 颜色 / CutOut / MixUp / CutMix

关键词:数据增强 / Augmentation / 鲁棒性

P2 --- 基础增强:几何变换

RandomCrop + RandomHorizontalFlip = 标配

RandomRotation:引入旋转不变性

RandomAffine:平移+旋转+缩放

注意:文字识别不要翻转!

P3 --- 基础增强:颜色变换

ColorJitter:亮度/对比度/饱和度/色调

RandomGrayscale:不依赖颜色

GaussianBlur:模拟模糊

端侧场景:室外→强颜色,医学→谨慎

P4 --- 三种高级增强

CutOut:随机遮挡 → 强迫全局特征

MixUp:两张图混合 → 标签也混合

CutMix:切一块贴另一张 → 最推荐!

CIFAR-10提升2-4%

P5 --- 自动增强+YOLO Mosaic

RandAugment:只需调2个参数

TrivialAugment:零参数自动增强

Mosaic:4图拼1图,YOLO标配

小目标检测的利器

P6 --- 今日作业

增强对比实验 + MixUp实战

评论区打卡 Day 26/100

标签:#数据增强 #MixUp #CutMix #深度学习


CSDN发布提示:CSDN版本建议在CutOut/MixUp/CutMix部分各放一张效果示意图,在对比实验部分放准确率柱状图,在Mosaic部分放拼接示意图。

相关推荐
vibecoding775 小时前
一文搞懂企业级 API 网关选型:12 个维度、4 类企业、7 步落地
人工智能·大模型·ai编程
Rocktech_ruixun5 小时前
机器人本地跑LLM大模型对主板硬件有什么要求?瑞迅科技RK3588/3568方案选型解析
人工智能·科技·嵌入式硬件·机器人·边缘计算
yxlalm5 小时前
Spring AI+RAG 01-项目背景与技术选型
java·人工智能·spring
tedcloud1235 小时前
Wand-Enhancer 怎么搭建?开源 Wand 客户端增强与远程控制工具介绍
大数据·服务器·人工智能·开源·音视频
科技苑6 小时前
如何用Python编程实现一个简单的Web爬虫?
人工智能·python
迅利科技6 小时前
新能源汽车零部件研发,SIMULIA一站式仿真解决方案如何缩短研发周期
人工智能·汽车
Databuff6 小时前
AI SSH工具,集齐SSH、SFTP、知识库RAG、AI助手
网络·人工智能·ssh
Blockchina7 小时前
从一篇文章到一条完整视频:用 Codex 搭建可复用的 AI 视频生产线
人工智能
Proaiapi7 小时前
一张图介绍gpt-image-2.5
人工智能·gpt
czxxxc7 小时前
当AI开始“动手”:一场从“会说”到“会做”的静默转折
人工智能