学习pytorch中归一化transforms.Normalize

torchvison:计算机视觉工具包

包含

  1. torchvison.transforms(常用的图像预处理方法);
  2. torchvision.datasets(常用数据集的dataset实现,MNIST,CIFAR-10,ImageNet等);
  3. torchvison.model(常用的模型预训练,AlexNet,VGG,ResNet,GoogleNet等)。

torchvision.transforms

常用的数据预处理方法,提升泛化能力。包括:数据中心化、数据标准化、缩放、裁剪、旋转、填充、噪声添加、灰度变换、线性变换、放射变换、亮度、饱和度和对比度变换等


数据标准化——transforms.Normalize

功能:逐channel的对图像进行标准化(均值变为0,标准差变为1),可以加快模型的收敛

  • output=(input-mean)/std
  • mean:各通道的均值
  • std:各通道的标准差
  • inplace:是否原地操作

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

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

1、transform.ToTensor() 

原始的PILImage格式或者numpy.array格式的数据格式化为可被pytorch快速处理的张量类型

  • 是将输入的数据shape W,H,C---> C,W,H。   从opencv读到的图片是通道数在第三个维度,现在经过ToTensor操作cv2图片变成了torch image类型,也就是通道在第一个维度。

  • 将所有数除以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]]])

2、 transforms.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)   #tensor([0., 0., 0.])
channel_std = torch.zeros(3)    #tensor([0., 0., 0.])

print(data.shape)  # torch.Size([1,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]]]])


N, C, H, W = data.shape[:4]   #N=1  C=3  H=5   W=5
data = data.view(N, C, -1)    #将w,h维度的数据展平,为batch,channel,data,然后对三个维度上的数分别求和和标准差
 
print(data.shape)   #torch.Size([1, 3, 25])
#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]]])


#展平后,w,h属于第二维度,对他们求平均,sum(0)为将同一纬度的数据累加
channel_mean += data.mean(2).sum(0)  #tensor([0.0118, 0.0118, 0.0118])

#展平后,w,h属于第二维度,对他们求标准差,sum(0)为将同一纬度的数据累加
channel_std += data.std(2).sum(0)   #tensor([0.0057, 0.0057, 0.0057])

#获取所有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)
print(data)
#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]]])

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]!!


Normalize之前,数据分布是在[0,1]

Normalize之后:

  • ①如果mean = std=(0.5,0.5,0.5),那么Normalize之后,数据分布是【-1,1】;因为最小值=(0-mean)/std=(0-0.5)/0.5=-1。同理最大值的等于1。
  • ②如果mean为数据的计算的mean,std也是数据计算的std。那么Normalize之后根据公式(x-mean)/std,结合均值和方差的性质,可以算出新数据的均值会变成0,方差会变成1。

总结:
如果是Normalize((0.5,0.5,0.5),(0.5,0.5,0.5)),确实是归一化到【-1,1】
如果是Normalize(channel_mean, channel_std),才是均值为0,标准差为1。


transforms 数据增强

数据增强又称为数据增广,数据扩增,它是对训练集进行变换,使训练集更丰富,从而让模型更具泛化能力。

transforms——裁剪

  1. transforms.CenterCrop  

  • 功能:从图像中心裁剪图片
  • size:所需裁剪图片尺寸

    2、transforms.RandomCrop

  • 功能:从图片中随机裁剪出尺寸为size的图片
  • size:所需裁剪图片尺寸
  • padding:设置填充大小。当为a时,上下左右均填充a个像素,当为(a,b)时,上下填充b个像素,左右填充a个像素。当为(a,b,c,d)时,左上右下分别填充abcd
  • pad_if_need:若图像小于设定size,则填充
  • padding_mode:填充模式:(1)constant像素值由fill设定(默认模式);(2)edge像素值由图像边缘像素决定(3)reflect镜像填充,最后一个像素不镜像,eg,[1,2,3,4]->[3,2,1,2,3,4,3,2];(4)symmetric镜像填充,最后一个像素镜像,eg,[1,2,3,4]->[2,1,1,2,3,4,4,3]
  • fill:constant时,设置填充的像素值

transforms——翻转和旋转

  1. RandomHorizontalFlip 水平翻转
  2. RandomVerticalFlip  垂直翻转
  3. RandomRotation  随机旋转图像

自定义tansfroms方法

class Compose(object):
    def __call__(self,img):
        for t in self.transforms:
               img = t(img)
        return img

transforms方法是在Compose类的__call__方法中被调用的,对一组transforms方法进行for循环,每次循序的挑选并执行transforms方法。

  • 自定义transforms要素:

  1. 仅接收一个参数,返回一个参数
  2. 注意上下游的输出和输入,数据类型必须匹配

通过类实现多参数传入,下图为自定义transforms的基本参数,一个init,一个call

class YourTransforms(object):
    def __init__(self,...):
        ...
    def __call__(self,img):
        ...
        return img

椒盐噪声

  • 椒盐噪声又称为又称为脉冲噪声,是一种随机出现的白点或者黑点,白点称为盐噪声,黑点称为椒噪声。
  • 信噪比(SNR):衡量噪声的比例,图像中为图像像素的占比

class AddPepperNoise(object):
	def __init__(self, snr, p):
		self.snr = snr
		self.p = p
	
	def __call__(self, img):
		'''
		添加椒盐噪声具体实现过程
		'''
		return img

你可能感兴趣的:(语义分割,计算机视觉,pytorch)