1. 程式人生 > >感知機演算法(PLA)程式碼實現

感知機演算法(PLA)程式碼實現

[TOC] ### 1. 引言 在這裡主要實現感知機演算法(PLA)的以下幾種情況: - PLA演算法的原始形式(二分類) - PLA演算法的對偶形式(二分類) - PLA演算法的作圖(二維) - PLA演算法的多分類情況(包括one vs. rest 和one vs. one 兩種情況) - PLA演算法的sklearn實現 為了方便起見,使用鳶尾花資料集進行PLA演算法的驗證。 ### 2. 載入庫和資料處理 ``` # 載入庫 import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.linear_model import Perceptron import warnings warnings.filterwarnings("ignore") # 設定圖形尺寸 plt.rcParams["figure.figsize"] = [14, 7] plt.rcParams["font.size"] = 14 # 載入鳶尾花資料集 iris_data = load_iris() xdata = iris_data["data"] ydata = iris_data["target"] ``` ### 3. 感知機的原始形式 感知機的詳細原理見我的[前一篇部落格](https://www.cnblogs.com/liangjianli/p/13357057.html) ``` class model_perceptron(object): """ 功能:實現感知機演算法 引數 w:權重,預設都為None 引數 b:偏置項,預設為0 引數 alpha:學習率,預設為0.001 引數 iter_epoch:迭代輪數,預設最大為1000 """ def __init__(self, w = None, b = 0, alpha = 0.001, max_iter_epoch = 1000): self.w = w self.b = b self.alpha = alpha self.max_iter_epoch = max_iter_epoch def linear_model(self, X): """功能:實現線性模型""" return np.dot(X, self.w) + self.b def fit(self, X, y): """ 功能:擬合感知機模型 引數 X:訓練集的輸入資料 引數 y:訓練集的輸出資料 """ # 按訓練集的輸入維度初始化w self.w = np.zeros(X.shape[1]) # 誤分類的樣本就為True state = np.sign(self.linear_model(X)) != y # 迭代輪數 total_iter_epoch = 1 while state.any() and (total_iter_epoch <= self.max_iter_epoch): # 使用誤分類點進行權重更新 self.w += self.alpha * y[state][0] * X[state][0] self.b += self.alpha * y[state][0] # 狀態更新 total_iter_epoch += 1 state = np.sign(self.linear_model(X)) != y print(f"fit model_perceptron(alpha = {self.alpha}, max_iter_epoch = {self.max_iter_epoch}, total_iter_epoch = {min(self.max_iter_epoch, total_iter_epoch)})") def predict(self, X): """ 功能:模型預測 引數 X:測試集的輸入資料 """ return np.sign(self.linear_model(X)) def score(self, X, y): """ 功能:模型評價(準確率) 引數 X:測試集的輸入資料 引數 y:測試集的輸出資料 """ y_predict = self.predict(X) y_score = (y_predict == y).sum() / len(y) return y_score ``` ``` # 二分類的情況(原始形式)/ 資料集的處理與劃分 X = xdata[ydata < 2] y = ydata[ydata < 2] y = np.where(y == 0, -1, 1) xtrain, xtest, ytrain, ytest = train_test_split(X, y) # 原始形式的驗證 ppn = model_perceptron() ppn.fit(xtrain, ytrain) ppn.predict(xtest) ppn.score(xtest, ytest) ``` 結果顯示(由於隨機劃分資料集,執行結果不一定和圖示相同): ![感知機的原始形式](https://img2020.cnblogs.com/blog/1684731/202007/1684731-20200722005316056-1526099280.png) ### 4. 感知機的對偶形式 ``` class perceptron_dual(object): """ 功能:實現感知機的對偶形式 引數 beta:每個例項點更新的次陣列成的向量 引數 w:權重,預設都為None 引數 b:偏置項,預設為0 引數 alpha:學習率,預設0.001 引數 max_iter_epoch:最大迭代次數,預設為1000 """ def __init__(self, alpha = 0.001, max_iter_epoch = 1000): self.beta = None self.w = None self.b = 0 self.alpha = alpha self.max_iter_epoch = max_iter_epoch def fit(self, X, y): # 例項點的數量 xnum = X.shape[0] # 初始化 self.beta = np.zeros(xnum) # gram矩陣 gram = np.dot(X, X.T) # 迭代條件 state = y*((self.beta * y * gram).sum(axis = 1) + self.b) <= 0 iter_epoch = 1 while state.any() and (iter_epoch <= self.max_iter_epoch): nx = X[state][0] ny = y[state][0] index = (X == nx).argmax() self.beta[index] += self.alpha self.b += ny # 更新條件 iter_epoch += 1 state = y*((self.beta * y * gram).sum(axis = 1) + self.b) <= 0 # 通過beta計算出w self.w = ((self.beta * y).reshape(-1, 1) * X).sum(axis = 0) print(f"fit perceptron_dual(alpha = {self.alpha}, total_iter_epoch = {min(self.max_iter_epoch, iter_epoch)})") def predict(self, X): """ 功能:模型預測 引數 X:測試集的輸入資料 """ y_predict = np.sign(X @ self.w + self.b) return y_predict def score(self, X, y): """ 功能:模型評價(準確率) 引數 X:測試集的輸入資料 引數 y:測試集的輸出資料 """ y_score = (self.predict(X) == y).sum() / len(y) return y_score ``` ``` # 二分類的情況(對偶形式)/ 資料集的處理與劃分 X = xdata[ydata < 2] y = ydata[ydata < 2] y = np.where(y == 0, -1, 1) xtrain, xtest, ytrain, ytest = train_test_split(X, y) # 對偶形式驗證 ppn = perceptron_dual() ppn.fit(xtrain, ytrain) ppn.predict(xtest) ppn.score(xtest, ytest) ``` 結果顯示(由於隨機劃分資料集,執行結果不一定和圖示相同): ![感知機的對偶形式](https://img2020.cnblogs.com/blog/1684731/202007/1684731-20200722012858846-862728564.png) ### 5. 多分類情況—one vs. rest 假設有k個類別,ovr策略是生成k個分類器,最後選取概率最大的預測結果 ``` class perceptron_ovr(object): """ 功能:實現感知機的多分類情形(採用one vs. rest策略) 引數 w:權重,預設都為None 引數 b:偏置項,預設為0 引數 alpha:學習率,預設0.001 引數 max_iter_epoch:最大迭代次數,預設為1000 """ def __init__(self, alpha = 0.001, max_iter_epoch = 1000): self.w = None self.b = None self.alpha = alpha self.max_iter_epoch = max_iter_epoch def linear_model(self, X): """功能:實現線性模型""" return np.dot(self.w, X.T) + self.b def fit(self, X, y): """ 功能:擬合感知機模型 引數 X:訓練集的輸入資料 引數 y:訓練集的輸出資料 """ # 生成各分類器對應的標記 self.y_class = np.unique(y) y_ovr = np.vstack([np.where(y == i, 1, -1) for i in self.y_class]) # 初始化w, b self.w = np.zeros([self.y_class.shape[0], X.shape[1]]) self.b = np.zeros([self.y_class.shape[0], 1]) # 擬合各分類器,並更新相應維度的w和b for index in range(self.y_class.shape[0]): ppn = model_perceptron(alpha = self.alpha, max_iter_epoch = self.max_iter_epoch) ppn.fit(X, y_ovr[index]) self.w[index] = ppn.w self.b[index] = ppn.b def predict(self, X): """ 功能:模型預測 引數 X:測試集的輸入資料 """ # 值越大,說明第i維的分類器將該點分得越開,即屬於該分類器的概率值越大 y_predict = self.linear_model(X).argmax(axis = 0) # 還原原資料集的標籤 for index in range(self.y_class.shape[0]): y_predict = np.where(y_predict == index, self.y_class[index], y_predict) return y_predict def score(self, X, y): """ 功能:模型評價(準確率) 引數 X:測試集的輸入資料 引數 y:測試集的輸出資料 """ y_score = (self.predict(X) == y).sum()/len(y) return y_score ``` ``` # 多分類資料集處理 xtrain, xtest, ytrain, ytest = train_test_split(xdata, ydata) # one vs. rest的驗證 ppn = perceptron_ovr() ppn.fit(xtrain, ytrain) ppn.predict(xtest) ppn.score(xtest, ytest) ``` 結果顯示(由於隨機劃分資料集,執行結果不一定和圖示相同): ![one vs. rest](https://img2020.cnblogs.com/blog/1684731/202007/1684731-20200722012654195-472433040.png) ### 6. 多分類情況—one vs. one 假設有k個類別,生成k(k-1)/2個二分類器,最後通過多數投票來選取預測結果 ``` from itertools import combinations class perceptron_ovo(object): """ 功能:實現感知機的多分類情形(採用one vs. one策略) 引數 w:權重,預設都為None 引數 b:偏置項,預設為0 引數 alpha:學習率,預設0.001 引數 max_iter_epoch:最大迭代次數,預設為1000 """ def __init__(self, alpha = 0.001, max_iter_epoch = 1000): self.w = None self.b = None self.alpha = alpha self.max_iter_epoch = max_iter_epoch def linear_model(self, X): """功能:實現線性模型""" return np.dot(self.w, X.T) + self.b def fit(self, X, y): """ 功能:擬合感知機模型 引數 X:訓練集的輸入資料 引數 y:訓練集的輸出資料 """ # 生成各分類器對應的標記(使用排列組合) self.y_class = np.unique(y) self.y_combine = [i for i in combinations(self.y_class, 2)] # 初始化w和b clf_num = len(self.y_combine) self.w = np.zeros([clf_num, X.shape[1]]) self.b = np.zeros([clf_num, 1]) for index, label in enumerate(self.y_combine): # 根據各分類器的標籤選取資料集 cond = pd.Series(y).isin(pd.Series(label)) xdata, ydata = X[cond], y[cond] ydata = np.where(ydata == label[0], 1, -1) # 擬合各分類器,並更新相應維度的w和b ppn = model_perceptron(alpha = self.alpha, max_iter_epoch = self.max_iter_epoch) ppn.fit(xdata, ydata) self.w[index] = ppn.w self.b[index] = ppn.b def voting(self, y): """ 功能:投票 引數 y:各分類器的預測結果,接受的是元組如(1, 1, 2) """ # 統計分類器預測結果的出現次數 y_count = np.unique(np.array(y), return_counts = True) # 返回出現次數最大的結果位置索引 max_index = y_count[1].argmax() # 返回某個例項投票後的結果 y_predict = y_count[0][max_index] return y_predict def predict(self, X): """ 功能:模型預測 引數 X:測試集的輸入資料 """ # 預測結果 y_predict = np.sign(self.linear_model(X)) # 還原標籤(根據排列組合的標籤) for index, label in enumerate(self.y_combine): y_predict[index] = np.where(y_predict[index] == 1, label[0], label[1]) # 列為某一個例項的預測結果,打包用於之後的投票 predict_zip = zip(*(i.reshape(-1) for i in np.vsplit(y_predict, self.y_class.shape[0]))) # 投票得到預測結果 y_predict = list(map(lambda x: self.voting(x), list(predict_zip))) return np.array(y_predict) def score(self, X, y): """ 功能:模型評價(準確率) 引數 X:測試集的輸入資料 引數 y:測試集的輸出資料 """ y_predict = self.predict(X) y_score = (y_predict == y).sum() / len(y) return y_score ``` ``` # 多分類資料集處理 xtrain, xtest, ytrain, ytest = train_test_split(xdata, ydata) # one vs. one的驗證 ppn = perceptron_ovo() ppn.fit(xtrain, ytrain) ppn.predict(xtest) ppn.score(xtest, ytest) ``` 結果顯示(由於隨機劃分資料集,執行結果不一定和圖示相同): ![one vs. one](https://img2020.cnblogs.com/blog/1684731/202007/1684731-20200722012747655-1642787601.png) 準確率一般比one vs. rest要高,但是生成的分類器多 ### 7. sklearn實現 主要使用sklearn中的Perceptron模組,其中可以實現多分類的情況(預設採用one vs. rest) ``` from sklearn.linear_model import Perceptron xtrain, xtest, ytrain, ytest = train_test_split(xdata, ydata) ppn = Perceptron(max_iter = 1000) ppn.fit(xtrain, ytrain) ppn.predict(xtest) ppn.score(xtest, ytest) ``` 結果顯示: ![sklearn實現](https://img2020.cnblogs.com/blog/1684731/202007/1684731-20200722013140127-439368261.png) ### 8. 感知機演算法的作圖 ``` from matplotlib.colors import ListedColormap def decision_plot(X, Y, clf, test_idx = None, resolution = 0.02): """ 功能:畫分類器的決策圖 引數 X:輸入例項 引數 Y:例項標記 引數 clf:分類器 引數 test_idx:測試集的index 引數 resolution:np.arange的間隔大小 """ # 標記和顏色設定 markers = ['o', 's', 'x', '^', '>'] colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan') cmap = ListedColormap(colors[:len(np.unique(Y))]) # 圖形範圍 xmin, xmax = X[:, 0].min() - 1, X[:, 0].max() + 1 ymin, ymax = X[:, 1].min() - 1, X[:, 1].max() + 1 x = np.arange(xmin, xmax, resolution) y = np.arange(ymin, ymax, resolution) # 網格 nx, ny = np.meshgrid(x, y) # 資料合併 xdata = np.c_[nx.reshape(-1), ny.reshape(-1)] # 分類器預測 z = clf.predict(xdata) z = z.reshape(nx.shape) # 作區域圖 plt.contourf(nx, ny, z, alpha = 0.4, cmap = cmap) plt.xlim(nx.min(), nx.max()) plt.ylim(ny.min(), ny.max()) # 畫點 for index, cl in enumerate(np.unique(Y)): plt.scatter(x=X[Y == cl, 0], y=X[Y == cl, 1], alpha=0.8, c = cmap(index), marker=markers[index], label=cl) # 突出測試集的點 if test_idx: X_test, y_test = X[test_idx, :], y[test_idx] plt.scatter(X_test[:, 0], X_test[:, 1], alpha=0.15, linewidths=2, marker='^', edgecolors='black', facecolors='none', s=55, label='test set') ``` ``` # 作圖時的資料處理 X = xdata[ydata < 2, :2] y = ydata[ydata < 2] y = np.where(y == 0, -1, 1) xtrain, xtest, ytrain, ytest = train_test_split(X, y) ppn = model_perceptron() ppn.fit(xtrain, ytrain) decision_plot(X, y, ppn) plt.legend() ``` 結果顯示: ![感知機作圖](https://img2020.cnblogs.com/blog/1684731/202007/1684731-20200722011004055-3237854