Image Processing 【Normlize和DeNormlize】
在深度学习图像处理中,Normalize(归一化)和DeNormalize(反归一化)是最基础但容易混淆的操作。本文通过Toy Example和可视化对比,让你彻底理解它们的数学原理和代码实现。
- 什么是归一化(Normalize)?
- 什么是反归一化(DeNormalize)
- 训练VAE可视化图像的数据变化流程
- 常见误区
什么是归一化(Normalize)?
归一化的目的是将图像的像素值从原始范围(如 [0, 255])映射到一个标准范围(如 [-1, 1] 或 [0, 1]),以便神经网络更容易学习。
xnormalized=xoriginal−meanstdx_{\text{normalized}} = \frac{x_{\text{original}} - \text{mean}}{\text{std}}xnormalized=stdxoriginal−mean
其中:
视觉基础模型ViT一般使用ImageNet的mean和std,如CLIP:mean:数据集的均值(如 [0.485, 0.456, 0.406] for ImageNet)std:数据集的标准差(如 [0.229, 0.224, 0.225] for ImageNet)传统VAE等low leve图像压缩模型,一般使用default的mean和std:mean:数据集的均值(如 [0.5, 0.5, 0.5] for default)std:数据集的标准差(如 [0.5, 0.5, 0.5] for default)
这个Norm的操作一般在那里使用呢?
- 训练的时候,在Dataset的
transforms
中 - 推理的时候,在Transformers的
AutoProcessor
中 - 需要注意的是:训练和推理的归一化必须一致;如果训练时用了 ImageNet 的 mean=[0.485, 0.456, 0.406],推理时也必须用相同的参数,否则模型性能会大幅下降。
# 训练的时候
from torchvision import transforms
# 定义归一化参数(例如 ImageNet 的 mean/std)
DATA_NORM = {"mean": [0.485, 0.456, 0.406],"std": [0.229, 0.224, 0.225]
}
# 在 Dataset 的 transforms 中加入 Normalize
train_transform = transforms.Compose([transforms.Resize(256),transforms.RandomCrop(224),transforms.ToTensor(), # 先转换到 [0,1]transforms.Normalize( # 再归一化到标准分布mean=DATA_NORM["mean"], std=DATA_NORM["std"]),
])
dataset = ImageFolder("data/train", transform=train_transform)# 推理的时候,底层已封装了数据预处理(包括归一化),用户无需关心具体参数。
from transformers import AutoImageProcessor
processor = AutoImageProcessor.from_pretrained("google/vit-base-patch16-224")
inputs = processor(images=image, return_tensors="pt") # 自动归一化
数据加载过程中,数值范围的变化:
ToTensor 将 [0, 255] → [0, 1]
Normalize 将 [0, 1] → 标准分布
数据集 → Mean 和 Std → 归一化后范围mean和std是default(0.5): [0, 1] → [-1, 1]mean和std是imagenet: [0, 1] → [-2.1, 2.5]
什么是反归一化(DeNormalize)
反归一化是归一化的逆操作,用于将处理后的数据还原到原始范围(如 [0, 255]),一般用于生成的图像的可视化。
xoriginal=xnormalized×std+meanx_{\text{original}} = x_{\text{normalized}} \times \text{std} + \text{mean}xoriginal=xnormalized×std+mean
如果输入是经过ToTensor的 [0, 1],想要输出是 [0, 255],则还需乘以 255,才能成功还原到原始像素值!
xoriginal=(xnormalized×std+mean)×255x_{\text{original}} = (x_{\text{normalized}} \times \text{std} + \text{mean}) \times 255xoriginal=(xnormalized×std+mean)×255
def denormalize(tensor, mean, std):# 输入 tensor 是 (C, H, W),反归一化后转 (H, W, C) 并缩放到 [0, 255]mean = torch.tensor(mean).view(3, 1, 1)std = torch.tensor(std).view(3, 1, 1)denorm_img = (tensor * std + mean) * 255.0return denorm_img.permute(1, 2, 0).clamp(0, 255).to(torch.uint8)# 测试反归一化
norm_img = transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])(toy_img)
denorm_img = denormalize(norm_img, mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
训练VAE可视化图像的数据变化流程
因为SSIM
/PSNR
一般在 [0,1]
之间计算,rFID
一般在[0,255]
之间计算:
常见误区
-
ToTensor 已经归一化到 [0, 1],Normalize 是进一步变换。
-
ImageNet 的 mean/std 会使数据超出 [0, 1],可视化时需
clamp
。 -
反归一化必须使用和归一化相同的 mean/std,否则颜色会错误。
# 下面的 denorm 仅适用于 mean=0.5, std=0.5:
samples = torch.clamp(127.5 * samples + 128.0, 0, 255) # 仅适用于 mean=0.5, std=0.5