批归一化层-BN层(Batch Normalization)
作用及影响:
直接作用:对输入BN层的张量进行数值归一化,使其成为均值为零,方差为一的张量。
带来影响:
1.使得网络更加稳定,结果不容易受到一些超参数的影响。
2.对于深层网络,减少梯度消失或爆炸的可能。
3.使网络每一层输出结果稳定,进而加快了模型训练的速度。
算法思想:
例如:
输入x(i) = [1, 2, 3, 4, 5],平均值μ = 3(全局平均值) , 方差σ = 2(全局方差) ,计算公式x(i) = (x(i) - μ)/σ
→x(i) = [-1, -0.5, 0, 0.5, 1] (得到的是第i行x结果)
再用x(i)×W(权重)+b(偏差值)→y(i) (得到的是第i行y结果)
最后输出y
Pytorch框架中的代码实现
默认权重初始值,且不考虑偏差值b
import torch
import torch.nn as nn
import numpy as np"""
基于pytorch的网络编写
测试BN层
权重w为默认初始化
偏差值b=0
"""
x=torch.randn(2,10)
#x为随机输入。第一维是batch_size,第二维是输入维度,这个输入就相当于2个10维向量的矩阵
bn = torch.nn.BatchNorm1d(10)
#定义bn层,参数要与输入的维度一致,这个维度与batch_size是无关的
print(bn.state_dict())
print(bn(x))
自定义框架中的代码实现(受到一位兄弟的启迪)
#weight = torch.randn(bn.state_dict()["weight"].shape)
#由于默认的bn初始化weight参数都为1,所以容易看不出最后scale的作用,这里随机生成一个新的权重代替初始权重
#去掉这两行当然也应当获得一致的结果,这里相当于增加一点难度
weight = bn.state_dict()["weight"]
bn.weight = torch.nn.Parameter(weight)
#原始的初始化权重,是[1,1,1,1...],为了方便对比我们这里还是继承初始化权重,如果需要自己设置权重可参考前面3行注释
#取出参数
w = bn.state_dict()["weight"].numpy()
b = bn.state_dict()["bias"].numpy()
#将输入转成numpy数组
x = x.numpy()
#计算均值,注意是沿batch_size的维度进行均值计算
p = np.mean(x, axis=0)
#按照公式计算var
v = np.mean(np.square(x - p), axis=0)
#按照公式计算,这里e=1e-5是为了防止分母为零,查看torch源码可以找到,torch中的e也等于1e-5
x = (x - p) / np.sqrt(v + 1e-5)
#最后的scale线性运算
y = w * x + b #偏差值b=0
print(y, "自定义bn输出")
输出结果对比:
在tensor中会四舍五入保留四位小数,通过对比也可以发现结果是一样的。