NNs(Neural Networks,神經網絡)和Polynomial Regression(多項式迴歸)等價性之思考,以及深度模型可解釋性原理研究與案例

1. Main Point

0x1:行文框架

  • 第二章:咱們會分別介紹NNs神經網絡和PR多項式迴歸各自的定義和應用場景。
  • 第三章:討論NNs和PR在數學公式上的等價性,NNs和PR是兩個等價的理論方法,只是用了不一樣的方法解決了同一個問題,這樣咱們就造成了一個統一的觀察視角,再也不將深度神經網絡當作是一個獨立的算法。
  • 第四章:討論通用逼近理論,這是爲了將視角提升到一個更高的框架體系,通用逼近理論證實了全部的目標函數均可以擬合,換句話說就是,全部的問題均可以經過深度學習解決。可是通用逼近理論並無告訴咱們具體用什麼模型。
  • 第五章/第六章:討論NNs和PR都存在的兩個主要潛在缺陷:1)多重共線性;2)過擬合性。討論這2個缺陷的目的是爲了讓咱們更好的理解複雜網絡的深層原理,以及解決過擬合問題的通用底層思惟,經過這樣的視角討論,咱們會發現,dropout和正則化並無本質的區別,只是看問題的視角不一樣罷了。
  • 第七章:討論一個很是棒的學術研究成果,LIME,它提供了一種使用簡單複合函數(線性函數、決策樹等)來近似局部逼近深度學習模型的理論和方法,爲咱們更好的理解深度模型的底層邏輯提供了新的視角。

0x2:Main Academic Point 

  • 多項式迴歸PR,和神經網絡NNs,在數學公式上具備近似等價性,都是是一個複合函數。
  • 對於任何單變量函數,只要基函數(神經元、一元線性單元)足夠多,神經網絡函數就能任意逼近它。
  • 對於任何多變量函數,必定能夠被多個單變量函數的複合來逼近。
  • NNs的學習是個數據擬合(最小二乘迴歸)的過程,本質上和PR的線性迴歸分析是同樣的,擬合過程是在學習基函數的線性組合。
  • 具體的NNs應用過程當中,選多少層、選多少基的問題在逼近論中就是沒有很好的解決方案,這是逼近論中就存在的問題,並非深度網絡帶來的問題。也就是說,最優神經網絡的構建問題,是須要從逼近論這個層面去突破的,單純研究神經網絡幫助並不會很大。 

0x3:Main Engineering Point 

  • 須要選取多大的神經網絡(也就是選用什麼樣的擬合函數)?具體地,網絡要多少層?每層多少節點?這個須要根據你要解決的具體問題而定,通常來講,問題越簡單,網絡的自由度就要越小,而目標問題越複雜,網絡的自由度就要適當放大。
  • 先創建一個較小的網絡來解決核心問題,而後一步一步擴展到全局問題。
  • 可視化你的結果!這樣有助於在訓練過程當中發現問題。咱們應該明確的看到這些數據:損失函數的變化曲線、權重直方圖、變量的梯度等。不能只看數值。

 

2. NNs and Polynomial Regression

這一章節,我麼分別對NNs和PR進行簡要介紹,爲下一章節討論它們兩者之間的等價性進行一些鋪墊。html

0x1:Polynomial Regression(多項式迴歸)

1. 爲何咱們須要多項式迴歸

線性迴歸模型是機器學習和數理統計中最簡單也最多見的模型,可是線性迴歸有一個最重要的假設前提就是,響應變量和解釋變量之間的確存在着 線性關係,不然就沒法創建有效(強擬合優度)的線性模型。
然而現實中的問題每每線性關係比較弱,甚至原本就不存在着線性關係。實際上,大部分的問題都是非線性關係,因此咱們須要非線性模型的多項式迴歸。

2. 多項式迴歸形式化定義

多項式迴歸就是把一次特徵轉換成高次特徵的線性組合多項式,下面用一元狀況進行舉例,多元狀況以此類推。
對於一元線性迴歸模型以下:

一元線性迴歸模型git

擴展成一元多項式迴歸模型(degree = d)就是:github

一元多項式迴歸模型web

通常地,考慮 n 維特徵(x1,x2,…,xn),d次冪的狀況(n元d次冪多項式):算法

其中,shell

上式即爲n元d次多項式的通用表達式,中間部分是一個排列組合公式的省略寫法。api

從特徵向量維度的角度來看,PolynomialFeatures(degree=d)將維度爲n的原始特徵(n元特徵)擴展到了一個維度爲的新特徵空間。能夠形象的理解爲,將d個相同小球排成一排後,用n個隔板將其進行分割,每一個隔間分配多少小球的問題,排列組合的結果爲種方法。數組

值得注意的一點是,n元d次多項式在特徵空間上具備兩個主要特色:安全

  • n元特徵的權重的離散化分配
  • n元特徵之間的特徵組合:例如當原始特徵爲a,b,次冪爲3時,不只僅會將a3,b3做爲新特徵,還會添加a2b,ab2和ab。 
另外一點值得注意的是,關於多項式特徵空間擴展的這個特色,在帶來更強擬合能力的同時,也引入了過擬合的潛在風險,即「too many turn paramets problem」。

3. 多項式迴歸代碼示例

# -*- coding: utf-8 -*-

import matplotlib.pyplot as plt
import numpy as np
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression


if __name__ == '__main__':
    # generate a random dataset
    np.random.seed(42)

    m = 100
    X = 6 * np.random.rand(m, 1) - 3
    y = 0.5 * X ** 2 + X + 2 + np.random.randn(m, 1)

    plt.plot(X, y, "b.")
    plt.xlabel("$x_1$", fontsize=18)
    plt.ylabel("$y$", rotation=0, fontsize=18)
    plt.axis([-3, 3, 0, 10])
    plt.show()

    # use Scikit-Learn PolynomialFeature class to constructing parameter terms.
    # a,b,degree=2: [a, b, a^2, ab, b^2]
    # a,b,degree=3: [a, b, a^2, ab, b^2, a^3, a^2b, ab^2, b^3]
    # a,b,c,degree=3: [a, b, c, a^2, ab, ac, b^2, bc, c^2, a^3, a^2b, a^2c, ab^2, ac^2, abc, b^3, b^2c, bc^2, c^3]
    poly_features = PolynomialFeatures(degree=2, include_bias=False)
    # fit the dataset with Polynomial Regression Function, and X_poly is the fitting X result
    X_poly = poly_features.fit_transform(X)
    print "X: ", X
    print "X_poly: ", X_poly

    lin_reg = LinearRegression()
    lin_reg.fit(X_poly, y)
    print(lin_reg.intercept_, lin_reg.coef_)

    # draw the prediction curve
    X_new = np.linspace(-3, 3, 100).reshape(100, 1)
    # fit the X_new dataset with Polynomial Regression Function, and X_new_poly is the fitting X result
    X_new_poly = poly_features.transform(X_new)
    y_new = lin_reg.predict(X_new_poly)
    plt.plot(X, y, "b.")
    plt.plot(X_new, y_new, "r-", linewidth=2, label="Predictions")
    plt.xlabel("$x_1$", fontsize=18)
    plt.ylabel("$y$", rotation=0, fontsize=18)
    plt.legend(loc="upper left", fontsize=14)
    plt.axis([-3, 3, 0, 10])
    plt.show()

代碼中有一個細節須要注意一下,PolynomialFeatures的參數d=2,即2次冪多項式。這裏之因此選2次冪是由於咱們事先知道了數據集的機率分佈,形狀大體爲一個二次函數。實際上,讀者朋友能夠本身修改代碼,改成degree=3/4..,多項式依然能夠擬合的很好,由於這個數據集中的噪聲點不是不少,不容易發生過擬合。網絡

可是在實際的工程中,數據集的維度數十萬都是很正常的,咱們不可能事先知道最適合的d次冪參數是多少。一個最經常使用的理論和方法就是設置一個相對較大的d次冪,即便用一個相對複雜的多項式函數去擬合數據,固然,d次冪參數也不能設置的過大,由於過於複雜的多項式函數會致使過擬合的發生。

Relevant Link:  

https://blog.csdn.net/tsinghuahui/article/details/80229299
https://www.jianshu.com/p/9185bc96bfa9
https://blog.csdn.net/qq_36523839/article/details/82924804

0x2:Neural Nets(NNs,神經網絡)

這個小節,咱們以數學公式爲探查視角,從簡單神經元到淺層神經網絡,逐步討論學習其公式形式,爲接下來討論NNs和PR的近似等價性做準備。

1. 單神經元(感知機)單層神經網絡 

單個神經元是神經網絡的最基本組成單元(1層1神經元的神經網絡退化爲感知機模型),單個的感知機的本質就是一個一元線性分類函數,用它能夠劃出一條線,把一維平面分割開:

可是,當面對更復雜的問題時,一元線性分類函數(一維平面)就沒法解決了。例如」電路模擬中的XOR運算問題「。

在數字邏輯中,異或是對兩個運算元的一種邏輯分析類型,符號爲XOR或EOR或⊕。與通常的或(OR)不一樣,當兩兩數值相同時爲否,而數值不一樣時爲真。異或的真值表以下:

XOR truth table
Input Output
A B
0 0 0
0 1 1
1 0 1
1 1

單層的神經元模型能夠對「與/與非/或」等邏輯進行很好的模擬,可是惟獨沒法解決異或問題,以下圖:

神經元模型對「與」、「與非」、「或」問題均可以找到一條完美的決策面。可是,對於XOR問題中的數據點,不管如何調整,都沒法找到一個完美的決策面。

沒法找出一條直線做爲決策邊界,可使(0,0)和(1,1)在一個區域,而(1,0)和(0,1)在另外一個區域。

2. 多神經元(感知機)多層神經網絡 

單神經元單層網絡(一元線性分類模型)沒法解決XOR問題的本質緣由是,XOR問題中數據向量的秩爲2,用秩爲1的一元線性分類函數是沒法線性表出XOR問題的數據向量的。按照線性代數的理論解釋,要線性表出一個秩爲2的向量組,必須用大於等於2的向量組,也就是說必須用二元及二元以上的線性分類器,才能實現對XOR問題的分類。
如今,咱們嘗試用一個包含2個神經元隱層的雙層感知機模型來解決XOR問題,
每個神經元都由一個感知機模型表示,使用閾值函數做爲它的激活函數。比特符號0和1,分別由0和+1表示。
頂部的神經元標註爲「Neuron 1」,有: 

該隱藏神經元構造的決策邊界斜率等於-1,在下圖中給出其位置:

隱藏層底部的神經元標註爲「Neuron 2」,有:

隱藏元構造的決策邊界方向和位置由下圖給出:

咱們能夠看到,兩個隱藏神經元已經各自完成了一半的分類任務,如今須要的是一個「複合決策函數」,將它們分類能力進行一個綜合,獲得原來兩個隱藏神經元造成的決策邊界構造線性組合。

輸出層的神經元標註爲「Neuron 3」,有:

底部隱藏神經元由一個興奮(正)鏈接到輸出神經元,而頂部隱藏神經元由一個更強的抑制(負)鏈接到輸出神經元。這樣,經過構造一個隱層(本質上是一個複合線性函數),咱們成功地解決了XOR問題。

如今,咱們將多神經元單層神經網絡抽象爲通常數學公式,以一個包含3個神經元的輸入層和1個神經元的隱藏層神經網絡爲例:

從複合線性函數的角度來分析,上圖所示的神經網絡等價於:

合併同類項後有:

能夠看到,上式本質上是一個多元1次冪線性方程組

目前爲止,看起來多神經元(感知機)神經網絡已經能夠很好解決問題了,但其實它還遠遠不夠。咱們繼續來看一個更復雜的案例。
假設咱們有這個需求,要將下面的三角形和圓形點進行正確的分類,以下圖:
能夠看到,數據集不一樣類別之間是彼此交錯的,任何單獨線性函數都沒法完美分類,以下圖:

不管單條直線決策面如何調整,都是完成二分類任務

面對這種狀況,就須要增長線性元的數量,改用多元複合線性函數來進行多元線性切割,以下圖:

從理論上說,若是容許複合線性函數中的單個線性函數彼此平行相交,則幾乎全部數據集均可以經過多元線性複合函數進行線性切割。換句話說,多元線性複合函數能夠無限逼近任意機率分佈(通用逼近理論)。

可是須要注意,對同一個分類任務來講,若是要實現完美分類,多元線性複合函數須要的函數元可能會不少,這就致使了維度爆炸問題,過於複雜的線性複合函數也間接增長了過擬合的風險。
延伸思考
未剪枝前的決策樹,本質上就是一個由感知機和階躍函數組合的多層神經網絡(DNN),這麼說可能有些抽象,咱們來看一個常規決策樹對特徵空間的劃分示意圖

決策樹的多層樹結構,本質就是感知機DNN的多層結構。從這個角度來看,決策樹和感知機DNN一樣都存在過擬合問題

延伸思考

以R2二維空間爲例,屬於目標函數的點可能分佈在空間中的任何位置,模型訓練的過程就是須要找到一個超分界面,將全部的點都分類到合適的類別中,這就是所謂的」模型記憶「。須要注意的是,如下兩個觀點都是錯誤的:

  • 有多少數據點就須要有多少神經元,每一個神經元負責記憶一個數據點
  • 樣本點中有多少pattern,就須要多少神經元,每一個神經元負責記憶一種pattern

正確的理解是:最少須要的神經元數量取決於目標函數機率分佈的規律性,若是目標函數在特徵空間中不一樣類別是彼此交錯分佈的,那麼爲了正確地」切割「出一個合適的超平面,就須要遠大於pattern數的基函數,這樣切出來的超平面邊界會很是的鋸齒狀,相應的也能夠想象,抗擾動能力也會相應降低。這也是爲何說越複雜的模型越容易過擬合的緣由

3. 非線性激活函數神經網絡

上一小節留下的問題是,有沒有既能實現完美分類,同時又能有效控制函數元數量的複合函數呢?答案是確定的,這就是咱們接下來要討論的非線性複合函數(包含非線性激活函數的神經網絡)。

咱們知道,使用階躍激活函數的多元感知機神經網絡,本質上是多個線性分界面的組合,以下圖:

上圖中,若是咱們能構造出一個彎曲的決策超曲面,就可能實現用少許的非線性函數,直接對數據集進行分類。

非線性激活函數有不少,不一樣的數學公式形式帶來了不一樣的數學特性,這裏咱們以sigmoid函數爲例:

單神經元后增長了一個非線性激活函數的神經網絡

 
3神經元后增長一個非線性激活函數做爲輸入層,隱藏層由單個神經元組成,後面加一個非線性激活函數,獲得一個非線性多神經元複合神經網絡
回到上面的例子,經過3個非線性分類函數能夠實現完美分類,而且具備更好的泛化能力。

Relevant Link:  

https://www.cnblogs.com/LittleHann/p/6629069.html - Neural Networks and Deep Learning(神經網絡與深度學習) - 學習筆記

 

3. NNs和PR的等價性

這個章節咱們來從神經網絡的觀點來看多項式擬合函數,並分析其等價性。

0x1:一元一次冪多項式函數和NNs的等價性

對於一元一次冪的逼近函數

能夠當作爲以下圖的三層神經網絡,

左:輸入層到隱層的權係數均爲常值1

右:輸入層到隱層當作爲「直接代入」(用虛線表示)

神經網絡中只有一個隱層。隱層上有個節點,激活函數分別爲基函數(從這裏咱們將基函數稱爲「激活函數」)。輸入層到隱層的權設爲常值1(左圖),也能夠當作爲將輸入層的值「直接代入」到激活函數(右圖)。隱層到輸出層的權爲基函數的組合係數

0x2:n元m次冪多項式函數和NNs的等價性

考慮通常的逼近函數。設中的一組基函數爲。則函數可當作爲以下圖的一個三層的神經網絡,

注意這裏隱層的激活函數都是維函數,從輸入層到隱層也是直接代入。輸出層的各個份量共享隱層的激活函數。

通常地, 一個多元線性迴歸方程,等價於一個3層人工神經網絡。也就是說,只要包含一個隱層的人工神經網絡,就能夠等價全部多形式迴歸模型。

更進一步地,若是給人工神經網絡加上非線性激活函數、增長網絡深度,這只是在增長神經網絡的自由度,多項式迴歸依然可以在一個限定的偏差ε內,近似地等價於該神經網絡。

並且在實際工程中,這個近似的程度還得具體目標分佈有關(目標問題場景),若是目標分佈較簡單,則在正則化稀疏學習的做用下,神經網絡會退化爲一個多項式函數。這就是爲什咱們在某些簡單的問題上,用隨機森林和深度神經網絡的效果是差很少的,甚至傳統隨機森林效果還要更好。

Relevant Link:  

http://staff.ustc.edu.cn/~lgliu/Resources/DL/What_is_DeepLearning.html

 

4. Universal Approximation Theorems(通用逼近理論)

上一章節咱們討論了NNs和PR的等價性,基本上來講,咱們能夠將NNs和PR視爲同一種函數模型。這個章節咱們就來討論一個對它們兩者都使用的通用逼近理論(universal approximation theorems),通用逼近理論告訴咱們,必定存在一個多層神經網絡或者多項式函數,能夠在必定的偏差ε內,近似地逼近任意函數分佈

雖然通用逼近定理並無給出如何找到這個NNs或PR,可是它從理論上證實了強存在性,這個存在性定理實在使人振奮,由於這意味着,在具體工程項目中,咱們總能夠應用深度神經網絡取得一個不錯的結果。

0x1:什麼是逼近問題

在討論具體的理論以前,咱們首先經過一個簡單的案例,對逼近問題創建一個直觀的感覺。

咱們先考慮最簡單的情形,即實數到實數的一元函數。假設經過實驗得到了m個樣本點。咱們但願求得反映這些樣本點規律的一個函數關係,以下圖所示。

1. 插值問題(Interpolation)

若是要求函數嚴格經過每一個樣本點,即:,則求解函數的問題稱爲插值問題(Interpolation)。插值問題通常須要針對樣本數據直接求解線性方程組的解。

插值問題更多僅限於理論分析,在實際的工程中,由於偏差和目標函數未知的緣故,幾乎不可能找到一個函數能完美經過全部的樣本點。因此,更多時候,咱們須要討論逼近問題,而插值問題就是逼近問題的一個特例(偏差爲0的逼近),相關討論,能夠參閱這篇文章

2. 逼近問題(Approximation)

通常地,因爲實驗數據帶有觀測偏差,所以在大部分狀況下,咱們只要求函數反映這些樣本點的趨勢,即函數靠近樣本點且偏差在某種度量意義下最小,稱爲逼近問題(Approximation)。

若記在某點的偏差爲,且記偏差向量爲。逼近問題就是要求向量的某種範數最小。通常採用歐氏範數(範數)做爲偏差度量的標準(均方偏差),即求以下極小化問題:

極小化問題,通常可經過極大似然估計或者矩估計的方法實現。

通用逼近理論討論的就是函數逼近問題,咱們接下來圍繞這個主題展開討論。 

0x2:逼近函數模型分類

在科學技術的各領域中,咱們所研究的事件通常都是有規律(因果關係)的,即自變量集合與應變量集合之間存在的對應關係一般用映射來描述,按照模型(函數)是否具有明確的函數表達式(機率分佈函數),能夠將模型大體分爲兩類:

  • 生成式模型:有些函數關係可由理論分析直接推導得出(先驗),不只爲進一步的分析研究工做提供理論基礎,也能夠方便的解決實際工程問題。好比,適合於宏觀低速物體的牛頓第二運動定律就是在實際觀察和概括中得出的普適性力學定律。
  • 判別式模型:可是,不少工程問題難以直接推導出變量之間的函數表達式;或者即便能得出表達式,公式也十分複雜,不利於進一步的分析與計算。這時能夠經過諸如採樣、實驗等方法得到若干離散的數據(稱爲樣本數據點),而後根據這些數據,但願能獲得這些變量之間的函數關係(後驗),這個過程稱爲數據擬合(Data fitting),在數理統計中也稱爲迴歸分析(Regression analysis)。迴歸分析中有一類特殊狀況,輸出的結果是離散型的(好比識別圖片裏是人、貓、狗等標籤的一種),此時問題稱爲分類(Classification)。

0x3:逼近函數方法

函數的表示是函數逼近論中的基本問題。在數學的理論研究和實際應用中常常遇到下類問題:在選定的一類函數中尋找某個函數,使它與已知函數(或觀測數據)在必定意義下爲最佳近似表示,並求出用近似表示而產生的偏差。這就是函數逼近問題

1. 逼近函數類

在實際問題中,首先要肯定函數的具體形式。這不單純是數學問題,還與所研究問題的運動規律及觀測數據有關,也與用戶的經驗有關。通常地,咱們在某個較簡單的函數類中去尋找咱們所須要的函數。這種函數類叫作逼近函數類

逼近函數類能夠有多種選擇,通常能夠在不一樣的函數空間(好比由一些基函數經過線性組合所張成的函數空間)中進行選擇。以下是一些經常使用的函數類。

1)多項式函數類

n次代數多項式,即由次數不大於n的冪基的線性組合的多項式函數:

其中爲實係數。

更經常使用的是由n次Bernstein基函數來表達的多項式形式(稱爲Bernstein多項式或Bezier多項式):

其中Bernstein基函數

2)三角多項式類

n階三角多項式,即由階數不大於n的三角函數基的線性組合的三角函數:

 

中爲實係數。

 

這些是經常使用的逼近函數類。在逼近論中,還有許多其餘形式的逼近函數類,好比由代數多項式的比構成的有理分式集(有理逼近);按照必定條件定義的樣條函數集(樣條逼近);徑向基函數(RBF逼近);由正交函數系的線性組合構成的(維數固定的)函數集等。

3)其餘基函數類

在逼近論中,還有許多其餘形式的逼近函數類,好比:

  • 由代數多項式的比構成的有理分式集(有理逼近)
  • 按照必定條件定義的樣條函數集(樣條逼近)
  • 徑向基函數(RBF逼近)
  • 由正交函數系的線性組合構成的(維數固定的)函數集等
  • GMM模型(高斯分佈基函數)

2. 萬能逼近定理

在函數逼近論中,若是一組函數成爲一組「基」函數,須要知足一些比較好的性質,好比:

  • 光滑性(線性可微)
  • 線性無關性
  • 權性(全部基函數和爲1)
  • 局部支集
  • 完備性:該組函數的線性組合是否可以以任意的偏差和精度來逼近給定的函數(即萬能逼近性質)
  • 正性
  • 凸性等。其中, 「完備性」是指,?

咱們重點來討論一下完備性,即「萬能逼近定理」,

Weierstrass逼近定理

上的任意連續函數g,及任意給定的,必存在n次代數多項式,使得:

Weierstrass逼近定理代表,只要次數n足夠高,n次多項式就能以任何精度逼近給定的函數。具體的構造方法有Bernstein多項式或Chebyshev多項式等。

相似地,由Fourier分析理論(或Weierstrass第二逼近定理),只要階數足夠高,n階三角函數就能以任何精度逼近給定的周期函數,n階高斯函數就能組成GMM分佈以逼近任意給定的機率分佈函數。

這些理論代表,多項式函數類、三角函數類、高斯函數在函數空間是「稠密」的,這就保障了用這些函數類來做爲逼近函數是「合理」的。

0x4:逼近函數選擇的最大挑戰

在一個逼近問題中選擇什麼樣的函數類做逼近函數類,這要取決於被逼近函數自己的特色,也和逼近問題的條件、要求等因素有關。在實際應用中,存在着兩個最大的挑戰,

  • 選擇什麼樣的逼近函數類?通常地,須要用戶對被逼近對象或樣本數據有一些「先驗知識」來決定選擇具體的逼近函數類。好比,
    • 若是被逼近的函數具備週期性,將三角函數做爲逼近函數是個合理的選擇;
    • 若是被逼近的函數具備奇點,將有理函數做爲逼近函數更爲合理,等等。
  • 即便肯定了逼近函數類,選擇多高的次數或階數?好比,若是選擇了多項式函數類,根據Lagrange插值定理,必定能找到一個次多項式來插值給定的個樣本點。但若是較大,則這樣獲得的高次多項式很容易形成「過擬合」(Overfitting)。而若是選擇的太小,則獲得的多項式容易形成「欠擬合」(Underfitting)。以下圖所示。過擬合或欠擬合函數在實際應用中是沒有用的,由於它們的泛化能力不好。

用不一樣次數的多項式擬合樣本點(藍色點)。

左:欠擬合;中:合適的擬合;右:過擬合。

這裏須要說起的是,一個逼近函數「表達能力」體如今該函數的未知參數(例如多項式中的係數)與樣本點個數的差,也稱爲「自由度」。

若是逼近函數的未知參數越多,則表達能力越強。然而,在實際的擬合問題中,逼近函數的擬合能力並不是越強越好。由於若是隻關注樣本點處的擬合偏差的話,很是強的表達能力會使得樣本點以外的函數值遠遠偏離指望的目標,反而下降擬合函數的預測性能,產生過擬合,如上圖(右)所示。擬合能力和過擬合規避之間的平衡,就是經過對自由度的控制來實現。

0x5:通用神經網絡

這一小節,咱們來討論一下通用神經網絡,主要是探尋NNs是如何同時實現通用逼近和防止過擬合這2個目標的,PR和NNs是等價的,所以本章的討論對PR也一樣成立。

對於通用神經網絡來講,網絡的結構設置都存在着以下兩個主要挑戰:

  • 隱層中的節點中使用什麼樣的激活函數(基函數)?(注意這裏激活函數不是特指sigmoid那種激活函數,而是泛指整個神經元的最終輸出函數)
    • 依賴專家先驗經驗
  • 隱層中設置多少個節點(基函數的個數和次數)?
    • 雖然有些基函數的性質很好,可是次數或階數太高(好比多項式基或三角函數基),就會產生震盪,也容易產生過擬合,使得擬合函數的性態很差。

接下來咱們來討論通用神經網絡是如何解決上述兩大挑戰的。

1. 使用簡單「元函數」做爲激活函數

如何在沒有太多領域先驗的狀況下,選擇合適的「基函數」的另外一個策略是「原子化構建基礎,數據驅動結構生成」。

注意到,對於任意一個很是值的一元函數,這裏咱們稱爲元函數,其沿着x方向的平移函數,以及沿着x方向的伸縮函數都與原函數線性無關。

也就是說,若是能有足夠多的元函數通過平移和伸縮變換,其線性組合所張成的函數空間就能有充分的表達能力(高秩矩陣)。因此接下來的問題就是,如何有效地獲得

一個天然的想法就是,咱們能夠以這個做爲激活函數,讓網絡自動地去學習這些激活函數的變換,來表達所須要的擬合函數呢?以下圖所示,

一元(單變量)函數的神經元結構

對單神經元來講,變量乘以一個伸縮,加上一個平移(稱爲偏置「bias」),即變量的仿射變換,成爲神經元的輸入,而後經過激活函數複合後成爲該神經元的輸出

對於多變量的情形(多元函數),神經元的結構以下圖所示,

在多神經元網絡中,每一層的全部神經元都互相鏈接,變量的線性組合,加上一個平移(稱爲偏置「bias」),即變量的仿射變換,成爲神經元的輸入;而後經過激活函數複合後成爲該神經元的輸出 

一個多元函數的神經網絡的結構以下圖所示,有一個輸入層,一個隱層及一個輸出層,

  • 輸入層除了變量外,還有一個常數節點1
  • 隱層包含多個節點,每一個節點的激活函數都是,隱層的輸出就是輸入層節點的線性組合加偏置(即仿射變換)代入到激活函數的複合函數
  • 輸出層是這些複合函數的組合

這個網絡的全部權係數(層與層神經元之間的權),(偏置項)及做爲這個神經網絡的參數變量,須要經過極小化損失函數來求解的。這個過程稱爲「訓練」或「學習」。

和迴歸分析相似,神經網絡的學習過程本質上就是在學習全部的係數參數。最後獲得的擬合函數爲一些基函數的線性組合表達。這些組合函數實質上就是表達函數的「基函數」。這樣就經過數據驅動的方式,獲得了一個最優的基函數線性組合。

從這個觀點來看,神經網絡本質上就是傳統的逼近論中的逼近函數的一種推廣。它不是經過指定的理論完備的基函數(例如多項式,三角多項式等)來表達函數的,而是經過簡單的基元函數(激活函數)的不斷變換獲得的「基函數」來表達函數的。

2. 使用超完備集實現萬能逼近

解決了基函數選擇的問題,咱們還要問個問題:將函數通過充分多的平移和伸縮(包括它們的組合)所線性張成的函數空間,其表達能力足夠強嗎?這個函數空間是否在全部函數空間是稠密的?

若是結論是確定的,那麼就是說,對於任何一個給定的函數,總能找到函數的屢次平移和縮放的函數,其線性組合可以逼近給定的這個函數。也就是說,神經網絡只要隱層的節點數足夠多,該網絡所表達的函數就能逼近任意的函數

這個結論在大多數狀況是成立的,由【萬能逼近定理】所保證。

爲空間中的單位立方體,咱們在這個定義域中來描述萬能逼近定理。記上的連續函數空間,上的可測函數空間,上相對測度μ的可積函數空間(即)。

設給定一元激活函數,首先給出以下定義,

【定義1】稱函數爲壓縮函數,若是單調不減,且知足

【定義2】稱函數爲可分辨的,若對於有限測度μ,由

可獲得

【定義3】記

爲全部由激活函數變換及線性累加所構成的m維函數空間(即具備n個節點的單隱層神經網絡所表達的m維函數)。

由以上定義,有如下幾個定理(涉及實分析和泛函分析), 

【定理1】若是壓縮函數,則中一致稠密,在中按以下距離下稠密:

【定理2】若是可分辨的,則中按連續函數距離下稠密。

【定理3】若是連續有界的很是值函數,則中稠密。

【定理4】若是無界的很是值函數,則中稠密。

通俗地說就是:對任意給定的一個中的函數,只要項數足夠多,中就存在一個函數,使得在必定精度下逼近。也就是說,包含m個神經元的單隱層的神經網絡所表達的維函數可以逼近中的任意一個函數 

基於萬能逼近定理,人工神經網絡每每會選擇一個超完備集神經元,即用大於目標函數維度的神經元數量,來構建一個複雜神經網絡,以保證近似逼近能力。

3. 使用稀疏學習在超完備集中選擇合適數量的基函數,以下降自由度

使用超完備集在得到萬能逼近能力的同時,會帶來過擬合問題。在人工神經網絡中加入稀疏學習,能夠有效避免該現象。

Relevant Link:    

http://staff.ustc.edu.cn/~lgliu/Resources/DL/What_is_DeepLearning.html
K. Hornik, et al. Multilayer feedforward networks are universal approximations. Neural Networks, 2: 359-366, 1989.
G. Cybenko. Approximation by superpositions of a sigmoidal function. Math. Control Signals System, 2: 303-314, 1989.
K. Hornik. Approximation capabilities of multilayer feedforward networks. Neural Networks, 4: 251-257, 1991.

 

5. PR(多項式迴歸)and NNs(神經網絡)Overfitting

在實際工程項目中,無論是直接應用VGG-xx或者本身設計一種全新的網絡結構,網絡的參數動輒都上千萬,網絡愈來愈複雜,參數愈來愈多。

但須要注意的是,擬合函數所帶的參數的個數與樣本數據的個數之間的差表明着這個擬合函數的「自由度」。網絡愈來愈「深」後,擬合模型中的可調整參數的數量就很是大。所以,層數很大的深度網絡(模型過於複雜)可以表達一個自由度很是大的函數空間,甚至遠高於目標函數空間(過完備空間),即自由度遠大於0。這樣就很容易致使過擬合(Overfitting),例以下圖所示,

過擬合可使得擬合模型可以插值全部樣本數據(擬合偏差爲0!)。但擬合偏差爲0不表明模型就是好的,由於模型只在訓練集上表現好;因爲模型擬合了訓練樣本數據中的噪聲,使得它在測試集上表現可能很差,泛化性能差等。

爲此,人們採起了不一樣的方法來緩解過擬合(沒法徹底避免),好比正則化、數據增廣、Dropout、網絡剪枝等。這些方法的底層原理,歸結爲一句話都是:稀疏表達和稀疏學習

0x1:稀疏表達和稀疏學習 - 緩解overfitting的有效手段

緩解逼近函數過擬合的有效手段是,在函數公式中對迴歸變量施加範數的正則項,例如L1/L2正則項,以達到對迴歸變量進行稀疏化,即大部分迴歸變量爲0(少數迴歸變量非0)。

這種優化稱爲稀疏優化。也就是說,對迴歸變量施加範數可以「自動」對基函數進行選擇,值爲0的係數所對應的基函數對最後的逼近無貢獻。這些非0的基函數反映了的樣本點集合的「特徵」,所以也稱爲特徵選擇。

咱們每每能夠多選取一些基函數(甚至能夠是線性相關的)及較高的次冪,使得基函數的個數比輸入向量的維數還要大,稱爲「超完備」基(Over-complete basis)或過冗餘基,在稀疏學習中亦稱爲「字典」。而後經過對基函數的係數進行稀疏優化(稀疏學習/字典學習),選擇出合適(非0係數)的基函數的組合來表達逼近函數。

稀疏學習與最近十年來流行的壓縮感知(Compressive sensing)理論與方法很是相關,也是機器學習領域的一種重要方法。其理論基礎由華裔數學家陶哲軒(2006年國際數學家大會菲爾茲獎得主)、斯坦福大學統計學教授David Donoho(2018年國際數學家大會高斯獎得主)等人所創建,已成功用於信號處理、圖像與視頻處理、語音處理等領域。

Relevant Link:     

https://cosx.org/2016/06/discussion-of-sparse-coding-in-deep-learning/

 

6. Multicollinearity(多重共線性) 

所謂多重共線性,簡單來講,是指迴歸模型中存在兩個或兩個以上的自變量彼此相關。其實本質上說,多重共線性和咱們上一章討論的超完備基本質上是同樣的,由於超完備基經常是稀疏的,其內部每每存在較多線性相關的結構。

在NNs和PR中,也一樣存在多重共線性問題,因此這章咱們也來討論這個問題,經過這些討論,咱們可以更加深入理解NNs和PR的等價性。

0x1:形成多重共線性的緣由

  • 解釋變量都享有共同的時間趨勢
  • 一個解釋變量是另外一個的滯後,兩者每每遵循一個趨勢
  • 因爲數據收集的基礎不夠寬,某些解釋變量可能會一塊兒變更
  • 某些解釋變量間存在某種近似的線性依賴關係

0x2:處理多重共線性的原則

  • 多重共線性是廣泛存在的,輕微的多重共線性問題可不採起措施
  • 若是模型僅用於預測,則只要擬合程度好,可不處理多重共線性問題,存在多重共線性的模型用於預測時,每每不影響預測結果

0x3:多重共線性的負面影響

  • 變量之間高度相關,可能使迴歸的結果混亂,甚至把分析引入歧途
  • 難以區分每一個解釋變量的單獨影響
  • 變量的顯著性檢驗失去意義,模型的線性關係檢驗(F檢驗)顯著,但幾乎全部迴歸係數bi的t檢驗卻不顯著
  • 對參數估計值的正負號產生影響,特別是估計係數的符號可能與預期的正相反,形成對迴歸係數的解釋是危險的。好比:違約率應該和貸款餘額是正相關的,但因爲有其餘因素的影響最終模型中貸款餘額的係數爲負,獲得「貸款餘額越大違約率越低」的危險解釋。可見,在創建迴歸模型時,並不會特徵變量越多越好,由於他們帶來問題比解決的問題可能更多
  • 迴歸模型缺少穩定性。樣本的微小擾動均可能帶來參數很大的變化,由於重複的特徵變量不少,任何一個擾動均可能被放大不少倍
  • 影響模型的泛化偏差

0x4:方差擴大因子VIF(variance inflation factor):定量評估多重共線性程度

方差擴大(膨脹)因子法是經過考察給定的解釋變量被方程中其餘全部解釋變量所解釋的程度,以此來判斷是否存在多重共線性的一種方法。
方程中的每個解釋變量都有一個方差擴大(膨脹)因子(variance inflation factor,VIF),它反映的是多重共線性在多大程度上增大估計係數方差的指標。
統計上能夠證實,解釋變量 、參數估計值 的方差可表示爲:
式中, 是變量 的方差擴大因子,即,
   
這裏的 是多個解釋變量輔助迴歸的可決係數。 越大,說明變量間多重共線性越嚴重,方差膨脹因子 也就越大。
經驗代表, 時,說明解釋變量與其他解釋變量之間有嚴重的多重共線性。且這種多重共線性可能會過分地影響模型擬合結果。

0x5:多重共線性影響舉例

# -*- coding: utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn import cross_validation


if __name__ == '__main__':
    # 首先捏造一份好的數據,樣本量爲100,特徵數爲8,且知足方程: y = 5x_0 + 6x_1 + 7x_2 + 8x_3 + 9x_4 + 10x_5 + 11x_6 + 12x_7 + b
    coef0 = np.array([5, 6, 7, 8, 9, 10, 11, 12])
    X1 = np.random.rand(100, 8)
    # 偏差項是指望爲0,標準差爲1.5的正態分佈隨機變量。
    y = np.dot(X1, coef0) + np.random.normal(0, 1.5, size=100)

    training = np.random.choice([True, False], p=[0.8, 0.2], size=100)
    lr1 = LinearRegression()
    lr1.fit(X1[training], y[training])
    # 係數的均方偏差MSE
    print "lr1 MSE: ", (((lr1.coef_ - coef0) ** 2).sum() / 8)
    # 測試集準確率(R2)
    print "lr1 R2:", (lr1.score(X1[~training], y[~training]))
    # 平均測試集準確率
    print "lr1 MR2:", (cross_validation.cross_val_score(lr1, X1, y, cv=5).mean())

    # 基於上面構造數據,另外構造出兩份數據,
    # 1. X2: 第一份數據則增長兩個共線性特徵,目的是顯著增長其VIF值
    # 2. X3: 第二份數據增長兩個隨機的特徵用做對比
    X2 = np.column_stack([X1, np.dot(X1[:, [0, 1]], np.array([1, 1])) + np.random.normal(0, 0.05, size=100)])
    X2 = np.column_stack([X2, np.dot(X2[:, [1, 2, 3]], np.array([1, 1, 1])) + np.random.normal(0, 0.05, size=100)])
    X3 = np.column_stack([X1, np.random.rand(100, 2)])

    # 拿這兩份數據從新用線性迴歸擬合模型
    lr2 = LinearRegression()
    lr2.fit(X2[training], y[training])
    # 係數的均方偏差MSE
    # 對於第二份共線性構造數據X2,由於多重共線性,能夠看到MSE增長了不少,準確率也降低了0.2%:
    print "lr2 MSE: ", (((lr2.coef_[:8] - coef0) ** 2).sum() / 8)
    # 測試集準確率(R2)
    print "lr2 R2: ", (lr2.score(X2[~training], y[~training]))
    # 平均測試集準確率
    print "lr2 MR2: ", (cross_validation.cross_val_score(lr2, X2, y, cv=5).mean())

    lr3 = LinearRegression()
    lr3.fit(X3[training], y[training])
    # 係數的均方偏差MSE
    # X3沒有明顯變化
    print "lr3 MSE: ", (((lr3.coef_[:8] - coef0) ** 2).sum() / 8)
    # 測試集準確率(R2)
    print "lr3 R2: ", (lr3.score(X3[~training], y[~training]))
    # 平均測試集準確率
    print "lr3 MR2: ", (cross_validation.cross_val_score(lr3, X3, y, cv=5).mean())

    # show lr2 VIF result
    vif2 = np.zeros((10, 1))
    for i in range(10):
        tmp = [k for k in range(10) if k != i]
        lr2.fit(X2[:, tmp], X2[:, i])
        vifi = 1 / (1 - lr2.score(X2[:, tmp], X2[:, i]))
        vif2[i] = vifi

    vif3 = np.zeros((10, 1))
    for i in range(10):
        tmp = [k for k in range(10) if k != i]
        lr2.fit(X3[:, tmp], X3[:, i])
        vifi = 1 / (1 - lr2.score(X3[:, tmp], X3[:, i]))
        vif3[i] = vifi
    plt.figure()
    ax = plt.gca()
    ax.plot(vif2)
    ax.plot(vif3)
    plt.xlabel('feature')
    plt.ylabel('VIF')
    plt.title('VIF coefficients of the features')
    plt.axis('tight')
    plt.show()

能夠看到,0、一、二、三、八、9個特徵的VIF都太高,其中第9個是咱們人工構造出了存在線性相關依賴的新特徵變量。

0x6:NNs中廣泛存在的多重共線性

在神經網絡中,同層的神經元和層與層之間的神經元之間都有可能存在多重共線性(線性相關),層內的多重共線性能夠經過正則化進行緩解,相比之下,層與層神經元之間存在的多重共線性就沒法避免了,它是廣泛存在的。 

這裏以一個「10 units」的全鏈接神經網絡爲例,

計算層與層之間神經元的平均VIF結果以下:

能夠看到隨着前向傳遞的進行,後面層的神經元的VIF愈來愈大,以層的視角來看,層與層之間的線性相關性逐漸提升,這個結論對PR也是一樣成立的。

這也從另外一個層面看到,對於神經網絡來講,真正起做用的也只有最後一層隱層,雖然訓練過程是全網絡總體反饋調整的,可是最終輸出層的結果大部分由最有一層隱層的基函數決定。 

Relevant Link:     

https://baike.baidu.com/item/%E6%96%B9%E5%B7%AE%E6%89%A9%E5%A4%A7%E5%9B%A0%E5%AD%90 
https://www.jianshu.com/p/0925347c5066
https://www.jianshu.com/p/ef1b27b8aee0
https://arxiv.org/pdf/1806.06850v1.pdf

 

7. 深度模型可解釋性初探:用簡單局部線性函數近似逼近深度神經網絡

0x1:基本原理說明

1.  Interpretable Data Representations(可解釋性數據表徵)

通常來講,模型的輸入層是可解釋性最強的,例如原始專家經驗特徵、圖像像素矩陣、文本原始詞序列向量等。 

假設輸入層維度爲d,可解釋性模型的維度d應該小於等於輸入層維度d,用」0/1「編碼來表徵輸入層的每個特徵是否出現,即,

2. 保真性和解釋性平衡 - 構建似然估計函數

Local Interpretable Model-agnostic Explanations(局部線性逼近可解釋模型)須要同時平衡兩個對立的目標:

  • 保真性:可解釋性複合線性函數和目標函數的逼近偏差要儘可能小
  • 可解釋性:可解釋性複合函數自己的複雜度要儘可能低,基元函數數量越少,就越能從中理解到人類可讀的可解釋性

經過極大似然估計來得到一個最優結果:

3. 基於局部擾動採樣的負反饋訓練過程

LIME捕獲局部線性特徵的過程以下圖所示,

將目標函數f()當作是一個零先驗黑盒,經過不斷重複f(x)->add random noise to x->f(x),勾勒出目標函數的近似局部邊界,並將獲取的樣本做爲打標數據輸入LIMIE模型進行負反饋訓練,這個作法和蒙特卡洛採樣的思想是相似的。

還有一點值得注意,LIMIE同時使用正則化來進行稀疏學習,進一步減小可解釋性單元,將可解釋單元集中在特定的一些重點特徵上,提升人類可讀性。

0x2:隨機森林特徵可解釋性

咱們知道,隨機森林本質上是一個最優赫夫曼編碼函數。在隨機森林每棵樹中,特徵節點的選擇和各個特徵節點所處的位置,自己就包含了一個複合線性決策函數的能力。可是,咱們最多也只能定性地瞭解有限特徵的相對重要性,對每個特徵定量的重要性評估沒法得知。

咱們經過LIME對一個隨機森林模型進行「local linear approximation(局部線性近似)」,藉助線性函數的強可解釋性,來定量研究隨機森林在預測中,各個特徵向量各自起到了多少的貢獻(似然機率)。

# -*- coding: utf-8 -*-

import lime
import sklearn
import numpy as np
import sklearn
import sklearn.ensemble
import sklearn.metrics
from sklearn.datasets import fetch_20newsgroups
from lime import lime_text
from sklearn.pipeline import make_pipeline
from lime.lime_text import LimeTextExplainer

if __name__ == '__main__':
    # we'll be using the 20 newsgroups dataset.
    # In particular, for simplicity, we'll use a 2-class subset: atheism and christianity.
    categories = ['alt.atheism', 'soc.religion.christian']
    newsgroups_train = fetch_20newsgroups(subset='train', categories=categories)
    newsgroups_test = fetch_20newsgroups(subset='test', categories=categories)
    class_names = ['atheism', 'christian']

    # use the tfidf vectorizer, commonly used for text.
    vectorizer = sklearn.feature_extraction.text.TfidfVectorizer(lowercase=False)
    train_vectors = vectorizer.fit_transform(newsgroups_train.data)
    test_vectors = vectorizer.transform(newsgroups_test.data)

    # use random forests for classification.
    # It's usually hard to understand what random forests are doing, especially with many trees.
    rf = sklearn.ensemble.RandomForestClassifier(n_estimators=500)
    rf.fit(train_vectors, newsgroups_train.target)

    pred = rf.predict(test_vectors)
    print sklearn.metrics.f1_score(newsgroups_test.target, pred, average='binary')

    # Lime explainers assume that classifiers act on raw text,
    # but sklearn classifiers act on vectorized representation of texts.
    # For this purpose, we use sklearn's pipeline, and implements predict_proba on raw_text lists.
    c = make_pipeline(vectorizer, rf)
    print(c.predict_proba([newsgroups_test.data[0]]))

    # Now we create an explainer object. We pass the class_names a an argument for prettier display.
    explainer = LimeTextExplainer(class_names=class_names)

    # We then generate an explanation with at most 6 features for an arbitrary document in the test set.
    idx = 83
    exp = explainer.explain_instance(newsgroups_test.data[idx], c.predict_proba, num_features=6)
    print('Document id: %d' % idx)
    print('Probability(christian) =', c.predict_proba([newsgroups_test.data[idx]])[0, 1])
    print('True class: %s' % class_names[newsgroups_test.target[idx]])

    # The classifier got this example right (it predicted atheism).
    # The explanation is presented below as a list of weighted features.
    print "exp.as_list(): ", exp.as_list()

    # These weighted features are a linear model,
    # which approximates the behaviour of the random forest classifier in the vicinity of the test example.
    # Roughly, if we remove 'Posting' and 'Host' from the document ,
    # the prediction should move towards the opposite class (Christianity) by about 0.27 (the sum of the weights for both features).
    # Let's see if this is the case.
    print('Original prediction:', rf.predict_proba(test_vectors[idx])[0, 1])
    tmp = test_vectors[idx].copy()
    tmp[0, vectorizer.vocabulary_['Posting']] = 0
    tmp[0, vectorizer.vocabulary_['Host']] = 0
    print('Prediction removing some features:', rf.predict_proba(tmp)[0, 1])
    print('Difference:', rf.predict_proba(tmp)[0, 1] - rf.predict_proba(test_vectors[idx])[0, 1])

    # Visualizing explanations
    # The explanations can be returned as a matplotlib barplot:
    fig = exp.as_pyplot_figure()
    fig.show()
    exp.save_to_file('./oi.html')

能夠看到,在將某一個document預測爲「atheism」這一類別的時候,總共有「edu」、「NNTP」、「Posting」、「Host」、「There」、「hava」這些單詞起到了似然機率貢獻,而且它們各自的貢獻比是不一樣的。

0x3:InceptionV3圖像識別預測可解釋性分析

google的InceptionV3神經網絡模型,採用卷積網絡對圖像進行了預訓練。這節,咱們用局部線性逼近方法,來對該網絡的局部可解釋性進行分析。

# -*- coding: utf-8 -*-

import matplotlib.pyplot as plt
import numpy as np
import os
import keras
import os, sys
try:
    import lime
except:
    sys.path.append(os.path.join('..', '..')) # add the current directory
    import lime
from lime import lime_image
from keras.applications import inception_v3 as inc_net
from keras.preprocessing import image
from keras.applications.imagenet_utils import decode_predictions
from skimage.io import imread
from skimage.segmentation import mark_boundaries
print('run using keras:', keras.__version__)


def transform_img_fn(path_list):
    out = []
    for img_path in path_list:
        img = image.load_img(img_path, target_size=(299, 299))
        x = image.img_to_array(img)
        x = np.expand_dims(x, axis=0)
        x = inc_net.preprocess_input(x)
        out.append(x)
    return np.vstack(out)


if __name__ == '__main__':
    # Using Inception
    # Here we create a standard InceptionV3 pretrained model,
    # and use it on images by first preprocessing them with the preprocessing tools
    inet_model = inc_net.InceptionV3()

    # Let's see the top 5 prediction for some image
    img_path = os.path.join('data', 'cat_and_mouse.jpg')
    print "img_path: ", img_path
    images = transform_img_fn([img_path])
    # I'm dividing by 2 and adding 0.5 because of how this Inception represents images
    plt.imshow(images[0] / 2 + 0.5)
    plt.show()
    preds = inet_model.predict(images)
    for x in decode_predictions(preds)[0]:
        print(x)

    # Explanation
    # Now let's get an explanation
    explainer = lime_image.LimeImageExplainer()
    # hide_color is the color for a superpixel turned OFF.
    # Alternatively, if it is NONE, the superpixel will be replaced by the average of its pixels.
    # Here, we set it to 0 (in the representation used by inception model, 0 means gray)
    explanation = explainer.explain_instance(images[0], inet_model.predict, top_labels=5, hide_color=0,
                                             num_samples=1000)

    # Now let's see the explanation for the Top class
    # We can see the top 5 superpixels that are most positive towards the class with the rest of the image hidden
    temp, mask = explanation.get_image_and_mask(explanation.top_labels[0], positive_only=True, num_features=5,
                                                hide_rest=True)
    plt.imshow(mark_boundaries(temp / 2 + 0.5, mask))
    plt.show()
    # Or with the rest of the image present:
    temp, mask = explanation.get_image_and_mask(explanation.top_labels[0], positive_only=True, num_features=5,
                                                hide_rest=False)
    plt.imshow(mark_boundaries(temp / 2 + 0.5, mask))
    plt.show()
    # We can also see the 'pros and cons' (pros in green, cons in red)
    temp, mask = explanation.get_image_and_mask(explanation.top_labels[0], positive_only=False, num_features=10,
                                                hide_rest=False)
    plt.imshow(mark_boundaries(temp / 2 + 0.5, mask))
    plt.show()
    # Or the pros and cons that have weight at least 0.1
    temp, mask = explanation.get_image_and_mask(explanation.top_labels[0], positive_only=False, num_features=1000,
                                                hide_rest=False, min_weight=0.1)
    plt.imshow(mark_boundaries(temp / 2 + 0.5, mask))
    plt.show() 

原始待預測打標圖像

經過局部線性逼近獲得的top預測類(tabby)分界面(核心特徵區)

with the rest of the image present

pros and cons' (pros in green, cons in red)

經過這個例子,咱們能夠更加深入的認識到,卷積神經網絡是如何經過選取捕獲像素圖中特定區域,實現目標檢測與目標識別任務的。

0x4:Recurrent neural networks可解釋性可視化探索 

# -*- coding: utf-8 -*-

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

from keras.models import Sequential
from keras.layers import LSTM, Dropout, Dense
from keras.optimizers import Adam
from keras.utils import to_categorical
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import classification_report
from lime import lime_tabular


def reshape_data(seq, n_timesteps):
    N = len(seq) - n_timesteps - 1
    nf = seq.shape[1]
    if N <= 0:
        raise ValueError('I need more data!')
    new_seq = np.zeros((N, n_timesteps, nf))
    for i in range(N):
        new_seq[i, :, :] = seq[i:i+n_timesteps]
    return new_seq


if __name__ == '__main__':
    # We will use the CO2 dataset, which measures the concentration of CO2 above Mauna Loa every week since about 1960.
    # The classification task will be deciding if the concentration is rising,
    # this is a problem that needs recurrency to solve (since the answer comes from the derivative),
    # and is less trivial than it sounds because there is noise in the data.
    df = pd.read_csv('data/co2_data.csv', index_col=0, parse_dates=True)

    fig, (left, right) = plt.subplots(nrows=1, ncols=2, figsize=(13, 5))
    df[['co2']].plot(ax=left)
    df[['co2_detrended']].plot(ax=right)
    fig.show()

    # Reshaping the dataset to be appropriate for the model
    N_TIMESTEPS = 12  # Use 1 year of lookback
    data_columns = ['co2', 'co2_detrended']
    target_columns = ['rising']

    scaler = MinMaxScaler(feature_range=(-1, 1))
    X_original = scaler.fit_transform(df[data_columns].values)
    X = reshape_data(X_original, n_timesteps=N_TIMESTEPS)
    y = to_categorical((df[target_columns].values[N_TIMESTEPS:-1]).astype(int))

    # Train on the first 2000, and test on the last 276 samples
    X_train = X[:2000]
    y_train = y[:2000]
    X_test = X[2000:]
    y_test = y[2000:]
    print(X.shape, y.shape)

    # Define the model
    model = Sequential()
    model.add(LSTM(32, input_shape=(N_TIMESTEPS, len(data_columns))))
    model.add(Dropout(0.2))
    model.add(Dense(2, activation='softmax'))

    optimizer = Adam(lr=1e-4)
    model.compile(loss='binary_crossentropy', optimizer=optimizer)
    # train the model
    model.fit(X_train, y_train, batch_size=100, epochs=100,
              validation_data=(X_test, y_test),
              verbose=2)

    y_pred = np.argmax(model.predict(X_test), axis=1)
    y_true = np.argmax(y_test, axis=1)
    print(classification_report(y_true, y_pred))

    plt.plot(y_true, lw=3, alpha=0.3, label='Truth')
    plt.plot(y_pred, '--', label='Predictions')
    plt.legend(loc='best')
    plt.show()

    # Explain the model with LIME
    explainer = lime_tabular.RecurrentTabularExplainer(X_train, training_labels=y_train, feature_names=data_columns,
                                                       discretize_continuous=True,
                                                       class_names=['Falling', 'Rising'],
                                                       discretizer='decile')
    exp = explainer.explain_instance(X_test[50], model.predict, num_features=10, labels=(1,))
    print exp
    exp.show_in_notebook()

We can see that the most important features are the de-trended CO2 concentration several timesteps in the past. In particular, we see that if that feature is low in the recent past, then the concentration is now probably rising.

0x5:Webshell Random Foreast模型可解釋性可視化探索 

1. 加載webshell黑白樣本

100個黑/300個白

2. TF_IDF特徵工程,隨機森林訓練

TF_IDF詞素似然機率

測試集預測結果

3. LIME局部特徵逼近

LIME中各個子線性模型的似然機率佔比

能夠看到,對於這個判黑樣原本說,主要是'eval'、'base64_decode'、'gzinflate'這些關鍵詞起到了似然機率貢獻做用,這也和咱們的安全領域先驗知識是吻合的。

同時,爲了進一步理解線性基元對目標函數的局部線性近似逼近,咱們手動disable掉2個top似然機率的基元函數,並觀察目標函數的預測結果,

去掉eval和base64_decode以後,目標函數的預測機率值等於這2個函數各自的基元函數的可解釋似然機率

這個實驗結果,證明了LIME基於隨機擾動負反饋的局部線性逼近的有效性,LIME的局部線性基元函數能夠較好的表明目標函數的整體特徵,局部彙總=整體,即1+1+1=3。

反過來,這種可解釋性也爲webshell領域裏的文本畸形變化對抗提供了理論依據,在文本詞維度的擾動能夠干擾文本詞維度的檢測機制。做爲防護方,要對抗這種對抗,就須要將模型抽象維度拉昇到更高的維度,例如apicall、opcode、彙編代碼層等。

4. 可視化LIME可解釋模型

LIME基元函數中對判黑和判白的各自似然機率佔比

Relevant Link:   

https://github.com/marcotcr/lime/blob/master/doc/notebooks/Tutorial%20-%20Image%20Classification%20Keras.ipynb  
https://arxiv.org/pdf/1602.04938.pdf
相關文章
相關標籤/搜索