感知器介紹
感知機(英語:Perceptron)是Frank Rosenblatt在1957年就職於Cornell航空實驗室(Cornell Aeronautical Laboratory)時所發明的一種人工神經網絡。它可以被視為一種最簡單形式的前饋式人工神經網絡,是一種二元線性分類器。
Frank Rosenblatt給出了相應的感知機學習算法,常用的有感知機學習、最小二乘法和梯度下降法。譬如,感知機利用梯度下降法對損失函數進行極小化,求出可將訓練數據進行線性划分的分離超平面,從而求得感知機模型。

在人工神經網絡領域中,感知機也被指為單層的人工神經網絡,以區別於較復雜的多層感知機(Multilayer Perceptron)。 作為一種線性分類器,(單層)感知機可說是最簡單的前向人工神經網絡形式。盡管結構簡單,感知機能夠學習並解決相當復雜的問題。感知機主要的本質缺陷是它不能處理線性不可分問題。
線性分類器的第一個迭代算法是1956年由Frank Rosenblatt提出的。這個算法被提出后,受到了很大的關注。感知器在神經網絡發展的歷史上占據着特殊的位置:它是第一個從算法上完整描述的神經網絡。在20世紀60年代和70年代,受感知器的啟發,工程師、物理學家以及數學家們紛紛投身於神經網絡不同方面的研究。這個算法在今天看來依然是有效的。
感知器結構與算法步驟
設有n維(特征數)輸入的單個感知機(如下圖所示),X1至X2為n維輸入向量的各個分量,W1至W2為各個輸入分量連接到感知機的權量(或稱權值),W0為偏置,激活函數(又曰激勵函數或傳遞函數),Z為標量輸出(也稱為凈輸入)。
第一步
這里z稱為凈輸入(net input),它的值等於一個樣本的每個維度值x與維度對應的權重值w相乘后的和。
第二步
計算結果Z是一個連續的值,我們需要將結果轉換為離散的分類值,因此,這里,我們使用一個轉換函數,該函數稱為激勵函數(激活函數),這里θ就是閾值。
第三步
更新權重值
感知器是一個自學習算法,即可以根據輸入的數據(樣本),不斷調整權重的更新,最終完成分類。對於權重的更新公式如下:
- η:學習速率(一個介於0.0到1.0之間的常數)
- y(i):是第i個樣本的真實類標(即真實值)
- y^(i):是第i個樣本的預測類標(預測值)。需要注意的是,權重向量中的所有權重值是同時更新的,這意味着在所有的權重 ΔwjΔwj 更新前,我們無法重新計算y^(i)。
- 這里的i和j怎么理解?每次計算z是同一個樣本的維度值和權重值相乘之和,每次更新權重值會對每個權重值進行更新。
- 類標是什么?類標就是分類的標簽,在這里類標就是1或者0。
更新原則:感知器的權重更新依據是:如果預測准確,則權重不進行更新,否則,增加權重,使其更趨向於正確的類別。
Python算法實現
1.對權重進行初始化。(初始化為0或者很小的數值。)
2.對訓練集中每一個樣本進行迭代,計算輸出值y。
- 根據輸出值y與真實值,更新權重。
- 循環步驟2。直到達到指定的次數(或者完全收斂)。
說明:
如果兩個類別線性可分,則感知器一定會收斂。
如果兩個類別線性不可分,則感知器一定不會收斂。
感知器收斂的前提是兩個類別必須是線性可分的,且學習速率足夠小。
如果兩個類別無法通過一個線性決策邊界進行划分,可以為模型在訓練數據集上的學習迭代次數設置一個最大值, 或者設置一個允許錯誤分類樣本數量的閾值,否則,感知器訓練算法將永遠不停的更新權值。
import pandas as pd import numpy as np import matplotlib.pyplot as plt class Perceptron(object): """Perceptron classifier. 參數: eta (學習率): float,取值范圍0.0-1.0 n_iter(在訓練集進行迭代的次數) : int random_state (隨機數產生器的種子): int 屬性: w_ (權重): ,np一維數組 errors_ (存儲每輪訓練集判斷錯誤的次數): list """ def __init__(self, eta=0.01, n_iter=50, random_state=1): self.eta = eta self.n_iter = n_iter self.random_state = random_state def fit(self, X, y): """Fit training data. Parameters ---------- X : 二維np數組,形式:[[樣本1維度值1,樣本1維度值2...],[樣本2維度值1,樣本2維度值2...],...] y : 一維np數組,形式:[樣本1的類標],樣本2的類標,...] Returns ------- self : object """ #設置隨機數種子 rgen = np.random.RandomState(self.random_state) #生成正態分布的隨機數,權重w self.w_ = rgen.normal(loc=0.0, scale=0.01, size=1 + X.shape[1]) self.errors_ = [] for _ in range(self.n_iter): # 迭代所有樣本,並根據感知器規則來更新權重 errors = 0 for xi, target in zip(X, y): # print(xi,target) update = self.eta * (target - self.predict(xi))
#更新權重值 self.w_[0] += update self.w_[1:] += update * xi #預測錯誤:update如果不為0,則表示判斷錯誤 errors += int(update != 0.0) self.errors_.append(errors) return self #計算z的函數 def net_input(self, X): """Calculate net input""" # ϕ(z) = w0 * 1 + w1∗x1 + w2∗x2 + ... + wm∗xm z = self.w_[0] * 1 + np.dot(X, self.w_[1:]) return z #閾值函數 def predict(self, X): """Return class label after unit step""" return np.where(self.net_input(X) >= 0.0, 1, -1) df = pd.read_csv('https://archive.ics.uci.edu/ml/' 'machine-learning-databases/iris/iris.data', header=None) print(df.tail()) # select setosa and versicolor #選擇0-100行的第5列數據 y = df.iloc[0:100, 4].values #0-100行中,選擇第5列的列名為Iris-setosa的數據做處理,如果 y = np.where(y == 'Iris-setosa', -1, 1) # [1,1,1,1,1,...-1,-1,-1] # extract sepal length and petal length X = df.iloc[0:100, [0, 2]].values ppn = Perceptron(eta=0.1, n_iter=10) #訓練數據 ppn.fit(X, y) #迭代次數與每次迭代時預測錯誤的次數作圖 plt.plot(range(1, len(ppn.errors_) + 1), ppn.errors_, marker='o') plt.xlabel('Epochs') plt.ylabel('Number of updates') plt.savefig('images/02_07.png', dpi=300) # plt.show()
怎么判斷感知器是否收斂
import pandas as pd import numpy as np import matplotlib.pyplot as plt class Perceptron(object): """Perceptron classifier. 參數: eta (學習率): float,取值范圍0.0-1.0 n_iter(在訓練集進行迭代的次數) : int random_state (隨機數產生器的種子): int 屬性: w_ (權重): ,np一維數組 errors_ (存儲每輪訓練集判斷錯誤的次數): list """ def __init__(self, eta=0.01, n_iter=50, random_state=1): self.eta = eta self.n_iter = n_iter self.random_state = random_state def fit(self, X, y): """Fit training data. Parameters ---------- X : 二維np數組,形式:[[樣本1維度值1,樣本1維度值2...],[樣本2維度值1,樣本2維度值2...],...] y : 一維np數組,形式:[樣本1的類標],樣本2的類標,...] Returns ------- self : object """ #設置隨機數種子 rgen = np.random.RandomState(self.random_state) #生成正態分布的隨機數,權重w self.w_ = rgen.normal(loc=0.0, scale=0.01, size=1 + X.shape[1]) self.errors_ = [] for _ in range(self.n_iter): # 迭代所有樣本,並根據感知器規則來更新權重 errors = 0 for xi, target in zip(X, y): # print(xi,target) update = self.eta * (target - self.predict(xi)) self.w_[0] += update self.w_[1:] += update * xi #預測錯誤:update如果不為0,則表示判斷錯誤 errors += int(update != 0.0) self.errors_.append(errors) return self #計算z的函數 def net_input(self, X): """Calculate net input""" # ϕ(z) = w0 * 1 + w1∗x1 + w2∗x2 + ... + wm∗xm z = self.w_[0] * 1 + np.dot(X, self.w_[1:]) return z #閾值函數 def predict(self, X): """Return class label after unit step""" return np.where(self.net_input(X) >= 0.0, 1, -1) df = pd.read_csv('https://archive.ics.uci.edu/ml/' 'machine-learning-databases/iris/iris.data', header=None) # print(df.tail()) # select setosa and versicolor y = df.iloc[0:100, 4].values y = np.where(y == 'Iris-setosa', -1, 1) # extract sepal length and petal length X = df.iloc[0:100, [0, 2]].values # plot data ppn = Perceptron(eta=0.1, n_iter=10) ppn.fit(X, y) plt.plot(range(1, len(ppn.errors_) + 1), ppn.errors_, marker='o') plt.xlabel('Epochs') plt.ylabel('Number of updates') plt.savefig('images/02_07.png', dpi=300) # plt.show()
怎么判斷兩個類別是否線性可分
使用散點圖顯示兩個類別的兩個維度
import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv('https://archive.ics.uci.edu/ml/' 'machine-learning-databases/iris/iris.data', header=None) print(df.tail()) # extract sepal length and petal length X = df.iloc[0:100, [0, 2]].values # plot data plt.scatter(X[:50, 0], X[:50, 1], color='red', marker='o', label='setosa') plt.scatter(X[50:100, 0], X[50:100, 1], color='blue', marker='x', label='versicolor') plt.xlabel('sepal length [cm]') plt.ylabel('petal length [cm]') plt.legend(loc='upper left') # plt.savefig('images/02_06.png', dpi=300) plt.show()
參考:
https://blog.csdn.net/qq_42442369/article/details/87613450
https://blog.csdn.net/u012806787/article/details/80116098
https://blog.csdn.net/xylin1012/article/details/71931900
https://blog.csdn.net/yawdeep/article/details/78827088