pytorch中的归一化:BatchNorm、LayerNorm 和 GroupNorm

news2024/11/26 12:33:54

1 归一化概述

训练深度神经网络是一项具有挑战性的任务。 多年来,研究人员提出了不同的方法来加速和稳定学习过程。 归一化是一种被证明在这方面非常有效的技术。

1.1 为什么要归一化

数据的归一化操作是数据处理的一项基础性工作,在一些实际问题中,我们得到的样本数据都是多个维度的,即一个样本是用多个特征来表示的,数据样本的不同特征可能会有不同的尺度,这样的情况会影响到数据分析的结果。为了解决这个问题,需要进行数据归一化处理。原始数据经过数据归一化后,各特征处于同一数量级,适合进行综合对比评价。

例如,我们现在用两个特征构建一个简单的神经网络模型。 这两个特征一个是年龄:范围在 0 到 65 之间,另一个是工资:范围从 0 到 10 000。我们将这些特征提供给模型并计算梯度。

不同规模的输入导致不同的权重更新和优化器的步骤向最小值的方向不均衡。这也使损失函数的形状不成比例。在这种情况下,就需要使用较低的学习速率来避免过冲,这就意味着较慢的学习过程。

所以我们的解决方案是输入进行归一化,通过减去平均值(定心)并除以标准偏差来缩小特征。

此过程也称为“漂白”,处理后所有的值具有 0 均值和单位方差,这样可以提供更快的收敛和更稳定的训练。

1.2 归一化的作用

在深度学习中,数据归一化是一项关键的预处理步骤,用于优化神经网络模型的训练过程和性能。归一化技术有助于解决梯度消失和梯度爆炸问题,加快模型的收敛速度,并提高模型的鲁棒性和泛化能力。详细介绍如下:

  • 梯度消失和梯度爆炸问题:在深度神经网络中,梯度消失和梯度爆炸是常见的问题。数据归一化可以缓解这些问题,使得梯度在合理的范围内进行传播,有助于提高模型的训练效果。

  • 特征尺度不一致:深度学习模型对特征的尺度非常敏感。如果不同特征具有不同的尺度范围,某些特征可能会主导模型的训练过程,而其他特征的影响可能被忽略。通过数据归一化,可以将不同特征的尺度统一到相同的范围,使得模型能够平衡地对待所有特征,避免尺度不一致带来的偏差。

  • 模型收敛速度:数据归一化可以加快模型的收敛速度。当数据被归一化到一个较小的范围时,模型可以更快地找到合适的参数值,并减少训练过程中的震荡和不稳定性。这样可以节省训练时间,提高模型的效率。

  • 鲁棒性和泛化能力:通过数据归一化,模型可以更好地适应不同的数据分布和噪声情况。归一化可以增加模型的鲁棒性,使得模型对输入数据的变化和扰动具有更好的容忍度。同时,归一化还有助于提高模型的泛化能力,使得模型在未见过的数据上表现更好。

1.3 归一化的步骤

归一化通过对数据的特定维度上进行归一化操作来调整输入数据的分布,使其具有零均值和单位方差。一般通过以下步骤对输入进行归一化:

  • 对于给定的输入数据,在给定的维度上计算其均值和方差。

  • 使用计算得到的均值和方差对输入数据进行标准化,将其零均值化并使其具有单位方差。

  • 对标准化后的数据进行缩放和平移操作,通过可学习的参数进行调整,以恢复模型对数据的表达能力。

进一步地,在归一化中,通过缩放和平移操作,引入了可学习的参数,即缩放参数(scale)和平移参数(shift)。这些参数用于在标准化后的数据上进行线性变换,以恢复模型的表达能力。

具体而言,在每个特征维度上,假设归一化后的数据为\hat{x},则通过以下公式计算最终的输出:y=\gamma \hat{x}+\beta 。其中, y是最终的输出, \gamma是缩放参数(scale), \beta是平移参数(shift)。这两个参数是可学习的,它们可以通过反向传播和优化算法(如随机梯度下降)来进行更新。在训练过程中,模型会通过梯度下降的方式来调整这些参数,使得模型能够自适应地对不同的数据分布进行缩放和平移。这样,模型可以根据实际情况自由地调整每个特征的重要性和偏置,从而更好地适应不同的数据分布。

2 pytorch中的归一化

BatchNorm、LayerNorm 和 GroupNorm 都是深度学习中常用的归一化方式。它们通过将输入归一化到均值为 0 和方差为 1 的分布中,来防止梯度消失和爆炸,并提高模型的泛化能力。

2.1 BatchNorm

一般 CNN 中,卷积层后面会跟一个 BatchNorm 层,减少梯度消失和爆炸,提高模型的稳定性。

在PyTorch中,可以使用torch.nn.BatchNorm1dtorch.nn.BatchNorm2dtorch.nn.BatchNorm3d等批归一化层来实现批归一化。

实例代码:

import torch
import torch.nn as nn
import numpy as np

feature_array = np.array([[[[1, 0],  [0, 2]],
                           [[3, 4],  [1, 2]],
                           [[-2, 9], [7, 5]],
                           [[2, 3],  [4, 2]]],

                          [[[1, 2],  [-1, 0]],
                            [[1, 2], [3, 5]],
                            [[4, 7], [-6, 4]],
                            [[1, 4], [1, 5]]]], dtype=np.float32)

feature_tensor = torch.tensor(feature_array.copy(), dtype=torch.float32)
bn_out = nn.BatchNorm2d(num_features=4, eps=1e-5)(feature_tensor)
print(bn_out)

for i in range(feature_array.shape[1]):
    channel = feature_array[:, i, :, :]
    mean = feature_array[:, i, :, :].mean()
    var = feature_array[:, i, :, :].var()
    print(mean)
    print(var)

    feature_array[:, i, :, :] = (feature_array[:, i, :, :] - mean) / np.sqrt(var + 1e-5)
print(feature_array)

运行结果显示:

tensor([[[[ 0.3780, -0.6299],
          [-0.6299,  1.3859]],

         [[ 0.2847,  1.0441],
          [-1.2339, -0.4746]],

         [[-1.1660,  1.1660],
          [ 0.7420,  0.3180]],

         [[-0.5388,  0.1796],
          [ 0.8980, -0.5388]]],


        [[[ 0.3780,  1.3859],
          [-1.6378, -0.6299]],

         [[-1.2339, -0.4746],
          [ 0.2847,  1.8034]],

         [[ 0.1060,  0.7420],
          [-2.0140,  0.1060]],

         [[-1.2572,  0.8980],
          [-1.2572,  1.6164]]]], grad_fn=<NativeBatchNormBackward0>)
0.625
0.984375
2.625
1.734375
3.5
22.25
2.75
1.9375
[[[[ 0.37796253 -0.6299376 ]
   [-0.6299376   1.3858627 ]]

  [[ 0.28474656  1.0440707 ]
   [-1.2339017  -0.4745776 ]]

  [[-1.1659975   1.1659975 ]
   [ 0.7419984   0.3179993 ]]

  [[-0.53881454  0.17960484]
   [ 0.8980242  -0.53881454]]]


 [[[ 0.37796253  1.3858627 ]
   [-1.6378376  -0.6299376 ]]

  [[-1.2339017  -0.4745776 ]
   [ 0.28474656  1.8033949 ]]

  [[ 0.10599977  0.7419984 ]
   [-2.0139956   0.10599977]]

  [[-1.2572339   0.8980242 ]
   [-1.2572339   1.6164436 ]]]]

2.2 LayerNorm

Transformer block 中会使用到 LayerNorm , 一般输入尺寸形为 :(batch_size, token_num, dim),会在最后一个维度做 归一化: nn.LayerNorm(dim)

与批归一化不同,层归一化的计算是针对单个样本的特征维度进行的,其目的是将每个样本在单个层内的特征维度进行归一化,以增强特征之间的独立性,并提供更稳定的特征表示。具有以下优点:

  • 适用于处理单个样本:相比于批归一化,层归一化的计算是基于单个样本在单个层内的特征维度进行的,而不依赖于小批量样本的统计信息。这使得层归一化适用于处理单个样本的情况,例如在循环神经网络(RNN)中,每个时间步的输入可以被看作是一个单独的样本。

  • 适用于动态计算图和序列数据:由于层归一化的计算不依赖于小批量样本的统计信息,它更适合在动态计算图和序列数据上进行计算。在处理变长序列数据或使用动态计算图的场景下,层归一化可以提供更好的性能和效果。

另外,在Transformer模型中使用层归一化(Layer Normalization)主要是因为其独立的特征维度归一化的性质:Transformer模型的核心是自注意力机制,它在每个位置对输入序列中的所有位置进行关注。由于每个位置的特征维度可以看作是独立的,对每个位置进行层归一化可以提供更稳定的特征表示,减少特征之间的耦合,这有助于模型更好地学习位置之间的依赖关系,提高模型的表示能力。并且,减少了特征之间的内部协变量转移,这有助于缓解深度神经网络中常见的梯度消失和梯度爆炸问题,提高模型的训练效果和收敛速度。

层归一化(Layer Normalization)一般来说在激活函数之前应用:在层归一化之后应用激活函数可以使得激活函数的输入保持在归一化后的范围内,避免激活函数的输入过大或过小。这种方式与批标准化的应用顺序相似。

例如:在传统的RNN中,通常是将输入序列经过线性变换后再应用激活函数(如tanh或ReLU)进行非线性变换。然而,这样的操作可能会导致梯度消失或梯度爆炸问题,并且不同时间步的输入之间可能存在较大的变化。通过在激活函数之前应用层归一化,可以解决上述问题。

示例代码:

import torch
import torch.nn as nn
import numpy as np

feature_array = np.array([[[[1, 0],  [0, 2]],
                           [[3, 4],  [1, 2]],
                           [[2, 3],  [4, 2]]],

                          [[[1, 2],  [-1, 0]],
                            [[1, 2], [3, 5]],
                            [[1, 4], [1, 5]]]], dtype=np.float32)


feature_array = feature_array.reshape((2, 3, -1)).transpose(0, 2, 1)
feature_tensor = torch.tensor(feature_array.copy(), dtype=torch.float32)

ln_out = nn.LayerNorm(normalized_shape=3)(feature_tensor)
print(ln_out)

b, token_num, dim = feature_array.shape
feature_array = feature_array.reshape((-1, dim))
for i in range(b*token_num):
    mean = feature_array[i, :].mean()
    var = feature_array[i, :].var()
    print(mean)
    print(var)

    feature_array[i, :] = (feature_array[i, :] - mean) / np.sqrt(var + 1e-5)
print(feature_array.reshape(b, token_num, dim))

代码运行显示:

tensor([[[-1.2247,  1.2247,  0.0000],
         [-1.3728,  0.9806,  0.3922],
         [-0.9806, -0.3922,  1.3728],
         [ 0.0000,  0.0000,  0.0000]],

        [[ 0.0000,  0.0000,  0.0000],
         [-0.7071, -0.7071,  1.4142],
         [-1.2247,  1.2247,  0.0000],
         [-1.4142,  0.7071,  0.7071]]], grad_fn=<NativeLayerNormBackward0>)
2.0
0.6666667
2.3333333
2.888889
1.6666666
2.888889
2.0
0.0
1.0
0.0
2.6666667
0.88888884
1.0
2.6666667
3.3333333
5.555556
[[[-1.2247357   1.2247357   0.        ]
  [-1.3728105   0.980579    0.3922316 ]
  [-0.98057896 -0.39223155  1.3728106 ]
  [ 0.          0.          0.        ]]

 [[ 0.          0.          0.        ]
  [-0.70710295 -0.70710295  1.4142056 ]
  [-1.2247427   1.2247427   0.        ]
  [-1.4142123   0.7071062   0.7071062 ]]]

2.3 GroupNorm

batch size 过大或过小都不适合使用 BN,而是使用 GN。

(1)当 batch size 过大时,BN 会将所有数据归一化到相同的均值和方差。这可能会导致模型在训练时变得非常不稳定,并且很难收敛。

(2)当 batch size 过小时,BN 可能无法有效地学习数据的统计信息。

比如,Deformable DETR 中,就用到了 GroupNorm

示例代码:

import torch
import torch.nn as nn
import numpy as np

feature_array = np.array([[[[1, 0],  [0, 2]],
                           [[3, 4],  [1, 2]],
                           [[-2, 9], [7, 5]],
                           [[2, 3],  [4, 2]]],

                          [[[1, 2],  [-1, 0]],
                            [[1, 2], [3, 5]],
                            [[4, 7], [-6, 4]],
                            [[1, 4], [1, 5]]]], dtype=np.float32)

feature_tensor = torch.tensor(feature_array.copy(), dtype=torch.float32)
gn_out = nn.GroupNorm(num_groups=2, num_channels=4)(feature_tensor)
print(gn_out)

feature_array = feature_array.reshape((2, 2, 2, 2, 2)).reshape((4, 2, 2, 2))

for i in range(feature_array.shape[0]):
    channel = feature_array[i, :, :, :]
    mean = feature_array[i, :, :, :].mean()
    var = feature_array[i, :, :, :].var()
    print(mean)
    print(var)

    feature_array[i, :, :, :] = (feature_array[i, :, :, :] - mean) / np.sqrt(var + 1e-5)
feature_array = feature_array.reshape((2, 2, 2, 2, 2)).reshape((2, 4, 2, 2))
print(feature_array)

运行结果显示:

tensor([[[[-0.4746, -1.2339],
          [-1.2339,  0.2847]],

         [[ 1.0441,  1.8034],
          [-0.4746,  0.2847]],

         [[-1.8240,  1.6654],
          [ 1.0310,  0.3965]],

         [[-0.5551, -0.2379],
          [ 0.0793, -0.5551]]],


        [[[-0.3618,  0.2171],
          [-1.5195, -0.9406]],

         [[-0.3618,  0.2171],
          [ 0.7959,  1.9536]],

         [[ 0.4045,  1.2136],
          [-2.2923,  0.4045]],

         [[-0.4045,  0.4045],
          [-0.4045,  0.6742]]]], grad_fn=<NativeGroupNormBackward0>)
1.625
1.734375
3.75
9.9375
1.625
2.984375
2.5
13.75
[[[[-0.4745776  -1.2339017 ]
   [-1.2339017   0.28474656]]

  [[ 1.0440707   1.8033949 ]
   [-0.4745776   0.28474656]]

  [[-1.8240178   1.6654075 ]
   [ 1.0309665   0.3965256 ]]

  [[-0.55513585 -0.23791535]
   [ 0.07930512 -0.55513585]]]


 [[[-0.3617867   0.21707201]
   [-1.5195041  -0.9406454 ]]

  [[-0.3617867   0.21707201]
   [ 0.79593074  1.9536481 ]]

  [[ 0.40451977  1.2135593 ]
   [-2.2922788   0.40451977]]

  [[-0.40451977  0.40451977]
   [-0.40451977  0.67419964]]]]

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【c++随笔16】reserve之后,使用std::copy会崩溃?

【c随笔16】reserve之后&#xff0c;使用std::copy会崩溃? 一、reserve之后&#xff0c;使用std::copy会崩溃?二、函数std::reserve、std::resize、std::copy1、std::resize&#xff1a;2、std::reserve&#xff1a;3、std::copy&#xff1a; 三、崩溃原因分析方案1、你可以使…

【概率方法】重要性采样

从一个极简分布出发 假设我们有一个关于随机变量 X X X 的函数 f ( X ) f(X) f(X)&#xff0c;满足如下分布 p ( X ) p(X) p(X)0.90.1 f ( X ) f(X) f(X)0.10.9 如果我们要对 f ( X ) f(X) f(X) 的期望 E p [ f ( X ) ] \mathbb{E}_p[f(X)] Ep​[f(X)] 进行估计&#xff0…

极速学习SSM之SpringMVC笔记

文章目录 一、SpringMVC简介1、什么是MVC2、什么是SpringMVC3、SpringMVC的特点 二、HelloWorld1、开发环境2、创建maven工程a>添加web模块b>打包方式&#xff1a;warc>引入依赖 3、配置web.xmla>默认配置方式b>扩展配置方式 4、创建请求控制器5、创建springMVC…

ansible中的角色

1.理解roles在企业中的定位及写法 查看创建目录结构 ansible - galaxy list 指定新的位置 vim ansible.cfg roles_path ~/.ansible/roles 建立项目 cd roles/ ansible-galaxy init vsftpd tree vsftpd/ 编辑任务执行&#xff08;顺序&#xff09;文件 vim vsftpd/tas…

selenium库的使用

来都来了给我点个赞收藏一下再走呗&#x1f339;&#x1f339;&#x1f339;&#x1f339;&#x1f339; 目录 一、下载需要用到的python库selenium 二、selenium的基本使用 1.在python代码引入库 2.打开浏览器 3.元素定位 1&#xff09;通过id定位 2&#xff09;通过标…

Go开发运维:Go服务发布到K8S集群

目录 一、实验 1.Go服务发布到k8s集群 二、问题 1.如何从Harbor拉取镜像 一、实验 1.Go服务发布到k8s集群 &#xff08;1&#xff09;linux机器安装go(基于CentOS 7系统) yum install go -y &#xff08;2&#xff09;查看版本 go version &#xff08;3&#xff09;创…

UE引擎 LandscapeGrass 实现机制分析(UE5.2)

前言 随着电脑和手机硬件性能越来越高&#xff0c;游戏越来越追求大世界&#xff0c;而大世界非常核心的一环是植被&#xff0c;目前UE5引擎提供给植被生成的主流两种方式为 手刷植被和LandscapeGrass(WeightMap程序化植被)。当然UE5.3推出新一代PCGFramework 节点程序化生成框…

办公word-从不是第一页添加页码

总结 实际需要注意的是&#xff0c;分隔符、分节符和分页符并不是一个含义 分隔符包含其他两个&#xff1b;分页符&#xff1a;是增加一页&#xff1b;分节符&#xff1a;指将文档分为几部分。 从不是第一页插入页码1步骤 1&#xff0c;插入默认页码 自己可以测试时通过**…

一文掌握Ascend C孪生调试

1 What&#xff0c;什么是孪生调试 Ascend C提供孪生调试方法&#xff0c;即CPU域模拟NPU域的行为&#xff0c;相同的算子代码可以在CPU域调试精度&#xff0c;NPU域调试性能。孪生调试的整体方案如下&#xff1a;开发者通过调用Ascend C类库编写Ascend C算子kernel侧源码&am…

784. 字母大小写全排列

字母大小写全排列 描述 : 给定一个字符串 s &#xff0c;通过将字符串 s 中的每个字母转变大小写&#xff0c;我们可以获得一个新的字符串。 返回 所有可能得到的字符串集合 。以 任意顺序 返回输出。 题目 : LeetCode 784.字母大小写全排列 : 784. 字母大小写全排列 分析…

JavaScript常用技巧专题二

文章目录 一、前言二、生成随机字符串三、转义HTML特殊字符四、单词首字母大写五、将字符串转换为小驼峰六、删除数组中的重复值七、移除数组中的假值八、获取两个数字之间的随机数九、将数字截断到固定的小数点十、日期10.1、计算两个日期之间天数10.2、从日期中获取是一年中的…

fd信息查看

一、/proc/pid/fdinfo和/proc/pid/fd的含义 二、实例 ref&#xff1a; linux下的/proc/pid/fdinfo和/proc/pid/fd_proc/417/fdinfo/0-CSDN博客 linux select read阻塞_图解Linux的IO模型和相关技术-CSDN博客

实现加盐加密方法以及java nio中基于MappedByteBuffer操作大文件

自己实现 传统MD5可通过彩虹表暴力破解&#xff0c; 加盐加密算法是一种常用的密码保护方法&#xff0c;它将一个随机字符串&#xff08;盐&#xff09;添加到原始密码中&#xff0c;然后再进行加密处理。 1. 每次调用方法产生一个唯一盐值&#xff08;UUID &#xff09;密码…

生产问题: 利用线程Thread预加载数据缓存,其它类全局变量获取缓存偶发加载不到

生产问题: 利用线程Thread预加载数据缓存偶发加载不到 先上代码 public class ThreadTest {//本地缓存Map<String, Object> map new HashMap<String, Object>();class ThreadA implements Runnable{Overridepublic void run() {System.out.println("Thread…

【崩坏:星穹铁道】卡芙卡狂喜!1.5版本新遗器值不值得刷?

《崩坏&#xff1a;星穹铁道》在1.5版本上线了4套新的遗器套装&#xff0c;照例还是两套外圈遗器两套内圈遗器&#xff0c;分别可以通过幽冥之径侵蚀隧洞副本和模拟宇宙第八世界刷取。 那么本期闲游盒小盒子就简单解析一下这4套新遗器&#xff0c;它们的属性适合哪些角色&#…

2021版吴恩达深度学习课程Deeplearning.ai 05序列模型 12.5

学习内容 05.序列模型 1.1 为什么用序列模型 1.序列模型常见的应用 1.2 注释 notation 1.*T_x(i)表示训练样本x(i)的序列长度&#xff0c;T_y(i)表示target(i)的序列长度2.训练集表示单词的方式*构建字典的方式*在训练集中查找出现频率最高的单词*网络搜集常用字典3.如果遇…

微服务——服务保护Sentinel

雪崩问题 在单体项目里面&#xff0c;如果某一个模块出问题会导致整个项目都有问题。 在微服务项目里面&#xff0c;单独一个服务出问题理论上是不会影响别的服务的。 但是如果有别的业务需要调用这一个模块的话还是会有问题。 问题产生原因和解决思路 最初那只是一个小小…

GEE影像升尺度(10m->250m)

GEE影像升尺度&#xff08;10m->250m&#xff09; 代码 var ext /* color: #d63000 *//* shown: false *//* displayProperties: [{"type": "rectangle"}] */ee.Geometry.Polygon([[[108.74625980473367, 28.562445155322063],[108.74625980473367, …

云计算在计算机领域的应用与发展

云计算在计算机领域的应用与发展 一、引言 随着科技的不断发展&#xff0c;计算机领域已经成为当今社会最为活跃和创新的领域之一。云计算作为一种新兴的计算模式&#xff0c;已经在计算机领域中得到了广泛的应用&#xff0c;并且正在不断地推动着计算机领域的发展。本文将探…

令牌桶算法理解学习(限流算法)

令牌桶算法是网络流量整形&#xff08;Traffic Shaping&#xff09;和速率限制&#xff08;Rate Limiting&#xff09;中最常使用的一种算法。典型情况下&#xff0c;令牌桶算法用来控制发送到网络上的数据的数目&#xff0c;并允许突发数据的发送。 用简单的话语来说就是限制…