您现在的位置是:首页 > 文章详情

通俗话说一说各种Normalization以及用deeplearning4j实现Layer Normalization

日期:2019-09-20点击:375

一、Normalization是什么

    Normalization一句话概括来说就是用一种办法,将一组数据压到均值为0,方差为1的正态分布上去,具体做法是数据集的每一个元素减去均值再除以标准差。公式如下:(请忽略参数g,g的问题很诡异,后面说)

    这个公式说的更直白一点就是,把每一个a,经过平移和缩放,得到一个新值。而这样做的一个理由是,平移缩放并不会改变原始数据的分布情况,原来最大的还是最大,原来最小的还是最小。

    Deeplearning中有很多Normalization的方法,有BN、LN、IN、GN等等,每一种Normalization公式都一样,只是沿着的轴不一样,BN就是沿着minibatch方向,LN就是沿着影藏层的output vector维方向,举个例子,对于四维张量[minibatch,depth、height、width],那就是沿着depth方向,把height、width维约简掉。

二、说说Layer Normalization

    论文地址:https://arxiv.org/pdf/1607.06450.pdf

    Layer Normalization对于时间序列数据有奇效,下面截一段论文的原文。这是在RNN上用Layer Normalization

   

        简短的话说一下论文的变量含义,a表示t时刻点rnn的预输出值(还没有经过激活函数哦),h表示rnn某一个隐层t时刻点的输出。

        那么,这里Normalization是哪一个维度呢?假设RNN的输入张量为[minibatch、layerSize、timesteps],这么这里Normalization的就是layerSize这一维。这样可能还是太抽象,请看下图:

    

 

    这里Normalization的就是红色箭头指向的每一个维度的向量,对于一条数据的每个time step而言,就求出一个均值和方差,进行变换,下一个time step类推下去。那么多个time step就有多个均值和方差。

三、重点说说参数g和b

    这里g和b的维度要和h的维度相同,也就是上图的values per time step这一维度,也就是layer size的大小,这里g和b是跟随着网络参数学出来的。开始实现时,g的所有值一般会初始化为1,b的所有值会被初始化为0,随着训练的进行,g和b就会被修改为任意值了。那么Normalization也就没有了正态分布的效果,相当于layer size维乘以了一个随机向量,注意这里是向量点积,那么就等同于给一个随机的噪声,居然也能起作用,也是一个不能解释的问题。有点没有道理,但就是这么难以置信,居然是work的。

四、deeplearning4j的自动微分实现Layer Normalization

import java.util.Map; import org.deeplearning4j.nn.conf.inputs.InputType; import org.deeplearning4j.nn.conf.layers.samediff.SDLayerParams; import org.deeplearning4j.nn.conf.layers.samediff.SameDiffLayer; import org.nd4j.autodiff.samediff.SDVariable; import org.nd4j.autodiff.samediff.SameDiff; import org.nd4j.linalg.api.ndarray.INDArray; public class LayerNormaliztion extends SameDiffLayer { // gain * standardize(x) + bias private double eps = 1e-5; private static String GAIN = "gain"; private static String BIAS = "bias"; private int nOut; private int timeStep; public LayerNormaliztion(int nOut, int timeStep) { this.timeStep = timeStep; this.nOut = nOut; } protected LayerNormaliztion() { } @Override public InputType getOutputType(int layerIndex, InputType inputType) { return InputType.recurrent(nOut); } @Override public void defineParameters(SDLayerParams params) { params.addWeightParam(GAIN, 1, nOut, 1); params.addWeightParam(BIAS, 1, nOut, 1); } @Override public SDVariable defineLayer(SameDiff sd, SDVariable layerInput, Map<String, SDVariable> paramTable, SDVariable mask) { SDVariable gain = paramTable.get(GAIN);//论文中的g SDVariable bias = paramTable.get(BIAS);//论文中的b SDVariable mean = layerInput.mean("mean", true, 1);//均值 SDVariable variance = sd.math().square(layerInput.sub(mean)).sum(true, 1).div(layerInput.getShape()[1]);//平方差 SDVariable standardDeviation = sd.math().sqrt("standardDeviation", variance.add(eps));//标准差,加上eps 防止分母为0 long[] maskShape = mask.getShape(); return gain.mul(layerInput.sub(mean).div(standardDeviation)).add(bias) .mul(mask.reshape(maskShape[0], 1, timeStep));//掩码掩掉多余长度 } @Override public void initializeParameters(Map<String, INDArray> params) { params.get(GAIN).assign(1); params.get(BIAS).assign(0); } public int getNOut() { return nOut; } public void setNOut(int nOut) { this.nOut = nOut; } public int getTimeStep() { return timeStep; } public void setTimeStep(int timeStep) { this.timeStep = timeStep; } }

    五、实战的结果

    就用RNN做文本分类而言,加上LN,收敛会更平稳,但准确率大大下降了。

    在deeplearning的世界里,任何一种方法被提出来,只是在解当前的问题,对于每一种具体的问题,肯定是case by case。

 

快乐源于分享。

   此博客乃作者原创, 转载请注明出处

 

原文链接:https://my.oschina.net/u/1778239/blog/3108483
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章