機器學習經典算法之KNN

1、前言
KNN 的英文叫 K-Nearest Neighbor,應該算是數據挖掘算法中最簡單的一種。
先用一個例子體會下。

/*請尊重做者勞動成果,轉載請標明原文連接:*/html

/* https://www.cnblogs.com/jpcflyer/p/11111817.html * /git

假設,咱們想對電影的類型進行分類,統計了電影中打鬥次數、接吻次數,固然還有其餘的指標也能夠被統計到,以下表所示。
咱們很容易理解《戰狼》《紅海行動》《碟中諜 6》是動做片,《前任 3》《春嬌救志明》《泰坦尼克號》是愛情片,可是有沒有一種方法讓機器也能夠掌握這個分類的規則,當有一部新電影的時候,也能夠對它的類型自動分類呢?
咱們能夠把打鬥次數當作 X 軸,接吻次數當作 Y 軸,而後在二維的座標軸上,對這幾部電影進行標記,以下圖所示。對於未知的電影 A,座標爲 (x,y),咱們須要看下離電影 A 最近的都有哪些電影,這些電影中的大多數屬於哪一個分類,那麼電影 A 就屬於哪一個分類。實際操做中,咱們還須要肯定一個 K 值,也就是咱們要觀察離電影 A 最近的電影有多少個。
 
 
2、KNN 的工做原理
「近朱者赤,近墨者黑」能夠說是 KNN 的工做原理。整個計算過程分爲三步:
1. 計算待分類物體與其餘物體之間的距離;
2. 統計距離最近的 K 個鄰居;
3.對於 K 個最近的鄰居,它們屬於哪一個分類最多,待分類物體就屬於哪一類。
 
K 值如何選擇
你能看出整個 KNN 的分類過程,K 值的選擇仍是很重要的。那麼問題來了,K 值選擇多少是適合的呢?
若是 K 值比較小,就至關於未分類物體與它的鄰居很是接近才行。這樣產生的一個問題就是,若是鄰居點是個噪聲點,那麼未分類物體的分類也會產生偏差,這樣 KNN 分類就會產生過擬合。
若是 K 值比較大,至關於距離過遠的點也會對未知物體的分類產生影響,雖然這種狀況的好處是魯棒性強,可是不足也很明顯,會產生欠擬合狀況,也就是沒有把未分類物體真正分類出來。
因此 K 值應該是個實踐出來的結果,並非咱們事先而定的。在工程上,咱們通常採用交叉驗證的方式選取 K 值。
交叉驗證的思路就是,把樣本集中的大部分樣本做爲訓練集,剩餘的小部分樣本用於預測,來驗證分類模型的準確性。因此在 KNN 算法中,咱們通常會把 K 值選取在較小的範圍內,同時在驗證集上準確率最高的那一個最終肯定做爲 K 值。
 
距離如何計算
在 KNN 算法中,還有一個重要的計算就是關於距離的度量。兩個樣本點之間的距離表明了這兩個樣本之間的類似度。距離越大,差別性越大;距離越小,類似度越大。
關於距離的計算方式有下面五種方式:
歐氏距離;
曼哈頓距離;
閔可夫斯基距離;
切比雪夫距離;
餘弦距離。
其中前三種距離是 KNN 中最經常使用的距離,如今分別講解下。
 
歐氏距離是咱們最經常使用的距離公式,也叫作歐幾里得距離。在二維空間中,兩點的歐式距離就是:
同理,咱們也能夠求得兩點在 n 維空間中的距離:
曼哈頓距離
在幾何空間中用的比較多。曼哈頓距離等於兩個點在座標系上絕對軸距總和。用公式表示就是:
 
閔可夫斯基距離
不是一個距離,而是一組距離的定義。對於 n 維空間中的兩個點 x(x1,x2,…,xn) 和 y(y1,y2,…,yn) , x 和 y 兩點之間的閔可夫斯基距離爲:
其中 p 表明空間的維數,當 p=1 時,就是曼哈頓距離;當 p=2 時,就是歐氏距離;當 p→∞時,就是切比雪夫距離。
那麼切比雪夫距離 怎麼計算呢?二個點之間的切比雪夫距離就是這兩個點座標數值差的絕對值的最大值,用數學表示就是:max(|x1-y1|,|x2-y2|)。
餘弦距離 實際上計算的是兩個向量的夾角,是在方向上計算二者之間的差別,對絕對數值不敏感。在興趣相關性比較上,角度關係比距離的絕對值更重要,所以餘弦距離能夠用於衡量用戶對內容興趣的區分度。好比咱們用搜索引擎搜索某個關鍵詞,它還會給你推薦其餘的相關搜索,這些推薦的關鍵詞就是採用餘弦距離計算得出的。
 
KD 樹
其實從上文你也能看出來,KNN 的計算過程是大量計算樣本點之間的距離。爲了減小計算距離次數,提高 KNN 的搜索效率,人們提出了 KD 樹(K-Dimensional 的縮寫)。KD 樹是對數據點在 K 維空間中劃分的一種數據結構。在 KD 樹的構造中,每一個節點都是 k 維數值點的二叉樹。既然是二叉樹,就能夠採用二叉樹的增刪改查操做,這樣就大大提高了搜索效率。
在這裏,咱們不須要對 KD 樹的數學原理了解太多,你只須要知道它是一個二叉樹的數據結構,方便存儲 K 維空間的數據就能夠了。並且在 sklearn 中,咱們直接能夠調用 KD 樹,很方便。
 
用 KNN 作迴歸
KNN 不只能夠作分類,還能夠作迴歸。首先講下什麼是迴歸。在開頭電影這個案例中,若是想要對未知電影進行類型劃分,這是一個分類問題。首先看一下要分類的未知電影,離它最近的 K 部電影大多數屬於哪一個分類,這部電影就屬於哪一個分類。
若是是一部新電影,已知它是愛情片,想要知道它的打鬥次數、接吻次數多是多少,這就是一個迴歸問題。
那麼 KNN 如何作迴歸呢?
對於一個新點,咱們須要找出這個點的 K 個最近鄰居,而後將這些鄰居的屬性的平均值賦給該點,就能夠獲得該點的屬性。固然不一樣鄰居的影響力權重能夠設置成不一樣的。舉個例子,好比一部電影 A,已知它是動做片,當 K=3 時,最近的 3 部電影是《戰狼》,《紅海行動》和《碟中諜 6》,那麼它的打鬥次數和接吻次數的預估值分別爲 (100+95+105)/3=100 次、(5+3+31)/3=13 次。
 
3、 如何在 sklearn 中使用 KNN
接下來,咱們先看下如何在 sklearn 中使用 KNN 算法,而後經過 sklearn 中自帶的手寫數字數據集來進行實戰。
在 Python 的 sklearn 工具包中有 KNN 算法。KNN 既能夠作分類器,也能夠作迴歸。若是是作分類,你須要引用:
1 from sklearn.neighbors import KNeighborsClassifier
若是是作迴歸,你須要引用:
1 from sklearn.neighbors import KNeighborsRegressor
從名字上你也能看出來 Classifier 對應的是分類,Regressor 對應的是迴歸。通常來講若是一個算法有 Classifier 類,都能找到相應的 Regressor 類。好比在決策樹分類中,你可使用 DecisionTreeClassifier,也可使用決策樹來作迴歸 DecisionTreeRegressor。
好了,咱們看下如何在 sklearn 中建立 KNN 分類器。
這裏,咱們使用構造函數 KNeighborsClassifier(n_neighbors=5, weights=‘uniform’, algorithm=‘auto’, leaf_size=30),這裏有幾個比較主要的參數,我分別來說解下:
1.n_neighbors:即 KNN 中的 K 值,表明的是鄰居的數量。K 值若是比較小,會形成過擬合。若是 K 值比較大,沒法將未知物體分類出來。通常咱們使用默認值 5。
2.weights:是用來肯定鄰居的權重,有三種方式:
weights=uniform,表明全部鄰居的權重相同;
weights=distance,表明權重是距離的倒數,即與距離成反比;
自定義函數,你能夠自定義不一樣距離所對應的權重。大部分狀況下不須要本身定義函數。
3.algorithm:用來規定計算鄰居的方法,它有四種方式:
algorithm=auto,根據數據的狀況自動選擇適合的算法,默認狀況選擇 auto;
algorithm=kd_tree,也叫做 KD 樹,是多維空間的數據結構,方便對關鍵數據進行檢索,不過 KD 樹適用於維度少的狀況,通常維數不超過 20,若是維數大於 20 以後,效率反而會降低;
algorithm=ball_tree,也叫做球樹,它和 KD 樹同樣都是多維空間的數據結果,不一樣於 KD 樹,球樹更適用於維度大的狀況;
algorithm=brute,也叫做暴力搜索,它和 KD 樹不一樣的地方是在於採用的是線性掃描,而不是經過構造樹結構進行快速檢索。當訓練集大的時候,效率很低。
4.leaf_size:表明構造 KD 樹或球樹時的葉子數,默認是 30,調整 leaf_size 會影響到樹的構造和搜索速度。
建立完 KNN 分類器以後,咱們就能夠輸入訓練集對它進行訓練,這裏咱們使用 fit() 函數,傳入訓練集中的樣本特徵矩陣和分類標識,會自動獲得訓練好的 KNN 分類器。而後可使用 predict() 函數來對結果進行預測,這裏傳入測試集的特徵矩陣,能夠獲得測試集的預測分類結果。
 
4、 如何用 KNN 對手寫數字進行識別分類
手寫數字數據集是個很是有名的用於圖像識別的數據集。數字識別的過程就是將這些圖片與分類結果 0-9 一一對應起來。完整的手寫數字數據集 MNIST 裏面包括了 60000 個訓練樣本,以及 10000 個測試樣本。若是你學習深度學習的話,MNIST 基本上是你接觸的第一個數據集。
今天咱們用 sklearn 自帶的手寫數字數據集作 KNN 分類,你能夠把這個數據集理解成一個簡版的 MNIST 數據集,它只包括了 1797 幅數字圖像,每幅圖像大小是 8*8 像素。
 
咱們先來規劃下整個 KNN 分類的流程:
數據加載:咱們能夠直接從 sklearn 中加載自帶的手寫數字數據集;
準備階段:在這個階段中,咱們須要對數據集有個初步的瞭解,好比樣本的個數、圖像長什麼樣、識別結果是怎樣的。你能夠經過可視化的方式來查看圖像的呈現。經過數據規範化可讓數據都在同一個數量級的維度。另外,由於訓練集是圖像,每幅圖像是個 8*8 的矩陣,咱們不須要對它進行特徵選擇,將所有的圖像數據做爲特徵值矩陣便可;
分類階段:經過訓練能夠獲得分類器,而後用測試集進行準確率的計算。
 
好了,按照上面的步驟,咱們一塊兒來實現下這個項目。
首先是加載數據和對數據的探索:
 1 # 加載數據
 2 digits = load_digits()
 3 data = digits.data
 4 # 數據探索
 5 print(data.shape)
 6 # 查看第一幅圖像
 7 print(digits.images[0])
 8 # 第一幅圖像表明的數字含義
 9 print(digits.target[0])
10 # 將第一幅圖像顯示出來
11 plt.gray()
12 plt.imshow(digits.images[0])
13 plt.show()
運行結果:
 1 (1797, 64)
 2 [[ 0.  0.  5. 13.  9.  1.  0.  0.]
 3 [ 0.  0. 13. 15. 10. 15.  5.  0.]
 4 [ 0.  3. 15.  2.  0. 11.  8.  0.]
 5 [ 0.  4. 12.  0.  0.  8.  8.  0.]
 6 [ 0.  5.  8.  0.  0.  9.  8.  0.]
 7 [ 0.  4. 11.  0.  1. 12.  7.  0.]
 8 [ 0.  2. 14.  5. 10. 12.  0.  0.]
 9 [ 0.  0.  6. 13. 10.  0.  0.  0.]]
10 0
咱們對原始數據集中的第一幅進行數據可視化,能夠看到圖像是個 8*8 的像素矩陣,上面這幅圖像是一個「0」,從訓練集的分類標註中咱們也能夠看到分類標註爲「0」。
sklearn 自帶的手寫數字數據集一共包括了 1797 個樣本,每幅圖像都是 8*8 像素的矩陣。由於並無專門的測試集,因此咱們須要對數據集作劃分,劃分紅訓練集和測試集。由於 KNN 算法和距離定義相關,咱們須要對數據進行規範化處理,採用 Z-Score 規範化,代碼以下:
 1 # 分割數據,將 25% 的數據做爲測試集,其他做爲訓練集(你也能夠指定其餘比例的數據做爲訓練集)
 2 train_x, test_x, train_y, test_y = train_test_split(data, digits.target, test_size=0.25, random_state=33)
 3 # 採用 Z-Score 規範化
 4 ss = preprocessing.StandardScaler()
 5 train_ss_x = ss.fit_transform(train_x)
 6 test_ss_x = ss.transform(test_x)
 7 而後咱們構造一個 KNN 分類器 knn,把訓練集的數據傳入構造好的 knn,並經過測試集進行結果預測,與測試集的結果進行對比,獲得 KNN 分類器準確率,代碼以下:
 8 # 建立 KNN 分類器
 9 knn = KNeighborsClassifier()
10 knn.fit(train_ss_x, train_y)
11 predict_y = knn.predict(test_ss_x)
12 print("KNN 準確率: %.4lf" % accuracy_score(predict_y, test_y))
運行結果:
1 KNN 準確率: 0.975
好了,這樣咱們就構造好了一個 KNN 分類器。以前咱們還講過 SVM、樸素貝葉斯和決策樹分類。咱們用手寫數字數據集一塊兒來訓練下這些分類器,而後對比下哪一個分類器的效果更好。代碼以下:
 1 # 建立 SVM 分類器
 2 svm = SVC()
 3 svm.fit(train_ss_x, train_y)
 4 predict_y=svm.predict(test_ss_x)
 5 print('SVM 準確率: %0.4lf' % accuracy_score(predict_y, test_y))
 6 # 採用 Min-Max 規範化
 7 mm = preprocessing.MinMaxScaler()
 8 train_mm_x = mm.fit_transform(train_x)
 9 test_mm_x = mm.transform(test_x)
10 # 建立 Naive Bayes 分類器
11 mnb = MultinomialNB()
12 mnb.fit(train_mm_x, train_y)
13 predict_y = mnb.predict(test_mm_x)
14 print(" 多項式樸素貝葉斯準確率: %.4lf" % accuracy_score(predict_y, test_y))
15 # 建立 CART 決策樹分類器
16 dtc = DecisionTreeClassifier()
17 dtc.fit(train_mm_x, train_y)
18 predict_y = dtc.predict(test_mm_x)
19 print("CART 決策樹準確率: %.4lf" % accuracy_score(predict_y, test_y))
運行結果以下:
1 SVM 準確率: 0.9867
2 多項式樸素貝葉斯準確率: 0.8844
3 CART 決策樹準確率: 0.8556
這裏須要注意的是,咱們在作多項式樸素貝葉斯分類的時候,傳入的數據不能有負數。由於 Z-Score 會將數值規範化爲一個標準的正態分佈,即均值爲 0,方差爲 1,數值會包含負數。所以咱們須要採用 Min-Max 規範化,將數據規範化到 [0,1] 範圍內。
好了,咱們整理下這 4 個分類器的結果。
你能看出來 KNN 的準確率仍是不錯的,和 SVM 不相上下。
你能夠本身跑一遍整個代碼,在運行前還須要 import 相關的工具包(下面的這些工具包你都會用到,因此都須要引用):
1 from sklearn.model_selection import train_test_split
2 from sklearn import preprocessing
3 from sklearn.metrics import accuracy_score
4 from sklearn.datasets import load_digits
5 from sklearn.neighbors import KNeighborsClassifier
6 from sklearn.svm import SVC
7 from sklearn.naive_bayes import MultinomialNB
8 from sklearn.tree import DecisionTreeClassifier
9 import matplotlib.pyplot as plt
代碼中,我使用了 train_test_split 作數據集的拆分,使用 matplotlib.pyplot 工具包顯示圖像,使用 accuracy_score 進行分類器準確率的計算,使用 preprocessing 中的 StandardScaler 和 MinMaxScaler 作數據的規範化。
相關文章
相關標籤/搜索