1. 程式人生 > >機器學習十大經典演算法之K-近鄰演算法(學習筆記)

機器學習十大經典演算法之K-近鄰演算法(學習筆記)

演算法概述

K-近鄰演算法(k-Nearest Neighbor,KNN)是機器學習演算法中最簡單最容易理解的演算法。該演算法的思路是:給定一個訓練資料集,對新的輸入例項,在訓練資料集中找到與該例項最鄰近的K個例項, 這K個例項的多數屬於某個類,就把該輸入例項分類到這個類中。一般K不大於20。

優點:精度高、對異常值不敏感、無資料輸入假定 缺點:計算複雜度高、空間複雜度高 適用資料範圍:數值型和標稱型

距離

  1. 歐式距離(Euclidean Distance) d(x,y)=i=1n(xiyi)2d(x,y) = \sqrt{\sum_{i=1}^{n} {(x_i-y_i)^2}}
  2. 曼哈頓距離(Manhattan Distance) d(x,y)=i=1nxiyid(x,y) = \sum_{i=1}^{n} {|x_i-y_i|}
  3. 餘弦距離(Cosine Distance) cos(θ)=xyxy\cos(\theta)=\frac {\vec{x}\cdot\vec{y}} {||x||\cdot||y||}
  4. 傑卡德距離(Jaccard Distance) J(A,B)=ABABJ(A,B)=\frac {|A\bigcap B|} {|A\bigcup B|}

搜尋近鄰點的方法

1.暴力搜尋

計算所有已知樣本與未知樣本的距離,選出最近的K個樣本進行投票。

2.KD樹

構造KD樹: (1)計算訓練集每個變數的方差,將最大方差的變數x作根節點的欄位選擇。 (2)按變數x對訓練集做升序排序,以變數x的中位數為分割點劃分為兩個子節點,分割點保留在根節點中。 (3)重複(1)、(2)直到滿足停止生產的條件為止。 KD樹搜尋: (1)測試集的資料點從某一節點開始,與劃分當前節點的變數資料進行比較判斷流入哪一側子節點,直到落入對應的葉節點中。 (2)以測試集的資料點為中心,以葉節點中訓練集資料點的最近距離為半徑構成球體(該最近距離的點為臨時近鄰點),如果球體與上一層的父節點構成的分割線相交,則需要從父節點的另一側葉節點重新搜尋近鄰點,如果比剛才的臨時近鄰點距離更近,則更新臨時近鄰點。 (3)重複(2)直到找到最終的近鄰點。

3.球樹

構造球樹: (1)以訓練集中距離最遠的兩個資料點的中間位置為球心構造一個超球體,則該超球體是包含所有訓練集樣本點的最小球體。 (2)從超球體中尋找離圓心最遠的點p1,再尋找離p1最遠的點p2,分別以這兩個點為球心通過距離計算構造兩個最小的超球體,將剩餘的點劃分到這兩個超球體中。 (3)重複(1)、(2)直到球體無法繼續劃分為止。 球樹搜尋: (1)從最大或者最小的超球體開始,尋找測試集資料點落入的超球體。 (2)計算測試集資料點到所在超球體中最近點的距離d,計算測試集資料點與所在超球體成對的另一個超球體球心的距離D。如果D<d+r(對應的另一個超球體的半徑),則需要回到上一層父節點對應的超球體搜尋近鄰點。 (3)重複(2)直到找到最終的近鄰點。

暴力搜尋簡單易懂,但只適合小樣本。KD樹和球樹雖然增加了計算複雜度,但是能在資料量較大時避免全域性搜尋,提高效率。

K值的選擇

K值太小,則模型複雜度較高,容易發生過擬合;K值太大,則會使得分類結果趨向眾數的類別,從而導致分類失敗。實際運用中通常採用交叉驗證法來來選取最優的K值。

程式碼實現

首先是要用到的幾個函式的官方文件說明。 1.

sklearn.model_selection.train_test_split(*arrays, **options) 在這裡插入圖片描述

sklearn.model_selection.cross_val_score(estimator, X, y=None, groups=None, scoring=None, cv=’warn’, n_jobs=None, verbose=0, fit_params=None, pre_dispatch=‘2*n_jobs’, error_score=’raise-deprecating’) 在這裡插入圖片描述 在這裡插入圖片描述

(1)sklearn.neighbors.KNeighborsClassifier(n_neighbors=5, weights=’uniform’, algorithm=’auto’, leaf_size=30, p=2, metric=’minkowski’, metric_params=None, n_jobs=None, **kwargs) (2)sklearn.neighbors.KNeighborsRegressor(n_neighbors=5, weights=’uniform’, algorithm=’auto’, leaf_size=30, p=2, metric=’minkowski’, metric_params=None, n_jobs=None, **kwargs) 在這裡插入圖片描述

接下來是程式碼。

一、分類

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn import model_selection
from sklearn import neighbors
from sklearn import metrics

data=pd.read_excel(r'C:\Users\sc\Desktop\data.xlsx')
#拆分為訓練集和測試集
predictors=['pre1','pre2','pre3','pre4','pre5']
classification=['class']
x_train,x_test,y_train,y_test=model_selection.train_test_split(data[predictors], data[classification],
                                                               test_size=0.25,random_state=1234)
#設定k值集合
K=np.arange(1,int(np.ceil(np.log2(data.shape[0]))))
#儲存不同k值的平均準確率
accuracy=[]
for k in K:
    #使用10折交叉驗證的方法,比對每一k值下KNN模型的預測準確率
    cv_result=model_selection.cross_val_score(neighbors.KNeighborsClassifier(n_neighbors=k,
weights='distance'),x_train,y_train,cv=10,scoring='accuracy')
    accuracy.append(cv_result.mean())

#查詢最大平均準確率的下標
arg_max=np.array(accuracy).argmax()
#繪製折線圖
plt.rcParams['font.sans-serif']=['Microsoft YaHei'] #中文和負號正常顯示
plt.rcParams['axes.unicode_minus']=False
plt.plot(K,accuracy) #折線圖
plt.scatter(K,accuracy) #散點圖
plt.text(K[arg_max],accuracy[arg_max],'最佳k值為%s' % int(K[arg_max]))
plt.show() #顯示圖形

#以最佳K值構建模型
knn_class=neighbors.KNeighborsClassifier(n_neighbors=int(K[arg_max]),weights='distance')
knn_class.fit(x_train,y_train)
predict=knn_class.predict(x_test)

#模型評估
print('Confusion Matrix:\n',pd.crosstab(y_test,predict)) #構建混淆矩陣
print('Overall Accuracy:',metrics.scorer.accuracy_score(y_test,predict)) #整體準確率
print('Assessment Report:\n',metrics.classification_report(y_test,predict)) #模型評估報告

在這裡插入圖片描述 在這裡插入圖片描述 從混淆矩陣來看,主要還是集中在主對角線,說明預測正確的佔多數;而整體預測準確率為91%;從模型評估報告來看,第一列表示預測精度(預測正確的類別個數/該類別預測的所有個數),第二列表示預測覆蓋率(預測正確的類別個數/該類別實際的所有個數),第三列是前兩列的加權結果,第四列是類別實際的樣本個數。

二、預測

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn import model_selection
from sklearn import neighbors
from sklearn import metrics
from sklearn.preprocessing import minmax_scale

data=pd.read_excel(r'C:\Users\sc\Desktop\data.xlsx')
#資料歸一化,消除量綱影響
predictors=data.columns[:-1]
X=minmax_scale(data[predictors])
#拆分為訓練集和測試集
x_train,x_test,y_train,y_test=model_selection.train_test_split(X, data.classify,
                                                               test_size=0.25,random_state=1234)

#設定k值集合
K=np.arange(1,int(np.ceil(np.log2(data.shape[0]))))
#儲存不同k值的平均MSE
mse=[]
for k in K:
    #使用10折交叉驗證的方法,比對每一k值下KNN模型的MSE
    cv_result=model_selection.cross_val_score(neighbors.KNeighborsRegressor(n_neighbors=k,
weights='distance'),x_train,y_train,cv=10,scoring='neg_mean_squared_error')
    mse.append((-1*cv_result).mean()) #將負數轉換為正數
#查詢最小均方誤差的下標
arg_min=np.array(mse).argmin()

#繪製折線圖
plt.rcParams['font.sans-serif']=['Microsoft YaHei'] #中文和負號正常顯示
plt.rcParams['axes.unicode_minus']=False
plt.plot(K,mse) #折線圖
plt.scatter(K,mse) #散點圖
plt.text(K[arg_min],mse[arg_min],'最佳k值為%s' % int(K[arg_min]))
plt.show() #顯示圖形

#以最佳K值構建模型
knn_reg=neighbors.KNeighborsRegressor(n_neighbors=int(K[arg_min]),weights='distance')
knn_reg.fit(x_train,y_train)
predict=knn_reg.predict(x_test)

#模型評估
print('MSE:',metrics.mean_squared_error(y_test,predict)) #均方誤差越小越好

參考文獻: [1]scikit-learn官方文件:http://scikit-learn.org/stable/index.html [2]Peter Harrington.《機器學習實戰》.人民郵電出版社,2013-6 [3]劉順祥.《從零開始學Python資料分析與挖掘》.清華大學出版社,2018