what
Normalization是数据标准化(归一化,规范化),Batch 可以理解为批量,加起来就是批量标准化。
和卷积层,激活层,全连接层一样,BN层也是属于网络中的一层,常见排列 conv -> BN ->relu。
why
BN算法的强大之处在下面几个方面:
可以选择较大的学习率,使得训练速度增长很快,具有快速收敛性。
可以不去理会Dropout,L2正则项参数的选择,如果选择使用BN,甚至可以去掉这两项。
去掉局部响应归一化层。(AlexNet中使用的方法,BN层出来之后这个就不再用了)
关于归一化,神经网络训练开始前,都要对数据做一个归一化处理,原因是:
1、网络学习的过程的本质就是学习数据分布,一旦训练数据和测试数据的分布不同,那么网络的泛化能力就会大大降低;
2、每一批次的数据分布如果不相同的话,那么网络就要在每次迭代的时候都去适应不同的分布,这样会大大降低网络的训练速度,这也就是为什么要对数据做一个归一化预处理的原因;
3、另外对图片进行归一化处理还可以处理光照,对比度等影响。
网络一旦训练起来,参数就要发生更新,出了输入层的数据外,其它层的数据分布是一直发生变化的,BN就是为了解决这个问题的。在每一层输入的时候,在加一个预处理多好。比如归一化到均值为0,方差为1,然后再送入输入进行学习:
但这样简单的归一化是的网络学习的特征被破坏,所以做了一些改进:变换重构,引入了可以学习的参数,这就是算法的关键之处:这两个希腊字母就是要学习的:
这样的时候可以恢复出原始的某一层学习到的特征的,因此我们引入这个可以学习的参数使得我们的网络可以恢复出原始网络所要学习的特征分布,最后BN层的前向传导公式为:
how
输入:输入数据x1…xm(这些数据是准备进入激活函数的数据)
计算过程中可以看到,
1.求数据均值
2.求数据方差
3.数据进行标准化(个人认为称作正态化也可以)
4.训练参数γ,β
5.输出y通过γ与β的线性变换得到新的值
在正向传播的时候,通过可学习的γ与β参数求出新的分布值
在反向传播的时候,通过链式求导方式,求出γ与β以及相关权值
代码
Python实现,使用类似pytorch的框架时发现,由于每个mini-batch中的数据是不同的,所以需要统计整个数据集中的均值和方差需要动态的追踪各个mini-batch,动态统计方式如下:
在测试阶段,不再更新BN层,直接使用之前的统计结果。
因为BN计算过程中需要保存running_mean和running_var,以及更新动量momentum和防止数值计算错误的eps,所以需要设计为类。
# -*- coding:utf-8 -*-
import numpy as np
from matplotlib import pyplot
class MyBN:
def __init__(self, momentum, eps, num_features):
"""
初始化参数值
:param momentum: 追踪样本整体均值和方差的动量
:param eps: 防止数值计算错误
:param num_features: 特征数量
"""
# 对每个batch的mean和var进行追踪统计
self._running_mean = 0
self._running_var = 1
# 更新self._running_xxx时的动量
self._momentum = momentum
# 防止分母计算为0
self._eps = eps
# 对应论文中需要更新的beta和gamma,采用pytorch文档中的初始化值
self._beta = np.zeros(shape=(num_features, ))
self._gamma = np.ones(shape=(num_features, ))
def batch_norm(self, x):
"""
BN向传播
:param x: 数据
:return: BN输出
"""
x_mean = x.mean(axis=0)
x_var = x.var(axis=0)
# 对应running_mean的更新公式
self._running_mean = (1-self._momentum)*x_mean + self._momentum*self._running_mean
self._running_var = (1-self._momentum)*x_var + self._momentum*self._running_var
# 对应论文中计算BN的公式
x_hat = (x-x_mean)/np.sqrt(x_var+self._eps)
y = self._gamma*x_hat + self._beta
return y
注意(BN 在CNN的实现):
1、全连接层和卷积层的BN是不太一样的(分别是在什么维度上求均值方差)
1)上面所说的是BN对于每一个神经元都做处理,对于卷积神经网络来说呢?比如某一层卷积层的维度是:100 * 100 * 6,如果对每一个神经元都进行BN的话,那就需要600万的参数,这是相当恐怖的,所以其实卷积神经网络使用BN的时候,也做了权重共享的策略,把一张特征图当做一个神经元来处理。
比如某层的特征维度是[n,c,h,w],分别是batch_num:n,维度: c,特征尺寸h,w。CNN中可把每个特征图看成是一个特征处理(神经元),因此在使用BN的时候,Mini-batch size的大小就是c * h * w,对于每一个特征图只有一对科学系的参数。说白了,就是相当于求所有样本(batch_num:n个)所对应的的一个特征图的所有神经元的平均值和方差,然后对这一个神经元做归一化。
2)FC层就是对每一个神经元求,不同于CNN。
2、训练和预测时的BN是不一样的
训练的时候是每个训练iter都要去计算均值,测试是直接使用全局的均值(这个全局的均值是每次训练1iteration都会使用移动平均法迭代地更新一个全局的值,训练结束后就先相当于使用全量训练数据产生了一个全局的固定值了)。
BN LN IN GN 区别
注意上图由于为了好展示(将H和W维度合并,不然的话是四维NCHW,现在降低为三维NC(HW合并))
可以看出,除了BN作用于N维度,其他都不在N维度。
可以粗略的进行如下总结:
BN是最传统的,如果batchsize允许够大的话,用在CNN中的效果依然是最好的;LN适合RNN;IN适合图像风格化任务;GN更适合小batchsize的CNN训练。
参考链接:
1、https://zhuanlan.zhihu.com/p/100672008