1. 程式人生 > >機器學習---用python實現感知機算法和口袋算法(Machine Learning PLA Pocket Algorithm Application)

機器學習---用python實現感知機算法和口袋算法(Machine Learning PLA Pocket Algorithm Application)

title 數組運算 and alt 假設 錯誤 pac 現在 畫出

之前在《機器學習---感知機(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)