對基於深度神經網絡的Auto Encoder用於異常檢測的一些思考

1、前言html

    現實中,大部分數據都是無標籤的,人和動物多數狀況下都是經過無監督學習獲取概念,故而無監督學習擁有廣闊的業務場景。舉幾個場景:網絡流量是正常流量仍是攻擊流量、視頻中的人的行爲是否正常、運維中服務器狀態是否異常等等。有監督學習的作法是給樣本標出label,那麼標label的過程確定是基於某一些規則(圖片除外),既然有了規則,何須要機器學習?基於規則寫程序就得了,究竟是先有雞,仍是先有蛋?若是計算機能夠本身在數據中發現規律,就解決了這個爭論。那麼基於深度神經網絡的auto encoder,能夠解決一部分問題。java

2、Auto Encoder介紹服務器

    Auto Encoder其實是一個信息壓縮的過程,把高維數據壓縮至低維度,先來看一下PCA。(如下圖片來自於臺大李宏毅教授的ppt)網絡

    

     PCA的解釋:原向量x乘以矩陣W獲得中間編碼c,再乘以W的轉置,獲得x head,獲得的x head但願與原x越接近越好,有一點要注意,從x到c的變換過程是線性的。app

    Deep Auto Encoder和PCA相似,只是網絡層數更深,變換是非線性的(由於能夠加入一些非線性的激活函數),Deep Auto Encoder變成成了以下的樣子:框架

    中間有個很窄的hidden layer的輸出就是壓縮以後的code,固然以bottle layer對稱的W沒必要要互爲轉置,也不要求必定要用RBM初始化參數,直接train,效果也很好。運維

    下面來看一下,對於MNIST手寫數字數據集用像素點、PCA、Deep Auto Encoder三種方式分別作tSNE的展示圖機器學習

    

    右上角爲deep auto encoder以後作tSNE,全部的數字都分的很開,效果比較好。ide

    總結一下,PCA和Deep auto encoder所作的事都相似,就是把原數據壓縮到一個低維向量,讓後再反解回來,反解回來的值但願與原來的值越接近越好。
3、Auto Encoder用於異常檢測
    對於自動編碼器用於異常檢測,能夠參考《Variational Autoencoder based Anomaly Detection using Reconstruction Probability》這篇論文 ,論文地址:http://dm.snu.ac.kr/static/docs/TR/SNUDM-TR-2015-03.pdf ,論文標題大概能夠這樣翻譯:基於變分自動編碼器重建機率的異常檢測。
    文中不是直接從變分自動編碼器切入,而是先介紹自動編碼器,論文言簡意賅,咱們先來看看論文中對Auto Encoder的訓練過程的描述函數

   
    說明:

        一、編碼器fφ,decoder gθ
        二、損失函數說明:這裏用二範數來表示了,二範數其實是歐幾里得距離,也就是均方偏差,也就是但願解碼出來的值和原值月接近越好。

        下面來看,若是將Auto Encoder用於異常檢測,仍是先看公式

    

    說明:

        一、先用正常的數據集訓練一個Auto Encoder

        二、用訓練出的Auto Encoder計算異常數據的重建偏差,重建偏差大於某個閥值α,則爲異常,不然則正常。

    文中有這樣一段描述:

    Autoencoder based anomaly detection is a deviation based anomaly detection method using  semi-supervised learning. It uses the reconstruction error as the anomaly score. Data points  with high reconstruction are considered to be anomalies.Only data with normal instances are used to train the autoencoder

    這段描述指出了兩點:

    一、半監督,用正常的數據訓練一個Auto Encoder

    二、重建偏差高的數據爲異常數據

    普通Deep Auto Encoder有個缺陷,通俗的話來說就是模型看過的相似的數據它知道,模型沒看過的數據,幾乎不可能能知道,那變分編碼器,就可能解決了一部分問題,經過用分佈類似來解釋這個問題,請看下面的公式:

    

    整個訓練的過程用通俗的話說明一下,首先從標準正態分佈中隨機L個數據和原x通過hφ函數後獲得隱含變量z,注意這裏是爲每個樣本x都隨機L個數據來生成z,loss函數告訴咱們兩件事情,

第一件,但願z的分佈和給定了x的條件下z的分佈越接近越好,第二件,但願給定了z的條件下反解回來的x和原分佈越接近越好。本質上而言,VAE是給原x加上了隨機噪聲,同時但願能夠反解回原來的值,

中間隱含變量就很是神奇了,能夠是高斯分佈,也能夠是伯努利分佈,用機率分佈來編碼,在自動生成中有妙用。

    那麼VAE是如何用於異常檢測的呢?請繼續往下看。

 

    上面的訓練過程,能夠這樣解釋,給定x的條件下獲取z的分佈的平均值和方差,就獲得了一個基於x的分佈,從該分佈中獲得L個隱含變量z,經過z反解回x,獲得重建機率,

    重建機率小於某個閥值爲異常,不然則爲正常。

4、一些思考

    論文中只給出了能夠這樣作以及這樣作的效果,可是論文中沒有解釋爲何這樣作。

    其實這個問題比較好類比,訓練數據都是正常的,就比如一我的生活的圈子裏只有貓,忽然來了一條狗,他就確定不認識了,只知道和貓不一樣,那麼經過數學中的線性迴歸來類推一下,根據一批正常點擬合了一條直線,有一個遊離於這個羣體的點,天然離這條直線很遠。一樣的能夠用數據邊界來解釋,神經網絡見過了大量的正常數據,便學習到了數據的邊界,遊離於邊界外的數據,天然也能夠摘出來。

    可是一樣有一個問題,既然原始樣本集裏已經有正常數據和異常數據了,經過有監督訓練一個分類模型就夠了。可是真實的場景裏,咱們是不知道怎麼標註數據的,若是說能夠標註,確定是基於人工指定的規則,既然有了規則,就基於規則寫if else(這裏是針對數據維度比較小的場景)了,何須要機器來學習,可是規則制定的是否正確還兩說,比方說金融領域常見的風險評估,某個用戶是否能夠放貸給他,已知該用戶的各類信息,例如:年齡、性別、職業、收入、信用卡帳單等等,這裏只是舉一個小例子。

    以上的疑問,總結起來就兩點

    一、如何標註數據,究竟是先有雞仍是先有蛋

    二、基於定死的規則標註的數據是否正確可用

    那麼,能不能徹底無監督的讓機器學習出識別這些異常數據的規則,我以爲經過Auto Encoder是能夠作到的,首先,對於異常識別的場景,正常的樣本數確定佔大多數,異常的只是少數,把整個樣本集徹底扔給Deep Auto Encoder,讓他去學習,一樣能夠找出異常數據。

    我寫了一個小例子來驗證個人觀點。

    框架:DL4J

    咱們有經典的數據集,根據天氣判斷是否打球,這裏是從weka的data中複製的,

@attribute outlook {sunny, overcast, rainy}
@attribute temperature {hot, mild, cool}
@attribute humidity {high, normal}
@attribute windy {TRUE, FALSE}
@attribute play {yes, no}

@data
sunny,hot,high,FALSE,no
sunny,hot,high,TRUE,no
overcast,hot,high,FALSE,yes
rainy,mild,high,FALSE,yes
rainy,cool,normal,FALSE,yes
rainy,cool,normal,TRUE,no
overcast,cool,normal,TRUE,yes
sunny,mild,high,FALSE,no
sunny,cool,normal,FALSE,yes
rainy,mild,normal,FALSE,yes
sunny,mild,normal,TRUE,yes
overcast,mild,high,TRUE,yes
overcast,hot,normal,FALSE,yes
rainy,mild,high,TRUE,no

    利用DL4J構建一個多層全鏈接神經網絡,代碼以下

public class OneHotEncoder {

	public static void main(String[] args) {
		Map<String, Integer> map = new HashMap<>();
		map.put("sunny", 0);
		map.put("overcast", 1);
		map.put("rainy", 2);
		map.put("hot", 3);
		map.put("mild", 4);
		map.put("cool", 5);
		map.put("high", 6);
		map.put("normal", 7);
		map.put("TRUE", 8);
		map.put("FALSE", 9);
		map.put("yes", 0);
		map.put("no", 1);

		String data = new StringBuilder().append("sunny,hot,high,FALSE,no").append("\n")
				.append("sunny,hot,high,TRUE,no").append("\n").append("overcast,hot,high,FALSE,yes").append("\n")
				.append("rainy,mild,high,FALSE,yes").append("\n").append("rainy,cool,normal,FALSE,yes").append("\n")
				.append("rainy,cool,normal,TRUE,no").append("\n").append("overcast,cool,normal,TRUE,yes").append("\n")
				.append("sunny,mild,high,FALSE,no").append("\n").append("sunny,cool,normal,FALSE,yes").append("\n")
				.append("rainy,mild,normal,FALSE,yes").append("\n").append("sunny,mild,normal,TRUE,yes").append("\n")
				.append("overcast,mild,high,TRUE,yes").append("\n").append("overcast,hot,normal,FALSE,yes").append("\n")
				.append("rainy,mild,high,TRUE,no").toString();
		INDArray feature = Nd4j.zeros(new int[] { 9, 10 });
		INDArray featureTest = Nd4j.zeros(new int[] { 5, 10 });
		INDArray label = Nd4j.zeros(14, 2);
		String[] rows = data.split("\n");
		int index=0;
		int indexTest=0;
		for (int i = 0; i < rows.length; i++) {
			String[] cols = rows[i].split(",");
			if(cols[cols.length-1].equals("yes")){
				for (int j = 0; j < cols.length - 1; j++) {
					feature.putScalar(index, map.get(cols[j]), 1.0);
				}
				index++;
			}else{
				for (int j = 0; j < cols.length - 1; j++) {
					featureTest.putScalar(indexTest, map.get(cols[j]), 1.0);
				}
				indexTest++;
			}
			
			label.putScalar(i, map.get(cols[cols.length - 1]), 1.0);
		}
		
		DataSet dataSet = new DataSet( Nd4j.vstack(feature,featureTest,feature,feature,feature,feature,feature,feature), Nd4j.vstack(feature,featureTest,feature,feature,feature,feature,feature,feature));
		
		DataSet dataSetTest = new DataSet(featureTest, featureTest);

		MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder().seed(123).activation(Activation.TANH)
				.weightInit(WeightInit.XAVIER).updater(new Sgd(0.1)).l2(1e-4).list()
				.layer(0, new DenseLayer.Builder().nIn(10).nOut(20).build())
				.layer(1, new DenseLayer.Builder().nIn(20).nOut(2).build())
				.layer(2, new DenseLayer.Builder().nIn(2).nOut(20).build())
				.layer(3, new OutputLayer.Builder(LossFunctions.LossFunction.MSE).activation(Activation.IDENTITY)
						.nIn(20).nOut(10).build())
				.backprop(true).pretrain(false).build();

		// run the model
		MultiLayerNetwork model = new MultiLayerNetwork(conf);
		model.init();
		model.setListeners(new ScoreIterationListener(1));
		LayerWorkspaceMgr mgr = LayerWorkspaceMgr.noWorkspaces();
		for (int i = 0; i < 10000; i++) {
			model.fit(dataSet);
			System.out.println(model.score(dataSetTest));

		}

	}
}

 上面有一段特殊代碼以下,這裏是把正常樣本的數量加大,形成正負樣本的不均衡

DataSet dataSet = new DataSet( Nd4j.vstack(feature,featureTest,feature,feature,feature,feature,feature,feature), Nd4j.vstack(feature,featureTest,feature,feature,feature,feature,feature,feature));

   訓練以後的結果以下:

    對於異常樣本,Loss和總體的Loss有一個數量級的誤差,徹底能夠做爲異常的檢測,這裏沒有刻意的用正樣原本訓練,而是用正負樣本一塊兒訓練,徹底無監督,就達到了異常檢測的效果,固然這適用於正負樣本很是不均衡的場景。

    爲何能夠這樣作,我想了一個不用數學公式推導的解釋,將寫在下次博客中。

    最後,DL4J是一個很是優秀的Deeplearning框架,對於Java系的小夥伴想了解Deeplearning的,能夠看看DL4J的例子。

快樂源於分享。

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

相關文章
相關標籤/搜索