1. 程式人生 > >機器學習筆記(六):KNN分類器

機器學習筆記(六):KNN分類器

1 KNN演算法

1.1 KNN演算法簡介

KNN(K-Nearest Neighbor)工作原理:存在一個樣本資料集合,也稱為訓練樣本集,並且樣本集中每個資料都存在標籤,即我們知道樣本集中每一資料與所屬分類對應的關係。輸入沒有標籤的資料後,將新資料中的每個特徵與樣本集中資料對應的特徵進行比較,提取出樣本集中特徵最相似資料(最近鄰)的分類標籤。一般來說,我們只選擇樣本資料集中前k個最相似的資料,這就是k近鄰演算法中k的出處,通常k是不大於20的整數。最後選擇k個最相似資料中出現次數最多的分類作為新資料的分類。

說明:KNN沒有顯示的訓練過程,它是“懶惰學習”的代表,它在訓練階段只是把資料儲存下來,訓練時間開銷為0,等收到測試樣本後進行處理。

舉例:以電影分類作為例子,電影題材可分為愛情片,動作片等,那麼愛情片有哪些特徵?動作片有哪些特徵呢?也就是說給定一部電影,怎麼進行分類?這裡假定將電影分為愛情片和動作片兩類,如果一部電影中接吻鏡頭很多,打鬥鏡頭較少,顯然是屬於愛情片,反之為動作片。有人曾根據電影中打鬥動作和接吻動作數量進行評估,資料如下:

電影名稱 打鬥鏡頭 接吻鏡頭 電影類別
Califoria Man 3 104 愛情片
Beautigul Woman 1 81 愛情片
Kevin Longblade 101 10 動作片
Amped II 98 2 動作片

1.2 距離量度

對於距離的度量,我們有很多的距離度量方式,但是最常用的是歐式距離,即對於兩個n維向量x和y,兩者的歐式距離定義為:

D (

x , y ) = ( x 1 y 1 ) 2 + ( x 2 y 2 ) 2 + + ( x n y n ) 2 = i = 1 n ( x i y i ) 2 D(x,y)=\sqrt{(x_1-y_1)^2+(x_2-y_2)^2+\dots+(x_n-y_n)^2}=\sqrt{\sum_{i=1}^n(x_i-y_i)^2}

大多數情況下,歐式距離可以滿足我們的需求,我們不需要再去操心距離的度量。

當然我們也可以用他的距離度量方式。比如曼哈頓距離,定義為:

D ( x , y ) = x 1 y 1 + x 2 y 2 + + x n y n = i = 1 n x i y i D(x,y)=|x_1-y_1|+|x_2-y_2|+\dots+|x_n-y_n|=\sum_{i=1}^n|x_i-y_i|

更加通用點,比如閔可夫斯基距離(Minkowski Distance),定義為:

D ( x , y ) = ( x 1 y 1 ) p + ( x 2 y 2 ) p + + ( x n y n ) p p = i = 1 n ( x i y i ) p p D(x,y)=\sqrt[p]{(|x_1-y_1|)^p+(|x_2-y_2|)^p+\dots+(|x_n-y_n|)^p}=\sqrt[p]{\sum_{i=1}^n(|x_i-y_i|)^p}

可以看出,歐式距離是閔可夫斯基距離距離在p=2時的特例,而曼哈頓距離是p=1時的特例。

給定一部電影資料(18,90)打鬥鏡頭18個,接吻鏡頭90個,如何知道它是什麼型別的呢?KNN是這樣做的,首先計算未知電影與樣本集中其他電影的距離,我們通常使用歐式距離和曼哈頓距離(這裡使用曼哈頓距離)
資料如下:

電影名稱 與未知分類電影的距離
Califoria Man 20.5
Beautigul Woman 19.2
Kevin Longblade 115.3
Amped II 118.9

現在我們按照距離的遞增順序排序,可以找到k個距離最近的電影,加入k=3,那麼來看排序的前3個電影的類別,愛情片,愛情片,動作片,下面來進行投票,這部未知的電影愛情片2票,動作片1票,那麼我們就認為這部電影屬於愛情片。

1.3 KNN演算法優缺點

優點:精度高,對異常值不敏感、無資料輸入假定

缺點:計算複雜度高、空間複雜度高

1.4 KNN演算法python程式碼實現

實現步驟:

(1)計算距離

(2)選擇距離最小的k個點

(3)排序

Python 實現KNN演算法:

import numpy as np
import operator

def createDataset():
    #四組二維特徵
    group = np.array([[5,115],[7,106],[56,11],[66,9]])
    #四組對應標籤
    labels = ('動作片','動作片','愛情片','愛情片')
    return group,labels

def classify(intX,dataSet,labels,k):
    '''
    KNN演算法
    '''
    #numpy中shape[0]返回陣列的行數,shape[1]返回列數
    dataSetSize = dataSet.shape[0]
    #將intX在橫向重複dataSetSize次,縱向重複1次
    #例如intX=([1,2])--->([[1,2],[1,2],[1,2],[1,2]])便於後面計算
    diffMat = np.tile(intX,(dataSetSize,1))-dataSet
    #二維特徵相減後乘方
    sqdifMax = diffMat**2
    #計算距離
    seqDistances = sqdifMax.sum(axis=1)
    distances = seqDistances**0.5
    print ("distances:",distances)
    #返回distance中元素從小到大排序後的索引
    sortDistance = distances.argsort()
    print ("sortDistance:",sortDistance)
    classCount = {}
    for i in range(k):
        #取出前k個元素的類別
        voteLabel = labels[sortDistance[i]]
        print ("第%d個voteLabel=%s",i,voteLabel)
        classCount[voteLabel] = classCount.get(voteLabel,0)+1
    #dict.get(key,default=None),字典的get()方法,返回指定鍵的值,如果值不在字典中返回預設值。
    #計算類別次數

    #key=operator.itemgetter(1)根據字典的值進行排序
    #key=operator.itemgetter(0)根據字典的鍵進行排序
    #reverse降序排序字典
    sortedClassCount = sorted(classCount.items(),key = operator.itemgetter(1),reverse = True)
    #結果sortedClassCount = [('動作片', 2), ('愛情片', 1)]
    print ("sortedClassCount:",sortedClassCount)
    return sortedClassCount[0][0]



if __name__ == '__main__':
    group,labels = createDataset()
    test = [20,101]
    test_class = classify(test,group,labels,3)
    print (test_class)

distances: [ 20.51828453 13.92838828 96.93296653 102.85912696]
sortDistance: [1 0 2 3]
第%d個voteLabel=%s 0 動作片
第%d個voteLabel=%s 1 動作片
第%d個voteLabel=%s 2 愛情片
sortedClassCount: [(‘動作片’, 2), (‘愛情片’, 1)]
動作片