比如上图,一般数据都是在下面的,而损失函数在最上面
forward:数据从下往上传
backward:求梯度的时候从上往下,由于梯度一般是一个比较小的数,那累成起来,等到了最下面的时候就会变得很小,也就是说梯度在上面一般比较大,而等到了下面就会变小了
因为上面层的梯度比较大,每次更新的时候,上面的梯度就会不断地去更新,因为你的学习率(假设是一个定值),而下面的梯度比较小,那对权重的更新就会比较小。那问题就是上面的东西会很快的收敛,而下面的收敛的会比较慢。导致的问题就是:每一次去更新下面的数据的时候,而下面的层会尝试去抽取一些底层的特征,比如一些局部的边缘,一些很简单的纹理的信息等,上面的层是一些高层语义的信息,但是你每次下面的一变,上面的虽然已经收敛了,但是也要重新变,(相当于上面的权重白学了)
问题:在训练持续的过程中,顶部的变化比较快,底部的变化比较慢,在底部不断持续地变化过程中需要不断地去持续训练顶部
在学习底部的特征的时候,能否避免顶部不断重复地训练,这就是BN要考虑的问题
问题整理:
- 损失出现在最后,后面的层训练较快
- 数据在最底部
- 底部的层训练比较慢
- 底部层一变化,则所有的顶层都得跟着变
- 最后的那些层需要重新学习多次
- 导致收敛变慢
- 我们可以在学习底部层的时候避免变化顶部层吗?
BN的核心思想:
在讲损失和数值稳定性的时候,说到数据分布会变化,方差和均值整个分布会在不同的层之间会变化,那一个简单的办法就是把每一层的均值和方差给固定住,不管你的梯度也好输出也好,假设他们都符合同一个分布,那它数据的整体的形状相对来说是比较稳定的 ,就不会带来特别大的变化,那在学习中间细微的变动的时候就会比较容易
- 固定小批量里面的均值和方差
μ^B=1∣B∣∑i∈Bxiσ^B2=1∣B∣∑i∈B(xi−μ^B)2+ϵ\begin{array}{l}
\hat{\boldsymbol{\mu}}_{\mathcal{B}}=\frac{1}{|\mathcal{B}|} \underset{i \in \mathcal{B}}{\sum} x_{i} \\
\hat{\boldsymbol{\sigma}}_{\mathcal{B}}^{2}=\frac{1}{|\mathcal{B}|} \underset{i \in \mathcal{B}}{\sum}\left(x_{i}-\hat{\boldsymbol{\mu}}_{\mathcal{B}}\right)^{2}+\epsilon
\end{array}μ^B=∣B∣1i∈B∑xiσ^B2=∣B∣1i∈B∑(xi−μ^B)2+ϵ
ϵ\epsilonϵ是为了防止其为0而加的一个很小的数
- 然后再做额外的调整(可学习的参数)
xi+1=γ⊙xi−μ^Bσ^B+βx_{i+1}=\boldsymbol{\gamma} \odot \frac{x_{i}-\hat{\boldsymbol{\mu}}_{\mathcal{B}}}{\hat{\boldsymbol{\sigma}}_{\mathcal{B}}}+\boldsymbol{\beta}xi+1=γ⊙σ^Bxi−μ^B+β
γ β\gamma\,\betaγβ均为可学习的参数,比如:如果把其变成一个均值为0方差为1的分布不是那么适合的话,那我可以通过去学习参数γ β\gamma\,\betaγβ来调整这个分布,使得你的值对神经网络更好一点,但是会限定住γ β\gamma\,\betaγβ的变化,不要变化的过于猛烈
对应的每一个特征,或者说每一列,就有一个γ β\gamma\,\betaγβ与之对应
如果其作用在全连接或者卷积的输出上时,BN会作用在激活函数前面,批量归一化是一个线性变换,而激活函数是一个非线性变换,所以BN就是把你的均值方差拉的比较好,让你变换不那么剧烈。
也可以作用在全连接层和卷积层输入上,即对你的输入做一个线性变换,让你的方差均值变的比较好。
对于全连接层,输入的数据为:Xn×dX_{n\times d}Xn×d,其中每一行是一个样本,每一列是一个特征,那BN是作用在每一个列上,每一列都会得到对应的标量的方差和均值,即均值为0方差为1,(在做数据预处理的时候经常会使用此操作)
对于卷积层,是作用在通道维上:把通道当成这个像素点的一个特征,对于输入的一个图片,里面的每一个像素都是一个样本,如果输入的图片为(B, C, H, W),那样本的数量就是B×H×WB\times H\times WB×H×W,即整个批量里面所有的像素都是一个样本,那每个像素对应的通道就是特征
BN批量归一化
- 可学习的参数为γ β\gamma\,\betaγβ
- 作用在:
- 全连接层和卷积层输出上,激活函数前
- 全连接层和卷积层输入上
- 对全连接层,作用在特征维
- 对卷积层,作用在通道维
批量归一化在做什么?
- 最初论文是想用它来减少内部协变量转移
- 后续有论文指出它可能就是通过在每个小批量里加入噪音来控制模型复杂度
xi+1=γ⊙xi−μ^Bσ^B+βx_{i+1}=\boldsymbol{\gamma} \odot \frac{x_{i}-\hat{\boldsymbol{\mu}}_{\mathcal{B}}}{\hat{\boldsymbol{\sigma}}_{\mathcal{B}}}+\boldsymbol{\beta}xi+1=γ⊙σ^Bxi−μ^B+β
噪音是指:μ^B σ^B\hat{\boldsymbol{\mu}}_{\mathcal{B}}\,\hat{\boldsymbol{\sigma}}_{\mathcal{B}}μ^Bσ^B,因为他们是在小批量上计算而来的,$\hat{\boldsymbol{\mu}}_{\mathcal{B}}$叫随机偏移,σ^B\hat{\boldsymbol{\sigma}}_{\mathcal{B}}σ^B叫随机缩放
然后再加上一个γ\gammaγ来使其变化的不那么剧烈 - 因此没必要和dropout混合使用,比如全连接之后你使用了BN,那就没必要再使用Dropout了
总结:
- 批量归一化固定小批量中的均值和方差,然后学习出适合的偏移和缩放
- 可以加速收敛速度,但一般不改变模型的精度(用了BN后,lr可以调的相对大一点,即BN允许用更大的学习率来训练,就是说:之前的网络较深的时候,上下学习速度不一致,而用来BN后,相当于把数据给搞成大致统一分布,那就可以用一个比较大的lr来梯度更新)
momentum是用来更新moving_mean,moving_var的,通常是一个固定的值
eps也通常是一个固定的值
moving_mean,var是全局的均值方差
2对应全连接层,两个维度(batch, 全连接层大小)
4对应卷积,4个维度(batch,channel, H,W)
(2, 4)是一个元组,里面只有数字2和4
mean = X.mean(dim=(0, 2, 3),keepdim=True)
# 是说在(B,H,W)上求均值,求出来的mean的维度保持不变,即也是4维的,是一个1*channel*1*1的
moving_mean = momentum * moving_mean + (1.0 - momentum)*mean
# 吴恩达指数移动加权平均
# 卡尔曼滤波
# 带Momentum的SGD也是这个思路
因为moving_mean/var不在param里面,所以要自己判断Moving_mean在哪里的device上
应用BN于LeNet模型
注意:BN在激活函数前
最后的全连接不用加BN,加不加都没有关系,不用加BN线性变换