1. 程式人生 > >獨家連載 | 線性神經網路應用

獨家連載 | 線性神經網路應用

3.3線性神經網路

3.3.1線性神經網路介紹

線性神經網路跟單層感知器非常類似,只是把單層感知器的sign啟用函式改成了purelin函式: y=x#(3.5)
purelin函式也稱為線性函式,函式影象為圖3.8:
圖3.8 線性函式

3.3.2線性神經網路分類案例

參考3.2.9中的案例,我們這次使用線性神經網路來完成相同的任務。線性神經網路的程式跟單層感知器的程式非常相似,大家可以思考一下需要修改哪些地方。

大家可以仔細閱讀下面程式碼3-4,找到修改的部分。

程式碼3-4:線性神經網路案例

import numpy  as  np
 import  matplotlib . pyplot  as  plt
# 定義輸入,我們習慣上用一行代表一個數據
X = np.array([[1,3,3],[1,4,3],[1,1,1],[1,2,1]])
# 定義標籤,我們習慣上用一行表示一個數據的標籤
T =np.array([[1],[1],[-1],[-1]])
# 權值初始化,3行1列
# np.random.random可以生成0-1的隨機數
W = np.random.random([3,1])
# 學習率設定
lr = 0.1
# 神經網路輸出
Y = 0
# 更新一次權值
def  train():
    # 使用全域性變數X,Y,W,lr
    global X,Y,W,lr
    # 同時計算4個數據的預測值
    # Y的形狀為(4,1)-4行1列
Y = np.dot(X,W)
    # T - Y得到4個的標籤值與預測值的誤差E。形狀為(4,1)
E =T - Y
    # X.T表示X的裝置矩陣,形狀為(3,4)
    # 我們一共有4個數據,每個資料3個值。定義第i個數據的第j個特徵值為xij
    # 如第1個數據,第2個值為x12
    # X.T.dot(T - Y)為一個3行1列的資料:
    # 第1行等於:x00×e0+x10×e1+x20×e2+x30×e3,它會調整第1個神經元對應的權值
    # 第2行等於:x01×e0+x11×e1+x21×e2+x31×e3,它會調整第2個神經元對應的權值
    # 第3行等於:x02×e0+x12×e1+x22×e2+x32×e3,它會影調整3個神經元對應的權值
    # X.shape表示X的形狀X.shape[0]得到X的行數,表示有多少個數據
    # X.shape[1]得到列數,表示每個資料有多少個特徵值。
delta_W =lr  (X.T.dot(E)) / X.shape[0]
W =W + delta_W
# 訓練100次
for i in range(100):
    # 更新一次權值
    train()
#————————以下為畫圖部分————————#
# 正樣本的xy座標
x1 = [3,4]
y1 = [3,3]
# 負樣本的xy座標
x2 = [1,2]
y2 = [1,1]
# 計算分界線的斜率以及截距
# 因為正負樣本的分界是0,所以分界線的表示式可以寫成:
# w0 × x0 + w1 × x1 + w2 × x2 = 0
# 其中x0為1,我們可以把x1,x2分別看成是平面座標系中的x和y
# 可以得到:w0 + w1×x + w2 × y = 0
# 從而推匯出:y = -w0/w2 - w1×x/w2,因此可以得到
k = - W[1] / W[2]
d = - W[0] / W[2]
# 設定兩個點
xdata = (0,5)
# 通過兩個點來確定一條直線,用紅色的線來畫出分界線
plt.plot(xdata,xdata  k + d,'r')
# 用藍色的點畫出正樣本
plt.scatter(x1,y1,c='b')
# 用黃色的點來畫出負樣本
plt.scatter(x2,y2,c='y')
# 顯示圖案
plt.show()

程式的輸出結果為:

線性神經網路的程式有兩處是對單層感知器程式進行了修改。

第一處是在train()函式中,將Y = np.sign(np.dot(X,W))改成了Y = np.dot(X,W)。因為線性神經網路的啟用函式是y=x,所以這裡就不需要np.sign()了。

第二處是在for i in range(100)中,把原來的:

# 訓練100次
for i in range(100):
    # 更新一次權值
    train()
    # 列印當前訓練次數
    print ('epoch:',i + 1)
    # 列印當前權值
    print ('weights:',W)
    # 計算當前輸出
Y = np.sign(np.dot(X,W))
    # .all()表示Y中的所有值跟T中所有值都對應相等,結果才為真
    if (Y == T).all():
        print ('Finished')
        # 跳出迴圈
        break

改成了:

# 訓練100次
for i in range(100):
    # 更新一次權值
    train()

在單層感知器中,當y等於t時,Δw=0就會為0,模型訓練就結束了,所以可以提前跳出迴圈。單層感知器使用的模型收斂條件是兩次迭代模型的權值已經不再發生變化,則可以認為模型收斂。

而線上性神經網路中,y會一直逼近t的值,不過一般不會得到等於t的值,所以可以對模型不斷進行優化。線性神經網路使用的模型收斂條件是設定一個最大迭代次數,當訓練了一定次數後就可以認為模型收斂了。

對比單層感知器和線性神經網路所得到的結果,我們可以看得出線性神經網路所得到的結果會比單層感知器得到的結果更理想。但是線性神經網路也還不夠優秀,當使用它處理非線性問題的時候,它就不能很好完成工作了。

3.3.3線性神經網路處理異或問題

首先我們先來回顧一下異或運算。
0與0異或等於0
0與1異或等於1
1與0異或等於1
1與1異或等於0

程式碼3-5:線性神經網路-異或問題

# 輸入資料
# 4個數據分別對應0與0異或,0與1異或,1與0異或,1與1異或
X = np.array([[1,0,0],[1,0,1],[1,1,0],  [1,1,1]])
# 標籤,分別對應4種異或情況的結果
# 注意這裡我們使用-1作為負標籤
T = np.array([[-1],[1],[1],[-1]])
# 權值初始化,3行1列
# np.random.random可以生成0-1的隨機數
W = np.random.random([3,1])
# 學習率設定
lr = 0.1
# 神經網路輸出
Y = 0
# 更新一次權值
def train():
    # 使用全域性變數X,Y,W,lr
    global X,Y,W,lr
    # 計算網路預測值
Y = np.dot(X,W)
    # 計算權值的改變
delta_W =lr  (X.T.dot(T - Y)) / X.shape[0]
    # 更新權值
W =W + delta_W
# 訓練100次
for i in range(100):
    #更新一次權值
    train()
#————————以下為畫圖部分————————#
# 正樣本
x1 = [0,1]
y1 = [1,0]
# 負樣本
x2 =[0,1]
y2 = [0,1]
# 計算分界線的斜率以及截距
k = - W[1] / W[2]
d = - W[0] / W[2]
# 設定兩個點
xdata = (-2,3)
# 通過兩個點來確定一條直線,用紅色的線來畫出分界線
plt.plot(xdata,xdata  k + d,'r')
# 用藍色的點畫出正樣本
plt.scatter(x1,y1,c='b')

程式的輸出結果為:

從結果我們能夠看出用一條直線並不能把異或問題中的兩個類別給劃分開來,因為這是一個非線性的問題,可以使用非線性的方式來進行求解。

其中一種方式是我們可以給神經網路加入非線性的輸入。程式碼3-5中的輸入訊號只有3個訊號x0,x1,x2,我們可以利用這3個訊號得到帶有非線性特徵的輸入:x0,x1,x2,x1×x1,x1×x2,x2×x2,其中x1×x1,x1×x2,x2×x2為非線性特徵。神經網路結構圖如圖3.9所示:
圖3.9 引入非線性輸入的線性神經網路

程式碼3-6:線性神經網路引入非線性特徵解決異或問題

import numpy as np
import matplotlib.pyplot as plt
# 輸入資料
# 原來X的3個特徵分別為:x0,x1,x2
# X = np.array([[1,0,0],
#               [1,0,1],
#               [1,1,0],  
#               [1,1,1]])
# 給網路輸入非線性特徵
# 現在X的6個特徵分別為:x0,x1,x2,x1×x1,x1×x2,x2×x2
X = np.array([[1,0,0,0,0,0],[1,0,1,0,0,1],[1,1,0,1,0,0],[1,1,1,1,1,1]])
# 標籤,分別對應4種異或情況的結果
T = np.array([[-1],[1],[1],[-1]])
# 權值初始化,6行1列
# np.random.random可以生成0-1的隨機數
W = np.random.random([6,1])
# 學習率設定
lr = 0.1
# 神經網路輸出
Y = 0
# 更新一次權值
def train():
    # 使用全域性變數X,Y,W,lr
    global X,Y,W,lr
    # 計算網路預測值
Y = np.dot(X,W)
    # 計算權值的改變
delta_W =lr  (X.T.dot(T - Y)) / X.shape[0]
    # 更新權值
W =W + delta_W
# 訓練1000次
for i in range(1000):
    #更新一次權值
    train()
# 計算模型預測結果並列印
Y = np.dot(X,W)
  print(Y)
#————————以下為畫圖部分————————#
# 正樣本
x1 = [0,1]
y1 = [1,0]
# 負樣本
x2 = [0,1]
y2 = [0,1]
# 因為正負樣本的分界是0,所以分界線的表示式可以寫成:
# w0x0 + w1x1 + w2x2 + w3x1x1 + w4x1x2 + w5x2x2 = 0
# 其中x0為1,我們可以把x1,x2分別看成是平面座標系中的x和y
# 可以得到:w0 + w1x + w2y + w3xx + w4xy + w5yy = 0
# 整理可得:w5y² + (w2+w4x)y + w0 + w1x + w3x² = 0
# 其中 a = w5, b = w2+w4x, c = w0 + w1x + w3x²
# 根據一元二次方程的求根公式:ay²+by+c=0,y=[-b±(b^2-4ac)^(1/2)]/2a
def calculate(x,root):
    # 定義引數
a = W[5]
b = W[2] +x  W[4]
c = W[0] +x  W[1] +x x W[3]
    # 有兩個根
    if root == 1:
        return (-b + np.sqrt(b b - 4 a c)) / (2  a)
    if root == 2:
        return (-b - np.sqrt(b b - 4 a  c)) / (2 a)
# 從-1到2之間均勻生成100個點
xdata = np.linspace(-1,2,100)
# 使用第一個求根公式計算出來的結果畫出第一條紅線
plt.plot(xdata,calculate(xdata,1),'r')
# 使用第二個求根公式計算出來的結果畫出第二條紅線
plt.plot(xdata,calculate(xdata,2),'r')
# 藍色點表示正樣本
plt.plot(x1,y1,'bo')
# 黃色點表示負樣本
plt.plot(x2,y2,'yo')
# 繪圖
plt.show()

程式的輸出結果為:

[[-0.98650596]
[ 0.990989  ]
[ 0.990989  ]
[-0.99302749]]

從輸出的預測值我們可以看出,預測值與真實標籤的數值是非常接近的,幾乎相等,說明預測值很符合我們想要的結果。而從輸出圖片中也能觀察到兩條曲線的內部是負樣本所屬的類別,兩條曲線的外部是正樣本所屬的類別。這兩條曲線很好地把兩個類別區分開了。

線性神經網路可以通過引入非線性的輸入特徵來解決非線性問題,但這並不是一種非常好的解決方案。下一章節我們將介紹一種新的神經網路,BP(Back Propagation)神經網路,通過學習BP神經網路我們可以獲得更好的解決問題的思路。

作者介紹