【機器學習】分類算法--K近鄰算法

前言

K近鄰算法通常寫作Knn算法,它是一種有監督的分類算法。Knn算法與K-means算法不同,K-means算法是是無監督的聚類算法。今天我就用iris的數據和大家聊聊什麼是Knn

圖爲iris部分數據,其中藍色表示setosa、綠色表示virginica,紅色表示我們需要預測的點


正文

Knn原理

通俗的講,就是找與自己特徵距離最近的k個點,根據k個點中出現類別最多的,作爲其預測分類。

假如我們先不考慮用什麼度量距離,從圖可以直觀的看到,當K=3時,我們的預測的點是virginica類別。

那麼K值取大一點可以嗎,答案是可以的。K值與分類的錯誤率變化曲線是先下降後上升的。在很多資料中提到,通常K是不大於20的整數,這應該是由預測類別數和樣本總數預估的結果。

 

距離度量

計算距離其實就是度量特徵的相似性,計算方法有:歐式距離、曼哈頓距離、馬氏距離、餘弦相似度、漢明距離

Knn常用是歐式距離,在二維平面上其表達式爲

                                                                     \rho =\sqrt{\left ( x_{2} - x_{1})^{2}+( y_{2} - y_{1})^{2}}

其在三維空間上的表達式爲

                                                         \rho =\sqrt{\left ( x_{2} - x_{1})^{2}+( y_{2} - y_{1})^{2}+( z_{2} - z_{1})^{2}}

由此,我們可以擴展得到更高維的計算表達式。那麼我們可以想到Knn暴力解決方法是遍歷計算所有點與預測點的距離,最終選取K個值,作爲分類參考。

整理一下Knn算法的實現邏輯:

  1. 計算已知類別數據集中的點與當前點之間的距離;
  2. 按照距離遞增次序排序;
  3. 選取與當前點距離最小的k個點;
  4. 確定前k個點所在類別的出現頻率;
  5. 返回前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做一個專題的學習分享

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章