k近鄰算法程序實現



算法原理

模型

k近鄰算法是一個多分類的機器學習算法。它的實現過程很容易理解,簡單來說,就是通過計算待分類樣本與所有訓練樣本的“距離”,然後取出離待分類樣本最近的k個訓練樣本,最後將這k個訓練樣本中類別最多的一種作爲該待分類樣本的預測類別。

圖解

如圖所示,假設有若干個訓練樣本,不同顏色代表不同的類別。

現在放入一個待分類樣本。

定義k值(假設爲3),定義距離(假設是歐式距離),計算該待分類樣本與所有訓練樣本的距離。

選取距離最小的k個訓練樣本。

將這k個訓練樣本中出現最多的那個類別作爲該待分類樣本的預測類別。此時圖上待分類樣本的預測類別是紅色。

具體步驟

1、已知訓練集{xi,yi}i=1n\{x_i,y_i\}_ {i=1}^n,訓練集的每個樣本包含特徵xix_i和類別yiy_i,設定k值,定義樣本間的距離計算公式(這裏的距離需要自己定義,可以是曼哈頓距離,歐式距離等等)。

2、對於每個特徵爲aa的待分類樣本AA,計算其與所有訓練樣本的距離
d=d(a,xi),i=1,2,...,n d=d(a,x_i),i=1,2,...,n

3、取出距離dd最近的k個訓練樣本
(xi1,yi1),(xi2,yi2),...,(xik,yik) (x_{i1},y_{i1}),(x_{i2},y_{i2}),...,(x_{ik},y_{ik})
{yi1,yi2,...,yik}\{y_{i1},y_{i2},...,y_{ik}\}中出現次數最多的類別作爲待分類樣本AA的預測類別。

4、重複執行2、3步直至所有待分類樣本預測完畢。

程序實現

初始化函數

def __init__(self, k=10):
    """
    k         最近鄰個數
    """
    self.k = k

訓練函數

def fit(self, X, y):
    self.X = X
    self.y = y

訓練函數只需存儲樣本的特徵和類別,不需要進行計算,這是knn的一個優點。

預測函數

def predict(self, X):
    res = []
    for x in X:
        # 計算平方和
        sum = np.sum((x-self.X)**2, axis=1)
        num = np.argsort(sum)[:self.k]
        pred_y = np.argmax(np.bincount(self.y[num]))
        res.append(pred_y)
    return np.array(res)

因爲需要計算每個待分類樣本與每個訓練樣本的距離,所以雖然實現過程簡單,但計算比較耗時。

整合全部代碼

class KNeighborsClassifier():
    def __init__(self, k=10):
        """
        k         最近鄰個數
        """
        self.k = k
        
    def fit(self, X, y):
        self.X = X
        self.y = y
    
    def predict(self, X):
        res = []
        for x in X:
            # 計算平方和
            sum = np.sum((x-self.X)**2, axis=1)
            num = np.argsort(sum)[:self.k]
            pred_y = np.argmax(np.bincount(self.y[num]))
            res.append(pred_y)
        return np.array(res)

實例化演示

導入相關庫並查看數據分佈。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split

iris = load_iris()
X = iris.data[:100]
y = iris.target[:100]

train_x,test_x,train_y,test_y = train_test_split(X, y, test_size=0.3, random_state=1)

color = ['red','yellow']
plt.rcParams['font.sans-serif'] = ['SimHei'] 
for i in range(2):
    plt.scatter(train_x[train_y==i,0], train_x[train_y==i,1], color=color[i])

plt.scatter(test_x[:,0], test_x[:,1],color='silver')
plt.legend([1,2,'待分類樣本'])


實例化函數並預測樣本類別。

clf = KNeighborsClassifier(k=20)
clf.fit(train_x, train_y)
pred_y = clf.predict(test_x)
accuracy_score(test_y, pred_y)

進一步

k值是一個超參數,k值的大小決定了模型的好壞,可以通過k折交叉驗證來選取適合的k值,注意k折交叉的k值和k近鄰的k值不是同一個意思,只是剛好都這麼叫而已。這裏採用5折交叉驗證,主要步驟是將訓練集均分爲5份小訓練集,取出其中的4份用於knn分類器的訓練,剩餘的1份用來驗證模型的效果,可以得到模型的準確率。不同的取法會得到不一樣的結果,對於每個k值都將計算得到5個模型的準確率,我們將在重新生成的二分類樣本集上進行操作。

from sklearn.model_selection import KFold
from sklearn.datasets import make_classification
X,y = make_classification(n_samples=300, n_features=5, shuffle=True, random_state=6, n_classes=2) 

color = ['red','yellow']
for i in range(2):
    plt.scatter(X[y==i,0], X[y==i,1], color=color[i])


進行5折交叉驗證,選出最優的k值。

kf = KFold(n_splits=5)

accuracy = []
for i in range(1,40):
    acc = []
    for train_index, test_index in kf.split(X):
        train_x,train_y = X[train_index],y[train_index]
        test_x,test_y = X[test_index],y[test_index]
        clf = KNeighborsClassifier(k=i)
        clf.fit(train_x,train_y)
        pred_y = clf.predict(test_x)
        acc.append(sum(pred_y==test_y)/len(pred_y))
    accuracy.append(acc)
        
accuracy = np.array(accuracy)
m,n = accuracy.shape
for i in range(n):
    plt.scatter(range(1,m+1),accuracy[:,i])
plt.errorbar(range(1,m+1),accuracy.mean(axis=1),accuracy.std(axis=1))


從圖上可以看出,當參數k取5的時候,模型的效果最好。

在數據量還不是很大的情況下,可以使用上述的k折交叉驗證或網格搜索來尋找最優的k值;但當數據量很大時,採用這些方法,雖然可以實現,但是同時計算時間也會很長,要慎重使用。

以上便是k近鄰算法的代碼實現,如有不對之處,歡迎交流。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章