通俗話說一說各類Normalization以及用deeplearning4j實現Layer Normalization

1、Normalization是什麼java

    Normalization一句話歸納來講就是用一種辦法,將一組數據壓到均值爲0,方差爲1的正態分佈上去,具體作法是數據集的每個元素減去均值再除以標準差。公式以下:(請忽略參數g,g的問題很詭異,後面說)api

    這個公式說的更直白一點就是,把每個a,通過平移和縮放,獲得一個新值。而這樣作的一個理由是,平移縮放並不會改變原始數據的分佈狀況,原來最大的仍是最大,原來最小的仍是最小。網絡

    Deeplearning中有不少Normalization的方法,有BN、LN、IN、GN等等,每一種Normalization公式都同樣,只是沿着的軸不同,BN就是沿着minibatch方向,LN就是沿着影藏層的output vector維方向,舉個例子,對於四維張量[minibatch,depth、height、width],那就是沿着depth方向,把height、width維約簡掉。ide

2、說說Layer Normalization函數

    論文地址:https://arxiv.org/pdf/1607.06450.pdfthis

    Layer Normalization對於時間序列數據有奇效,下面截一段論文的原文。這是在RNN上用Layer Normalizationcode

   

        簡短的話說一下論文的變量含義,a表示t時刻點rnn的預輸出值(尚未通過激活函數哦),h表示rnn某一個隱層t時刻點的輸出。orm

        那麼,這裏Normalization是哪個維度呢?假設RNN的輸入張量爲[minibatch、layerSize、timesteps],這麼這裏Normalization的就是layerSize這一維。這樣可能仍是太抽象,請看下圖:blog

    

 

    這裏Normalization的就是紅色箭頭指向的每個維度的向量,對於一條數據的每一個time step而言,就求出一個均值和方差,進行變換,下一個time step類推下去。那麼多個time step就有多個均值和方差。get

3、重點說說參數g和b

    這裏g和b的維度要和h的維度相同,也就是上圖的values per time step這一維度,也就是layer size的大小,這裏g和b是跟隨着網絡參數學出來的。開始實現時,g的全部值通常會初始化爲1,b的全部值會被初始化爲0,隨着訓練的進行,g和b就會被修改成任意值了。那麼Normalization也就沒有了正態分佈的效果,至關於layer size維乘以了一個隨機向量,注意這裏是向量點積,那麼就等同於給一個隨機的噪聲,竟然也能起做用,也是一個不能解釋的問題。有點沒有道理,但就是這麼難以置信,竟然是work的。

4、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;
	}

}

    5、實戰的結果

    就用RNN作文本分類而言,加上LN,收斂會更平穩,但準確率大大降低了。

    在deeplearning的世界裏,任何一種方法被提出來,只是在解當前的問題,對於每一種具體的問題,確定是case by case。

 

快樂源於分享。

此博客乃做者原創, 轉載請註明出處

相關文章
相關標籤/搜索