4. 数据预处理与增强:让模型看到更真实的世界

数据预处理和增强,说白了就是给模型“喂饭”前的准备工作。你想想看,工业相机拍出来的原始图像,直接扔给神经网络,十有八九要出问题。我刚开始做缺陷检测项目时,就吃过这个亏——模型在实验室跑得好好的,一上产线就崩。后来才发现,是数据预处理没做好。

这一节,我们重点聊四个核心环节:图像归一化、尺寸统一、数据增强策略,以及一个比较高级的技巧——CutMix。每个环节我都会结合项目实战经验来讲,希望能帮你少走弯路。

核心观点:数据预处理不是“可有可无”的步骤,而是决定模型能否在工业场景中稳定运行的基石。预处理做得好,模型训练事半功倍;做不好,后面再怎么调参也是白费力气。
数据预处理与增强知识体系 原始图像数据 图像归一化 均值/方差归一化 尺寸统一 Resize / Padding 数据增强 翻转/旋转/噪声 CutMix 混合增强策略 💡 实战要点: • 归一化:消除光照差异,让模型关注缺陷本身而非亮度变化 • 尺寸统一:保证batch训练效率,避免显存碎片化 • 数据增强:模拟真实产线的各种异常情况,提升模型泛化能力 • CutMix:解决小样本问题,让模型学会关注局部特征

4.1 图像归一化:消除光照差异的“魔法”

工业场景中,光照变化是最大的敌人。同一款产品,早上拍和下午拍,亮度可能差30%。如果直接把原始像素值喂给模型,它会把光照变化当成缺陷来学。这问题我遇到过不止一次。

归一化,就是让图像数据变成“标准格式”。常用的方法有两种:

归一化方法 公式 适用场景 我的建议
Min-Max归一化 (x - min) / (max - min) 图像对比度差异大 适合简单场景,但易受异常像素干扰
Z-score归一化 (x - μ) / σ 光照变化复杂 工业场景首选,鲁棒性更好
直方图均衡化 基于累积分布函数 低对比度图像 慎用,可能放大噪声

我个人习惯用Z-score归一化。为什么?因为工业产线的光照变化往往是全局性的,不是局部的。Z-score能很好地消除这种全局偏移。举个例子:

import cv2
import numpy as np

def z_score_normalize(image):
    """
    Z-score归一化
    适用于工业缺陷检测场景
    """
    # 计算均值和标准差
    mean = np.mean(image)
    std = np.std(image)
    
    # 防止除零
    if std == 0:
        return image
    
    # 归一化
    normalized = (image - mean) / std
    
    # 缩放到0-255范围
    normalized = ((normalized - normalized.min()) / 
                  (normalized.max() - normalized.min()) * 255).astype(np.uint8)
    
    return normalized

# 使用示例
img = cv2.imread('defect_sample.jpg', cv2.IMREAD_GRAYSCALE)
img_norm = z_score_normalize(img)
实战技巧:归一化参数(均值和标准差)最好从训练集统计得到,而不是每张图单独计算。这样可以保证所有图像使用相同的“标准”,模型学到的特征更稳定。

4.2 尺寸统一:别让模型“看不清”

工业相机拍出来的图像,尺寸五花八门。有的产线用500万像素相机,有的用1200万。如果不统一尺寸,模型训练时batch size都设不了——你想想看,不同尺寸的图像怎么拼成一个tensor?

尺寸统一有两种主流策略:

  • 直接Resize:简单粗暴,但会改变缺陷的形态。比如一个圆形缺陷,Resize后可能变成椭圆。这在精密检测中是不能接受的。
  • Padding + Resize:先按比例缩放,再用黑色或灰色填充到目标尺寸。这样缺陷的形态保持不变,但会引入无效区域。

我建议用第二种方法。特别是对于PCB焊点检测这类场景,缺陷的几何形态是判断是否合格的关键指标。直接Resize会丢失这些信息。

def resize_with_padding(image, target_size=(224, 224), pad_color=0):
    """
    保持宽高比的尺寸统一
    target_size: (width, height)
    pad_color: 填充颜色,0为黑色
    """
    h, w = image.shape[:2]
    target_w, target_h = target_size
    
    # 计算缩放比例
    scale = min(target_w / w, target_h / h)
    new_w = int(w * scale)
    new_h = int(h * scale)
    
    # 缩放图像
    resized = cv2.resize(image, (new_w, new_h))
    
    # 创建画布并填充
    canvas = np.full((target_h, target_w) + image.shape[2:], 
                     pad_color, dtype=np.uint8)
    
    # 计算偏移量
    x_offset = (target_w - new_w) // 2
    y_offset = (target_h - new_h) // 2
    
    # 粘贴缩放后的图像
    canvas[y_offset:y_offset+new_h, x_offset:x_offset+new_w] = resized
    
    return canvas
注意:填充颜色不要随意选。对于灰度图,用0(黑色)或128(灰色)都可以。但对于彩色图,建议用训练集的平均像素值。我曾经在一个项目中用了纯黑色填充,结果模型把黑色区域当成了“背景缺陷”,闹了个大笑话。

4.3 数据增强策略:让模型见过“世面”

工业缺陷检测最大的痛点是——正负样本极度不平衡。正常产品占99%,缺陷产品只有1%。如果不做数据增强,模型会学成一个“永远说正常”的废物。

常用的数据增强策略,我按实用程度排个序:

增强方法 适用场景 增强幅度建议 注意事项
水平翻转 所有场景 50%概率 注意文字、方向性缺陷
旋转(±15°) 圆形、对称产品 30%概率 旋转后需裁剪或填充
高斯噪声 低光照场景 σ=0.01~0.05 噪声太强会掩盖缺陷
亮度调整 光照变化大的产线 ±20% 模拟早晚光照差异
Cutout 遮挡场景 随机遮挡10%区域 遮挡区域用随机值填充

这里我要特别强调一点:数据增强不是越多越好。我见过有人把旋转角度设到±90°,结果模型把“竖着的缺陷”和“横着的缺陷”当成了两种不同的东西。过犹不及。

我的经验:对于工业场景,优先使用“几何变换类”增强(翻转、小角度旋转),因为它们模拟的是产品在产线上的真实姿态变化。颜色变换类增强(亮度、对比度)要谨慎使用,因为工业相机的光照是可控的,过度变化反而会让模型学到错误的相关性。

4.4 CutMix:小样本缺陷的“救星”

CutMix是我最近两年在工业项目中用得越来越多的技术。它的核心思想很简单:把两张图“拼”在一起,让模型同时学习两种特征。

具体做法是:

  1. 从训练集中随机选两张图:一张正常产品,一张缺陷产品
  2. 在正常产品上随机裁剪一个矩形区域
  3. 用缺陷产品的对应区域替换掉这个矩形
  4. 标签也按面积比例混合:比如正常占70%,缺陷占30%

这样做的好处是:模型不会只盯着“整张图是不是正常”,而是学会关注“局部区域有没有异常”。这对于小缺陷检测特别有效。

def cutmix(image1, image2, mask1, mask2, alpha=1.0):
    """
    CutMix数据增强
    image1, image2: 输入图像
    mask1, mask2: 对应的标签(缺陷掩码)
    alpha: Beta分布的参数,控制混合比例
    """
    h, w = image1.shape[:2]
    
    # 随机生成混合比例
    lam = np.random.beta(alpha, alpha)
    
    # 随机裁剪区域
    cut_ratio = np.sqrt(1. - lam)
    cut_w = int(w * cut_ratio)
    cut_h = int(h * cut_ratio)
    
    cx = np.random.randint(w)
    cy = np.random.randint(h)
    
    x1 = np.clip(cx - cut_w // 2, 0, w)
    y1 = np.clip(cy - cut_h // 2, 0, h)
    x2 = np.clip(cx + cut_w // 2, 0, w)
    y2 = np.clip(cy + cut_h // 2, 0, h)
    
    # 混合图像
    mixed_image = image1.copy()
    mixed_image[y1:y2, x1:x2] = image2[y1:y2, x1:x2]
    
    # 混合标签(按面积比例)
    area_ratio = (x2 - x1) * (y2 - y1) / (w * h)
    mixed_mask = mask1.copy()
    mixed_mask[y1:y2, x1:x2] = mask2[y1:y2, x1:x2]
    
    return mixed_image, mixed_mask, 1. - area_ratio
使用建议:CutMix不要用在所有训练数据上。我一般设置30%-50%的概率使用CutMix,其余时候用普通增强。这样既能让模型学到局部特征,又不会让它对“拼接图”产生依赖。

4.5 实战中的“坑”与“解”

说了这么多理论,最后分享几个我在项目中踩过的坑:

  • 坑一:数据增强后忘记同步标签。比如旋转图像后,缺陷的坐标位置也变了,但标签没更新。结果模型学了一堆错误的位置信息。
  • 坑二:增强参数设置太激进。有一次我把亮度调整范围设到±50%,结果模型把“暗图”和“亮图”当成了两种不同的缺陷类型。
  • 坑三:验证集也做了增强。这是新手最容易犯的错误——验证集应该保持原始数据分布,增强只用在训练集上。
避坑指南:我曾经在一个项目中,因为数据增强代码写错了,把训练集的标签和图像搞混了。模型训练了三天,loss一直不降。最后发现是增强函数里忘了返回标签。从那以后,我每次写完增强代码,都会先跑一个batch检查:图像和标签是否一一对应。

数据预处理和增强,看似简单,实则是工业缺陷检测项目中最容易出问题的环节。做好了,模型泛化能力提升30%不是问题;做不好,后面再怎么调参也是白费。希望这一节的内容能帮你打好基础,少走弯路。