pytorch中归一化transforms.Normalize的真正计算过程

关于transforms.Normalize的真正理解

    • 问题
    • transform.ToTensor()
      • 代码示例
    • transforms.Normalize()
      • 求解mean和std
      • 自己实现
      • 官方实现
    • 结论

我们都知道,当图像数据输入时,需要对图像数据进行预处理,常用的预处理方法,本文不再赘述,本文重在讲讲transform.ToTensor和transforms.Normalize。

问题

transform.ToTensor(),
transform.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))

究竟是什么意思?(0.5,0.5,0.5),(0.5,0.5,0.5)又是怎么来的呢?

transform.ToTensor()

  1. 是将输入的数据shape W,H,C ——> C,W,H
  2. 将所有数除以255,将数据归一化到【0,1】

代码示例

import torch
import numpy as np
from torchvision import transforms
import cv2
#自定义图片数组,数据类型一定要转为‘uint8’,不然transforms.ToTensor()不会归一化
data = np.array([
                [[1,1,1],[1,1,1],[1,1,1],[1,1,1],[1,1,1]],
                [[2,2,2],[2,2,2],[2,2,2],[2,2,2],[2,2,2]],
                [[3,3,3],[3,3,3],[3,3,3],[3,3,3],[3,3,3]],
                [[4,4,4],[4,4,4],[4,4,4],[4,4,4],[4,4,4]],
                [[5,5,5],[5,5,5],[5,5,5],[5,5,5],[5,5,5]]
        ],dtype='uint8')
print(data)
print(data.shape)   #(5,5,3)
data = transforms.ToTensor()(data)
print(data)
print(data.shape)	#(3,5,5)

输出:
tensor([[[0.0039, 0.0039, 0.0039, 0.0039, 0.0039],
         [0.0078, 0.0078, 0.0078, 0.0078, 0.0078],
         [0.0118, 0.0118, 0.0118, 0.0118, 0.0118],
         [0.0157, 0.0157, 0.0157, 0.0157, 0.0157],
         [0.0196, 0.0196, 0.0196, 0.0196, 0.0196]],

        [[0.0039, 0.0039, 0.0039, 0.0039, 0.0039],
         [0.0078, 0.0078, 0.0078, 0.0078, 0.0078],
         [0.0118, 0.0118, 0.0118, 0.0118, 0.0118],
         [0.0157, 0.0157, 0.0157, 0.0157, 0.0157],
         [0.0196, 0.0196, 0.0196, 0.0196, 0.0196]],

        [[0.0039, 0.0039, 0.0039, 0.0039, 0.0039],
         [0.0078, 0.0078, 0.0078, 0.0078, 0.0078],
         [0.0118, 0.0118, 0.0118, 0.0118, 0.0118],
         [0.0157, 0.0157, 0.0157, 0.0157, 0.0157],
         [0.0196, 0.0196, 0.0196, 0.0196, 0.0196]]])

transforms.Normalize()

看了许多文章,都是说:transform.Normalize()通过公式

x = (x - mean) / std

即同一纬度的数据减去这一维度的平均值,再除以标准差,将归一化后的数据变换到【-1,1】之间。可真是这样吗??

求解mean和std

我们需要求得一批数据的mean和std,代码如下:

import torch
import numpy as np
from torchvision import transforms

# 这里以上述创建的单数据为例子
data = np.array([
                [[1,1,1],[1,1,1],[1,1,1],[1,1,1],[1,1,1]],
                [[2,2,2],[2,2,2],[2,2,2],[2,2,2],[2,2,2]],
                [[3,3,3],[3,3,3],[3,3,3],[3,3,3],[3,3,3]],
                [[4,4,4],[4,4,4],[4,4,4],[4,4,4],[4,4,4]],
                [[5,5,5],[5,5,5],[5,5,5],[5,5,5],[5,5,5]]
        ],dtype='uint8)

#将数据转为C,W,H,并归一化到[0,1]
data = transforms.ToTensor()(data)
# 需要对数据进行扩维,增加batch维度
data = torch.unsqueeze(data,0)

nb_samples = 0.
#创建3维的空列表
channel_mean = torch.zeros(3)
channel_std = torch.zeros(3)
print(data.shape)
N, C, H, W = data.shape[:4]
data = data.view(N, C, -1)     #将w,h维度的数据展平,为batch,channel,data,然后对三个维度上的数分别求和和标准差
print(data.shape)
#展平后,w,h属于第二维度,对他们求平均,sum(0)为将同一纬度的数据累加
channel_mean += data.mean(2).sum(0)  
#展平后,w,h属于第二维度,对他们求标准差,sum(0)为将同一纬度的数据累加
channel_std += data.std(2).sum(0)
#获取所有batch的数据,这里为1
nb_samples += N
#获取同一batch的均值和标准差
channel_mean /= nb_samples
channel_std /= nb_samples
print(channel_mean, channel_std)
#tensor([0.0118, 0.0118, 0.0118]) tensor([0.0057, 0.0057, 0.0057])

以此便可求得均值和标准差。我们再带入公式:

x = (x - mean) / std

自己实现

data = np.array([
                [[1,1,1],[1,1,1],[1,1,1],[1,1,1],[1,1,1]],
                [[2,2,2],[2,2,2],[2,2,2],[2,2,2],[2,2,2]],
                [[3,3,3],[3,3,3],[3,3,3],[3,3,3],[3,3,3]],
                [[4,4,4],[4,4,4],[4,4,4],[4,4,4],[4,4,4]],
                [[5,5,5],[5,5,5],[5,5,5],[5,5,5],[5,5,5]]
        ],dtype='uint8')
data = transforms.ToTensor()(data)
for i in range(3):
    data[i,:,:] = (data[i,:,:] - channel_mean[i]) / channel_std[i]
print(data)

输出:
tensor([[[-1.3856, -1.3856, -1.3856, -1.3856, -1.3856],
         [-0.6928, -0.6928, -0.6928, -0.6928, -0.6928],
         [ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000],
         [ 0.6928,  0.6928,  0.6928,  0.6928,  0.6928],
         [ 1.3856,  1.3856,  1.3856,  1.3856,  1.3856]],

        [[-1.3856, -1.3856, -1.3856, -1.3856, -1.3856],
         [-0.6928, -0.6928, -0.6928, -0.6928, -0.6928],
         [ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000],
         [ 0.6928,  0.6928,  0.6928,  0.6928,  0.6928],
         [ 1.3856,  1.3856,  1.3856,  1.3856,  1.3856]],

        [[-1.3856, -1.3856, -1.3856, -1.3856, -1.3856],
         [-0.6928, -0.6928, -0.6928, -0.6928, -0.6928],
         [ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000],
         [ 0.6928,  0.6928,  0.6928,  0.6928,  0.6928],
         [ 1.3856,  1.3856,  1.3856,  1.3856,  1.3856]]])

官方实现

data = np.array([
                [[1,1,1],[1,1,1],[1,1,1],[1,1,1],[1,1,1]],
                [[2,2,2],[2,2,2],[2,2,2],[2,2,2],[2,2,2]],
                [[3,3,3],[3,3,3],[3,3,3],[3,3,3],[3,3,3]],
                [[4,4,4],[4,4,4],[4,4,4],[4,4,4],[4,4,4]],
                [[5,5,5],[5,5,5],[5,5,5],[5,5,5],[5,5,5]]
        ],dtype='uint8')
data = transforms.ToTensor()(data)
data = transforms.Normalize(channel_mean, channel_std)(data)
print(data)

输出:
tensor([[[-1.3856, -1.3856, -1.3856, -1.3856, -1.3856],
         [-0.6928, -0.6928, -0.6928, -0.6928, -0.6928],
         [ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000],
         [ 0.6928,  0.6928,  0.6928,  0.6928,  0.6928],
         [ 1.3856,  1.3856,  1.3856,  1.3856,  1.3856]],

        [[-1.3856, -1.3856, -1.3856, -1.3856, -1.3856],
         [-0.6928, -0.6928, -0.6928, -0.6928, -0.6928],
         [ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000],
         [ 0.6928,  0.6928,  0.6928,  0.6928,  0.6928],
         [ 1.3856,  1.3856,  1.3856,  1.3856,  1.3856]],

        [[-1.3856, -1.3856, -1.3856, -1.3856, -1.3856],
         [-0.6928, -0.6928, -0.6928, -0.6928, -0.6928],
         [ 0.0000,  0.0000,  0.0000,  0.0000,  0.0000],
         [ 0.6928,  0.6928,  0.6928,  0.6928,  0.6928],
         [ 1.3856,  1.3856,  1.3856,  1.3856,  1.3856]]])

我们观察数据发现,通过求解的均值和标准差,求得的标准化的值,并非在【-1,1】,( 借此谴责部分博客 )。

结论

经过这样处理后的数据符合标准正态分布,即均值为0,标准差为1。使模型更容易收敛。并非是归于【-1,1】!!

你可能感兴趣的:(笔记,教程,卷积神经网络,pytorch,深度学习,计算机视觉)