为什么 Pytorch 官方使用 mean=[0.485, 0.456, 0.406] 和 std=[0.229, 0.224, 0.225] 来归一化图像?

Why Pytorch officially use mean=[0.485, 0.456, 0.406] and std=[0.229, 0.224, 0.225] to normalize images?

在此页面 (https://pytorch.org/vision/stable/models.html) 中,它表示“所有预训练模型都希望输入图像以相同的方式归一化,即形状为 (3 x H) 的 3 通道 RGB 图像的小批量x W),其中 H 和 W 预计至少为 224。必须将图像加载到 [0, 1] 范围内,然后使用 mean = [0.485, 0.456, 0.406]std = [0.229, 0.224, 0.225] 进行归一化。 =17=]

正常化的meanstd不应该是[0.5, 0.5, 0.5][0.5, 0.5, 0.5]吗?为什么设置这么奇怪的值?

使用 Imagenet 的均值和标准差是一种常见的做法。它们是根据数百万张图像计算得出的。如果您想在自己的数据集上从头开始训练,您可以计算新的均值和标准差。否则,建议使用具有自己的均值和标准差的 Imagenet 预试验模型。

在那个例子中,他们使用的是 ImageNet 的均值和标准差,但如果你看一下他们的 MNIST 示例,均值和标准差是一维的(因为输入是灰度——没有 RGB 通道)。

是否使用ImageNet'smean和stddev取决于你的数据。假设你的数据是“自然场景”(人、建筑、动物、各种lighting/angles/backgrounds等)的普通照片,并假设你的数据集以同样的方式存在偏差ImageNet是(在class平衡方面),那么用ImageNet的场景统计归一化就可以了。如果照片以某种方式“特殊”(颜色过滤、对比度调整、不寻常的照明等)或“非自然主题”(医学图像、卫星图像、手绘图等),那么我建议正确规范化您的数据集模型训练前!*

这里有一些示例代码可以帮助您入门:

import os
import torch
from torchvision import datasets, transforms
from torch.utils.data.dataset import Dataset
from tqdm.notebook import tqdm
from time import time

N_CHANNELS = 1

dataset = datasets.MNIST("data", download=True,
                 train=True, transform=transforms.ToTensor())
full_loader = torch.utils.data.DataLoader(dataset, shuffle=False, num_workers=os.cpu_count())

before = time()
mean = torch.zeros(1)
std = torch.zeros(1)
print('==> Computing mean and std..')
for inputs, _labels in tqdm(full_loader):
    for i in range(N_CHANNELS):
        mean[i] += inputs[:,i,:,:].mean()
        std[i] += inputs[:,i,:,:].std()
mean.div_(len(dataset))
std.div_(len(dataset))
print(mean, std)

print("time elapsed: ", time()-before)

在计算机视觉中,“自然场景”具有特定的含义,与自然与人造无关,参见https://en.wikipedia.org/wiki/Natural_scene_perception

* 否则你 运行 由于损失函数的延长而陷入优化问题——参见 my answer here

我无法按计划计算标准偏差,但使用下面的代码做到了。灰度imagenet的train数据集均值和标准差为(四舍五入):

平均值:0.44531356896770125

标准偏差:0.2692461874154524

def calcSTD(d):
    meanValue = 0.44531356896770125
    squaredError = 0
    numberOfPixels = 0
    for f in os.listdir("/home/imagenet/ILSVRC/Data/CLS-LOC/train/"+str(d)+"/"): 
        if f.endswith(".JPEG"):
            
            image = imread("/home/imagenet/ILSVRC/Data/CLS-LOC/train/"+str(d)+"/"+str(f))
                
            ###Transform to gray if not already gray anyways  
            if  np.array(image).ndim == 3:
                matrix = np.array(image)
                blue = matrix[:,:,0]/255
                green = matrix[:,:,1]/255
                red = matrix[:,:,2]/255
                gray = (0.2989 * red + 0.587 * green + 0.114 * blue)
            else:
                gray = np.array(image)/255
            ###----------------------------------------------------       
                    
            for line in gray:
                for pixel in line:


                    squaredError += (pixel-meanValue)**2
                    numberOfPixels += 1
    
    return (squaredError, numberOfPixels)

a_pool = multiprocessing.Pool()
folders = []
[folders.append(f.name) for f in os.scandir("/home/imagenet/ILSVRC/Data/CLS-LOC/train") if f.is_dir()]
resultStD = a_pool.map(calcSTD, folders)

StD = (sum([intensity[0] for intensity in resultStD])/sum([pixels[1] for pixels in resultStD]))**0.5
print(StD)

来源:

TL;DR

我相信原因是,就像(深度)机器学习中的许多事情一样, 它恰好运行良好。

详情

statistic中的单词'normalization'可以适用于不同的变换。 例如: for all x in X: x->(x - min(x))/(max(x)-min(x) 将 X 的值归一化并拉伸到 [0..1] 范围。

另一个例子: for all x in X: x->(x - mean(X))/stdv(x) 将图像转换为均值 = 0,标准差 = 1。这种转换称为标准分数,有时也称为标准化。如果我们将结果乘以 sigma,然后加上 mu,我们会将结果设置为 mean=mu 和 stdv=sigma

Pytorch 不做任何这些 - 相反它应用标准分数,但不是 X 的平均值和 stdv 值(要归一化的图像),而是平均值和平均 stdv 的值一大组 Imagenet 图像。但不会设置这些值的平均值和标准差。

如果图像恰好与 Imagenet 集的平均值具有相同的均值和标准差 - 它将被转换为具有均值 0 和标准差 1。 否则,它将转换为它的均值和标准差以及所述平均值的函数。

我不清楚这严格意味着什么(为什么是 stdv 的平均值?为什么将标准分数应用于平均值?)。

也许有人可以澄清一下?

然而,与深度机器学习中的许多事物一样,该理论尚未完全建立。我的猜测是人们尝试了不同的标准化,而这个恰好表现良好。

这并不意味着这是最好的规范化,只是说它是一个不错的规范化。当然,如果您使用的是使用此特定规范化学习的预训练值,则最好使用与训练中使用的相同规范化进行推理或派生模型。