做者|Dorian Lazar 編譯|VK 來源|Towards Data Sciencepython
感知器是人工神經網絡的組成部分,它是大腦中生物神經元的簡化模型。感知器是最簡單的神經網絡,僅由一個神經元組成。感知器算法由Frank Rosenblatt於1958年發明。算法
如下是生物神經元的圖示:數組
經由樹突接收到神經元的大部分輸入信號。其餘神經元與這些樹突造成約1,000至10,000個鏈接。來自鏈接的信號稱爲突觸,經過樹突傳播到細胞體內。細胞體內的電位增長,一旦達到閾值,神經元就會沿着軸突發出一個尖峯,該軸突經過軸突末端鏈接到大約100個其餘神經元。網絡
感知器是真實神經元的簡化模型,它嘗試經過如下過程來模仿它:接收輸入信號,將它們稱爲x1,x2,…,xn,計算這些輸入的加權和z,而後將其傳遞閾值函數ϕ並輸出結果。app
但將w0做爲閾值和將w0做爲偏置添加到和中並將閾值改成0是同樣的。咱們考慮一個始終設置爲1的附加輸入信號x0。機器學習
下面是一個感知器:函數
要使用向量表示法,咱們能夠將全部輸入x0、x一、…、xn和全部權重w0、w一、…、wn放入向量x和w中,當它們的點積爲正時輸出1,不然輸出-1。學習
如下是僅使用2個輸入x1和x2的幾何表示,以便咱們能夠在2維中繪製:測試
如上所示,具備2個輸入的感知器的決策邊界是一條直線。若是有3個輸入,則決策邊界爲二維平面。通常來講,若是咱們有n個輸入,決策邊界將是一個稱爲n-1維的超平面,該超平面將咱們的n維特徵空間分紅兩部分:一部分是將點分類爲正的,另外一部分是將點分類爲負的(按照慣例,咱們將認爲剛好在決策邊界上的點是負的)。所以,感知器是一個二元分類器,其權值是線性的。動畫
在上面的圖像中,w'表示沒有偏移項w0的權重向量。w'垂直於決策邊界並指向正分類點的性質。該向量決定了決策邊界的斜率,而偏移項w0決定了決策邊界沿w'軸的偏移。
到目前爲止,咱們討論了感知器如何根據輸入信號及其權值作出決策。可是,感知者其實是如何學習的呢?如何找到合適的參數w0,w1,…,wn,以便進行良好的分類?
感知器算法是基於如下簡單更新規則的迭代算法:
其中y是當前數據點x的標籤(要麼-1要麼+1),w是權重向量。
咱們的更新規則怎麼說?點積x⋅w只是感知器基於當前權重的預測(其符號與預測標籤的符號相同)。表達式y(x⋅w)只能小於或等於0,前提是實際標籤y不一樣於預測標籤φ(x⋅w)。所以,若是真標籤和預測標籤之間不匹配,那麼咱們更新權重:w=w+yx;不然,咱們讓它們保持原樣。
那麼,爲何w=w+yx更新規則有效?由於它試圖在if條件下將y(x⋅w)的值推向0的正邊,從而正確地對x進行分類。若是數據集是線性可分的,經過對每一個點進行必定次數的迭代,權值最終會收斂到每一個點都被正確分類的狀態。讓咱們經過在更新後從新評估if條件來查看更新規則的效果:
也就是說,在特定數據點的權重更新以後,if條件中的表達式應該更接近於正數,從而正確分類。
僞代碼中的完整感知器算法以下:
咱們如今將在python中從頭開始實現感知器算法,只使用numpy做爲矩陣向量操做的外部庫。咱們將把它做爲一個類來實現,這個類的接口相似於Scikit-Learn這樣的通用機器學習包中的其餘分類器。咱們將爲此類實現3個方法:.fit()
、.predict()
和.score()
。
.fit()
方法將用於訓練感知器。它指望第一個參數是2D numpy數組X。該數組的行是數據集的樣本,列是特徵。第二個參數y應該是1D的numpy數組,它包含X中每行數據的標籤。第三個參數n_iter是咱們讓算法運行的迭代次數。
def fit(self, X, y, n_iter=100): n_samples = X.shape[0] n_features = X.shape[1] # 偏置都加1 self.weights = np.zeros((n_features+1,)) X = np.concatenate([X, np.ones((n_samples, 1))], axis=1) for i in range(n_iter): for j in range(n_samples): if y[j]*np.dot(self.weights, X[j, :]) <= 0: self.weights += y[j]*X[j, :]
.predict()
方法將用於預測新數據的標籤。它首先檢查weights對象屬性是否存在,若是不存在,則表示感知器還沒有訓練,而後顯示警告消息並返回。該方法須要一個與.fit
()方法形狀相同的參數X。而後咱們在X和權重之間作一個矩陣乘法,而後把它們映射到-1或+1。咱們使用np.vectorize()
將此映射應用於矩陣乘法結果向量中的全部元素。
def predict(self, X): if not hasattr(self, 'weights'): print('The model is not trained yet!') return n_samples = X.shape[0] X = np.concatenate([X, np.ones((n_samples, 1))], axis=1) y = np.matmul(X, self.weights) y = np.vectorize(lambda val: 1 if val > 0 else -1)(y) return y
score()
方法計算並返回預測的準確性。它指望輸入矩陣X和標籤向量y做爲參數。
def score(self, X, y): pred_y = self.predict(X) return np.mean(y == pred_y)
我如今要作的是展現幾個可視化的例子,說明決策邊界是如何收斂到一個解的。
爲了作到這一點,我將使用ScikitLearn的datasets.make_classification()
和datasets.make_circles()
函數建立幾個由200個樣本組成的2特徵分類數據集。這是用於建立下兩個數據集的代碼:
X, y = make_classification( n_features=2, n_classes=2, n_samples=200, n_redundant=0, n_clusters_per_class=1 )
還有一個數據集:
X, y = make_circles(n_samples=200, noise=0.03, factor=0.7)
對於每一個示例,我將把數據分紅150個用於訓練,50個用於測試。左邊顯示訓練集,右邊顯示測試集。當決策邊界收斂到一個解決方案時,決策邊界將在兩邊顯示。可是決策邊界將僅根據左邊的數據(訓練集)進行更新。
我要展現的第一個數據集是線性可分的。下面是完整數據集的圖像:
這是一個簡單的數據集,咱們的感知器算法在通過訓練集的兩次迭代後就會收斂到一個解。所以,每一個數據點的動畫幀都會改變。綠點是目前在算法中測試的那個。
在該數據集上,算法對訓練樣本和測試樣本進行了正確分類。
若是數據集不是線性可分的呢?若是正反兩個例子像下圖同樣混淆在一塊兒呢?
好吧,感知器算法將不能正確分類全部的例子,但它將試圖找到一條線,最好的分開他們。在這個例子中,咱們的感知器獲得了88%的測試精度。下面的動畫幀在每次迭代後都會經過全部訓練示例進行更新。
下面的數據集如何呢?
它是可分離的,但顯然不是線性的。因此你可能會認爲一個感知器不適合這個任務。但感知器的問題是,它的決策邊界是線性的,就權重而言,不必定就輸入而言。咱們能夠擴充輸入向量x,使其包含原始輸入的非線性函數。例如,除了原始輸入x1和x2以外,咱們還能夠將項x1平方、x1乘以x2和x2平方相加。
下面的 polynomial_features(X, p)(X,p)
函數可以將輸入矩陣X轉換成一個矩陣,該矩陣包含p次多項式的全部項做爲特徵。它使用polynom()
函數計算表示要相乘列的索引列表,以得到p階項。
def polynom(indices_list, indices, a, b, p): indices = [*indices] if p == 0: indices_list.append(indices) return for i in range(a, b): indices.append(i) polynom(indices_list, indices, i, b, p-1) indices = indices[0:-1] def polynomial_features(X, p): n, d = X.shape features = [] for i in range(1, p+1): l = [] polynom(l, [], 0, d, i) for indices in l: x = np.ones((n,)) for idx in indices: x = x * X[:, idx] features.append(x) return np.stack(features, axis=1)
在咱們的例子中,咱們將在X矩陣中添加2級項做爲新特徵。
X = polynomial_features(X, 2)
如今,讓咱們看看使用這個轉換後的數據集進行訓練時會發生什麼:
注意,對於繪圖,咱們只使用原始輸入來保持它的二維性。決策邊界在擴展特徵空間中仍然是線性的,如今是5D。可是,當咱們繪製投影到原始特徵空間的決策邊界時,它具備非線性形狀。
經過這種方法,咱們的感知器算法可以在不修改算法自己的狀況下正確地分類訓練和測試實例。咱們只改變了數據集。
經過這種特徵加強方法,咱們可使用線性算法在數據中建模很是複雜的模式。
可是,這種方法不是頗有效。想象一下,若是咱們有1000個輸入特徵,而且咱們想用最多10次多項式項來擴充它,會發生什麼。幸運的是,這個問題能夠經過使用核函數來避免。但這是另外一篇文章的主題,我不想把這篇文章寫得太長。
歡迎關注磐創AI博客站: http://panchuang.net/
sklearn機器學習中文官方文檔: http://sklearn123.com/
歡迎關注磐創博客資源彙總站: http://docs.panchuang.net/