k-近鄰算法

一、算法原理
算法原理是什么?允許我不嚴謹的說一下:首先有一堆有標簽的樣本,比如有一堆各種各樣的鳥(樣本集),我知道各種鳥的不同外貌(特征),比如羽毛顏色有無腳蹼身體重量身體長度以及最重要的它屬于哪一種鳥類別/標簽);然后給我一只不是這堆鳥中的一只鳥(測試樣本),讓我觀察了它的羽毛顏色等后,讓我說出它屬于哪一種鳥?我的做法是:遍歷之前的一堆鳥,分別比較每一只鳥的羽毛顏色、身體重量等特征與給定鳥的相應特征,并給出這兩只鳥的相似度。最終,從那一堆鳥中找出相似度最大的前k只,然后統計這k只鳥的分類,最后把分類數量最多的那只鳥的類別作為給定的類別。雖然結果不一定準確,但是是有理論支持的,那就是概率論,哈哈。
下面來看一下書上對這個算法的原理介紹:存在一個訓練樣本集,并且每個樣本都存在標簽(有監督學習)。輸入沒有標簽的新樣本數據后,將新數據的每個特征與樣本集中數據對應的特征進行比較,然后算法提取出與樣本集中特征最相似的數據(最近鄰)的分類標簽。一般來說,我們只選擇樣本數據集中前k個最相似的數據,這就是k-近鄰算法中k的出處,而且k通常不大于20。最后選擇k個最相似數據中出現次數最多的分類,作為新數據的分類。
二、如何解決問題
沒接觸過的同學應該能懂了吧。書中的舉例是對電影的題材進行分類:愛情片or動作片。依據電影中打斗鏡頭和接吻鏡頭的數量。下面來看一下如何用kNN來解決這個問題。


要解決給定一部電影,判斷其屬于哪一種電影這個問題,就需要知道這個未知電影存在多少個打斗鏡頭和接吻鏡頭,如上圖所示,問號位置所代表的兩種鏡頭次數分別是多少?
下面我們來看一下圖中電影的特征值,如下表:

    相信看過數據以后,即使不知道未知電影(?)屬于哪一種類型,但是可以通過某個計算方法計算出來。

第一步:首先計算未知電影與已知電影的相似度(抽象距離--相似度越小,距離越遠)。具體如何計算暫且不考慮。下面看一下相似度列表:

第二步:再將相似度列表排序,選出前k個最相似的樣本。此處我們假設k=3,將上表中的相似度進行排序后前3分別是:He’s Not Really into DudesBeautiful WomanCalifornia Man
第三步:統計最相似樣本的分類。此處很容易知道這3個樣本均為愛情片。
第四步:將分類最多的類別作為未知電影的分類。那么我們就得出結論,未知電影屬于愛情片。

    下面貼一下書上總結的k近鄰算法的一般流程:
#coding=UTF8  
from numpy import *  
import operator  
  
def createDataSet():  
    """ 
    函數作用:構建一組訓練數據(訓練樣本),共4個樣本 
    同時給出了這4個樣本的標簽,及labels 
    """  
    group = array([  
        [1.0, 1.1],  
        [1.0, 1.0],  
        [0. , 0. ],  
        [0. , 0.1]  
    ])  
    labels = ['A', 'A', 'B', 'B']  
    return group, labels  
  
def classify0(inX, dataset, labels, k):  
    """ 
    inX 是輸入的測試樣本,是一個[x, y]樣式的 
    dataset 是訓練樣本集 
    labels 是訓練樣本標簽 
    k 是top k最相近的 
    """  
    # shape返回矩陣的[行數,列數],  
    # 那么shape[0]獲取數據集的行數,  
    # 行數就是樣本的數量  
    dataSetSize = dataset.shape[0]   
      
    """ 
    下面的求距離過程就是按照歐氏距離的公式計算的。 
    即 根號(x^2+y^2) 
    """  
    # tile屬于numpy模塊下邊的函數  
    # tile(A, reps)返回一個shape=reps的矩陣,矩陣的每個元素是A  
    # 比如 A=[0,1,2] 那么,tile(A, 2)= [0, 1, 2, 0, 1, 2]  
    # tile(A,(2,2)) = [[0, 1, 2, 0, 1, 2],  
    #                  [0, 1, 2, 0, 1, 2]]  
    # tile(A,(2,1,2)) = [[[0, 1, 2, 0, 1, 2]],  
    #                    [[0, 1, 2, 0, 1, 2]]]   
    # 上邊那個結果的分開理解就是:  
    # 最外層是2個元素,即最外邊的[]中包含2個元素,類似于[C,D],而此處的C=D,因為是復制出來的  
    # 然后C包含1個元素,即C=[E],同理D=[E]  
    # 最后E包含2個元素,即E=[F,G],此處F=G,因為是復制出來的  
    # F就是A了,基礎元素  
    # 綜合起來就是(2,1,2)= [C, C] = [[E], [E]] = [[[F, F]], [[F, F]]] = [[[A, A]], [[A, A]]]  
    # 這個地方就是為了把輸入的測試樣本擴展為和dataset的shape一樣,然后就可以直接做矩陣減法了。  
    # 比如,dataset有4個樣本,就是4*2的矩陣,輸入測試樣本肯定是一個了,就是1*2,為了計算輸入樣本與訓練樣本的距離  
    # 那么,需要對這個數據進行作差。這是一次比較,因為訓練樣本有n個,那么就要進行n次比較;  
    # 為了方便計算,把輸入樣本復制n次,然后直接與訓練樣本作矩陣差運算,就可以一次性比較了n個樣本。  
    # 比如inX = [0,1],dataset就用函數返回的結果,那么  
    # tile(inX, (4,1))= [[ 0.0, 1.0],  
    #                    [ 0.0, 1.0],  
    #                    [ 0.0, 1.0],  
    #                    [ 0.0, 1.0]]  
    # 作差之后  
    # diffMat = [[-1.0,-0.1],  
    #            [-1.0, 0.0],  
    #            [ 0.0, 1.0],  
    #            [ 0.0, 0.9]]  
    diffMat = tile(inX, (dataSetSize, 1)) - dataset  
      
    # diffMat就是輸入樣本與每個訓練樣本的差值,然后對其每個x和y的差值進行平方運算。  
    # diffMat是一個矩陣,矩陣**2表示對矩陣中的每個元素進行**2操作,即平方。  
    # sqDiffMat = [[1.0, 0.01],  
    #              [1.0, 0.0 ],  
    #              [0.0, 1.0 ],  
    #              [0.0, 0.81]]  
    sqDiffMat = diffMat ** 2  
      
    # axis=1表示按照橫軸,sum表示累加,即按照行進行累加。  
    # sqDistance = [[1.01],  
    #               [1.0 ],  
    #               [1.0 ],  
    #               [0.81]]  
    sqDistance = sqDiffMat.sum(axis=1)  
      
    # 對平方和進行開根號  
    distance = sqDistance ** 0.5  
      
    # 按照升序進行快速排序,返回的是原數組的下標。  
    # 比如,x = [30, 10, 20, 40]  
    # 升序排序后應該是[10,20,30,40],他們的原下標是[1,2,0,3]  
    # 那么,numpy.argsort(x) = [1, 2, 0, 3]  
    sortedDistIndicies = distance.argsort()  
      
    # 存放最終的分類結果及相應的結果投票數  
    classCount = {}  
      
    # 投票過程,就是統計前k個最近的樣本所屬類別包含的樣本個數  
    for i in range(k):  
        # index = sortedDistIndicies[i]是第i個最相近的樣本下標  
        # voteIlabel = labels[index]是樣本index對應的分類結果('A' or 'B')  
        voteIlabel = labels[sortedDistIndicies[i]]  
        # classCount.get(voteIlabel, 0)返回voteIlabel的值,如果不存在,則返回0  
        # 然后將票數增1  
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1  
      
    # 把分類結果進行排序,然后返回得票數最多的分類結果  
    # classCount.items():迭代器,獲得每一個對象
    # operator.itemgetter(1):表示獲取函數一維數據進行排序,即按照其投票數進行排序
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)  
    return sortedClassCount[0][0] 
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,241評論 2 375

推薦閱讀更多精彩內容