系列博客,原文在筆者所維護的github上:https://aka.ms/beginnerAI, 點擊star加星不要吝嗇,星越多筆者越努力。
第6章 多入單出的單層神經網路
6.0 線性二分類
6.0.1 提出問題
我們經常看到中國象棋棋盤中,用楚河漢界分割開了兩個陣營的棋子。回憶歷史,公元前206年前后,楚漢相爭,當時劉邦和項羽麾下的城池,在中原地區的地理位置示意圖如圖6-1所示,部分樣本數據如表6-1所示。

圖6-1 樣本數據可視化
- 紅色圓點,楚,項羽的城池
- 綠色叉子,漢,劉邦的城池
表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]之間了。
問題:
- 經緯度相對坐標值為(0.58,0.92)時,屬於楚還是漢?
- 經緯度相對坐標值為(0.62,0.55)時,屬於楚還是漢?
- 經緯度相對坐標值為(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函數。
- 公式
- 導數
具體求導過程可以參考8.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 正向傳播
矩陣運算
分類計算
損失函數計算
二分類交叉熵損失函數:
6.1.3 反向傳播
求損失函數對a的偏導
求a對z的偏導
求損失函數loss對z的偏導
使用鏈式法則鏈接公式4和公式5:
我們驚奇地發現,使用交叉熵函數求導得到的分母,與Logistic分類函數求導后的結果,正好可以抵消,最后只剩下了\(a-y\)這一項。真的有這么巧合的事嗎?實際上這是依靠科學家們的聰明才智尋找出了這種匹配關系,以滿足以下條件:
- 損失函數滿足二分類的要求,無論是正例還是反例,都是單調的;
- 損失函數可導,以便於使用反向傳播算法;
- 讓計算過程非常簡單,一個減法就可以搞定。
多樣本情況
我們用三個樣本做實例化推導:
代入公式6結果:
所以,用矩陣運算時可以簡化為矩陣相減的形式:\(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,則幾率等於:
上式中,如果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\)來表示,即:
對公式10兩邊取自然指數:
令\(z=e^{-(xw+b)}\):
公式12就是公式2!對數幾率的函數形式可以認為是這樣得到的。
以上推導過程,實際上就是用線性回歸模型的預測結果來逼近樣本分類的對數幾率。這就是為什么它叫做邏輯回歸(logistic regression),但其實是分類學習的方法。這種方法的優點如下:
- 直接對分類可能性建模,無需事先假設數據分布,避免了假設分布不准確所帶來的問題;
- 不僅預測出類別,而是得到了近似的概率,這對許多需要利用概率輔助決策的任務很有用;
- 對率函數是任意階可導的凸函數,有很好的數學性,許多數值優化算法都可以直接用於求取最優解。
系列博客,原文在筆者所維護的github上:https://aka.ms/beginnerAI, 點擊star加星不要吝嗇,星越多筆者越努力。
6.2 用神經網絡實現線性二分類
我們先看看如何用神經網絡在兩組不同標簽的樣本之間畫一條明顯的分界線。這條分界線可以是直線,也可以是曲線。這就是二分類問題。如果只畫一條分界線的話,無論是直線還是曲線,我們可以用一支假想的筆(即一個神經元),就可以達到目的,也就是說筆的走向,完全依賴於這一個神經元根據輸入信號的判斷。
再看楚漢城池示意圖,在兩個顏色區域之間似乎存在一條分割的直線,即線性可分的。
- 從視覺上判斷是線性可分的,所以我們使用單層神經網絡即可;
- 輸入特征是經度和緯度,所以我們在輸入層設置兩個輸入X1=經度,X2=維度;
- 最后輸出的是一個二分類,分別是楚漢地盤,可以看成非0即1的二分類問題,所以我們只用一個輸出單元就可以了。
6.2.1 定義神經網絡結構
根據前面的猜測,看來我們只需要一個二入一出的神經元就可以搞定。這個網絡只有輸入層和輸出層,由於輸入層不算在內,所以是一層網絡,見圖6-3。

圖6-3 完成二分類任務的神經元結構
與上一章的網絡結構圖的區別是,這次我們在神經元輸出時使用了分類函數,所以有個A的輸出,而不是以往的Z的直接輸出。
輸入層
輸入經度\(x_1\)和緯度\(x_2\)兩個特征:
權重矩陣
輸入是2個特征,輸出一個數,則\(W\)的尺寸就是2x1:
B的尺寸是1x1,行數永遠是1,列數永遠和W一樣。
輸出層
損失函數
二分類交叉熵損失函數:
6.2.2 反向傳播
我們在6.1節已經推導了loss對z的偏導數,結論為\(A-Y\)。接下來,我們求loss對w的導數。本例中,w的形式是一個2行1列的向量,所以求w的偏導時,要對向量求導:
上式中\(x_1x_2\)是一個樣本的兩個特征值。如果是多樣本的話,公式4將會變成其矩陣形式,以3個樣本為例:
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值是返回的預測結果:
- 經緯度相對值為(0.58,0.92)時,概率為0.65,屬於漢;
- 經緯度相對值為(0.62,0.55)時,概率為0.30,屬於楚;
- 經緯度相對值為(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。
基本公式回顧
下面我們以單樣本雙特征值為例來說明神經網絡的二分類過程,這是用代數方式來解釋其工作原理。
- 正向計算
- 分類計算
- 損失函數計算
用圖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\)點:
\(B\)點:
\(C\)點:
從6.1節中我們知道當\(z>0\)時,\(Logistic(z) > 0.5\)為正例,反之為負例,所以我們只需要看三個點的\(z\)值是否大於0或小於0就可以了,不用再計算\(Logistic\)的函數值。
其中,\(A、B\)點是處於正確的分類區,而\(C\)點處於錯誤的分類區。此時\(C\)點的損失函數值為(注意\(C\)的標簽值\(y=0\)):
讀者可能對1.313這個值沒有什么概念,是大還是小呢?我們不妨計算一下分類正確的\(A、B\)點的坐標:
可見,對於分類正確的\(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\)值是否大於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)區間內實現分類
我們用正例來舉例:
做公式變形,兩邊取自然對數,可以得到:
即:
對上式做一下變形,把\(x_2\)放在左側,其他項放在右側(假設\(w_2>0\),則不等號方向不變):
簡化一下兩個系數,令\(w'=-w1/w2,b'=-b/w2\):
公式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_1,w_2,b\)三個值以后,換算成\(w',b'\),以便在二維平面上畫出分割線,來直觀地判斷神經網絡訓練結果的正確性。
思考與練習
- 在推導公式5時,假設 \(w_2 > 0\),這樣做有什么問題嗎?如果假設 \(w_2<0\) 會是什么情況?
系列博客,原文在筆者所維護的github上:https://aka.ms/beginnerAI, 點擊star加星不要吝嗇,星越多筆者越努力。
6.4 二分類結果可視化
6.4.1 可視化的重要性
我們雖然得到了結果,但都是一些神秘的數字,我們如何知道它們是正確還是錯誤的呢?
后面我們會講到,在實際的工程實踐中,一般我們會把樣本分成訓練集、驗證集、測試集,用測試集來測試訓練結果的正確性。在本例中我們沒有這樣做,原因有二:
- 樣本數據量比較少,一共只有200個樣本,如果再分成兩部分,會造成數據集覆蓋不全面,存在很大的差異,對訓練、驗證、測試都沒有幫助
- 由於本例的數據特征比較少,所以我們有更好的手段:可視化。在神經網絡學習初期,可視化的訓練過程與結果會對讀者有巨大的幫助。
神經網絡的可視化,說簡單也很簡單,說難也很難,關鍵是對框架系統的理解,對運行機制和工作原理的理解,掌握了這些,可視化就會使一件輕而易舉且令人愉快的事情。
6.4.2 權重值的含義
在6.2節中的訓練結果如下,這幾個關於W,B數字如何解讀呢?
W= [[-7.66469954]
[ 3.15772116]]
B= [[2.19442993]]
A= [[0.65791301]
[0.30556477]
[0.53019727]]
在6.1節中我們一起學習了線性二分類的原理,其中提到了如果我們能夠根據訓練結果,在圖上畫出一條直線來分割正例和負例兩個區域,是不是就很直觀了呢?
對公式2來說,當a大於0.5時,屬於正例(屬於漢),當a小於0.5時,屬於負例(屬於楚)。那么a=0.5時,就是楚漢邊界啦!
把x2留在等式左側,其它的挪到右側去,就可以得到一條直線的方程了:
好了,這就是標准的直線方程\(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, Level2


