PyTorch框架学习十八——Layer Normalization、Instance Normalization、Group Normalization
PyTorch框架學(xué)習(xí)十八——Layer Normalization、Instance Normalization、Group Normalization
- 一、為什么要標(biāo)準(zhǔn)化?
- 二、BN、LN、IN、GN的異同
- 三、Layer Normalization
- 四、Instance Normalization
- 五、Group Normalization
上次筆記介紹了Batch Normalization以及它在PyTorch中的使用:https://blog.csdn.net/qq_40467656/article/details/108375181
這次筆記將介紹由BN引發(fā)的其他標(biāo)準(zhǔn)化層,它們各自適用于不同的應(yīng)用場景,分別是適用于變長網(wǎng)絡(luò)的Layer Normalization;適用于圖像生成的Instance Normalization;適用于小mini-batch的Group Normalization。
一、為什么要標(biāo)準(zhǔn)化?
這個(gè)在上次BN的筆記中介紹過,本意是為了解決ICS問題,即隨著網(wǎng)絡(luò)層數(shù)加深,數(shù)據(jù)分布異常(很小或很大),從而導(dǎo)致訓(xùn)練困難。詳情回顧:https://blog.csdn.net/qq_40467656/article/details/108375181
二、BN、LN、IN、GN的異同
- 同:都做了標(biāo)準(zhǔn)化的工作。
- 異:均值和方差的求取方式不一樣,即選擇的計(jì)算區(qū)域不一樣,這個(gè)可以看完下一小節(jié)的詳細(xì)介紹回過來看,可能會更能理解。
三、Layer Normalization
LN提出的起因是因?yàn)锽N不適用于變長的網(wǎng)絡(luò),如RNN,這部分的內(nèi)容還沒有接觸過,但是可以簡單理解為這種網(wǎng)絡(luò)的神經(jīng)元個(gè)數(shù)是會變化的,不是一樣的,如下圖所示:
ps:注意這里的橫軸不是數(shù)據(jù)樣本個(gè)數(shù),只是代表這層網(wǎng)絡(luò)層神經(jīng)元可能會變?yōu)?/3/4個(gè),在每種個(gè)數(shù)的情況下,樣本數(shù)還是一個(gè)batchsize的大小。
第一次可能有五個(gè)特征,計(jì)算得到五個(gè)均值和方差,而第二輪計(jì)算時(shí),網(wǎng)絡(luò)層的神經(jīng)元變?yōu)?個(gè),而BN里計(jì)算均值和方差是需要用到之前的結(jié)果的,這里之前的五個(gè)均值方差就對應(yīng)不了三個(gè)特征,所以BN在這種情況下是不適用的。
那么LN是怎么計(jì)算均值和方差的呢?以一維的情況為例:
之所以稱為Layer Norm,就是對該層的數(shù)據(jù)求均值和方差,不再按照特征那個(gè)維度去求,每個(gè)樣本都單獨(dú)求其均值方差,可以理解為逐樣本的求取方式。
二維三維的情況類似,如下圖所示:
LN需要注意的地方:
- 不再有running_mean和running_var
- gamma和beta為逐元素的
LN在PyTorch中的實(shí)現(xiàn):
torch.nn.LayerNorm(normalized_shape: Union[int, List[int], torch.Size], eps: float = 1e-05, elementwise_affine: bool = True)參數(shù)如下所示:
下面看一個(gè)PyTorch實(shí)現(xiàn)的例子:
import torch import numpy as np import torch.nn as nn import sys, os hello_pytorch_DIR = os.path.abspath(os.path.dirname(__file__)+os.path.sep+".."+os.path.sep+"..") sys.path.append(hello_pytorch_DIR) from tools.common_tools import set_seedset_seed(1) # 設(shè)置隨機(jī)種子# ======================================== nn.layer norm flag = 1 # flag = 0 if flag:batch_size = 8num_features = 3features_shape = (3, 4)feature_map = torch.ones(features_shape) # 2Dfeature_maps = torch.stack([feature_map * (i + 1) for i in range(num_features)], dim=0) # 3Dfeature_maps_bs = torch.stack([feature_maps for i in range(batch_size)], dim=0) # 4D# feature_maps_bs shape is [8, 3, 3, 4], B * C * H * Wln = nn.LayerNorm(feature_maps_bs.size()[1:], elementwise_affine=True)# ln = nn.LayerNorm(feature_maps_bs.size()[1:], elementwise_affine=False)# ln = nn.LayerNorm([3, 3, 4])# ln = nn.LayerNorm([3, 3])output = ln(feature_maps_bs)print("Layer Normalization")print(ln.weight.shape)print(feature_maps_bs[0, ...])print(output[0, ...])結(jié)果如下:
Layer Normalization torch.Size([3, 3, 4]) tensor([[[1., 1., 1., 1.],[1., 1., 1., 1.],[1., 1., 1., 1.]],[[2., 2., 2., 2.],[2., 2., 2., 2.],[2., 2., 2., 2.]],[[3., 3., 3., 3.],[3., 3., 3., 3.],[3., 3., 3., 3.]]]) tensor([[[-1.2247, -1.2247, -1.2247, -1.2247],[-1.2247, -1.2247, -1.2247, -1.2247],[-1.2247, -1.2247, -1.2247, -1.2247]],[[ 0.0000, 0.0000, 0.0000, 0.0000],[ 0.0000, 0.0000, 0.0000, 0.0000],[ 0.0000, 0.0000, 0.0000, 0.0000]],[[ 1.2247, 1.2247, 1.2247, 1.2247],[ 1.2247, 1.2247, 1.2247, 1.2247],[ 1.2247, 1.2247, 1.2247, 1.2247]]], grad_fn=<SelectBackward>)這邊只打印了第一個(gè)數(shù)據(jù)的結(jié)果,它的均值是2,所以中間一個(gè)3×4的特征標(biāo)準(zhǔn)化之后全為0。
四、Instance Normalization
IN層的提出是因?yàn)樵趫D像生成任務(wù)中,一個(gè)batch里的圖像的風(fēng)格可能不盡相同,不能通過BN的計(jì)算方式去將各個(gè)風(fēng)格的特征混為一談,所以BN在這種情況下會不適用。
那么,IN層的計(jì)算方式的思路是逐Instance(channel)地計(jì)算均值和方差,如下圖所示:
它是每一個(gè)樣本每一個(gè)特征都去計(jì)算均值方差然后標(biāo)準(zhǔn)化。
IN層在PyTorch中的實(shí)現(xiàn)如下所示:(以二維為例)
torch.nn.InstanceNorm2d(num_features: int, eps: float = 1e-05, momentum: float = 0.1, affine: bool = False, track_running_stats: bool = False)參數(shù)如下所示:
看一個(gè)IN的例子:
flag = 1 # flag = 0 if flag:batch_size = 3num_features = 3momentum = 0.3features_shape = (2, 2)feature_map = torch.ones(features_shape) # 2Dfeature_maps = torch.stack([feature_map * (i + 1) for i in range(num_features)], dim=0) # 3Dfeature_maps_bs = torch.stack([feature_maps for i in range(batch_size)], dim=0) # 4Dprint("Instance Normalization")print("input data:\n{} shape is {}".format(feature_maps_bs, feature_maps_bs.shape))instance_n = nn.InstanceNorm2d(num_features=num_features, momentum=momentum, affine=True, track_running_stats=True)for i in range(1):outputs = instance_n(feature_maps_bs)print(outputs)print("\niter:{}, running_mean.shape: {}".format(i, instance_n.running_mean.shape))print("iter:{}, running_var.shape: {}".format(i, instance_n.running_var.shape))print("iter:{}, weight.shape: {}".format(i, instance_n.weight.shape))print("iter:{}, bias.shape: {}".format(i, instance_n.bias.shape))結(jié)果如下:
Instance Normalization input data: tensor([[[[1., 1.],[1., 1.]],[[2., 2.],[2., 2.]],[[3., 3.],[3., 3.]]],[[[1., 1.],[1., 1.]],[[2., 2.],[2., 2.]],[[3., 3.],[3., 3.]]],[[[1., 1.],[1., 1.]],[[2., 2.],[2., 2.]],[[3., 3.],[3., 3.]]]]) shape is torch.Size([3, 3, 2, 2]) tensor([[[[0., 0.],[0., 0.]],[[0., 0.],[0., 0.]],[[0., 0.],[0., 0.]]],[[[0., 0.],[0., 0.]],[[0., 0.],[0., 0.]],[[0., 0.],[0., 0.]]],[[[0., 0.],[0., 0.]],[[0., 0.],[0., 0.]],[[0., 0.],[0., 0.]]]], grad_fn=<ViewBackward>)iter:0, running_mean.shape: torch.Size([3]) iter:0, running_var.shape: torch.Size([3]) iter:0, weight.shape: torch.Size([3]) iter:0, bias.shape: torch.Size([3])五、Group Normalization
GN的提出是因?yàn)?#xff0c;隨著如今數(shù)據(jù)樣本變得越來越大,以現(xiàn)有的GPU能力可能只能放置比較小的mini-batch,而一個(gè)batch比較少的數(shù)據(jù)的話,使用BN可能計(jì)算得到的均值和方差就有較大的偏差,估計(jì)的值不準(zhǔn),所以BN在小mini-batch的場景下不適用。
那么GN的計(jì)算思路就是:數(shù)據(jù)樣本不夠,通道(特征)數(shù)來湊,其如下所示:
圖中所示是將一個(gè)樣本的兩個(gè)特征劃分為一個(gè)group,這里只是為了說明GN的原理,實(shí)際上特征數(shù)是很多的,比如256,那么我們分為兩組的話,一組有128個(gè)特征通道,數(shù)量還是比較可觀的,在這樣的分組下對每一組單獨(dú)求取均值方差然后標(biāo)準(zhǔn)化。
注意:
- 不再有running_mean和running_var,與LN一致。
- gamma和beta為逐通道的。
應(yīng)用場景:大模型,小batch size的任務(wù)。
GN在PyTorch中的實(shí)現(xiàn)如下:
torch.nn.GroupNorm(num_groups: int, num_channels: int, eps: float = 1e-05, affine: bool = True)參數(shù)如下所示:
下面看一個(gè)例子:
flag = 1 # flag = 0 if flag:batch_size = 2num_features = 4num_groups = 2 # 3 Expected number of channels in input to be divisible by num_groupsfeatures_shape = (2, 2)feature_map = torch.ones(features_shape) # 2Dfeature_maps = torch.stack([feature_map * (i + 1) for i in range(num_features)], dim=0) # 3Dfeature_maps_bs = torch.stack([feature_maps * (i + 1) for i in range(batch_size)], dim=0) # 4Dgn = nn.GroupNorm(num_groups, num_features)outputs = gn(feature_maps_bs)print("Group Normalization")print(gn.weight.shape)print(outputs[0])結(jié)果如下:
Group Normalization torch.Size([4]) tensor([[[-1.0000, -1.0000],[-1.0000, -1.0000]],[[ 1.0000, 1.0000],[ 1.0000, 1.0000]],[[-1.0000, -1.0000],[-1.0000, -1.0000]],[[ 1.0000, 1.0000],[ 1.0000, 1.0000]]], grad_fn=<SelectBackward>)最后放一張BN、LN、IN和GN的計(jì)算方式示例圖,幫助理解:
總結(jié)
以上是生活随笔為你收集整理的PyTorch框架学习十八——Layer Normalization、Instance Normalization、Group Normalization的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 散列表(字典)
- 下一篇: GIOU loss+DIOU loss+