深度學習基礎之-3.3線性二分類的神經網絡實現

線性二分類的神經網絡實現

提出問題
回憶歷史,公元前206年,楚漢相爭,當時劉邦項羽麾下的城池地理位置如下:
在這裏插入圖片描述
0.紅色圓點,項羽的城池
1.綠色叉子,劉邦的城池
其中,在邊界處有一些紅色和綠色重合的城池,表示雙方激烈爭奪的拉鋸戰。

樣本序號 1 2 3 119
經度相對值 0 .025 4.109 7.767
緯度相對值 3 .408 8.012 1.872
1=漢, 0=楚 1 1 0 1

問題:

經緯度相對值爲(5,1)時,屬於楚還是漢?
經緯度相對值爲(6,9)時,屬於楚還是漢?
經緯度相對值爲(5,5)時,屬於楚還是漢?

你可能會覺得這個太簡單了,這不是有圖嗎?定位座標值後一下子就找到對應的區域了。但是我們要求你用機器學習的方法來解決這個看似簡單的問題,以便將來的預測行爲是快速準確的,而不是拿個尺子在圖上去比劃。再說了,我們用這個例子,主要是想讓大家對問題和解決方法都有一個視覺上的清晰認識,而這類可以可視化的問題,在實際生產環境中並不多見,絕大多數都是屬於樂山大佛——一頭霧水。

問題分析

從圖示來看,在兩個顏色區間之間似乎存在一條直線,即線性可分的。我們如何通過神經網絡精確地找到這條分界線呢?

從視覺上判斷是線性可分的,所以我們使用單層神經網絡即可
輸入特徵是經度和緯度,所以我們在輸入層設置兩個輸入X1=經度,X2=維度
最後輸出的是兩個分類,分別是楚漢地盤,可以看成非0即1的二分類問題,所以我們只用一個輸出單元就可以了
定義神經網絡結構
根據前一節學習的二分類原理,我們只需要一個二入一出的神經元就可以搞定。這個網絡只有輸入層和輸出層,由於輸入層不算在內,所以是一層網絡。
在這裏插入圖片描述

這次我們第一次使用了分類函數,所以有個A的輸出,而不是以往的Z的輸出。

輸入層

輸入經度(x1)和緯度(x2)兩個特徵:

X=(x1,1 x2,1) X=\begin{pmatrix} x_{1,1} \ x_{2,1} \end{pmatrix}

權重矩陣W1/B1

輸入層是2個特徵,則W的尺寸就是1x2:

W=(w1,1w1,2) W=\begin{pmatrix} w_{1,1} & w_{1,2} \end{pmatrix}

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

B=(b1,1) B=\begin{pmatrix} b_{1,1} \end{pmatrix}

輸出層

Z=WX+BZ=W \cdot X+B A=Sigmoid(Z)A = Sigmoid(Z)

損失函數

二分類交叉熵函損失數 Cross Entropy

J=[YlnA+(1Y)ln(1A)] J = -[YlnA+(1-Y)ln(1-A)]

分類的方式是,可以指定當A > 0.5時是正例,A <= 0.5時就是反例。或者根據實際情況指定別的閾值比如0.3,0.8等等。

此時反向傳播矩陣運算的公式推導結果是:

(交叉熵函數求導)JA=[YA1Y1A]=AYA(1A) \frac{\partial{J}}{\partial{A}}=-[\frac{Y}{A}-\frac{1-Y}{1-A}]=\frac{A-Y}{A(1-A)} \tag{交叉熵函數求導} (Sigmoid激活函數求導)AZ=A(1A) \frac{\partial{A}}{\partial{Z}}=A(1-A) \tag{Sigmoid激活函數求導} (矩陣運算求導)ZW=XT,ZB=1 \frac{\partial{Z}}{\partial{W}}=X^T , \frac{\partial{Z}}{\partial{B}}=1 \tag{矩陣運算求導}

所以W的梯度:

JW=JAAZZW \frac{\partial{J}}{\partial{W}} = \frac{\partial{J}}{\partial{A}} \frac{\partial{A}}{\partial{Z}} \frac{\partial{Z}}{\partial{W}} =AYA(1A)A(1A)XT=\frac{A-Y}{A(1-A)} \cdot A(1-A) \cdot X^T =(AY)XT=(A-Y)X^T

加粗樣式B的梯度:

JB=JAAZZB \frac{\partial{J}}{\partial{B}}=\frac{\partial{J}}{\partial{A}}\frac{\partial{A}}{\partial{Z}}\frac{\partial{Z}}{\partial{B}} =AYA(1A)A(1A)=\frac{A-Y}{A(1-A)}A(1-A) =AY=A-Y

樣本數據
下載後拷貝到您要運行的Python文件所在的文件夾。

點擊下載訓練樣本數據X

點擊下載訓練標籤數據Y

樣本特徵值
XmX_m表示第m個樣本值,xm,nx_{m,n}表示第m個樣本的第n個特徵值。樣本數據集中一共有200個數據,每個數據有兩個特徵:經度和緯度。所以定義矩陣如下: X=(X1X2X200)=(x1,1x2,1x200,1 x1,2x2,2x200,2) X = \begin{pmatrix} X_1 &amp; X_2 \dots X_{200} \end{pmatrix}= \begin{pmatrix} x_{1,1} &amp; x_{2,1} \dots x_{200,1} \ x_{1,2} &amp; x_{2,2} \dots x_{200,2} \end{pmatrix} =(0.0254.1097.7672.762 3.4088.0121.8722.653 ) =\begin{pmatrix} 0.025 &amp; 4.109 &amp; 7.767 &amp; \dots &amp; 2.762 \ 3.408 &amp; 8.012 &amp; 1.872 &amp; \dots &amp; 2.653 \ \end{pmatrix}
樣本標籤值
一般來說,在標記樣本時,我們會用1,2,3這樣的標記,來指明是哪一類。在本例的二分類情況下,我們只需要把正例標記爲1,負例標記爲0。這個需要檢查原始樣本數據的格式,在自己的code中做相應的轉化。如果你認爲劉邦是“好人”,你就把漢標記爲正例。對於一般的疾病分類來說,我們習慣於把陽性(有疾病嫌疑)標記爲正例。

Y=(Y1Y2Ym)=(1101) Y = \begin{pmatrix} Y_1 &amp; Y_2 \dots Y_m \end{pmatrix}= \begin{pmatrix} 1 &amp; 1 &amp; 0 \dots 1 \end{pmatrix}

代碼實現
我們先無恥地從第5章的代碼庫ch05中,把一些已經寫好的函數copy過來,形成一個BaseClassification.py文件,其中會包括神經網絡訓練的基本過程函數,加載數據,數據的歸一化函數,結果顯示函數等等。
加載數據
基本的加載數據和對樣本數據的歸一化工作,都可以用前一章的代碼來完成,統一集成在BaseClassification.py中了,下面代碼中的from BaseClassification import *就是完成了代碼引入的工作。但是對於標籤數據,需要一個特殊處理。

import numpy as np
import matplotlib.pyplot as plt
from pathlib import Path
import math
from BaseClassification import *

x_data_name = "X2.dat"
y_data_name = "Y2.dat"

def ToBool(YData):
    num_example = YData.shape[1]
    Y = np.zeros((1, num_example))
    for i in range(num_example):
        if YData[0,i] == 1:     # 第一類的標籤設爲0
            Y[0,i] = 0
        elif YData[0,i] == 2:   # 第二類的標籤設爲1
            Y[0,i] = 1
        # end if
    # end for
    return Y

遍歷標籤數據YData中所有記錄,設置類別爲1的標籤爲負例0,設置類別爲2的標籤爲正例1。下載的數據中,被標記爲1和2,表示第1類和第2類,需要轉換成0/1。

上述函數在本例中並沒有用,因爲樣本本身就是0/1標記的。

前向計算
前向計算需要增加分類函數調用:

def Sigmoid(x):
    s=1/(1+np.exp(-x))
    return s

前向計算

def ForwardCalculationBatch(W, B, batch_X):
    Z = np.dot(W, batch_X) + B
    A = Sigmoid(Z)
    return A

計算損失函數值
損失函數不再是均方差了,而是交叉熵函數對於二分類的形式。

def CheckLoss(W, B, X, Y):
    m = X.shape[1]
    A = ForwardCalculationBatch(W,B,X)
    
    p1 = 1 - Y
    p2 = np.log(1-A)
    p3 = np.log(A)

    p4 = np.multiply(p1 ,p2)
    p5 = np.multiply(Y, p3)

    LOSS = np.sum(-(p4 + p5))  #binary classification
    loss = LOSS / m
    return loss

推理函數

def Inference(W,B,X_norm,xt):
    xt_normalized = NormalizePredicateData(xt, X_norm)
    A = ForwardCalculationBatch(W,B,xt_normalized)
    return A, xt_normalized

主程序

if __name__ == '__main__':
    # SGD, MiniBatch, FullBatch
    method = "SGD"
    # read data
    XData,YData = ReadData(x_data_name, y_data_name)
    X, X_norm = NormalizeData(XData)
    Y = ToBool(YData)
    W, B = train(method, X, Y, ForwardCalculationBatch, CheckLoss)
    print("W=",W)
    print("B=",B)
    xt = np.array([5,1,6,9,5,5]).reshape(2,3,order='F')
    result, xt_norm = Inference(W,B,X_norm,xt)
    print(result)
    print(np.around(result))

運行結果

epoch=99, iteration=199, loss=0.093750
W= [[-18.18569771   6.49279869]]
B= [[7.77920305]]
result=
[[0.33483134 0.93729121 0.87242717]]
[[0. 1. 1.]]

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

經緯度相對值爲(5,1)時,概率爲0.33,屬於楚
經緯度相對值爲(6,9)時,概率爲0.93,屬於漢
經緯度相對值爲(5,5)時,概率爲0.87,屬於漢
損失函數值記錄
在這裏插入圖片描述

PS:

Sigmoid的輸出值域是(0,1)。從前面講過的二分類原理看,Sigmoid是假設所有正類的標籤值都是,負類的標籤值都是0。而Tanh要求的是-1和1,所以如果要用tanh做分類函數的話需要將標籤歸一化到[-1, 1]之間。

https://github.com/microsoft/ai-edu/blob/master/B-教學案例與實踐/B6-神經網絡基本原理簡明教程/06.2-線性二分類實現.md

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章