首页 >> 大全

参考2-知乎-如何区分并记住常见的几种 Normalization 算法⭐⭐⭐

2023-07-22 大全 27 作者:考证青年

面试高频2-BN、LN、WN相关 终极问题? 1. BN、LN、WN、IN、CN分别是怎样计算的? CV和NLP方面有什么区别?

参考2-知乎-如何区分并记住常见的几种 算法⭐⭐⭐

2. BN在测试和训练时候的分别使用?

参考3-深入理解Batch

2. 几种分别有什么优点? 3. 几种 分别可以work的原因的分析整理。

参考3-深入理解Batch

4. 各自实现的代码整理。 一些参考: 参考0-知乎问题:深度学习中 Batch 为什么效果好? 参考1-详解深度学习中的,BN/LN/WN⭐⭐⭐-转载 参考2-知乎-如何区分并记住常见的几种 算法⭐⭐⭐ 参考3-深入理解Batch 参考4-BN究竟起了什么作用?一个闭门造车的分析 参考5-《Batch Deep by Shift》阅读笔记与实现 参考6-Batch 原理与实战 原理-实现全套-【DL】规范化:你确定了解我吗? ⭐如何区分并记住常见的几种 算法 参考1-详解深度学习中的,BN/LN/WN⭐⭐⭐-转载 1.为什么需要 ? 深度学习中的ICS( Shift)问题及其影响。 1.1 独立同分布和白化

机器学习的炼丹师最喜欢的数据有什么特点? 窃以为,莫过于“独立同分布”了,即 and ,简称为 i.i.d。 独立同分布 并非所有机器学习模型的必备要求(比如:Naive Bayes 模型就是简历在特征彼此独立的基础之上, 而 和 神经网络则在非独立的特征数据上依然可以训练出很好的模型),但是独立同分布的数据可以简化常规机器学习模型的训练、提升机器学习模型的预测能力,已经是一个共识。

因此,在把数据喂给机器学习模型之前,“白化()”是一个重要的数据预处理步骤。白化一般包含两个目的:

(1)去除特征之间的相关性 —> 独立;

(2)使得所有特征具有相同的均值和方差 —> 同分布。

白化最典型的方法就是PCA,可以参考阅读 。

1.2 深度学习中的 Shift

深度神经网络模型的训练为什么会很困难? 其中一个重要的原因是, 深度神经网络涉及到很多层的叠加, 而每一层的参数更新会到导致上层的输入数据分布发生变化, 通过层层叠加, 高层的输入分布变化会非常剧烈,这就使高层需要不断去重新始应底层的参数更新。 为了训练好模型,我们需要非常谨慎的设定学习率、初始化权重、以及尽可能细致的参数更新策略。

将这一现象总结为 Shift,简称 ICS. 什么是 ICS 呢?

@魏秀参

在一个回答中做出了一个很好的解释:

1.3 ICS会导致什么问题?

简而言之, 每个神经元的输入数据不再是‘独立同分布’。

其一: 上层参数需要不断始应新的输入数据分布, 降低学习速度。

其二: 下层输入的变化可能趋向于变大或者变小,导致上层落入饱和区,使得学习过早停止。

其三: 每层的更新都会影响到其他层,因此每层的参数更新策略需要尽可能的谨慎。

的通用框架和基本思想 从主流 方法中提炼出的抽象框架

我们以神经网络中的一个普通神经元为例。 神经元接收一组输入向量:

通过某种运算后,输出一个标量值:

由于ICS问题的存在,X的分不可能相差很大。 要解决独立同分布问题, “理论正确”的方法就是对每一层的数据都进行白化操作。然而标准的白化操作代价高昂,特别是我们还希望白化操作是可微的,保证白化操作可以通过反向传播来更新梯度。

因此,以 BN 为代表的 方法退而求其次,进行了简化的白化操作。基本思想是:在将 x 送给神经元之前,先对其做平移和伸缩变换, 将 x 的分布规范化成在固定区间范围的标准分布。

通用变换框架就如下所示:

h = f ( g ⋅ x − μ σ + b ) h=f\left(\{g} \cdot \frac{\{x}-\mu}{\sigma}+\{b}\right) h=f(g⋅σx−μ​+b)

我们来看看这个公式中的各个参数。

(1) μ \mu μ 是平移参数(shift ), σ \sigma σ 是缩放参数(scale ) 。 通过两个参数进行 shift 和 scale 变化:

x ^ = x − μ σ \hat{\{x}}=\frac{\{x}-\mu}{\sigma} x^=σx−μ​

得到的数据符合均值为 0、方差为 1 的标准分布。

(2) b \{b} b 是再平移参数(re-shift ), g g g 是再缩放参数(re-scale )。 将上一步得到的 x ^ \{\hat{x}} x^ 进一步变换为:

y = g ⋅ x ^ + b \{y}=\{g} \cdot \hat{\{x}}+\{b} y=g⋅x^+b

最终得到的数据符合均值为: b \{b} b、方差为 g 2 \{g^2} g2的分布。

奇不奇怪?奇不奇怪?

说好的处理 ICS,第一步都已经得到了标准分布,第二步怎么又给变走了?

答案是——为了保证模型的表达能力不因为规范化而下降。

我们可以看到,第一步的变换将输入数据限制到了一个全局统一的确定范围(均值为 0、方差为 1)。下层神经元可能很努力地在学习,但不论其如何变化,其输出的结果在交给上层神经元进行处理之前,将被粗暴地重新调整到这一固定范围。

沮不沮丧?沮不沮丧?

难道我们底层神经元人民就在做无用功吗?

所以,为了尊重底层神经网络的学习结果,我们将规范化后的数据进行再平移和再缩放,使得每个神经元对应的输入范围是针对该神经元量身定制的一个确定范围(均值为 b \{b} b 、方差为 g 2 \{g^2} g2 )。 和 的参数都是可学习的,这就使得 层可以学习如何去尊重底层的学习结果。

除了充分利用底层学习的能力, 另一方面的重要意义在于 保证获得非线性的表达能力。 等激活函数在神经网络中有着重要作用, 通过区分饱和区和非饱和区,使得神经网络的数据变换具有了非线性计算能力。 而第一步的规范化会将几乎所有的数据映射到激活函数的非饱合区(线性区),仅利用了线性变化能力, 从而降低了神经网络的表达能力。 而进行再变换,则而可以将数据从线性区变换到非线性区,恢复模型的表达能力。

那么问题又来了——

3. 主流 方法梳理

上一节中,提炼了的通用公式:

h = f ( g ⋅ x − μ σ + b ) h=f\left(\{g} \cdot \frac{\{x}-\mu}{\sigma}+\{b}\right) h=f(g⋅σx−μ​+b)

对照于这一公式,我们来梳理主流的四种规范化方法。

3.1 Batch —— 纵向规范化

Batch 于2015年由 提出,开 之先河。其规范化针对单个神经元进行,利用网络训练时一个 mini-batch 的数据来计算该神经元 x i \{x_i} xi​ 的均值和方差,因而称为 Batch 。

μ i = 1 M ∑ x i , σ i = 1 M ∑ ( x i − μ i ) 2 + ϵ \mu_{i}=\frac{1}{M} \sum x_{i}, \quad \{i}=\sqrt{\frac{1}{M} \sum\left(x_{i}-\mu_{i}\right)^{2}+\} μi​=M1​∑xi​,σi​=M1​∑(xi​−μi​)2+ϵ​

其中 M M M是 mini-batch 的大小。

按上图所示,相对于一层神经元的水平排列,BN 可以看做一种纵向的规范化。由于 BN 是针对单个维度定义的,因此标准公式中的计算均为 -wise 的。

BN独立的规范化每一个输入维度 x i \{x_i} xi​, 但是规范化的参数是一个 mini-batch的一阶统计量和二阶统计量。 这就要求 每一个mini-batch 的统计量是整体统计量的近似估计, 或者说 每一个mini-batch彼此之间,以及和整体数据,都应该是独立同分布的。 (没懂, 这里和一般神经网络学习有什么区别) 分布差距较小的 mini-batch 可以看作是为规范化操作和模型训练引入了噪声,可以增强模型的鲁棒性。 但是如果每个 min-batch的原始分布差别很大,那么不同的 mini-batch 的数据将会进行不一样的数据变换,这就增加了模型训练的难度。

因此, BN比较适用的场景是: 每个 mini-batch较大,数据分布比较接近。 在训练之前做好充分的, 否则效果会差很多。

另外 由于BN需要在运行过程中统计每个 mini-batch的一阶统计量和二阶统计量,因此不适用于 动态的网络结构 和 RNN网络。 不过,也有研究者专门提出了适用于RNN的方法, 不展开。

3.2 Layer --横向规范化

层规范化就是针对 BN 的上述不足而提出的。与 BN 不同,LN 是一种横向的规范化,如图所示。它综合考虑一层所有维度的输入,计算该层的平均输入值和输入方差,然后用同一个规范化操作来转换各个维度的输入。 [没懂,为啥不用除以神经个数dim]

μ = ∑ i x i , σ = ∑ i ( x i − μ ) 2 + ϵ \mu=\sum_{i} x_{i}, \quad \sigma=\sqrt{\sum_{i}\left(x_{i}-\mu\right)^{2}+\} μ=i∑​xi​,σ=i∑​(xi​−μ)2+ϵ​

其中 i i i 枚举了该层所有的输入神经元。对应到标准公式中,四大参数 μ \mu μ, σ \sigma σ, g g g, b b b 均为标量(BN中是向量),所有输入共享一个规范化变换。[这块没懂, 输入神经元指的是什么? 先LN,然后再进行W计算? ]

3.3 —— 参数规范化

前面我们讲的模型框架

h = f ( g ⋅ x − μ σ + b ) h=f\left(\{g} \cdot \frac{\{x}-\mu}{\sigma}+\{b}\right) h=f(g⋅σx−μ​+b)

中,经过规范化之后的 y y y作为输入送到下一个神经元,应用以 w \{w} w 为参数的 f w ( . ) \{f_w(.)} fw​(.) 函数定义的变换。最普遍的变换是线性变换,即 f w ( x ) = w . x \{f_w(x)=w.x} fw​(x)=w.x.

BN 和 LN 均将规范化应用于输入的特征数据 x x x,而 WN 则另辟蹊径,将规范化应用于线性变换函数的权重 w w w ,这就是 WN 名称的来源。

具体而言,WN 提出的方案是,将权重向量 w \{w} w分解为向量方向 v ^ \{\hat{v}} v^ 和向量模 g g g 两部分:

w = g ⋅ v ^ = g ⋅ v ∥ v ∥ \{w}=g \cdot \hat{\{v}}=g \cdot \frac{\{v}}{\|\{v}\|} w=g⋅v^=g⋅∥v∥v​

回忆一下,BN 和 LN 是用输入的特征数据的方差对输入数据进行 scale,而 WN 则是用 神经元的权重的欧氏范式对输入数据进行 scale。虽然在原始方法中分别进行的是特征数据规范化和参数的规范化,但本质上都实现了对数据的规范化,只是用于 scale 的参数来源不同。

另外,我们看到这里的规范化只是对数据进行了 scale,而没有进行 shift,因为我们简单地令 μ = 0 \{\mu} = 0 μ=0. 但事实上,这里留下了与 BN 或者 LN 相结合的余地——那就是利用 BN 或者 LN 的方法来计算输入数据的均值 μ \mu μ 。

3.4 —— 余弦规范化

还能怎么做?

我们再来看看神经元的经典变换 f w ( x ) = w . x \{f_w(x)=w.x} fw​(x)=w.x.

对输入数据 x x x 的变换已经做过了,横着来是 LN,纵着来是 BN。

对模型参数 w w w 的变换也已经做过了,就是 WN。

好像没啥可做的了。

然而天才的研究员们盯上了中间的那个点,对,就是 f w ( x ) = w . x \{f_w(x)=w.x} fw​(x)=w.x.

他们说,我们要对数据进行规范化的原因,是数据经过神经网络的计算之后可能会变得很大,导致数据分布的方差爆炸,而这一问题的根源就是我们的计算方式——点积,权重向量 w w w和 特征数据向量 x x x 的点积。向量点积是无界()的啊!

那怎么办呢?我们知道向量点积是衡量两个向量相似度的方法之一。哪还有没有其他的相似度衡量方法呢?有啊,很多啊!夹角余弦就是其中之一啊!而且关键的是,夹角余弦是有确定界的啊,[-1, 1] 的取值范围,多么的美好!仿佛看到了新的世界!

于是, 就出世了。他们不处理权重向量 w w w ,也不处理特征数据向量 x x x ,就改了一下线性变换的函数:

f w ( x ) = cos ⁡ θ = w ⋅ x ∥ w ∥ ⋅ ∥ x ∥ f_{\{w}}(\{x})=\cos \theta=\frac{\{w} \cdot \{x}}{\|\{w}\| \cdot\|\{x}\|} fw​(x)=cosθ=∥w∥⋅∥x∥w⋅x​

其中 θ \theta θ 是 w w w 和 x x x 的夹角。然后就没有然后了,所有的数据就都是 [-1, 1] 区间范围之内的了!

不过,回过头来看,CN 与 WN 还是很相似的。我们看到上式中,分子还是 w w w 和 x x x 的内积,而分母则可以看做用 w w w 和 x x x 二者的模之积进行规范化。对比一下 WN 的公式:

f w ( W N ( x ) ) = f v ( g ⋅ x ∥ v ∥ ) f_{\{w}}(W N(\{x}))=f_{\{v}}\left(g \cdot \frac{\{x}}{\|\{v}\|}\right) fw​(WN(x))=fv​(g⋅∥v∥x​)

一定程度上可以理解为,WN 用 权重的模 ∣ ∣ v ∣ ∣ ||\{v}|| ∣∣v∣∣ 对输入向量进行 scale,而 CN 在此基础上用输入向量的模 ∣ ∣ x ∣ ∣ ||\{x}|| ∣∣x∣∣ 对输入向量进行了进一步的 scale.

CN 通过用余弦计算代替内积计算实现了规范化,但成也萧何败萧何。原始的内积计算,其几何意义是 输入向量在权重向量上的投影,既包含 二者的夹角信息,也包含 两个向量的scale信息。去掉scale信息,可能导致表达能力的下降,因此也引起了一些争议和讨论。具体效果如何,可能需要在特定的场景下深入实验。

现在,BN, LN, WN 和 CN 之间的来龙去脉是不是清楚多了? 说实话不是非常清楚。

4. 为什么有效?

我们以下面这个简化的神经网络为例来分析。

4.1 的权重伸缩不变性 4.2 的数据伸缩不变性

整理? 1. 几种 具体都是怎样操作的? 2. 分别介绍几种的优点? 参考2-知乎-如何区分并记住常见的几种 算法⭐⭐⭐

1. ⭐Batch ⭐ 1.1原理、计算过程

对应NLP, 1本书, N页(batch), 每页有L行(), 每行有D个字(dim), 然后求平均的过程怎样对应呢?

求和: N*D吗? 好像也不是,对于 d i m 1 dim_1 dim1​ 求和N,然后求平均, 对于 d i m 2 dim_2 dim2​求和N然后求平均, 然后分别算出 变换后的值?

1.2 训练和测试区别 1.3 代码实现

我们可以在 下自己写一个 BN ,看看和官方的版本是否一致,以检验上述理解是否正确:

# coding=utf8
import torch
from torch import nn# track_running_stats=False,求当前 batch 真实平均值和标准差,
# 而不是更新全局平均值和标准差
# affine=False, 只做归一化,不乘以 gamma 加 beta(通过训练才能确定)
# num_features 为 feature map 的 channel 数目
# eps 设为 0,让官方代码和我们自己的代码结果尽量接近
bn = nn.BatchNorm2d(num_features=3, eps=0, affine=False, track_running_stats=False)# 乘 10000 为了扩大数值,如果出现不一致,差别更明显
x = torch.rand(10, 3, 5, 5)*10000 
official_bn = bn(x)# 把 channel 维度单独提出来,而把其它需要求均值和标准差的维度融合到一起
x1 = x.permute(1,0,2,3).view(3, -1)mu = x1.mean(dim=1).view(1,3,1,1)
# unbiased=False, 求方差时不做无偏估计(除以 N-1 而不是 N),和原始论文一致
# 个人感觉无偏估计仅仅是数学上好看,实际应用中差别不大
std = x1.std(dim=1, unbiased=False).view(1,3,1,1)my_bn = (x-mu)/stddiff=(official_bn-my_bn).sum()
print('diff={}'.format(diff)) # 差别是 10-5 级的,证明和官方版本基本一致

2. Layer 2.1 原理

2.3 代码实现

如下代码对比了 官方 API 计算 LN,和依据原理逐步计算 LN 得到的结果:

_知乎的算法_知乎对应

import torch
from torch import nnx = torch.rand(10, 3, 5, 5)*10000# normalization_shape 相当于告诉程序这本书有多少页,每页多少行多少列
# eps=0 排除干扰
# elementwise_affine=False 不作映射
# 这里的映射和 BN 以及下文的 IN 有区别,它是 elementwise 的 affine,
# 即 gamma 和 beta 不是 channel 维的向量,而是维度等于 normalized_shape 的矩阵
ln = nn.LayerNorm(normalized_shape=[3, 5, 5], eps=0, elementwise_affine=False)official_ln = ln(x)x1 = x.view(10, -1)
mu = x1.mean(dim=1).view(10, 1, 1, 1)
std = x1.std(dim=1,unbiased=False).view(10, 1, 1, 1)my_ln = (x-mu)/stddiff = (my_ln-official_ln).sum()print('diff={}'.format(diff)) # 差别和官方版本数量级在 1e-5

3. 3.1 原理

3.2 代码实现

import torch
from torch import nnx = torch.rand(10, 3, 5, 5) * 10000# track_running_stats=False,求当前 batch 真实平均值和标准差,
# 而不是更新全局平均值和标准差
# affine=False, 只做归一化,不乘以 gamma 加 beta(通过训练才能确定)
# num_features 为 feature map 的 channel 数目
# eps 设为 0,让官方代码和我们自己的代码结果尽量接近
In = nn.InstanceNorm2d(num_features=3, eps=0, affine=False, track_running_stats=False)official_in = In(x)x1 = x.view(30, -1)
mu = x1.mean(dim=1).view(10, 3, 1, 1)
std = x1.std(dim=1, unbiased=False).view(10, 3, 1, 1)my_in = (x-mu)/stddiff = (my_in-official_in).sum()
print('diff={}'.format(diff)) # 误差量级在 1e-5

4.Group 4.1 原理

4.2 代码

import torch
from torch import nnx = torch.rand(10, 20, 5, 5)*10000# 分成 4 个 group
# 其余设定和之前相同
gn = nn.GroupNorm(num_groups=4, num_channels=20, eps=0, affine=False)
official_gn = gn(x)# 把同一 group 的元素融合到一起
x1 = x.view(10, 4, -1)
mu = x1.mean(dim=-1).reshape(10, 4, -1)
std = x1.std(dim=-1).reshape(10, 4, -1)x1_norm = (x1-mu)/std
my_gn = x1_norm.reshape(10, 20, 5, 5)diff = (my_gn-official_gn).sum()print('diff={}'.format(diff)) # 误差在 1e-4 级

5.总结

⭐⭐NLP对应-在NLP任务中的问题与改进

NLP中没有通道这个概念,做 LN和BN的图例也相对简单了很多,LN就是对于一个单词经过神经网络时候的某一层的所有神经元(单词向量维度层面)做归一化。 而BN相当于对于 batch中一个单词某一个维度放在一起做归一化处理。 和CV以及最开始的BN、LN有一点区别,但是大致都是相同的。

问题: 做BN, LN时候是考虑句子长度嘛? 一起做吗? 还是针对单个神经元分别做? 从CV看好像是整个一起做?

考虑参数量: 每个N都会有两个权重需要学习。

拿个例子说明: 句子长度: 10, : 5, = 20:

⭐问题3: 可以work的原因分析? 1. 参考3-深入理解Batch

2.

3. 知乎-回答

4. 知乎-回答

5. 知乎回答

6. 知乎-回答

7. 知乎-回答

8. 参考6-Batch 原理与实战

问题2: 训练和测试时候区别?

问题5: BN代码实现 1. 深入理解Batch

import torchclass BatchNorm2d(torch.nn.Module):def __init__(self, channel, eps=1e-5, affine=True, momentum=0.9):super().__init__()'''channel: eps:affine:momentum: '''# 初始化训练参数self.gamma = torch.nn.Parameter(torch.ones(1, channel, 1, 1))self.beta = torch.parameter(torch.zeros(1, channel, 1, 1))slef.eps = epsself.momentum = momentumself.register_buffer('running_mean', torch.zeros(channel))self.register_buffer('running_variance', torch.ones(channel))def forward(self, input):# input shape must be (n, c, h, w)if self.training:means = input.mean((0, 2, 3), keepdim=True)vars = ((inputs-means)**2).sum(0, 2, 3), kkepdim=True) self.running_mean = self.moment*running_mean + (1-self.momentum) * means   self.running_var = self.momentum * self.running_var + (1-self.momentum) * meanselse:means = self.runing_meanvars = self.running_varoutput = (input - means) / torch.sqrt(vars + self.eps) if self.affine:output = output * self.gamma  + self.betareturn output 

原理-实现全套-【DL】规范化:你确定了解我吗? torch.nn..详解 自己理解 BN只要涉及到batch内部一起操作都可以称作BN;LN只要某一层神经网络的神经元一起操作或者特征一起操作都可以称作LN;神经元的选择是区别NLP、CV等使用的一个基本区别;CNN上使用BN, 把 整张图特征当作了一个神经元,所以在整个batch上的归一化定义为: 整个batch上同一个神经元的归一化, KHW。

参考-原理及代码详解

5. 在使用时候, 不管1d, 2d唯一的区别就在于 BN时候,仅仅对于 输入第二个维度开始到后面一起计算。 NLP里面也是,如果1d(), 输入是: [2, 3] batch,dim 或者[2, 3, 4] batch, dim, 时候. 都是关于后面 一起计算方差、均值。

6. BN参数: 通道数量2 LN参数: BERT内部: dim2 BN的NLP感觉也是dim2

代码、源码分析部分

源码-torch.nn.()

源码-torch.nn.()

批归一化 及 torch.nn. 函数详解

为什么要做特征归一化/标准化?

为什么要做特征归一化/标准化?

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了