一文读懂PCA
作者把在训练期间参数的改变而导致网络激活分布的改变叫做内部协变量偏移,对此我们有两个版本版本的解释:
PCA是在对观测数据进行基变换,新的坐标系使各数据维度线性无关,坐标系的重要程度从大到小衰减。
求解过程:
其中特征向量,就是最大方差方向,每个特征向量对应的特征值就是这个数据维度的方差。
PCA白化实际上就是在数据通过PCA进行基变换后再把数据进行标准化,让数据每个维度的方差全部为1。
公式推导如下:
符号定义:X:原始数据矩阵 M:原始数据协方差矩阵 设S1/2S^{1/2}S1/2为白化矩阵
对M特征值分解:
U就是我们要找的变换矩阵,转换数据基坐标:
XPCA=UXX_{PCA}=UXXPCA=UX
然后进行白化操作:
lambda为特征值
其中有的特征值很小,会造成数值溢出,就给它加上了1个常数项,于是把白化矩阵改为:
我们发现,白化操作可以让观测数据的方差与均值固定,去除每个维度的相关性。这样确实可以加快模型的收敛,但是也面临着一个问题:
如果忽略了E[x]对b的依赖(也就是反向传播计算梯度的时候考虑均值的影响)
从上面案例中我们发现,更新偏置b前后函数的输出没有改变,也就是损失没有改变,反而b不断增加,这会使模型变得更糟。
我们把归一化操作定义为Norm,如果反向传播不考虑Norm,那么更新的梯度就会与Norm抵消,如果考虑,就会增加很大的计算量。
由于白化的计算代价很大,作者提出了简化的版本,从对整个数据集进行归一化改成对每一个Batch的每一层神经元的output归一化来确保均值与方差固定。
如果把每层的输出固定下来,可能会对网络产生负面的影响,所以我们加入两个可学习的参数:贝塔与伽马使均值与方差变得可以调节。
其中伽马初始化为这一batch对应层输出的方差,贝塔初始化为其均值,从而保证整个network的capacity。(有关capacity的解释:实际上BN可以看作是在原模型上加入的“新操作”,这个新操作很大可能会改变某层原来的输入。当然也可能不改变,不改变的时候就是“还原原来输入”。如此一来,既可以改变同时也可以保持原输入,那么模型的容纳能力(capacity)就提升了。)
总体流程如下:
反向传播梯度计算公式如下:
当BN操作应用在卷积层后,作者找到了一个符合卷积神经网络特性的方法,归一化作用在了通道维度上。
我们用代码输出结果展示一下:
用pytorch生成 Batch=2 channel = 3 hw 2 * 2 的特征图:
计算均值
计算举例:
(0+1+2+3+12+13+14+15)/8 = 7.5
class BatchNorm(nn.Block):# num_features:完全连接层的输出数量或卷积层的输出通道数。# num_dims:2表示完全连接层,4表示卷积层def __init__(self, num_features, num_dims, **kwargs):super().__init__(**kwargs)if num_dims == 2:shape = (1, num_features)else:shape = (1, num_features, 1, 1)# 参与求梯度和迭代的拉伸和偏移参数,分别初始化成1和0self.gamma = self.params.get('gamma', shape=shape, init=init.One())self.beta = self.params.get('beta', shape=shape, init=init.Zero())# 非模型参数的变量初始化为0和1self.moving_mean = np.zeros(shape)self.moving_var = np.ones(shape)def forward(self, X):# 如果X不在内存上,将moving_mean和moving_var# 复制到X所在显存上if self.moving_mean.ctx != X.ctx:self.moving_mean = self.moving_mean.copyto(X.ctx)self.moving_var = self.moving_var.copyto(X.ctx)# 保存更新过的moving_mean和moving_varY, self.moving_mean, self.moving_var = batch_norm(X, self.gamma.data(), self.beta.data(), self.moving_mean,self.moving_var, eps=1e-12, momentum=0.9)return Y