(2) 李航《統計學習方法》基於Python實現——感知機

一:概述

感知機(perceptron)是二類分類的線性分類模型,其輸入爲實例的特徵向量,輸出爲實例的類別,取+1和-1兩類。感知機對應於輸入空間(特徵空間)中將實例劃分爲正負兩類的超平面,屬於判別模型。感知機學習旨在求出將訓練數據進行線性劃分的分離超平面,爲此,導入基於誤分類的損失函數,利用梯度下降法對損失函數進行極小化,求得感知機模型。

感知機學習算法分爲原始形式和對偶形式,由Rosenblatt在1957年提出,是神經網絡和支持向量機的基礎。

相關概念:

  • 監督學習的任務就是學習一個模型,應用這個模型,對給定的輸入預測相應的輸出。這個模型的一般形式爲決策函數:Y=f(X)Y=f(X),或者條件概率分佈:P(YX)P(Y | X)
  • 監督學方法又可以分爲生成方法和判別方法,所學到的模型又分別稱爲生成模型和判別模型。
    • 1:生成方法由數據學習聯合概率分佈P(X,Y)P(X, Y),然後求出條件概率分佈P(YX)P(Y | X)作爲預測的模型,即生成模型:P(YX)=P(X,Y)P(X) P(Y | X)=\frac{P(X, Y)}{P(X)}
      這樣的方法之所以稱爲生成方法,是因爲模型表示了給定輸入X,產生輸出Y的生成關係。典型的生成模型有:樸素貝葉斯和隱馬爾科夫模型。
    • 2:判別方法由數據直接學習決策函數f(X)f(X)或者條件概率P(YX)P(Y | X)作爲預測的模型,即判別模型,判別方法關心的是對給定的輸入X,應該預測什麼樣的輸出Y。典型的判別模型包括:kk近鄰法,感知機,決策樹,邏輯迴歸,最大熵模型,支持向量機,提升方法和條件隨機場等。

二:感知機學習策略

在引入感知機學習策略之前,首先介紹數據集的線性可分性:

  • 假設給定一個數據集:T={(x1,y1),(x2,y2), ,(xN,yN)}T=\left\{\left(x_{1}, y_{1}\right),\left(x_{2}, y_{2}\right), \cdots,\left(x_{N}, y_{N}\right)\right\},其中,xiX=Rn,yiY={+1,1},i=1,2, ,Nx_{i} \in \mathcal{X}=\mathbf{R}^{n}, \quad y_{i} \in \mathcal{Y}=\{+1,-1\}, \quad i=1,2, \cdots, N
    如果存在某個超平面:wx+b=0w \cdot x+b=0,能夠將數據集的正實例點和負實例點完全正確地劃分到超平面的兩側,即對所有yi=+1y_{i}=+1的實例ii,有wxi+b>0w \cdot x_{i}+b>0,對所有yi=1y_{i}=-1的實例ii,有wxi+b<0w \cdot x_{i}+b<0,則稱數據集TT是線性可分的,否則就不是線性可分。

因此,如果我們進一步假設訓練數據是線性可分的,那麼感知機的目標就是找到能夠將正負實例點完全分開的超平面,也就是求得的超平面的參數w,bw ,b。這就需要用到學習策略!

大多數的機器學習模型訓練過程:需要首先找到損失函數,然後轉化爲最優化問題,用梯度下降等方法進行更新參數,按照這個邏輯最終學習到我們模型的參數w,b。

但是不幸的是,這樣的損失函數並不是w,b連續可導(無法用函數形式來表達出誤分類點的個數),無法進行優化。於是我們想轉爲另一種選擇,誤分類點到超平面的總距離(直觀來看,總距離越小越好)。

首先,輸入空間Rn\mathbf{R}^{n}中任一點x0x_{0}到超平面SS的距離:1wwx0+b\frac{1}{\|w\|}\left|w \cdot x_{0}+b\right|,這裏w\|w\|WWL2L_{2}範數。

其次對於誤分類點來說,由於wxi+bw \cdot x_{i}+b經過sign函數後的結果與其類別(-1或+1)不相等,所以yi(wxi+b)<0y_{i}\left(w \cdot x_{i}+b\right)<0,我們再轉換一下:yi(wxi+b)>0-y_{i}\left(w \cdot x_{i}+b\right)>0

所以誤分類點到超平面的距離爲:1wyi(wxi+b)-\frac{1}{\|w\|} y_{i}\left(w \cdot x_{i}+b\right),這樣假設超平面SS的誤分類集合爲MM

那麼所有誤分類點到超平面SS的總距離爲:1wxiMyi(wxi+b)-\frac{1}{\|w\|} \sum_{x_{i} \in M} y_{i}\left(w \cdot x_{i}+b\right)

不考慮1w\frac{1}{\|w\|},就得到感知機的損失函數:L(w,b)=xiMyi(wxi+b)L(w, b)=-\sum_{x_{i} \in M} y_{i}\left(w \cdot x_{i}+b\right)

問題:就是爲什麼可以不考慮1w\frac{1}{\|w\|},不用總距離表達式作爲損失函數呢?

===> 感知機的任務是進行二分類工作,它最終並不關心得到的超平面離各點的距離有多少(所以我們最後纔可以不考慮1w\frac{1}{\|w\|}),只是關心我最後是否已經正確分類正確(也就是考慮誤分類點的個數),比如說下面紅色與綠線,對於感知機來說,效果任務是一樣好的。
在這裏插入圖片描述
注意:在SVM的評價標準中(綠線是要比紅線好的

三:感知機學習算法的原始形式

以上,我們知道感知機學習算法由誤分類驅動的,具體採用隨機梯度下降方法。首先,任意選取一個超平面w0,b0w_{0}, b_{0},然後用梯度下降不斷地極小化目標函數,極小化過程中不是一次使MM中所有誤分類的梯度下降,而是一次隨機選取一個誤分類點使其下降。

假設誤分類點集合MM是固定的,那麼損失函數L(w,b)L(w, b)的梯度由:
wL(w,b)=xiMyixi\nabla_{w} L(w, b)=-\sum_{x_{i} \in M} y_{i} x_{i}bL(w,b)=xiMyi\nabla_{b} L(w, b)=-\sum_{x_{i} \in M} y_{i} 給出。

隨機選取一個誤分類點(xi,yi)\left(x_{i}, y_{i}\right),對w,bw, b進行更新:
ww+ηyixiw \leftarrow w+\eta y_{i} x_{i}bb+ηyib \leftarrow b+\eta y_{i},式中η(0<η1)\eta(0<\eta \leqslant 1)是步長,在感知機中成爲學習率。這樣可以期望損失函數L(w,b)L(w, b)不斷減小,直到爲0,求得最後的參數w,bw,b的結果。

四:Python 實現感知機

  • 基於Python實現的感知機

代碼

import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt

# 導入數據
iris = load_iris()
df = pd.DataFrame(iris.data,columns=iris.feature_names)
df['label'] = iris.target

# 選擇其中的4個特徵進行訓練
df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']

plt.scatter(df[:50]['sepal length'],df[:50]['sepal width'],label='0')
plt.scatter(df[50:100]['sepal length'],df[50:100]['sepal width'],label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()
plt.show()

原始數據集篩選特徵後的分佈結果:
在這裏插入圖片描述

感知機構造

# 取前100條數據,爲了方便展示,取2個特徵
# 首先數據類型轉換,爲了後面的數學計算
data = np.array(df.iloc[:100, [0, 1, -1]])

X, y = data[:,:-1], data[:,-1]

y = np.array([1 if i == 1 else -1 for i in y])

class Model():
    def __init__(self):
        self.w = np.ones(len(data[0])-1,dtype=np.float32)
        self.b = 0
        self.l_rate =0.1
    
    def sign(self,x,w,b):
        y = np.dot(x,w) + b
        return y
    
    # 選擇隨機梯度下降方法進行擬合
    def fit(self,X_train,y_train):
        is_wrong =False
        while not is_wrong:
            wrong_count = 0
            for d in range(len(X_train)):
                X = X_train[d]
                y = y_train[d]
                if y * self.sign(X,self.w,self.b) <= 0:
                    self.w = self.w +self.l_rate * np.dot(y,X)
                    self.b = self.b + self.l_rate *y 
                    wrong_count += 1
               
            if wrong_count ==0:
                is_wrong = True
        return 'Perceptron Model!'
    
    def score(self):
        pass
    
# 擬合
perceptron = Model()
perceptron.fit(X,y)

# 畫出圖形
x_points = np.linspace(4, 7,10)
y_ = -(perceptron.w[0]*x_points + perceptron.b)/perceptron.w[1]
plt.plot(x_points, y_)

plt.plot(data[:50, 0], data[:50, 1], 'bo', color='blue', label='0')
plt.plot(data[50:100, 0], data[50:100, 1], 'bo', color='orange', label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()

運行結果
在這裏插入圖片描述

五 運用sklean直接構造感知機

from sklearn.linear_model import Perceptron
clf = Perceptron(fit_intercept=False, max_iter=1000, shuffle=False)
clf.fit(X, y)

# Weights assigned to the features.
print(clf.coef_)

# 截距 Constants in decision function.
print(clf.intercept_)

x_ponits = np.arange(4, 8)
y_ = -(clf.coef_[0][0]*x_ponits + clf.intercept_)/clf.coef_[0][1]
plt.plot(x_ponits, y_)

plt.plot(data[:50, 0], data[:50, 1], 'bo', color='blue', label='0')
plt.plot(data[50:100, 0], data[50:100, 1], 'bo', color='orange', label='1')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()

運行結果
在這裏插入圖片描述

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