1. 程式人生 > >通俗話說一說各種Normalization以及用deeplearning4j實現Layer Normalization

通俗話說一說各種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。

 

快樂源於分享。

   此部落格乃作者原創, 轉載請註明