k-近鄰算法

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


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

    相信看過(guò)數(shù)據(jù)以后,即使不知道未知電影(?)屬于哪一種類型,但是可以通過(guò)某個(gè)計(jì)算方法計(jì)算出來(lái)。

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

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

    下面貼一下書上總結(jié)的k近鄰算法的一般流程:
#coding=UTF8  
from numpy import *  
import operator  
  
def createDataSet():  
    """ 
    函數(shù)作用:構(gòu)建一組訓(xùn)練數(shù)據(jù)(訓(xùn)練樣本),共4個(gè)樣本 
    同時(shí)給出了這4個(gè)樣本的標(biāo)簽,及l(fā)abels 
    """  
    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 是輸入的測(cè)試樣本,是一個(gè)[x, y]樣式的 
    dataset 是訓(xùn)練樣本集 
    labels 是訓(xùn)練樣本標(biāo)簽 
    k 是top k最相近的 
    """  
    # shape返回矩陣的[行數(shù),列數(shù)],  
    # 那么shape[0]獲取數(shù)據(jù)集的行數(shù),  
    # 行數(shù)就是樣本的數(shù)量  
    dataSetSize = dataset.shape[0]   
      
    """ 
    下面的求距離過(guò)程就是按照歐氏距離的公式計(jì)算的。 
    即 根號(hào)(x^2+y^2) 
    """  
    # tile屬于numpy模塊下邊的函數(shù)  
    # tile(A, reps)返回一個(gè)shape=reps的矩陣,矩陣的每個(gè)元素是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]]]   
    # 上邊那個(gè)結(jié)果的分開理解就是:  
    # 最外層是2個(gè)元素,即最外邊的[]中包含2個(gè)元素,類似于[C,D],而此處的C=D,因?yàn)槭菑?fù)制出來(lái)的  
    # 然后C包含1個(gè)元素,即C=[E],同理D=[E]  
    # 最后E包含2個(gè)元素,即E=[F,G],此處F=G,因?yàn)槭菑?fù)制出來(lái)的  
    # F就是A了,基礎(chǔ)元素  
    # 綜合起來(lái)就是(2,1,2)= [C, C] = [[E], [E]] = [[[F, F]], [[F, F]]] = [[[A, A]], [[A, A]]]  
    # 這個(gè)地方就是為了把輸入的測(cè)試樣本擴(kuò)展為和dataset的shape一樣,然后就可以直接做矩陣減法了。  
    # 比如,dataset有4個(gè)樣本,就是4*2的矩陣,輸入測(cè)試樣本肯定是一個(gè)了,就是1*2,為了計(jì)算輸入樣本與訓(xùn)練樣本的距離  
    # 那么,需要對(duì)這個(gè)數(shù)據(jù)進(jìn)行作差。這是一次比較,因?yàn)橛?xùn)練樣本有n個(gè),那么就要進(jìn)行n次比較;  
    # 為了方便計(jì)算,把輸入樣本復(fù)制n次,然后直接與訓(xùn)練樣本作矩陣差運(yùn)算,就可以一次性比較了n個(gè)樣本。  
    # 比如inX = [0,1],dataset就用函數(shù)返回的結(jié)果,那么  
    # 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就是輸入樣本與每個(gè)訓(xùn)練樣本的差值,然后對(duì)其每個(gè)x和y的差值進(jìn)行平方運(yùn)算。  
    # diffMat是一個(gè)矩陣,矩陣**2表示對(duì)矩陣中的每個(gè)元素進(jìn)行**2操作,即平方。  
    # sqDiffMat = [[1.0, 0.01],  
    #              [1.0, 0.0 ],  
    #              [0.0, 1.0 ],  
    #              [0.0, 0.81]]  
    sqDiffMat = diffMat ** 2  
      
    # axis=1表示按照橫軸,sum表示累加,即按照行進(jìn)行累加。  
    # sqDistance = [[1.01],  
    #               [1.0 ],  
    #               [1.0 ],  
    #               [0.81]]  
    sqDistance = sqDiffMat.sum(axis=1)  
      
    # 對(duì)平方和進(jìn)行開根號(hào)  
    distance = sqDistance ** 0.5  
      
    # 按照升序進(jìn)行快速排序,返回的是原數(shù)組的下標(biāo)。  
    # 比如,x = [30, 10, 20, 40]  
    # 升序排序后應(yīng)該是[10,20,30,40],他們的原下標(biāo)是[1,2,0,3]  
    # 那么,numpy.argsort(x) = [1, 2, 0, 3]  
    sortedDistIndicies = distance.argsort()  
      
    # 存放最終的分類結(jié)果及相應(yīng)的結(jié)果投票數(shù)  
    classCount = {}  
      
    # 投票過(guò)程,就是統(tǒng)計(jì)前k個(gè)最近的樣本所屬類別包含的樣本個(gè)數(shù)  
    for i in range(k):  
        # index = sortedDistIndicies[i]是第i個(gè)最相近的樣本下標(biāo)  
        # voteIlabel = labels[index]是樣本index對(duì)應(yīng)的分類結(jié)果('A' or 'B')  
        voteIlabel = labels[sortedDistIndicies[i]]  
        # classCount.get(voteIlabel, 0)返回voteIlabel的值,如果不存在,則返回0  
        # 然后將票數(shù)增1  
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1  
      
    # 把分類結(jié)果進(jìn)行排序,然后返回得票數(shù)最多的分類結(jié)果  
    # classCount.items():迭代器,獲得每一個(gè)對(duì)象
    # operator.itemgetter(1):表示獲取函數(shù)一維數(shù)據(jù)進(jìn)行排序,即按照其投票數(shù)進(jìn)行排序
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)  
    return sortedClassCount[0][0] 
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容