1. 程式人生 > 實用技巧 >感知機筆記

感知機筆記

目錄


感知機模型

感知機定義

假設輸入空間(特徵空間)是\(\chi \subseteq {R^n}\),輸出空間是\(\gamma = \{ + 1, - 1\}\).輸入\(x \in \chi\)表示例項的特徵向量,對應於輸入空間(特徵空間)的點:;輸出\(y \in \gamma\)表示例項的類別。由輸入空間到輸出空間的函式,如下:

\[f(x) = sign(w \bullet x + b) \]

稱為感知機,其中,\(w \in {R^n}\)叫作權值或權值向量,\(b\)叫作偏置(bias),\(sign\)

為符號函式。

感知機是一種線性分類模型,屬於判別模型。感知機模型的假設空間是定義在特徵空間中的所有線性分類模型或線性分類器,即函式集合\(\{ f|f(x) = w \bullet x + b\}\).

感知機的幾何解釋:線性方程

\[w \bullet x + b = 0 \]

對應於特徵空間\({R^n}\)中的一個超平面\(S\),其中,\(w\)是超平面的法向量,\(b\)是超平面的截距。這個超平面將特徵空間劃分為兩個部分。位於兩部分的點(特徵向量)分別被分正、負兩類。因此,超平面S稱為分離超平面。

感知機學習,由訓練資料集(例項的特徵向量及類別)

\[T{\rm{ = \{ (}}{{\bf{x}}_1}{\rm{,}}{{\rm{y}}_1}{\rm{),(}}{{\bf{x}}_2}{\rm{,}}{{\rm{y}}_2}{\rm{)}},...,{\rm{(}}{{\bf{x}}_N}{\rm{,}}{{\rm{y}}_N}{\rm{)\} }} \]

感知機預測,通過學習得到的感知機模型,對於新的輸入例項給出對應的輸出類別。

資料集的線性可分

給定資料集,如果存在一個超平面能夠將資料集的正例項點和負例項點完全分開,則稱資料集是線性可分的,否則不可分。

感知機學習策略

感知機學習的目標是求得一個能夠將訓練集正例項點和負例項點完全分開得分離超平面。為了找出這樣得超平面,即確定感知機模型引數\(w,b\),需要確定一個學習策略,即定義(經驗)損失函式並將損失函式極小化。

輸出空間\(R^n\)中任一點\(x_0\)到超平面\(S\)的距離:

\[\frac{1}{{\left\| w \right\|}}(w \bullet {x_0} + b) \]

對於誤分類的資料\(({x_i},{y_i})\)來說,總有\(- {y_i}(w \bullet {x_i} + b) > 0\)成立。

這樣,假設超平面\(S\)的誤分類點集合為\(M\),那麼所有誤分類點到超平面\(S\)的總距離為

\[- \frac{1}{{\left\| w \right\|}}\sum\limits_{{x_i} \in M} {{y_i}(w \bullet {x_i} + b)} \]

不考慮\(\frac{1}{{\left\| w \right\|}}\),就得到感知機學習的損失函式。即

\[L(w,b) = - \sum\limits_{{x_i} \in M} {{y_i}(w \bullet {x_i} + b)} \]

感知機學習演算法

原始形式

感知機學習演算法是誤分類驅動的,具體採用隨機梯度下降法。首先,任意選取一個超平面\(w_0,b_0\),然後用梯度下降法不斷地極小化目標函式。極小化過程不是一次使\(M\)中的所有誤分類點的梯度下降,而是一次隨機選取一個誤分類點使梯度下降。

  • 損失函式的梯度:

\[{\nabla _w}L(w,b) = - \sum\limits_{{x_i} \in M} {{y_i}{x_i},} {\nabla _b}L(w,b) = - \sum\limits_{{x_i} \in M} {{y_i}.} \]

  • 更新方式:

\[w \leftarrow w + \eta {y_i}{x_i},b \leftarrow b + \eta {y_i} \]

隨機選取一個誤分類點\(({x_i},{y_i})\)進行引數更新,\(\eta (0 < \eta \le 1)\)是步長,在統計學習中又稱為學習率。這樣通過迭代可以期待損失函式\(L(w,b)\)不斷減小,直到為0.

解: 構建最優化問題:

\[\mathop {\min }\limits_{w,b} L(w,b) = - \sum\limits_{{x_i} \in M} {{y_i}(w \bullet {x_i} + b)} \]

按照上述演算法,求出\(w,b\)即可,為了方便令\(\eta {\rm{ = 1}}\).
(1)取初值\({w_0} = 0,{b_0} = 0\)
(2)對\({x_1} = {(3,3)^T},{y_1}({w_0} \bullet {x_1} + {b_0}) = 0\),未能被正確分類,更新\(w,b\)

\[{w_1} = {w_0} + {y_1}{x_x} = {(3,3)^T},{b_1} = {b_0} + {y_1} = 1 \]

得到線性模型

\[{w_1} \bullet x + {b_1} = 3{x^{(1)}} + 3{x^{(2)}} + 1 \]

(3)對\(x_1,x_2\),顯然,\({y_i}({w_1} \bullet {x_i} + {b_1}) > 0\)被正確分類,不修改\(w,b\)
\({x_3} = {(1,1)^T},{y_3}({w_1} \bullet {x_3} + {b_1}) < 0\),更新\(w,b\)

\[{w_2} = {w_1} + {y_3}{x_3} = {(2,2)^T},{b_2} = {b_1} + {y_3} = 0 \]

得到線性模型

\[{w_2} \bullet x + {b_2} = 2{x^{(1)}} + 2{x^{(2)}} \]

如此繼續下去,直到

\[\begin{array}{l} {w_7} = {(1,1)^T},{b_7} = - 3 \\ {w_7} \bullet x + {b_7} = {x^{(1)}} + {x^{(2)}} - 3 \\ \\ \end{array} \]

對所有資料點\({y_i}({w_7} \bullet {x_i} + {b_7}) > 0\),沒有誤分類點,損失函式達到極小。
分離超平面為:\({x^{(1)}} + {x^{(2)}} - 3 = 0\)
感知機模型為:\(f(x) = sign({x^{(1)}} + {x^{(2)}}{\rm{ - }}3)\)

  • python驗證
import numpy as np
X=np.array([[3,3],
           [4,3],
           [1,1]])
Y=np.array([[1],
           [1],
           [-1]])
#初始化
w_0,b_0=[0,0],0
stop_sign="start"
while stop_sign:
    m,_=X.shape
    for i in range(m):
        x=X[i,:]
        y=Y[i]
        temp=(w_0@x+b_0)*y
        if temp<=0:
            #更新引數設定
            w_0+=y*x
            b_0+=y
            #w_0,b_0=updateParams(x,w_0,b_0,y)
            sign=str(i)
            print("誤分類點:","x"+sign,", w:",w_0,"b:",b_0[0],"超平面:",str(w_0[0])+"*x^(1)+"+str(w_0[1])+"*x^(2)+"+str(b_0[0]))
            break
    else:
        stop_sign=""

輸出結果:

誤分類點: x0 , w: [3 3] b: 1 超平面: 3*x^(1)+3*x^(2)+1
誤分類點: x2 , w: [2 2] b: 0 超平面: 2*x^(1)+2*x^(2)+0
誤分類點: x2 , w: [1 1] b: -1 超平面: 1*x^(1)+1*x^(2)+-1
誤分類點: x2 , w: [0 0] b: -2 超平面: 0*x^(1)+0*x^(2)+-2
誤分類點: x0 , w: [3 3] b: -1 超平面: 3*x^(1)+3*x^(2)+-1
誤分類點: x2 , w: [2 2] b: -2 超平面: 2*x^(1)+2*x^(2)+-2
誤分類點: x2 , w: [1 1] b: -3 超平面: 1*x^(1)+1*x^(2)+-3

對偶形式

對偶形式的基本思想是,將\(w,b\)表示為例項\(x_i\)和標記\(y_i\)的線性組合的形式,通過求解其係數而求得\(w,b\)。在原始形式中可假設初始值為0,根據誤分類點逐步修改\(w,b\),設修改\(n\)次,則\(w,b\)關於\(({x_i},{y_i})\)的增量分別是\({\alpha _i}{y_i}{x_i},{\alpha _i}{y_i}({\alpha _i} = {n_i}\eta )\),最後學習到\(w,b\)可以表示為:

\[w = \sum\limits_{i = 1}^N {{\alpha _i}{y_i}{x_i}} ,b = \sum\limits_{i = 1}^N {{\alpha _i}{y_i}} \]

對偶形式中訓練例項僅以內積的形式出現,為了方便,可以預先將訓練集中例項間的內積計算出來並以矩陣的形式儲存,這個矩陣就是所謂的Gram矩陣,

\[G = {[{x_i} \bullet {x_j}]_{N*N}} \]

對偶形式對應的演算法:

上述案例的對偶形式解法:

  • Python驗證
import numpy as np

def calAndUpdateParams(result,alpha,y,b):
    result=np.dot(result,alpha)+y*b
    #更新引數
    sign=""
    for num,i in enumerate(result):
        if i<=0:
            alpha[num]+=1
            b=y[num]+b
            #第幾個樣本點誤分
            sign="x"+str(num+1)
            #print("num:",num,"b:",b)
            break
    print("誤分樣本點:",sign," , alpha: ",alpha.tolist()," , b  :",b,end=" ")
    return alpha,b,sign

X=np.array([[3,3],
           [4,3],
           [1,1]])
y=np.array([[1],
           [1],
           [-1]])
alpha=np.zeros_like(y)

#內積矩陣
gramMat=np.zeros((3,3))
m=X.shape[0]
for i in range(m):
    for j in range(m):
#         print(X[i,:],X[j,:], sum(X[i,:]*X[j,:]))
        gramMat[i,j]=sum(X[i,:]*X[j,:])
    
result=np.dot(y,y.T)*gramMat 
#初始化
eta,b=1,0

stop_sign="start"
while stop_sign:
    alpha,b,stop_sign=calAndUpdateParams(result,alpha,y,b)
    print("\n")

輸出結果:

誤分樣本點: x1  , alpha:  [[1], [0], [0]]  , b  : [1] 
誤分樣本點: x3  , alpha:  [[1], [0], [1]]  , b  : [0] 
誤分樣本點: x3  , alpha:  [[1], [0], [2]]  , b  : [-1] 
誤分樣本點: x3  , alpha:  [[1], [0], [3]]  , b  : [-2] 
誤分樣本點: x1  , alpha:  [[2], [0], [3]]  , b  : [-1] 
誤分樣本點: x3  , alpha:  [[2], [0], [4]]  , b  : [-2] 
誤分樣本點: x3  , alpha:  [[2], [0], [5]]  , b  : [-3] 
誤分樣本點:   , alpha:  [[2], [0], [5]]  , b  : [-3]

小結

  • 感知機學習的策略是極小化損失函式:

\[\mathop {\min }\limits_{w,b} L(w,b) = - \sum\limits_{{x_i} \in M} {{y_i}(w \bullet {x_i} + b)} \]

損失函式對應於誤分類點到分離超平面的總距離

  • 感知機學習演算法是基於隨機梯度下降法的對損失函式的最優化演算法,有原始形式和對偶形式。演算法簡單易於實現。原始形式中,首先任意選取一個超平面,然後用梯度下降法不斷極小化目標函式。在這個過程中隨機選取一個分類點使其梯度下降。

  • 當訓練資料集線性可分時,感知學習演算法是收斂的。感知機演算法在訓練資料集上的誤分類次數\(k\)滿足不等式:

\[k \le {(\frac{R}{\gamma })^2} \]

當訓練資料集線性可分時,感知機學習演算法存在無窮多個解,其解由於不同的初值或不同的迭代順序而可能有所不同。


異或問題

Minsky與Papert指出:感知機因為是線性模型,所以不能表示複雜函式,如異或(XOR),驗證感知機為什麼不能表示異或。

#驗證異或問題
import numpy as np
import matplotlib.pyplot as plt
import time
X=np.array([[0,0],
           [0,1],
           [1,1],
           [1,0]])
Y=np.array([[-1],[1],[-1],[1]])

x_plot=np.linspace(0,1,50)
fig,ax=plt.subplots()
marker=["ro","bp","ro","bp"]
for num,i in enumerate(marker):
    ax.plot(X[num,0],X[num,1],i,markersize=25)
ax.grid(ls='--')
ax.set_xlim(-0.25,1.25)
#初始化
w_0,b_0=[-1,-1],1
stop_sign="start"
result=[]
while stop_sign:
    m,_=X.shape
    for i in range(m):
        x=X[i,:]
        y=Y[i]
        temp=(w_0@x+b_0)*y
        if temp<=0:
            #更新引數設定
            w_0+=y*x
            b_0+=y
            a_0,a_1=w_0
            y_plot=-b_0[0]/a_1-a_0/a_1*x_plot
            #print(y_plot)
            #ax.cla()
            ax.plot(x_plot,y_plot,'--')
            ax.set_ylim(-0.5,1.2)
            ax.set_title(f"異或問題(XOR)(Time:{time.asctime()})",fontproperties="simhei",fontsize=12)
            #ax.legend()
            plt.pause(0.5)
            sign=str(i)
            print("誤分類點:","x"+sign,", w:",w_0,"b:",b_0[0],"超平面:",str(w_0[0])+"*x^(1)+"+str(w_0[1])+"*x^(2)+"+str(b_0[0]))
            break
    else:
        stop_sign=""
    #plt.show()

參考: