1. 程式人生 > >神經網路系列之五 -- 線性二分類的方法與原理

神經網路系列之五 -- 線性二分類的方法與原理

系列部落格,原文在筆者所維護的github上:https://aka.ms/beginnerAI, 點選star加星不要吝嗇,星越多筆者越努力。

第6章 多入單出的單層神經網路

6.0 線性二分類

6.0.1 提出問題

我們經常看到中國象棋棋盤中,用楚河漢界分割開了兩個陣營的棋子。回憶歷史,公元前206年前後,楚漢相爭,當時劉邦和項羽麾下的城池,在中原地區的地理位置示意圖如圖6-1所示,部分樣本資料如表6-1所示。

圖6-1 樣本資料視覺化

  1. 紅色圓點,楚,項羽的城池
  2. 綠色叉子,漢,劉邦的城池

表6-1 樣本資料抽樣

樣本序號 X1:經度相對值 X2:緯度相對值 Y:1=漢, 0=楚
1 0.325 0.888 1
2 0.656 0.629 0
3 0.151 0.101 1
4 0.785 0.024 0
... ... ... ...
200 0.631 0.001 0

我們在上一章學習了特徵歸一化的方法。在本例中,中原地區的經緯度座標其實應該是一個兩位數以上的實數,比如(35.234, -122.455)。為了簡化問題,我們已經把它們歸一化到[0,1]之間了。

問題:

  1. 經緯度相對座標值為(0.58,0.92)時,屬於楚還是漢?
  2. 經緯度相對座標值為(0.62,0.55)時,屬於楚還是漢?
  3. 經緯度相對座標值為(0.39,0.29)時,屬於楚還是漢?

讀者可能會覺得這個太簡單了,這不是有圖嗎?定位座標值後在圖上一比劃,一下子就能找到對應的區域了。但是我們要求用機器學習的方法來解決這個看似簡單的問題,以便將來的預測行為是快速準確的,而不是拿個尺子在圖上去比劃。

另外,本著用簡單的例子說明覆雜的原理的原則,我們用這個看似簡單的例子,是想讓讀者對問題和解決方法都有一個視覺上的清晰認識,而這類可以視覺化的問題,在實際生產環境中並不多見。

6.0.2 邏輯迴歸模型

迴歸問題可以分為兩類:線性迴歸和邏輯迴歸。在第二步中,我們學習了線性迴歸模型,在第三步中,我們將一起學習邏輯迴歸模型。

邏輯迴歸的英文是Logistic Regression,邏輯迴歸是用來計算“事件=Success”和“事件=Failure”的概率。當因變數的型別屬於二元(1 / 0,真/假,是/否)變數時,我們就應該使用邏輯迴歸。

回憶線性迴歸,使用一條直線擬合樣本資料,而邏輯迴歸是“擬合”0或1兩個數值,而不是具體的連續數值,所以它叫廣義線性模型。邏輯迴歸又稱logistic迴歸分析,常用於資料探勘,疾病自動診斷,經濟預測等領域。

例如,探討引發疾病的危險因素,並根據危險因素預測疾病發生的概率等。以胃癌病情分析為例,選擇兩組人群,一組是胃癌組,一組是非胃癌組,兩組人群必定具有不同的體徵與生活方式等。因此因變數就為是否胃癌,值為“是”或“否”;自變數就可以包括很多了,如年齡、性別、飲食習慣、幽門螺桿菌感染等。

自變數既可以是連續的,也可以是分類的。然後通過logistic迴歸分析,可以得到自變數的權重,從而可以大致瞭解到底哪些因素是胃癌的危險因素。同時根據該權值可以根據危險因素預測一個人患癌症的可能性。

邏輯迴歸的另外一個名字叫做分類器,分為線性分類器和非線性分類器,本章中我們學習線性分類器。而無論是線性還是非線性分類器,又分為兩種:二分類問題和多分類問題,在本章中我們學習二分類問題。線性多分類問題將會在下一章講述,非線性分類問題在後續的步驟中講述。

綜上所述,我們本章要學習的路徑是:迴歸問題->邏輯迴歸問題->線性邏輯迴歸即分類問題->線性二分類問題。

表6-2示意說明了線性二分類和非線性二分類的區別。

表6-2 直觀理解線性二分類與非線性二分類的區別

線性二分類 非線性二分類

我們先學習如何解決線性二分類為標題,在此基礎上可以擴充套件為非線性二分類問題。

系列部落格,原文在筆者所維護的github上:https://aka.ms/beginnerAI, 點選star加星不要吝嗇,星越多筆者越努力。

6.1 二分類函式

此函式對線性和非線性二分類都適用。

6.1.1 二分類函式

對率函式Logistic Function,即可以做為啟用函式使用,又可以當作二分類函式使用。而在很多不太正規的文字材料中,把這兩個概念混用了,比如下面這個說法:“我們在最後使用Sigmoid啟用函式來做二分類”,這是不恰當的。在本書中,我們會根據不同的任務區分啟用函式和分類函式這兩個概念,在二分類任務中,叫做Logistic函式,而在作為啟用函式時,叫做Sigmoid函式。

  • 公式

\[Logistic(z) = \frac{1}{1 + e^{-z}} \rightarrow a\]

  • 導數

\[Logistic'(z) = a(1 - a)\]

具體求導過程可以參考8.1節。

  • 輸入值域

\[(-\infty, \infty)\]

  • 輸出值域

\[(0,1)\]

  • 函式影象(圖6-2)

圖6-2 Logistic函式影象

  • 使用方式

此函式實際上是一個概率計算,它把\((-\infty, \infty)\)之間的任何數字都壓縮到\((0,1)\)之間,返回一個概率值,這個概率值接近1時,認為是正例,否則認為是負例。

訓練時,一個樣本x在經過神經網路的最後一層的矩陣運算結果作為輸入z,經過Logistic計算後,輸出一個\((0,1)\)之間的預測值。我們假設這個樣本的標籤值為0屬於負類,如果其預測值越接近0,就越接近標籤值,那麼誤差越小,反向傳播的力度就越小。

推理時,我們預先設定一個閾值比如0.5,則當推理結果大於0.5時,認為是正類;小於0.5時認為是負類;等於0.5時,根據情況自己定義。閾值也不一定就是0.5,也可以是0.65等等,閾值越大,準確率越高,召回率越低;閾值越小則相反,準確度越低,召回率越高。

比如:

  • input=2時,output=0.88,而0.88>0.5,算作正例
  • input=-1時,output=0.27,而0.27<0.5,算作負例

6.1.2 正向傳播

矩陣運算

\[ z=x \cdot w + b \tag{1} \]

分類計算

\[ a = Logistic(z)={1 \over 1 + e^{-z}} \tag{2} \]

損失函式計算

二分類交叉熵損失函式:

\[ loss(w,b) = -[y \ln a+(1-y) \ln(1-a)] \tag{3} \]

6.1.3 反向傳播

求損失函式對a的偏導

\[ \frac{\partial loss}{\partial a}=-[{y \over a}+{-(1-y) \over 1-a}]=\frac{a-y}{a(1-a)} \tag{4} \]

求a對z的偏導

\[ \frac{\partial a}{\partial z}= a(1-a) \tag{5} \]

求損失函式loss對z的偏導

使用鏈式法則連結公式4和公式5:

\[ \frac{\partial loss}{\partial z}=\frac{\partial loss}{\partial a}\frac{\partial a}{\partial z} \]
\[ =\frac{a-y}{a(1-a)} \cdot a(1-a)=a-y \tag{6} \]

我們驚奇地發現,使用交叉熵函式求導得到的分母,與Logistic分類函式求導後的結果,正好可以抵消,最後只剩下了\(a-y\)這一項。真的有這麼巧合的事嗎?實際上這是依靠科學家們的聰明才智尋找出了這種匹配關係,以滿足以下條件:

  1. 損失函式滿足二分類的要求,無論是正例還是反例,都是單調的;
  2. 損失函式可導,以便於使用反向傳播演算法;
  3. 讓計算過程非常簡單,一個減法就可以搞定。

多樣本情況

我們用三個樣本做例項化推導:

\[Z= \begin{pmatrix} z_1 \\ z_2 \\ z_3 \end{pmatrix}, A=Logistic\begin{pmatrix} z_1 \\ z_2 \\ z_3 \end{pmatrix}= \begin{pmatrix} a_1 \\ a_2 \\ a_3 \end{pmatrix} \]

\[ \begin{aligned} J(w,b)=&-[y_1 \ln a_1+(1-y_1)\ln(1-a_1)] \\ &-[y_2 \ln a_2+(1-y_2)\ln(1-a_2)] \\ &-[y_3 \ln a_3+(1-y_3)\ln(1-a_3)] \\ \end{aligned} \]
代入公式6結果:
\[ \begin{aligned} {\partial J(w,b) \over \partial Z}&= \begin{pmatrix} {\partial J(w,b) / \partial z_1} \\ {\partial J(w,b) / \partial z_2} \\ {\partial J(w,b) / \partial z_3} \end{pmatrix} \\ &=\begin{pmatrix} a_1-y_1 \\ a_2-y_2 \\ a_3-y_3 \end{pmatrix}=A-Y \end{aligned} \]

所以,用矩陣運算時可以簡化為矩陣相減的形式:\(A-Y\)。

6.1.4 對數機率的來歷

經過數學推導後可以知道,神經網路實際也是在做這樣一件事:經過調整w和b的值,把所有正例的樣本都歸納到大於0.5的範圍內,所有負例都小於0.5。但是如果只說大於或者小於,無法做準確的量化計算,所以用一個對率函式來模擬。

說到對率函式,還有一個問題,它為什麼叫做“對數機率”函式呢?

我們舉例說明:假設有一個硬幣,丟擲落地後,得到正面的概率是0.5,得到反面的概率是0.5,這兩個概率叫做probability。如果用正面的概率除以反面的概率,0.5/0.5=1,這個數值叫做odds,即機率。

泛化一下,如果正面的概率是a,則反面的概率就是1-a,則機率等於:

\[odds = \frac{a}{1-a} \tag{9}\]

上式中,如果a是把樣本x的預測為正例的可能性,那麼1-a就是其負例的可能性,a/(1-a)就是正負例的比值,稱為機率(odds),反映了x作為正例的相對可能性,而對機率取對數就叫做對數機率(log odds, logit)。

假設概率如表6-3。

表6-3 概率到對數機率的對照表

概率a 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1
反概率 (1-a) 1 0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1 0
機率 odds 0 0.11 0.25 0.43 0.67 1 1.5 2.33 4 9 \(\infty\)
對數機率 ln(odds) N/A -2.19 -1.38 -0.84 -0.4 0 0.4 0.84 1.38 2.19 N/A

可以看到機率的值不是線性的,不利於分析問題,所以在表中第4行對機率取對數,可以得到一組成線性關係的值,並可以用直線方程\(xw+b\)來表示,即:

\[ \ln(odds) = \ln \frac{a}{1-a}= xw + b \tag{10} \]

對公式10兩邊取自然指數:

\[ \frac{a}{1-a}=e^{xw+b} \tag{11} \]

\[ a=\frac{1}{1+e^{-(xw+b)}} \]

令\(z=e^{-(xw+b)}\):

\[ a=\frac{1}{1+e^{-z}} \tag{12} \]

公式12就是公式2!對數機率的函式形式可以認為是這樣得到的。

以上推導過程,實際上就是用線性迴歸模型的預測結果來逼近樣本分類的對數機率。這就是為什麼它叫做邏輯迴歸(logistic regression),但其實是分類學習的方法。這種方法的優點如下:

  • 直接對分類可能性建模,無需事先假設資料分佈,避免了假設分佈不準確所帶來的問題;
  • 不僅預測出類別,而是得到了近似的概率,這對許多需要利用概率輔助決策的任務很有用;
  • 對率函式是任意階可導的凸函式,有很好的數學性,許多數值優化演算法都可以直接用於求取最優解。

系列部落格,原文在筆者所維護的github上:https://aka.ms/beginnerAI, 點選star加星不要吝嗇,星越多筆者越努力。

6.2 用神經網路實現線性二分類

我們先看看如何用神經網路在兩組不同標籤的樣本之間畫一條明顯的分界線。這條分界線可以是直線,也可以是曲線。這就是二分類問題。如果只畫一條分界線的話,無論是直線還是曲線,我們可以用一支假想的筆(即一個神經元),就可以達到目的,也就是說筆的走向,完全依賴於這一個神經元根據輸入訊號的判斷。

再看楚漢城池示意圖,在兩個顏色區域之間似乎存在一條分割的直線,即線性可分的。

  1. 從視覺上判斷是線性可分的,所以我們使用單層神經網路即可;
  2. 輸入特徵是經度和緯度,所以我們在輸入層設定兩個輸入X1=經度,X2=維度;
  3. 最後輸出的是一個二分類,分別是楚漢地盤,可以看成非0即1的二分類問題,所以我們只用一個輸出單元就可以了。

6.2.1 定義神經網路結構

根據前面的猜測,看來我們只需要一個二入一出的神經元就可以搞定。這個網路只有輸入層和輸出層,由於輸入層不算在內,所以是一層網路,見圖6-3。

圖6-3 完成二分類任務的神經元結構

與上一章的網路結構圖的區別是,這次我們在神經元輸出時使用了分類函式,所以有個A的輸出,而不是以往的Z的直接輸出。

輸入層

輸入經度\(x_1\)和緯度\(x_2\)兩個特徵:

\[ X=\begin{pmatrix} x_{1} & x_{2} \end{pmatrix} \]

權重矩陣

輸入是2個特徵,輸出一個數,則\(W\)的尺寸就是2x1:

\[ W=\begin{pmatrix} w_{1} \\ w_{2} \end{pmatrix} \]

B的尺寸是1x1,行數永遠是1,列數永遠和W一樣。

\[ B=\begin{pmatrix} b_{1} \end{pmatrix} \]

輸出層

\[ \begin{aligned} z &= X \cdot W + B =\begin{pmatrix} x_1 & x_2 \end{pmatrix} \begin{pmatrix} w_1 \\ w_2 \end{pmatrix} + (b_1) \\ &=x_1 \cdot w_1 + x_2 \cdot w_2 + b_1 \end{aligned} \tag{1} \]
\[a = Logistic(z) \tag{2}\]

損失函式

二分類交叉熵損失函式:

\[ loss(w,b) = -[yln a+(1-y)ln(1-a)] \tag{3} \]

6.2.2 反向傳播

我們在6.1節已經推導了loss對z的偏導數,結論為\(A-Y\)。接下來,我們求loss對w的導數。本例中,w的形式是一個2行1列的向量,所以求w的偏導時,要對向量求導:

\[ \frac{\partial loss}{\partial w}= \begin{pmatrix} {\partial loss / \partial w_1} \\ {\partial loss / \partial w_2} \end{pmatrix} \]
\[ =\begin{pmatrix} \frac{\partial loss}{\partial z}\frac{\partial z}{\partial w_1} \\ \\ \frac{\partial loss}{\partial z}\frac{\partial z}{\partial w_2} \end{pmatrix} = \begin{pmatrix} (a-y)x_1 \\ (a-y)x_2 \end{pmatrix} \]
\[ =(x_1 \ x_2)^T (a-y) \tag{4} \]

上式中\(x_1x_2\)是一個樣本的兩個特徵值。如果是多樣本的話,公式4將會變成其矩陣形式,以3個樣本為例:

\[ {\partial J(w,b) \over \partial w}= \begin{pmatrix} x_{11} & x_{12} \\ x_{21} & x_{22} \\ x_{31} & x_{32} \end{pmatrix}^T \begin{pmatrix} a_1-y_1 \\ a_2-y_2 \\ a_3-y_3 \end{pmatrix} \]
\[ =X^T(A-Y) \tag{5} \]

6.2.3 程式碼實現

我們先第5章的HelperClass5中,把一些已經寫好的類copy過來,然後稍加改動,就可以滿足我們的需要了。

由於以前我們的神經網路只會做線性迴歸,現在多了一個做分類的技能,所以我們加一個列舉型別,可以讓呼叫者通過指定引數來控制神經網路的功能。

class NetType(Enum):
    Fitting = 1,
    BinaryClassifier = 2,
    MultipleClassifier = 3,

然後在超參類裡把這個新引數加在初始化函式裡:

class HyperParameters(object):
    def __init__(self, eta=0.1, max_epoch=1000, batch_size=5, eps=0.1, net_type=NetType.Fitting):
        self.eta = eta
        self.max_epoch = max_epoch
        self.batch_size = batch_size
        self.eps = eps
        self.net_type = net_type

再增加一個Logistic分類函式:

class Logistic(object):
    def forward(self, z):
        a = 1.0 / (1.0 + np.exp(-z))
        return a

以前只有均方差函式,現在我們增加了交叉熵函式,所以新建一個類便於管理:

class LossFunction(object):
    def __init__(self, net_type):
        self.net_type = net_type
    # end def

    def MSE(self, A, Y, count):
        ...

    # for binary classifier
    def CE2(self, A, Y, count):
        ...

上面的類是通過初始化時的網路型別來決定何時呼叫均方差函式(MSE),何時呼叫交叉熵函式(CE2)的。

下面修改一下NeuralNet類的前向計算函式,通過判斷當前的網路型別,來決定是否要線上性變換後再呼叫sigmoid分類函式:

class NeuralNet(object):
    def __init__(self, params, input_size, output_size):
        self.params = params
        self.W = np.zeros((input_size, output_size))
        self.B = np.zeros((1, output_size))

    def __forwardBatch(self, batch_x):
        Z = np.dot(batch_x, self.W) + self.B
        if self.params.net_type == NetType.BinaryClassifier:
            A = Sigmoid().forward(Z)
            return A
        else:
            return Z

最後是主過程:

if __name__ == '__main__':
    ......
    params = HyperParameters(eta=0.1, max_epoch=100, batch_size=10, eps=1e-3, net_type=NetType.BinaryClassifier)
    ......

與以往不同的是,我們設定了超參中的網路型別是BinaryClassifier。

6.2.4 執行結果

圖6-4所示的損失函式值記錄很平穩地下降,說明網路收斂了。

圖6-4 訓練過程中損失函式值的變化

最後幾行的列印輸出:

......
99 19 0.20742586902509108
W= [[-7.66469954]
 [ 3.15772116]]
B= [[2.19442993]]
A= [[0.65791301]
 [0.30556477]
 [0.53019727]]

打印出來的W,B的值對我們來說是幾個很神祕的數字,下一節再解釋。A值是返回的預測結果:

  1. 經緯度相對值為(0.58,0.92)時,概率為0.65,屬於漢;
  2. 經緯度相對值為(0.62,0.55)時,概率為0.30,屬於楚;
  3. 經緯度相對值為(0.39,0.29)時,概率為0.53,屬於漢。

分類的方式是,可以指定當A > 0.5時是正例,A <= 0.5時就是反例。有時候正例反例的比例不一樣或者有特殊要求時,也可以用不是0.5的數來當閾值。

程式碼位置

ch06, Level1

系列部落格,原文在筆者所維護的github上:https://aka.ms/beginnerAI, 點選star加星不要吝嗇,星越多筆者越努力。

6.3 線性二分類原理

6.3.1 線性分類和線性迴歸的異同

此原理對線性和非線性二分類都適用。

回憶一下前面學習過的線性迴歸,通過用均方差函式的誤差反向傳播的方法,不斷矯正擬合直線的角度(Weights)和偏移(Bias),因為均方差函式能夠準確地反映出當前的擬合程度。那麼線上性分類中,我們能不能採取類似的方法呢?

線性分類,試圖在含有兩種樣本的空間中劃出一條分界線,讓雙方截然分開,就好像是中國象棋的棋盤中的楚河漢界一樣。與線性迴歸相似的地方是,兩者都需要劃出那條“直線”來,但是不同的地方也很明顯,見表6-4。

表6-4 線性迴歸和線性分類的比較

線性迴歸 線性分類
相同點 需要在樣本群中找到一條直線 需要在樣本群中找到一條直線
不同點 用直線來擬合所有樣本,使得各個樣本到這條直線的距離儘可能最短 用直線來分割所有樣本,使得正例樣本和負例樣本儘可能分佈在直線兩側

可以看到線性迴歸中的目標--“距離最短”,還是很容易理解的,但是線性分類的目標--“分佈在兩側”,用數學方式如何描述呢?我們可以有代數和幾何兩種方式來描述。

6.3.2 二分類的代數原理

代數方式:通過一個分類函式計算所有樣本點在經過線性變換後的概率值,使得正例樣本的概率大於0.5,而負例樣本的概率小於0.5。

基本公式回顧

下面我們以單樣本雙特徵值為例來說明神經網路的二分類過程,這是用代數方式來解釋其工作原理。

  1. 正向計算

\[ z = x_1 w_1+ x_2 w_2 + b \tag{1} \]

  1. 分類計算

\[ a={1 \over 1 + e^{-z}} \tag{2} \]

  1. 損失函式計算

\[ loss = -[y \ln (a)+(1-y) \ln (1-a)] \tag{3} \]

用圖6-5舉例來說明計算過程。

圖6-5 不正確的分類線試圖分類紅綠兩色樣本點

平面上有三個點,分成兩類,綠色方塊為正類,紅色三角為負類。各個點的座標為:\(A(2,4),B(2,1),C(3,3)\)。

分類線為L1時

假設神經網路第一次使用\(L_1\)做為分類線,此時:\(w_1=-1,w_2=2,b=-2\),我們來計算一下三個點的情況。

\(A\)點:

\[ z_A = (-1)\times 2 + 2 \times 4 -2 = 4 > 0 \tag{正確} \]

\(B\)點:

\[ z_B = (-1)\times 2 + 2 \times 1 -2 = -2 < 0 \tag{正確} \]

\(C\)點:

\[ z_C = (-1)\times 3 + 2 \times 3 -2 = 1 > 0 \tag{錯誤} \]

從6.1節中我們知道當\(z>0\)時,\(Logistic(z) > 0.5\)為正例,反之為負例,所以我們只需要看三個點的\(z\)值是否大於0或小於0就可以了,不用再計算\(Logistic\)的函式值。

其中,\(A、B\)點是處於正確的分類區,而\(C\)點處於錯誤的分類區。此時\(C\)點的損失函式值為(注意\(C\)的標籤值\(y=0\)):

\[ a_C = Sigmoid(z_C) = 0.731 \]

\[ loss_Z = -(0 \cdot \ln(0.731) + 1 \cdot \ln(1-0.731))=1.313 \]

讀者可能對1.313這個值沒有什麼概念,是大還是小呢?我們不妨計算一下分類正確的\(A、B\)點的座標:

\[ loss_A = 0.018, \quad loss_B = 0.112 \]

可見,對於分類正確的\(A、B\)點來說,其損失函式值比\(C\)點要小很多,所以對於\(C\)點的反向傳播的力度就大。對比總結如表6-5。

表6-5 對比三個點在各個環節的計算值

座標值 z值 a值 y值 loss值 分類情況
A (2,4) 4 0.982 1 0.018 正確
B (2,1) -2 0.119 0 0.112 正確
C (3,3) 1 0.731 0 1.313 錯誤
  • 在正例情況y=1時,a如果越靠近1,表明分類越正確,此時損失值會越小。點A就是這種情況:a=0.982,距離1不遠;loss值0.018,很小;
  • 在負例情況y=0時,a如果越靠近0,表明分類越正確,此時損失值會越小。點B就是這種情況:a=0.119,距離0不遠;loss值0.112,不算很大;
  • 點C是分類錯誤的情況,a=0.731,應該是小於0.5的,卻距離0遠,距離1反而近,所以它的loss=1.313,從與其它兩個點比較的相對值來看,是非常大的,這樣誤差就大,反向傳播的力度也大。

分類線為L2時

我們假設經過反向傳播後,神經網路把直線的位置調整到\(L_2\),以\(L_2\)做為分類線,即\(w_1=-1,w_2=1,b=-1\),則三個點的\(z\)值都會是符合其分類的:

\[ z_A = (-1)\times 2 + 1 \times 4 -1 = 1 > 0 \tag{正確} \]

\[ z_B = (-1)\times 2 + 1 \times 1 -1 = -2 < 0 \tag{正確} \]

\[ z_C = (-1)\times 3 + 1 \times 3 -1 = -1 < 0 \tag{正確} \]

這裡可能會產生一個疑問:既然用\(z\)值是否大於0這個條件就可以判斷出分類是否正確,那麼二分類理論中為什麼還要用\(Logistic\)函式做一次分類呢?

原因是這樣的:只有\(z\)值的話,我們只能知道是大於0還是小於0,並不能有效地進行反向傳播,也就是說我們無法告訴神經網路反向傳播的誤差的力度有多大。比如\(z=5\)和\(z=-1\)相比,難度意味著前者的力度是後者的5倍嗎?

而有了\(Logistic\)分類計算後,得到的值是一個\((0,1)\)之間的概率,比如:當\(z=5\)時,\(Logistic(5) = 0.993\);當\(z=-1\)時,\(Logistic(-1)=0.269\)。這兩個數值的含義是這兩個樣本在分類區內的概率,前者概率為99.3%,偏向正例,後者概率為26.9%,偏向負例。然後再計算損失函式,就可以得到神經網路可以理解的反向傳播誤差,比如上面曾經計算過的\(loss_A、loss_B、loss_C\)。

6.3.3 二分類的幾何原理

幾何方式:讓所有正例樣本處於直線的上方,所有負例樣本處於直線的下方,儘可能處於雙方的中間。

二分類函式的幾何作用

二分類函式的最終結果是把正例都對映到圖6-6中的上半部分的曲線上,而把負類都對映到下半部分的曲線上。

圖6-6 Logistic函式把輸入的點對映到(0,1)區間內實現分類

我們用正例來舉例:

\[a = Logistic(z) = {1 \over 1 + e^{-z}} > 0.5\]

做公式變形,兩邊取自然對數,可以得到:

\[z > 0\]

即:
\[ z = x_1 \cdot w_1 + x_2 \cdot w_2 + b > 0 \]

對上式做一下變形,把\(x_2\)放在左側,其他項放在右側(假設\(w_2>0\),則不等號方向不變):
\[ x_2 > - {w_1 \over w_2}x_1 - {b \over w_2} \tag{5} \]

簡化一下兩個係數,令\(w'=-w1/w2,b'=-b/w2\):

\[ x_2 > w' \cdot x_1 + b' \tag{6} \]

公式6用幾何方式解釋,就是:有一條直線,方程為\(z = w' \cdot x_1+b'\),所有的正例樣本都處於這條直線的上方;同理可得所有的負例樣本都處於這條直線的下方。

圖6-7 用直線分開的兩類樣本

我們再觀察一下分類正確的圖,如圖6-7所示。假設綠色方塊為正類:標籤值\(y=1\),紅色三角形為負類:標籤值\(y=0\)。從幾何關係上理解,如果我們有一條直線,其公式為:\(z = w' \cdot x_1+b'\),如圖中的虛線\(L_1\)所示,則所有正類的樣本的\(x_2\)都大於\(z\),而所有的負類樣本的\(x_2\)都小於\(z\),那麼這條直線就是我們需要的分割線。

這就說明神經網路的工作原理和我們在二維平面上的直觀感覺是相同的,即當樣本處於直線上方時,會被判為正例;反之則是負例。

我們還有一個額外的收穫,即:

\[w' = - w1 / w2 \tag{7}\]

\[b' = -b/w2 \tag{8}\]

我們可以使用神經網路計算出\(w_1,w_2,b\)三個值以後,換算成\(w',b'\),以便在二維平面上畫出分割線,來直觀地判斷神經網路訓練結果的正確性。

思考與練習

  1. 在推導公式5時,假設 \(w_2 > 0\),這樣做有什麼問題嗎?如果假設 \(w_2<0\) 會是什麼情況?

系列部落格,原文在筆者所維護的github上:https://aka.ms/beginnerAI, 點選star加星不要吝嗇,星越多筆者越努力。

6.4 二分類結果視覺化

6.4.1 視覺化的重要性

我們雖然得到了結果,但都是一些神祕的數字,我們如何知道它們是正確還是錯誤的呢?

後面我們會講到,在實際的工程實踐中,一般我們會把樣本分成訓練集、驗證集、測試集,用測試集來測試訓練結果的正確性。在本例中我們沒有這樣做,原因有二:

  1. 樣本資料量比較少,一共只有200個樣本,如果再分成兩部分,會造成資料集覆蓋不全面,存在很大的差異,對訓練、驗證、測試都沒有幫助
  2. 由於本例的資料特徵比較少,所以我們有更好的手段:視覺化。在神經網路學習初期,視覺化的訓練過程與結果會對讀者有巨大的幫助。

神經網路的視覺化,說簡單也很簡單,說難也很難,關鍵是對框架系統的理解,對執行機制和工作原理的理解,掌握了這些,視覺化就會使一件輕而易舉且令人愉快的事情。

6.4.2 權重值的含義

在6.2節中的訓練結果如下,這幾個關於W,B數字如何解讀呢?

W= [[-7.66469954]
 [ 3.15772116]]
B= [[2.19442993]]
A= [[0.65791301]
 [0.30556477]
 [0.53019727]]

在6.1節中我們一起學習了線性二分類的原理,其中提到了如果我們能夠根據訓練結果,在圖上畫出一條直線來分割正例和負例兩個區域,是不是就很直觀了呢?

\[ z = x_{1} \cdot w_1 + x_{2} \cdot w_2 + b \tag{1} \]
\[ a=Logistic(z) \tag{2} \]

對公式2來說,當a大於0.5時,屬於正例(屬於漢),當a小於0.5時,屬於負例(屬於楚)。那麼a=0.5時,就是楚漢邊界啦!

\[a = 0.5, 相當於z=0\]
\[z = x_{1} \cdot w_1 + x_{2} \cdot w_2 + b = 0\]

把x2留在等式左側,其它的挪到右側去,就可以得到一條直線的方程了:

\[x_{2} \cdot w_2 = -x_{1} \cdot w_1 - b\]
\[x_2 = -{w_1 \over w_2}x_1 - {b \over w_2} \tag{3}\]

好了,這就是標準的直線方程\(y=ax+b\)的形式了。這個公式等同於二分類原理中的公式7,8。

6.4.3 程式碼實現

用Python程式碼實現公式3如下:

def draw_split_line(net,):
    b12 = -net.B[0,0]/net.W[1,0]
    w12 = -net.W[0,0]/net.W[1,0]
    print(w12,b12)
    x = np.linspace(0,1,10)
    y = w12 * x + b12
    plt.plot(x,y)
    plt.axis([-0.1,1.1,-0.1,1.1])
    plt.show()

上面程式碼中的計算w12,b12就是根據公式3來的,只不過我們的W的定義是(w1, w2),而python是zero-based,所以:
\(w_1 = W[0,0],w_2 = W[0,1],b = B[0,0]\)。

同時需要展示樣本資料,以便判斷分割線和樣本資料的吻合程度:

def draw_source_data(net, dataReader):
    fig = plt.figure(figsize=(6.5,6.5))
    X,Y = dataReader.GetWholeTrainSamples()
    for i in range(200):
        if Y[i,0] == 1:
            plt.scatter(X[i,0], X[i,1], marker='x', c='g')
        else:
            plt.scatter(X[i,0], X[i,1], marker='o', c='r')
        #end if
    #end for

最後還可以顯示一下三個預測點的位置,看看是否正確:

def draw_predicate_data(net):
    x = np.array([0.58,0.92,0.62,0.55,0.39,0.29]).reshape(3,2)
    a = net.inference(x)
    print("A=", a)
    for i in range(3):
        if a[i,0] > 0.5:
            plt.scatter(x[i,0], x[i,1], marker='^', c='g', s=100)
        else:
            plt.scatter(x[i,0], x[i,1], marker='^', c='r', s=100)
        #end if
    #end for

主程式:

# 主程式
if __name__ == '__main__':
    ......
    # show result
    draw_source_data(net, reader)
    draw_predicate_data(net)
    draw_split_line(net)
    plt.show()

6.4.4 執行結果

圖6-8為二分類結果。

圖6-8 稍有欠缺的二分類結果

雖然藍色的分割線大體分開了楚漢兩國,但是細心的讀者會發現在上下兩端,還是綠點在分割線右側,而紅點在分割線左側的情況。這說明我們的神經網路的訓練精度不夠。所以,稍微改一下超參,再訓練一遍:

params = HyperParameters(eta=0.1, max_epoch=10000, batch_size=10, eps=1e-3, net_type=NetType.BinaryClassifier)

把max_epoch從100改成了10000,再跑一次。

圖6-9 訓練過程中損失函式值的變化

從圖6-9的曲線看,損失函式值一直在下降,說明網路還在繼續收斂。再看圖6-10的直線位置,已經比較完美地分開了紅色和綠色區域。

圖6-10 比較完美的二分類的結果

三個三角點是求解問題的三個座標,其中第三個三角點處於分割線附近,用肉眼不是很容易分得出來,看列印輸出:

W= [[-42.62417571]
 [ 21.36558218]]
B= [[10.5773054]]
A= [[0.99597669]
 [0.01632475]
 [0.53740392]]
w12= 1.994992477013739
b12= -0.49506282174794675

前兩個點的概率分別是0.995和0.016,可以明確地區分正例負例,第三個點是0.537,大於0.5,可以算作正例。

在matplot的繪圖控制元件中,我們也可以放大區域性觀察,可以圖6-11的細節。

圖6-11 放大後的細節,綠色點確實在直線左側,分類正確

第三個點位於左側正例區域。

好了,我們已經自信滿滿地找到了解釋神經網路工作的原理,有數值計算驗證,有公式推導,有圖形顯示,至少可以自圓其說了。但實際情況是不是這樣呢?有沒有更高深的原理還沒有接觸到呢?暫且留下這個問題,留在以後的章節去繼續學習。

程式碼位置

ch06, Lev