我愿称之为全网最通透的layernorm讲解(往下翻)

news/2024/11/17 11:29:07/文章来源:https://www.cnblogs.com/chentiao/p/18350985

   在我们平常面试和工程中会用到BN和LN,但或许没有去了解过BN和LN到底在那个维度上进行的正则化(减均值除以标准差)。下面将会采用各种例子来为大家介绍BN层和LN层各个参数以及差别。

 一、BatchNorm(批标准化):

  BatchNorm一共有三个函数分别是BatchNorm1d,BatchNorm2d,BatchNorm3d,她们的输入的tensor的维度是不一样的,以及参数的定义也是不一样的,我们一个一个的说。

 BatchNorm1d:

torch.nn.BatchNorm1d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)

参数含义:

  num_features:如果你输出的tensor是(N,C,L)维度的,那么这里定义为C;如果你输入的tensor是(N,L)维度的,则此处设定为L。这里N表示batch_size,C是数据的channel(通道),L是特征维度(数据长度)。

  eps:对输入数据进行归一化时加在分母上,防止除零。

  momentum :计算整个样本全局均值running_mean和方差running_var时是采用动量的模式进行,这个设定的是这个动量的大小,后文会提到。

  affine:一个布尔值,当设置为True时,此模块具有可学习的仿射参数weight和bias,一般我们做正则化是使得数据服从N(0,1),但是经过仿射变换可以到到N(bias,weight^2)的正态分布。这两个参数可以学习的,初始化时weigt为1,bias为0

  track_running_stats: 设为True时,BatchNorm层会统计全局均值running_mean和方差running_var

  从参数的含义我们可以知道,针对不同的tensor输出,我们提前设定的num_features时不一样的,当做BN的tensor维度是(N,C,L)时,我们定义的num_features是C,意味着我们会根据每个通道的不同样本的L长度特征进行相加再除以N*L得到均值,因此会得到C个均值。再具体点的实例就是输入为(5,3,10)的tensor,我们会取[0,0,:],[1,0,:],....[4,0,:]这些向量的数值(一共有5*10个数字)加起来除以5*10得到第一个通道的均值,并对以上数字进行正则化。当做BN的tensor维度是(N,L),我们定义的num_features是L,因此我们会计算出L个均值和方差,可以看成(N,L,1)的形式,每一个特征长度为1,只有L个通道,具体点的实例:输入维度为(4,5)的tensor,会取[0,0],[1,0],[2,0],[3,0]这4个数进行正则化,可以知道我们最终会得到L个均值和方差

  momentum参数的应用是为了计算全局样本的均值和方差的,因为当训练完样本数据后,我们可以得到一个整个样本的均值和方差,但是这个均值和方差的得到不是把所有样本都计算遍历一遍计算得到的,而是在每一个betch经过BatchNorm1d的时候,内部会储存下该次batch的均值和方差,并通过以下等式来计算得到全局的均值和方差。

xnew=(1momentum)×xcur+momentum×xbatchxnew=(1−momentum)×xcur+momentum×xbatch

  如果track_running_stats=False,则在内部不会进行计算全局均值running_mean和方差running_var

  下面直接上例子来看看:

?
1
2
3
4
5
6
7
m = nn.BatchNorm1d(5, affine=False, momentum=0.1)
tensor = torch.FloatTensor([i for i in range(20)]).reshape(4,5)
print(tensor)
output = m(tensor)
print(output)
print(m.running_mean)
print(m.running_var)

  结果如下:

复制代码
### tensor的取值
tensor([[ 0., 1., 2., 3., 4.],[ 5., 6., 7., 8., 9.],[10., 11., 12., 13., 14.],[15., 16., 17., 18., 19.]])
### BN之后的结果 tensor([[
-1.3416, -1.3416, -1.3416, -1.3416, -1.3416],[-0.4472, -0.4472, -0.4472, -0.4472, -0.4472],[ 0.4472, 0.4472, 0.4472, 0.4472, 0.4472],[ 1.3416, 1.3416, 1.3416, 1.3416, 1.3416]])
### 全局均值(由于momentum=0.1,running_mean初始值为0,所以这是根据公式计算过后的结果) tensor([
0.7500, 0.8500, 0.9500, 1.0500, 1.1500])
### 全局方差 tensor([
5.0667, 5.0667, 5.0667, 5.0667, 5.0667])
复制代码

  我们再来看看,输入的tensor是三维的情况:

?
1
2
3
4
5
6
7
m = nn.BatchNorm1d(2, affine=False, momentum=0.1)
tensor = torch.FloatTensor([i for i in range(18)]).reshape(3,2,3)
print(tensor)
output = m(tensor)
print(output)
print(m.running_mean)
print(m.running_var)

  结果如下:

复制代码
### tensor的取值
tensor([[[ 0., 1., 2.],[ 3., 4., 5.]],[[ 6., 7., 8.],[ 9., 10., 11.]],[[12., 13., 14.],[15., 16., 17.]]])
### BN之后的取值 tensor([[[
-1.4094e+00, -1.2081e+00, -1.0067e+00],[-1.4094e+00, -1.2081e+00, -1.0067e+00]],[[-2.0135e-01, -2.9802e-08, 2.0135e-01],[-2.0135e-01, 5.9605e-08, 2.0135e-01]],[[ 1.0067e+00, 1.2081e+00, 1.4094e+00],[ 1.0067e+00, 1.2081e+00, 1.4094e+00]]])
### 全局均值和方差 tensor([
0.7000, 1.0000]) tensor([3.6750, 3.6750])
复制代码

  大概检验一下,根据计算公式,第一个均值应该是每个样本通道1的所有特征求和得到的均值也就是(0+1+2+6+7+8+12+13+14)/ 9 = 7 (全局均值是乘以了momentum=0.1的结果),方差应该是:[(07)2+.....+(147)2]/9=24.66[(0−7)2+.....+(14−7)2]/9=24.66(注意这里是有偏样本方差分母是N)。[0,0,0]位置这个数BN之后为:(07)/24.66=1.4094(0−7)/24.66=−1.4094,有些同学可能或说为什么全局方差不对呢?因为全局方差中计算的是无偏的样本方差(分母是N-1),并且初始值running_var=1.

 BatchNorm2d:

torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)

 参数:

  num_features:输入的tensor是(N,C,H,W)维度,num_features定义为C。其中N表示batch_size

  为什么会出现BatchNorm2d呢?那是因为1d只能处理(N,L)和(N,C,L)两种输入,但是在CV领域特征图常常是(N,C,H,W),这时候1d就没法处理了,就需要2d了,其实有了1d的了解,2d就是把剩下两个维度的数全部搞在一起进行计算均值和方差。具体点的例子就是输入tensor为(5,3,100,120),那我们定义的num_featurs=3那么我们将提取[0,0,:,:],[1,0,:,:],[2,0,:,:]...[4,0,:,:]这几个矩阵的所有数(一共有5*100*120个数字)计算均值和方差,因此我们可以知道我们最后得到的依然是3组均值和方差。

  其他参数和1d是一样的,作用也是一样的。我们接下来看下例子。

?
1
2
3
4
5
6
7
m = nn.BatchNorm2d(3, affine=False, momentum=0.1)
tensor = torch.FloatTensor([i for i in range(36)]).reshape(3,3,2,2)
print(tensor)
output = m(tensor)
print(output)
print(m.running_mean)
print(m.running_var)

  结果如下:

复制代码
tensor([[[[ 0.,  1.],[ 2.,  3.]],[[ 4.,  5.],[ 6.,  7.]],[[ 8.,  9.],[10., 11.]]],[[[12., 13.],[14., 15.]],[[16., 17.],[18., 19.]],[[20., 21.],[22., 23.]]],[[[24., 25.],[26., 27.]],[[28., 29.],[30., 31.]],[[32., 33.],[34., 35.]]]])
tensor([[[[-1.3690, -1.2676],[-1.1661, -1.0647]],[[-1.3690, -1.2676],[-1.1661, -1.0647]],[[-1.3690, -1.2676],[-1.1661, -1.0647]]],[[[-0.1521, -0.0507],[ 0.0507,  0.1521]],[[-0.1521, -0.0507],[ 0.0507,  0.1521]],[[-0.1521, -0.0507],[ 0.0507,  0.1521]]],[[[ 1.0647,  1.1661],[ 1.2676,  1.3690]],[[ 1.0647,  1.1661],[ 1.2676,  1.3690]],[[ 1.0647,  1.1661],[ 1.2676,  1.3690]]]])
## 全局均值和方差,计算方式看前文 tensor([
1.3500, 1.7500, 2.1500]) tensor([11.5091, 11.5091, 11.5091])
复制代码

  我们就简单计算下前两个均值就好了,第一个均值:(0+1+2+3+12+13+14+15+24+25+26+27)/ 12 = 13.5,第二个均值:(4+5+6+7+16+17+18+19+28+29+30+31)/ 12 = 17.5大家可以看看我就计算了那些数字。

 BatchNorm3d:

torch.nn.BatchNorm3d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None)

   有了前面的知识,其实我们也能猜到3d处理的就是输入tensor为(N,C,D,H,W)的情况,此时num_features应该定义C。给一个例子来说就是进行把最后三个维度的数字按照样本个数全部加起来,数字总数为N*D*H*W,最后依然会产生C个均值和方差。

 二、LayerNorm(层标准化):

torch.nn.LayerNorm(normalized_shape, eps=1e-05, elementwise_affine=True, device=None, dtype=None)

  参数看起来和BatchNorm差不多,但是LayerNorm不会记录全局的均值和方差。最重要的就是前三个参数。

  normalized_shape:可以设定为:int,列表,或者torch.Size([3, 4])

  eps:对输入数据进行归一化时加在分母上,防止除零。

  elementwise_affine:是否进行仿射变换,如果是True则此模块具有可学习的仿射参数weight和bias,使得能够使得数据不仅仅是服从N(0,1)正态分布。

normalized_shape传入整数

  如果normalized_shape传入的是整数,那么会在输入tensor的最后一维一定要和这个整数一样,比如normalized_shape=4,则tensor的最后一个维度一定要为4,而进行的正则化就是最最后一位的数字进行的,更具体的例子,输入tensor维度为(3,4),那么会对[0,0],[0,1],[0,2],[0,3]位置的加一起求均值和方差(一共只有4个数),那么就会出现3个均值和方差。看例子:

?
1
2
3
4
5
6
7
m = nn.LayerNorm(4, elementwise_affine=True)
tensor = torch.FloatTensor([i for i in range(12)]).reshape(3,4)
print(tensor)
output = m(tensor)
print(output)
print(m.weight)
print(m.bias)

  结果:

复制代码
tensor([[ 0.,  1.,  2.,  3.],[ 4.,  5.,  6.,  7.],[ 8.,  9., 10., 11.]])
tensor([[-1.3416, -0.4472,  0.4472,  1.3416],[-1.3416, -0.4472,  0.4472,  1.3416],[-1.3416, -0.4472,  0.4472,  1.3416]],grad_fn=<NativeLayerNormBackward0>)
Parameter containing:
tensor([1., 1., 1., 1.], requires_grad=True)
Parameter containing:
tensor([0., 0., 0., 0.], requires_grad=True)
复制代码

  大家可以自行计算下[0,1,2,3]这四个数正则化之后的结果。是不是就是图中所示。因此normalized_shape传入的是整数还是比较好理解的。

normalized_shape传入列表

  如果normalized_shape传入的是列表,比如[3,4],那么需要要求传入的tensor需要最后两个维度需要满足[3, 4],会把最后两个维度以用12个数据进行求均值和方差并正则化。具体一点的例子,传入的tensor维度为(N,C,3,4)那么会对【0,0,:,:】这12个数进行正则化,【0,1,:,:】这12个数进行正则化.....因此最后得到会得到N*C个均值和方差。看例子。

?
1
2
3
4
5
6
7
m = nn.LayerNorm([3,4], elementwise_affine=True)
tensor = torch.FloatTensor([i for i in range(12*4)]).reshape(2,2,3,4)
print(tensor)
output = m(tensor)
print(output)
print(m.weight)
print(m.bias)

  结果如下:

复制代码
tensor([[[[ 0.,  1.,  2.,  3.],[ 4.,  5.,  6.,  7.],[ 8.,  9., 10., 11.]],[[12., 13., 14., 15.],[16., 17., 18., 19.],[20., 21., 22., 23.]]],[[[24., 25., 26., 27.],[28., 29., 30., 31.],[32., 33., 34., 35.]],[[36., 37., 38., 39.],[40., 41., 42., 43.],[44., 45., 46., 47.]]]])
tensor([[[[-1.5933, -1.3036, -1.0139, -0.7242],[-0.4345, -0.1448,  0.1448,  0.4345],[ 0.7242,  1.0139,  1.3036,  1.5933]],[[-1.5933, -1.3036, -1.0139, -0.7242],[-0.4345, -0.1448,  0.1448,  0.4345],[ 0.7242,  1.0139,  1.3036,  1.5933]]],[[[-1.5933, -1.3036, -1.0139, -0.7242],[-0.4345, -0.1448,  0.1448,  0.4345],[ 0.7242,  1.0139,  1.3036,  1.5933]],[[-1.5933, -1.3036, -1.0139, -0.7242],[-0.4345, -0.1448,  0.1448,  0.4345],[ 0.7242,  1.0139,  1.3036,  1.5933]]]],grad_fn=<NativeLayerNormBackward0>)
Parameter containing:
tensor([[1., 1., 1., 1.],[1., 1., 1., 1.],[1., 1., 1., 1.]], requires_grad=True)
Parameter containing:
tensor([[0., 0., 0., 0.],[0., 0., 0., 0.],[0., 0., 0., 0.]], requires_grad=True)
复制代码

  大家也可以自行计算下[0, 1, 2, 3,4,5...11]这12个数字最后正则化之后的结果。

  这里要注意的是weight和bias在训练过程中是会更新的,并且会在一次正则化中使用多次,比如上面(2,2,3,4)的例子,(0,0,:,:)会使用weight和bias对应位置的数字,(0,1,:,:)也会使用对应位置数字。

 

参考网页:

BatchNorm2d — PyTorch 1.10 documentation

BatchNorm1d — PyTorch 1.10 documentation

BatchNorm3d — PyTorch 1.10 documentation

LayerNorm — PyTorch 1.10 documentation

pytorch LayerNorm参数详解,计算过程_拿铁大侠的博客-CSDN博客_nn.layernorm使用

pytorch BatchNorm参数详解,计算过程_拿铁大侠的博客-CSDN博客_batchnorm 参数

【PyTorch】详解pytorch中nn模块的BatchNorm2d()函数_安静-CSDN博客_nn.batchnorm2d

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/780353.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

远程桌面授权服务远程代码执行漏洞(CVE-2024-38077)漏洞预警

影响范围 开启了RDL服务的Windows Server 2000到2025都会受到影响 满足以上条件可以直接RCE 关于RDL 服务名全称,Remote Desktop Licensing,如图:这个就是RDL服务,一般运维应该不会刻意去安装这个的,常用自带默认的远程桌面服务加个白名单就够了:解决办法 1、没装RDL服务…

boot sharing

1.BootRom 计算机系统启动过程的第一步,负责进行硬件初始化和加载Boot Loader到RAM等基本操作 根据不同的启动模式(硬件拨码)去不同的存储设备取bootlaoder(决定指令从哪来 bootrom: rom:掉电不易失存储器,通常为nor flash,可以芯片内执行 boot:存储在上述介质中的一小段…

Flowable自动审核、相邻节点审核、已存在审核人审核

Flowable 监听器的使用 需求背景: 1、发起人发起流程,可以实现发起人自动审核 2、上一节点的审核人,与当前节点审核人相同时,自动审核 3、整个流程实例中,存在已审核的审核人,当前节点自动审核 流程图如下所示:针对于自动审核设置,其实是由前端内置bpnm.xml 去设置的,…

ALLEGRO软件打开提示说没有内存

打开云雾管理器,确认下是不是没有内存,如果是内存没问题就重启下电脑

大数据框架之一——Hadoop学习第四天

1、MapReduce序列化(接着昨天的知识继续学习)序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。 当两个进程在进行远…

div-固定在页面中间,不被其他元素覆盖

最开始设置的子元素D 是text-align:center,子元素C的内容过长的时候,会发现子元素D不在页面正中了 所以需要把子元素D设置成固定中间,把子元素D设置成固定中间后,发现元素B把子元素D给覆盖了一部分, 所以需要在父元素A和元素B之间加一个空的div,给div设置高度后,父元素A…

div-实现靠左和靠右

display:block; display:inline-table;<div style="margin-bottom: 8px;display:block;"><div style="float:left;color:#ABABAB;font-size:12px;">实际工时</div><div style="float:right;font-size:12px;">{{ item.ac…

pyinstaller 打包uvicorn的坑

0.1 你使用fastapi搭建服务,并使用uvicorn来启动。 0.2 你使用pyinstaller打包成exe文件,并且需要隐藏控制台窗口。 0.3 执行exe文件时,程序报错了(模块相关错误和日志相关错误),网上找的资料都无法解决你的问题。 如果满足这些条件,可以继续往下看,我的方法主要参考ch…

JAVA线程资源共享问题

JAVA多线程共享资源问题 场景引入 这个场景是一个典型的多线程共享资源的场景,主要目的是测试和观察多个线程对共享变量 sum 进行并发操作时是否会出现线程安全问题 场景描述 共享资源:共享变量 sum,初始值为 0。 多个线程同时对 sum 进行操作,一个线程负责自增操作,另一个…

透过vivo的鞋子和亚马逊的椅子:探究新商业文明

有一种领先,叫微微领先。在亚马逊的会议室中,有一把永远为用户准备的椅子;在vivo,则要求不能只坐在会议室看PPT上的用户需求,而是要穿着用户的鞋子在企业奔跑。殊途同归,将“以用户导向”作为企业长期发展的基石,亚马逊和vivo真正将“让用户Happy”通过道、法、术落到了…