KNN近鄰算法 詳解

前言

通過本文薯嗤,你將了解并深刻理解什么是 KNN算法。
當(dāng)然纤泵,閱讀本文前骆姐,你最好會點python,
這樣閱讀起來才會沒有障礙噢

春節(jié)后的第一篇文章,
在這里祝大家新的一年工作順心捏题!心想事成玻褪!新年又有新高度!

什么是 KNN近鄰算法公荧?

通常我們都知道這么一句話 “近朱者赤近墨者黑” 带射,
KNN算法就是這句話的完美詮釋了。

我們想要判斷某個東西屬于哪個分類循狰,
那么我們只需要找到最接近該東西的 K 個鄰居窟社,
這些鄰居中哪種分類占比最大券勺,
那么我們就認(rèn)為該東西就屬于這個分類!

KNN近鄰算法 實踐

這里我們會使用到 sklearnnumpy 兩個庫灿里,
當(dāng)然就算你不熟悉也沒關(guān)系关炼,
這里主要就是為了直觀的感受一下 KNN 算法

  • 導(dǎo)入數(shù)據(jù)
    這里我們使用 sklearn中自帶的測試數(shù)據(jù)集:鳶尾花數(shù)據(jù)钠四。
    import numpy as np
    import matplotlib 
    import matplotlib.pyplot as plt
    from sklearn import datasets
    
    #這里我們采集的是 sklearn 包里面自帶的 鳶尾花 的數(shù)據(jù)
    digits = datasets.load_iris()
    
    # 鳶尾花的種類 用 0盗扒,1,2 標(biāo)識
    y = digits.target
    
    # 鳶尾花的 特征缀去,為了可視化的需求,我們這里只取前兩個特征
    x = digits.data[:,:2]
    
    # 在2d平面上畫出鳶尾花的分布情況
    #為了方便顯示甸祭,我們這里只取標(biāo)識為 0 和 1 兩種鳶尾花的數(shù)據(jù)
    plt.scatter(x[y==0,0],x[y==0,1],color='r')
    plt.scatter(x[y==1,0],x[y==1,1],color='b')
    plt.show()
    
鳶尾花分布圖.png
  • 拆分?jǐn)?shù)據(jù)
    一般來說缕碎,對于數(shù)據(jù)集我們需要拆分為測試 和 訓(xùn)練 數(shù)據(jù),
    以方便我們后續(xù)對訓(xùn)練的模型進行預(yù)測評分
    # 將數(shù)據(jù)拆分為 測試數(shù)據(jù) 和 訓(xùn)練數(shù)據(jù)
    from sklearn.model_selection import train_test_split
    x_train,x_test,y_train,y_test = train_test_split(x,y,test_size=0.2)
    # 顯示如下圖
    plt.scatter(x_train[y_train==0,0],x_train[y_train==0,1],color='r')
    plt.scatter(x_train[y_train==1,0],x_train[y_train==1,1],color='b')
    
    # 測試數(shù)據(jù)我們用 黃色顯示
    plt.scatter(x_test[y_test==0,0],x_test[y_test==0,1],color='y')
    plt.scatter(x_test[y_test==1,0],x_test[y_test==1,1],color='y')
    
    plt.show()
    
    
測試數(shù)據(jù) 和 訓(xùn)練數(shù)據(jù)
  • 訓(xùn)練模型 和 評價模型
    其實對于KNN可以認(rèn)為是沒有訓(xùn)練這一步的池户,
    不過為了迎合標(biāo)準(zhǔn),我們加入了這一步。
    訓(xùn)練好模型后实蓬,
    之前拆分的 測試數(shù)據(jù) 就派上用處了艘包,
    將 測試數(shù)據(jù) 代入模型 進行預(yù)測,
    因為 測試數(shù)據(jù) 的 真實值 是知道的寨典,
    這樣就可以判斷我們測試的結(jié)果 是否準(zhǔn)確 了氛雪,

    from sklearn.neighbors import KNeighborsClassifier
    
    # 使用 sklearn knn算法模型進行訓(xùn)練和預(yù)測
    knn = KNeighborsClassifier(n_neighbors=5)
    knn.fit(x_train,y_train)
    y_predict = knn.predict(x_test)
    
    # 真實數(shù)據(jù)分布情況
    plt.scatter(x[y==0,0],x[y==0,1],color='r')
    plt.scatter(x[y==1,0],x[y==1,1],color='b')
    plt.show()
    
    # 預(yù)測數(shù)據(jù)分布情況
    plt.scatter(x_train[y_train==0,0],x_train[y_train==0,1],color='r')
    plt.scatter(x_train[y_train==1,0],x_train[y_train==1,1],color='b')
    
    plt.scatter(x_test[y_predict==0,0],x_test[y_predict==0,1],color='r')
    plt.scatter(x_test[y_predict==1,0],x_test[y_predict==1,1],color='b')
    
    plt.show()
    
    
真實鳶尾花分布圖.png
測試鳶尾花分布圖

通過兩幅圖的對比,
我們很明顯的看到 左下角的一個點預(yù)測錯誤耸成,其余都正確 报亩,
這里我們很直觀的就可以感受到 KNN 算法的整個流程,
其中最關(guān)鍵的還是在 預(yù)測數(shù)據(jù)那塊井氢,
那么接下來我們就來剖析下 KNN 的原理吧

KNN算法 手寫實現(xiàn)

  • 思路
    首先我們理一下弦追,knn的幾個關(guān)鍵因素:
    ① neighbors,我們該選取幾個鄰居作為目標(biāo)分類的依據(jù)花竞。
    ② 和鄰居之間的距離怎么計算

    1. neighbors 很簡單劲件,我們讓調(diào)用者自己傳入就好了
    2. 和鄰居之間的距離,我們采用簡單的 歐拉距離 公式計算, 如下:
      distance = \sqrt{ \sum_{i=1}^n (X_i^a-X_i^b)^2}

當(dāng)然约急,真正要寫好 KNN算法 肯定不是我們考慮的這么簡單零远,
但是主要思路是這樣,
所以我們根據(jù)這個思路先來把簡單的 KNN 實現(xiàn)一下吧烤宙。

  • 實現(xiàn)
    有了上面的思路遍烦,我們直接來看代碼吧!

from math import sqrt
from collections import Counter

class MyKNN:
    # 初始化
    def __init__(self,n_neighbors=5):
        self.n_neighbors = n_neighbors
        self.X = None
        self.Y = None

    def fit(self,x,y):
        """
        KNN 算是一個比較特殊的算法躺枕,其實它是沒有一個訓(xùn)練過程的服猪,
        這里簡單的將訓(xùn)練數(shù)據(jù)保存起來就好了  
        """
        self.X = x
        self.Y = y
    
    def _predict(self,x):
        """
        預(yù)測單個樣本的所屬分類
        """
        # 歐拉距離的計算
        distances = [sqrt(np.sum((i-x)**2)) for i in self.X]
        # 排序
        sort_distances_index = np.argsort(distances)
        # 找出最近的 n_neighbors 個鄰居
        neighbors_index = sort_distances_index[:self.n_neighbors]
        neighbors = self.Y[neighbors_index]
        # 返回最近鄰居中分類占比最大的那個分類標(biāo)識
        return Counter(neighbors).most_common(1)[0][0]
    
    def predict(self, X_predict):
        """
        預(yù)測多個樣本的分類
        """
        # 通過單個樣本分類直接 預(yù)測就 ok了
        y_predict = [self._predict(x) for x in X_predict]
        
        return np.array(y_predict)

上面這個代碼應(yīng)該是相當(dāng)簡單了供填,
如果你有興趣,可以把 KNN近鄰算法 實踐 這一節(jié)的 預(yù)測代碼用我們手寫的跑一遍罢猪,
這里就不重復(fù)了近她,實現(xiàn)的效果大同小異,

但是從上面的代碼我們也可以看出來膳帕,咱們是采用遍歷的方式來求所有距離的粘捎,
如果你和 sklearn 中的算法做下對比,
會發(fā)現(xiàn)運行的速度會有非常大的區(qū)別危彩,
這其中是有很多優(yōu)化的點的攒磨,
不過這里不再我們的討論這列,
或者說汤徽,其實我自己也沒怎么去研究那些 KD-tree娩缰,ball-tree 之類的數(shù)據(jù)結(jié)構(gòu),
某種程度來說谒府,
其實這也是數(shù)學(xué)的魅力拼坎,
就像一個排序...都能給你整出那么多幺兒子,

KNN 調(diào)參

實踐了完疫,手寫了泰鸡,
不知道現(xiàn)在你對knn是不是有了一個比較深入的了解,
嗯壳鹤,只想說一句盛龄,
不愧是最簡單的算法之一,是真的很簡單...
話雖如此器虾,但是如果你覺得這樣就可以用好 KNN 那就有點太想當(dāng)然了,
學(xué)好一個算法不是目的讯嫂,
用好一個算法才是真正的牛逼...
下面我們就來談?wù)?KNN 的 調(diào)參...
這也是用好 KNN 的最最關(guān)鍵的一步

  • 超參數(shù) K
    這個 K 就是我們上面的選取的鄰居的個數(shù),
    選取數(shù)目的不一樣兆沙,
    對于我們預(yù)測的結(jié)果肯定也是有差異的欧芽,
    那么下面我們來尋找一下最優(yōu)的 K 把

    1. 首先我們得有一個評價標(biāo)準(zhǔn),
      這里我們簡單的就用 正確率 來評價這個 k 的好壞葛圃,
      定義一個 打分函數(shù) score 來返回模型的 正確率
    def score(y_predict,y_true):
      """
      傳入 預(yù)測的 y  和 真實的 y 千扔,判斷一下他們的正確率
      """
      return np.sum(y_predict == y_true)/len(y_true)
    
    1. 尋找最佳的 K 值
      尋找我們簡單的使用循環(huán)遍歷所有的 K 值,
      得出相應(yīng)的 準(zhǔn)確率库正,
      從而找到 最佳 K 值曲楚。
      best_score = 0
      best_k = 0
      
      # 循環(huán) 10 以內(nèi)的 k值進行預(yù)測,并且求出最佳的 k 值
      for i in range(1,10):
          knn = MyKNN(n_neighbors=i)
          knn.fit(x_train,y_train)
          y_predict = knn.predict(x_test)
      
          s = score(y_predict,y_test)
      
          if(s>best_score):
              best_score = s
              best_k = i
      
      print("best_score = ",best_score)
      print("best_k = ",best_k)
      
      
  • 和距離有關(guān)的超參數(shù)
    1. 權(quán)重
      什么是權(quán)重呢褥符?
      舉個簡單的栗子龙誊,我有三個朋ABC,
      其中A喜歡吃蘋果,
      BC喜歡吃梨喷楣,
      我和A的關(guān)系非常好趟大,和BC只是普通朋友鹤树,
      那么我是喜歡吃蘋果還是梨呢?
      如果不考慮權(quán)重逊朽,我肯定是喜歡吃梨罕伯,
      因為我的三個朋友有兩個喜歡吃梨,
      但是如果考慮權(quán)重的話叽讳,就不一定了追他,
      鑒于我和A的關(guān)系非常好,
      那么我喜歡吃蘋果的概率可能更大岛蚤。

      當(dāng)然不要計較這個例子因果關(guān)系~~~邑狸,
      我們只需要知道,
      關(guān)于距離的遠近對于預(yù)測的結(jié)果應(yīng)該是可以考慮 不同權(quán)重的灭美,
      距離越近推溃,那么 權(quán)重越高,
      我們上面寫的算法那肯定是沒有考慮届腐,
      不管遠近權(quán)重都是1。
      但是在 sklearn 中你是可以找到 weight 這個超參數(shù)的
      兩者距離越近蜂奸,那么權(quán)重越高犁苏,
      從而得出一個帶權(quán)重的結(jié)果,

      具體模型需不需要帶權(quán)重扩所,
      根據(jù)業(yè)務(wù)肯定是會不一樣的围详,
      并沒有絕對的好壞之分,
      但是帶權(quán)重有一個好處就是:
      可以有效避免 平票 的問題祖屏。

    2. 距離的計算方式
      上面我們采用的是歐拉距離作為距離的計算方式助赞,
      實際上,在 sklearn 中袁勺,采用的是另外一種方式雹食,
      叫做明可夫斯基距離,公式如下:
      distance = ( \sum_{i=1}^n |X_i^a-X_i^b|^p)^\frac{1}{p}

      其實仔細觀察我們會發(fā)現(xiàn)期丰,
      當(dāng) P = 2 就是我們用到的 歐拉距離 了群叶,
      當(dāng) P = 1 就是所謂的 曼哈頓距離 了,
      所以這里我們又得到一個可以調(diào)節(jié)的 超參數(shù) P钝荡,
      sklearn 中你是可以找到 P 這個 超參數(shù)的街立,
      來調(diào)整距離計算的方式

      當(dāng)然還有很多距離的計算方式,
      比如:余弦相似度埠通,皮爾森相似度等

這一小節(jié)我們主要介紹了 KNN的 另外兩個超參數(shù)赎离,
并且知道通過循環(huán)遍歷的方式可以求解 最優(yōu) 超參數(shù),
而實際上端辱,針對這個尋找最優(yōu)超參數(shù)的問題梁剔,
sklearn 提供了 一種叫做 網(wǎng)格搜索的 方式虽画,
這里我們就不多說了,
如果你感興趣,可以自行百度找找相關(guān)資料憾朴。

KNN是否可以用于回歸算法狸捕?

前面我們說了,KNN算法是一個分類算法众雷,
但事實上其同樣可以用來處理回歸問題灸拍,
思路也很簡單,
找到相應(yīng)的鄰居砾省,然后根據(jù)鄰居的打分來求自己的打分鸡岗,
將分類問題就轉(zhuǎn)換成了回歸問題了。

最后编兄,我們在總結(jié)下 KNN 的優(yōu)缺點

  • 優(yōu)點
    簡單轩性,并且效果還不錯
    天然適合多分類問題

  • 缺點
    效率低, 樣本越多狠鸳,維度越多揣苏,其執(zhí)行時間復(fù)雜度呈線性增長
    高度數(shù)據(jù)相關(guān)性
    結(jié)果不具有可解釋性

最后,哈件舵,真的是最后了

如果你覺的文章對你有幫助卸察,

那么...請讓你的小手點下贊可好。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末铅祸,一起剝皮案震驚了整個濱河市坑质,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌临梗,老刑警劉巖涡扼,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異盟庞,居然都是意外死亡吃沪,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門茫经,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巷波,“玉大人,你說我怎么就攤上這事卸伞∧鳎” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵荤傲,是天一觀的道長垮耳。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么终佛? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任俊嗽,我火速辦了婚禮,結(jié)果婚禮上铃彰,老公的妹妹穿的比我還像新娘绍豁。我一直安慰自己,他們只是感情好牙捉,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布竹揍。 她就那樣靜靜地躺著,像睡著了一般邪铲。 火紅的嫁衣襯著肌膚如雪芬位。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天带到,我揣著相機與錄音昧碉,去河邊找鬼。 笑死揽惹,一個胖子當(dāng)著我的面吹牛被饿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播搪搏,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼锹漱,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了慕嚷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤毕泌,失蹤者是張志新(化名)和其女友劉穎喝检,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體撼泛,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡挠说,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了愿题。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片损俭。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖潘酗,靈堂內(nèi)的尸體忽然破棺而出杆兵,到底是詐尸還是另有隱情,我是刑警寧澤仔夺,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布琐脏,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏日裙。R本人自食惡果不足惜吹艇,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望昂拂。 院中可真熱鬧受神,春花似錦、人聲如沸格侯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽养交。三九已至精算,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間碎连,已是汗流浹背灰羽。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鱼辙,地道東北人廉嚼。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像倒戏,于是被迫代替她去往敵國和親怠噪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容