通俗話說一說各種Normalization以及用deeplearning4j實現Layer Normalization
一、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。
快樂源於分享。
此部落格乃作者原創, 轉載請註明