前言
K近鄰算法通常寫作Knn算法,它是一種有監督的分類算法。Knn算法與K-means算法不同,K-means算法是是無監督的聚類算法。今天我就用iris的數據和大家聊聊什麼是Knn
圖爲iris部分數據,其中藍色表示setosa、綠色表示virginica,紅色表示我們需要預測的點
正文
Knn原理
通俗的講,就是找與自己特徵距離最近的k個點,根據k個點中出現類別最多的,作爲其預測分類。
假如我們先不考慮用什麼度量距離,從圖可以直觀的看到,當K=3時,我們的預測的點是virginica類別。
那麼K值取大一點可以嗎,答案是可以的。K值與分類的錯誤率變化曲線是先下降後上升的。在很多資料中提到,通常K是不大於20的整數,這應該是由預測類別數和樣本總數預估的結果。
距離度量
計算距離其實就是度量特徵的相似性,計算方法有:歐式距離、曼哈頓距離、馬氏距離、餘弦相似度、漢明距離
Knn常用是歐式距離,在二維平面上其表達式爲
其在三維空間上的表達式爲
由此,我們可以擴展得到更高維的計算表達式。那麼我們可以想到Knn暴力解決方法是遍歷計算所有點與預測點的距離,最終選取K個值,作爲分類參考。
整理一下Knn算法的實現邏輯:
- 計算已知類別數據集中的點與當前點之間的距離;
- 按照距離遞增次序排序;
- 選取與當前點距離最小的k個點;
- 確定前k個點所在類別的出現頻率;
- 返回前k個點出現頻率最高的類別作爲當前點的預測分類。
代碼實現
代碼運行環境:python 3.5.2
1、自定義函數實現
# -*- coding: UTF-8 -*-
import pandas as pd
import numpy as np
import operator
iris = pd.read_csv('F:\ML\iris.data',header = None)
iris.columns=['sepal_lengh_cm','sepal_width_cm','petal_length_cm','petal_width_cm','class']
seto = iris.iloc[0:50,:]
virg = iris.iloc[100:150,:]
data = pd.concat([seto,virg]).reset_index(drop=True)
labels = data['class']
data = data[['sepal_lengh_cm','sepal_width_cm']]
def classify_knn(pred, trainData, labels, k):
"""
@Function: knn分類
@Args: pred:用於分類的輸入向量 (1xN)
trainData:輸入的訓練樣本集 (NxM)
labels:標籤向量 (1xM vector)
k:用於比較的近鄰數量 (should be an odd number)
@Returns: sortedClassCount[0][0]:分類結果
"""
trainDataSize = trainData.shape[0]
diffMat = np.tile(pred, (trainDataSize, 1)) - trainData
#對求差後的矩陣求平方
sqDiffMat = diffMat**2
#對矩陣的每一行求和
sqDistances = sqDiffMat.sum(axis=1)
#求矩陣的和的平方根
distances = sqDistances**0.5
#對上式結果進行排序,argsort函數返回的是:數組值從小到大的索引值
sortedDistIndicies = distances.argsort()
#創建字典
classCount = {}
#給字典賦值
for i in range(k):
#字典的key
voteIlabel = labels[sortedDistIndicies[i]]
#classCount.get(voteIlabel,0):如果字典鍵的voteIlabel有值就返回值,無值則返回0,
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
#對classCount進行排序,默認的sorted是升序,所以加入reverse=True
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
#返回分類結果
return sortedClassCount[0][0]
---------------------------------------------------------------------------------
Input:
classify_knn([6.0,3.5], data, labels, 3)
Output: Iris-virginica
2、sklearn方法調用
import pandas as pd
import numpy as np
from sklearn import neighbors
iris = pd.read_csv('F:\ML\iris.data',header = None)
iris.columns=['sepal_lengh_cm','sepal_width_cm','petal_length_cm','petal_width_cm','class']
seto = iris.iloc[0:50,:]
vers = iris.iloc[50:100,:]
virg = iris.iloc[100:150,:]
data = pd.concat([seto,virg]).reset_index(drop=True)
labels = data['class']
data = data[['sepal_lengh_cm','sepal_width_cm']]
classify_knn = neighbors.KNeighborsClassifier(3)
classify_knn.fit(data, labels)
result = classify_knn.predict([[6.0,3.5]])
print(result)
-----------------------------------------------------------------
Output:
['Iris-virginica']
小結
優點:精度高、對異常值不敏感、無數據輸入假定
缺點:由於所有的訓練數據都會在內存中計算,所以時間複雜度高、空間複雜度
由本次練習得出,學習scikit-learn也能提高自己編碼的效率,後面會對scikit-learn做一個專題的學習分享