機器學習---用python實現感知機算法和口袋算法(Machine Learning PLA Pocket Algorithm Application)
之前在《機器學習---感知機(Machine Learning Perceptron)》一文中介紹了感知機算法的理論知識,現在讓我們來實踐一下。
有兩個數據文件:data1和data2,分別用於PLA和Pocket Algorithm。可在以下地址下載:
先回顧一下感知機算法:
1,初始化w
2,找出一個分類錯誤點
3,修正錯誤,假設叠代次數為t次(t=1,2,...),那麽修正公式為:
4,直至沒有分類錯誤點,返回最終的w
接下來讓我們安照算法步驟,一步一步進行。
首先導入需要用到的庫,其中pandas用於讀取數據文件,matplotlib用於畫圖,numpy用於數組運算:
import pandas as pd import matplotlib.pyplot as plt fig,ax=plt.subplots() import numpy as np
讀取數據文件:
data=pd.read_csv(r"...\data1.csv",header=None)
提取特征和目標:
X=data.iloc[:,[0,1]] #提取特征 y=data[2] #提取目標
提取不同類別的數據,用於畫圖:
x_positive=X[y==1]
x_negative=X[y==-1]
畫圖:
ax.scatter(x_positive[0],x_positive[1],marker="o",label="y=+1") ax.scatter(x_negative[0],x_negative[1],marker="x",label="y=-1") ax.legend() ax.set_xlabel("x1") ax.set_ylabel("x2") ax.set_title("Original Data")
從上圖可以看到數據是線性可分的,接下來我們先把數據歸一化:
mean=X.mean(axis=0) sigma=X.std(axis=0) X=(X-mean)/sigma
再畫圖看看:
設置好特征和權重,用於數組運算:
X[2]=np.ones((X.shape[0],1)) #給特征增加一列常數項 X=X.values #把特征轉換成ndarray格式 ###初始化w### w=X[0].copy() #選取原點到第一個點的向量作為w的初始值 w[2]=0 #增加一項---閾值,閾值初始化為0 w=w.reshape(3,1)
畫出初始法向量和初始分類直線(因為是在二維空間,所以是直線):
###畫出初始法向量### ax.scatter(w[0],w[1],color="red") ax.plot([0,w[0]],[0,w[1]]) ###畫出初始分類直線### line_x=np.linspace(-3,3,10) line_y=(-w[2]-w[0]*line_x)/w[1] ax.plot(line_x,line_y)
註:因為w1x1+w2x2+b=0,現在我們已經有了w和b的值,因此只需要設置x1的值,就可以計算出x2的值。
註:註意要適當調整一下圖像比例,否則顯示不對,具體請見完整代碼。
現在我們已經完成初始化w的工作,接下去就是要找出分類錯誤點。現在的思路是:先計算出在當前參數w下的預測目標,然後把其和目標y進行比較,這樣就可以知道分類錯誤的地方了。
scores=np.dot(X,w) #把特征和權重點乘,得到目前參數下預測出的目標分數 y_pred=np.ones((scores.shape[0],1)) #設置預測目標,初始化值全為1,形狀和目標分數相同 y=y.values.reshape((y_pred.shape[0],1)) #把目標轉換成ndarray格式,形狀和預測目標相同 loc_negative=np.where(scores<0)[0] #標記分數為負數的地方 y_pred[loc_negative]=-1 #使標記為負數的地方預測目標變為-1 loc_wrong=np.where(y_pred!=y)[0] #標記分類錯誤的地方
找出分類錯誤點後,我們對w進行修正(這裏選取第一個分類錯誤點進行更新):
w=w+y[loc_wrong][0]*X[loc_wrong,:][0].reshape(3,1)
最後進行叠代就可以找出最終的w:
for i in range(100): scores=np.dot(X,w) #把特征和權重點乘,得到此參數下預測出的目標分數 y_pred=np.ones((scores.shape[0],1)) #設置預測目標,初始化值全為1,形狀和目標分數相同 loc_negative=np.where(scores<0)[0] #標記分數為負數的地方 y_pred[loc_negative]=-1 #使標記為負數的地方預測目標變為-1 loc_wrong=np.where(y_pred!=y)[0] #標記分類錯誤的地方 print("錯誤分類點有{}個。".format(len(loc_wrong))) if len(loc_wrong)>0: w=w+y[loc_wrong][0]*X[loc_wrong,:][0].reshape(3,1) else: break print("參數w:{}".format(w)) print("分類直線:{}x1+{}x2+{}=0".format(w[0][0],w[1][0],w[2][0])) line_x=np.linspace(-3,3,10) line_y=(-w[2]-w[0]*line_x)/w[1] ax.plot(line_x,line_y)
運行結果:
錯誤分類點有5個。 錯誤分類點有2個。 錯誤分類點有3個。 錯誤分類點有0個。 參數w:[[0.24622161] [2.81328976] [1. ]] 分類直線:0.2462216100520832x1+2.8132897563595076x2+1.0=0
畫出的分類直線:
最後的最後,將上述代碼整理一下。感知機算法完整代碼如下(除去用於畫圖的代碼,核心代碼不到20行):
import pandas as pd import matplotlib.pyplot as plt fig,ax=plt.subplots(figsize=(6,6)) import numpy as np data=pd.read_csv(r"...\data1.csv",header=None) X=data.iloc[:,[0,1]] #提取特征 y=data[2] #提取目標 ###把數據歸一化### mean=X.mean(axis=0) sigma=X.std(axis=0) X=(X-mean)/sigma ###提取不同類別的數據,用於畫圖### x_positive=X[y==1] x_negative=X[y==-1] ax.scatter(x_positive[0],x_positive[1],marker="o",label="y=+1") ax.scatter(x_negative[0],x_negative[1],marker="x",label="y=-1") ax.legend() ax.set_xlabel("x1") ax.set_ylabel("x2") ax.set_title("Standardized Data") ax.set_xlim(-2,2.6) ax.set_ylim(-2,2.6) X[2]=np.ones((X.shape[0],1)) #給特征增加一列常數項 X=X.values #把特征轉換成ndarray格式 ###初始化w### w=X[0].copy() #選取原點到第一個點的向量作為w的初始值 w[2]=0 #增加一項---閾值,閾值初始化為0 w=w.reshape(3,1) y=y.values.reshape(100,1) #把目標轉換成ndarray格式,形狀和預測目標相同 def compare(X,w,y): ###用於比較預測目標y_pred和實際目標y是否相符,返回分類錯誤的地方loc_wrong### ###輸入特征,權重,目標### scores=np.dot(X,w) #把特征和權重點乘,得到此參數下預測出的目標分數 y_pred=np.ones((scores.shape[0],1)) #設置預測目標,初始化值全為1,形狀和目標分數相同 loc_negative=np.where(scores<0)[0] #標記分數為負數的地方 y_pred[loc_negative]=-1 #使標記為負數的地方預測目標變為-1 loc_wrong=np.where(y_pred!=y)[0] #標記分類錯誤的地方 return loc_wrong def update(X,w,y): ###用於更新權重w,返回更新後的權重w### ###輸入特征,權重,目標### w=w+y[compare(X,w,y)][0]*X[compare(X,w,y),:][0].reshape(3,1) return w def perceptron(X,w,y): ###感知機算法,顯示最終的權重和分類直線,並畫出分類直線### ###輸入特征,初始權重,目標### while len(compare(X,w,y))>0: print("錯誤分類點有{}個。".format(len(compare(X,w,y)))) w=update(X,w,y) print("參數w:{}".format(w)) print("分類直線:{}x1+{}x2+{}=0".format(w[0][0],w[1][0],w[2][0])) line_x=np.linspace(-3,3,10) line_y=(-w[2]-w[0]*line_x)/w[1] ax.plot(line_x,line_y) plt.show()
接下來看一下data2文件和口袋算法的應用。首先回顧一下Pocket Algorithm:
1,初始化w,把w作為最好的解放入口袋
2,隨機找出一個分類錯誤點
3,修正錯誤,假設叠代次數為t次(t=1,2,...),那麽修正公式為:
4,如果wt+1比w犯的錯誤少,那麽用wt+1替代w,放入口袋
5,經過t次叠代後停止,返回口袋裏最終的結果
口袋算法和PLA差不多,因此代碼還是用上述感知機算法的框架,只需要局部修改一下即可。
首先畫出原始數據圖像和歸一化後的數據圖像:(代碼和之前類似,故在此不再贅述)
可以看到數據是線性不可分的。接下來修改一下perceptron函數和update函數。
def perceptron_pocket(X,w,y): ###感知機口袋算法,顯示n次叠代後最好的權重和分類直線,並畫出分類直線### ###輸入特征,初始權重,目標### best_len=len(compare(X,w,y)) #初始化最少的分類錯誤點個數 best_w=w #初始化口袋裏最好的參數w for i in range(100): print("錯誤分類點有{}個。".format(len(compare(X,w,y)))) w=update(X,w,y) #如果當前參數下分類錯誤點個數小於最少的分類錯誤點個數,那麽更新最少的分類錯誤點個數和口袋裏最好的參數w if len(compare(X,w,y))<best_len: best_len=len(compare(X,w,y)) best_w=w print("參數best_w:{}".format(best_w)) print("分類直線:{}x1+{}x2+{}=0".format(best_w[0][0],best_w[1][0],best_w[2][0])) print("最少分類錯誤點的個數:{}個".format(best_len)) line_x=np.linspace(-3,3,10) line_y=(-best_w[2]-best_w[0]*line_x)/best_w[1] ax.plot(line_x,line_y)
perceptron函數主要增加了一個口袋,用於存放最好的解,函數名稱改為perceptron_pocket。
def update(X,w,y): ###用於更新權重w,返回更新後的權重w### ###輸入特征,權重,目標### num=len(compare(X,w,y)) #分類錯誤點的個數 w=w+y[compare(X,w,y)][np.random.choice(num)]*X[compare(X,w,y),:][np.random.choice(num)].reshape(3,1) return w
update函數將“選取第一個分類錯誤點進行更新”修改為“隨機選取分類錯誤點進行更新”。
運行結果如下(由於帶有隨機性,每次運行結果都不同):
錯誤分類點有9個。 錯誤分類點有30個。 錯誤分類點有11個。 錯誤分類點有24個。 錯誤分類點有5個。 錯誤分類點有22個。 錯誤分類點有16個。 錯誤分類點有17個。 錯誤分類點有5個。 錯誤分類點有15個。 錯誤分類點有5個。 錯誤分類點有15個。 錯誤分類點有6個。 錯誤分類點有13個。 錯誤分類點有7個。 錯誤分類點有12個。 錯誤分類點有9個。 錯誤分類點有14個。 錯誤分類點有12個。 錯誤分類點有16個。 錯誤分類點有9個。 錯誤分類點有10個。 錯誤分類點有12個。 錯誤分類點有12個。 錯誤分類點有7個。 錯誤分類點有16個。 錯誤分類點有5個。 錯誤分類點有7個。 錯誤分類點有6個。 錯誤分類點有10個。 錯誤分類點有6個。 錯誤分類點有9個。 錯誤分類點有11個。 錯誤分類點有7個。 錯誤分類點有5個。 錯誤分類點有11個。 錯誤分類點有6個。 錯誤分類點有8個。 錯誤分類點有6個。 錯誤分類點有12個。 錯誤分類點有6個。 錯誤分類點有11個。 錯誤分類點有14個。 錯誤分類點有10個。 錯誤分類點有5個。 錯誤分類點有5個。 錯誤分類點有5個。 錯誤分類點有4個。 錯誤分類點有6個。 錯誤分類點有6個。 錯誤分類點有6個。 錯誤分類點有9個。 錯誤分類點有6個。 錯誤分類點有10個。 錯誤分類點有6個。 錯誤分類點有7個。 錯誤分類點有6個。 錯誤分類點有10個。 錯誤分類點有6個。 錯誤分類點有10個。 錯誤分類點有5個。 錯誤分類點有10個。 錯誤分類點有6個。 錯誤分類點有8個。 錯誤分類點有6個。 錯誤分類點有5個。 錯誤分類點有6個。 錯誤分類點有7個。 錯誤分類點有9個。 錯誤分類點有7個。 錯誤分類點有6個。 錯誤分類點有7個。 錯誤分類點有6個。 錯誤分類點有4個。 錯誤分類點有6個。 錯誤分類點有10個。 錯誤分類點有6個。 錯誤分類點有11個。 錯誤分類點有15個。 錯誤分類點有10個。 錯誤分類點有5個。 錯誤分類點有5個。 錯誤分類點有6個。 錯誤分類點有9個。 錯誤分類點有6個。 錯誤分類點有8個。 錯誤分類點有5個。 錯誤分類點有10個。 錯誤分類點有5個。 錯誤分類點有10個。 錯誤分類點有6個。 錯誤分類點有10個。 錯誤分類點有18個。 錯誤分類點有9個。 錯誤分類點有10個。 錯誤分類點有10個。 錯誤分類點有5個。 錯誤分類點有5個。 錯誤分類點有6個。 錯誤分類點有8個。 參數best_w:[[-1.09227879] [ 5.19393394] [ 1. ]] 分類直線:-1.0922787897353627x1+5.193933943326238x2+1.0=0 最少分類錯誤點的個數:4個
分類直線畫圖如下:
口袋算法完整代碼如下:
import pandas as pd import matplotlib.pyplot as plt fig,ax=plt.subplots() import numpy as np data=pd.read_csv(r"...\data2.csv",header=None) X=data.iloc[:,[0,1]] #提取特征 y=data[2] #提取目標 ###把數據歸一化### mean=X.mean(axis=0) sigma=X.std(axis=0) X=(X-mean)/sigma ###提取不同類別的數據,用於畫圖### x_positive=X[y==1] x_negative=X[y==-1] ax.scatter(x_positive[0],x_positive[1],marker="o",label="y=+1") ax.scatter(x_negative[0],x_negative[1],marker="x",label="y=-1") ax.legend() ax.set_xlabel("x1") ax.set_ylabel("x2") ax.set_title("standardized Data") X[2]=np.ones((X.shape[0],1)) #增加一列常數項 X=X.values #把特征轉換成ndarray格式 ###初始化w### w=X[0].copy() #選取原點到第一個點的向量作為w的初始值 w[2]=0 #增加一項---閾值,閾值初始化為0 w=w.reshape(3,1) y=y.values.reshape(100,1) #把目標轉換成ndarray格式,形狀和預測目標相同 def compare(X,w,y): ###用於比較預測目標y_pred和實際目標y是否相符,返回分類錯誤的地方loc_wrong### ###輸入特征,權重,目標### scores=np.dot(X,w) #把特征和權重點乘,得到此參數下預測出的目標分數 y_pred=np.ones((scores.shape[0],1)) #設置預測目標,初始化值全為1,形狀和目標分數相同 loc_negative=np.where(scores<0)[0] #標記分數為負數的地方 y_pred[loc_negative]=-1 #使標記為負數的地方預測目標變為-1 loc_wrong=np.where(y_pred!=y)[0] #標記分類錯誤的地方 return loc_wrong def update(X,w,y): ###用於更新權重w,返回更新後的權重w### ###輸入特征,權重,目標### num=len(compare(X,w,y)) #分類錯誤點的個數 w=w+y[compare(X,w,y)][np.random.choice(num)]*X[compare(X,w,y),:][np.random.choice(num)].reshape(3,1) return w def perceptron_pocket(X,w,y): ###感知機口袋算法,顯示n次叠代後最好的權重和分類直線,並畫出分類直線### ###輸入特征,初始權重,目標### best_len=len(compare(X,w,y)) #初始化最少的分類錯誤點個數 best_w=w #初始化口袋裏最好的參數w for i in range(100): print("錯誤分類點有{}個。".format(len(compare(X,w,y)))) w=update(X,w,y) #如果當前參數下分類錯誤點個數小於最少的分類錯誤點個數,那麽更新最少的分類錯誤點個數和口袋裏最好的參數w if len(compare(X,w,y))<best_len: best_len=len(compare(X,w,y)) best_w=w print("參數best_w:{}".format(best_w)) print("分類直線:{}x1+{}x2+{}=0".format(best_w[0][0],best_w[1][0],best_w[2][0])) print("最少分類錯誤點的個數:{}個".format(best_len)) line_x=np.linspace(-3,3,10) line_y=(-best_w[2]-best_w[0]*line_x)/best_w[1] ax.plot(line_x,line_y) plt.show()
機器學習---用python實現感知機算法和口袋算法(Machine Learning PLA Pocket Algorithm Application)