機器學習筆記(六):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,兩者的歐式距離定義為:
大多數情況下,歐式距離可以滿足我們的需求,我們不需要再去操心距離的度量。
當然我們也可以用他的距離度量方式。比如曼哈頓距離,定義為:
更加通用點,比如閔可夫斯基距離(Minkowski Distance),定義為:
可以看出,歐式距離是閔可夫斯基距離距離在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)]
動作片