1. 程式人生 > >K臨近演算法-KNN

K臨近演算法-KNN

K最近鄰(k-Nearest Neighbor,KNN)分類演算法,是一個理論上比較成熟的方法,也是最簡單的機器學習演算法之一。該方法的思路是:如果一個樣本在特徵空間中的k個最相似(即特徵空間中最鄰近)的樣本中的大多數屬於某一個類別,則該樣本也屬於這個類別。

KNN方法雖然從原理上也依賴於極限定理,但在類別決策時,只與極少量的相鄰樣本有關。由於KNN方法主要靠周圍有限的鄰近的樣本,而不是靠判別類域的方法來確定所屬類別的,因此對於類域的交叉或重疊較多的待分樣本集來說,KNN方法較其他方法更為適合。

K 近鄰演算法使用的模型實際上對應於對特徵空間的劃分。K 值的選擇,距離度量和分類決策規則是該演算法的三個基本要素:

1.K 值的選擇會對演算法的結果產生重大影響。K值較小意味著只有與輸入例項較近的訓練例項才會對預測結果起作用,但容易發生過擬合;如果 K 值較大,優點是可以減少學習的估計誤差,但缺點是學習的近似誤差增大,這時與輸入例項較遠的訓練例項也會對預測起作用,使預測發生錯誤。在實際應用中,K 值一般選擇一個較小的數值,通常採用交叉驗證的方法來選擇最優的 K 值。隨著訓練例項數目趨向於無窮和 K=1 時,誤差率不會超過貝葉斯誤差率的2倍,如果K也趨向於無窮,則誤差率趨向於貝葉斯誤差率。

2.該演算法中的分類決策規則往往是多數表決,即由輸入例項的 K 個最臨近的訓練例項中的多數類決定輸入例項的類別

3.距離度量一般採用 Lp 距離,當p=2時,即為歐氏距離,在度量之前,應該將每個屬性的值規範化,這樣有助於防止具有較大初始值域的屬性比具有較小初始值域的屬性的權重過大。

以上是摘自百度百科的一些概念,以下是我個人見解.

KNN是一種分類方法,所謂KNN就是在訓練樣本資料集中取距離最近的前K項資料,並將他們之中出現最多次數的標籤即認為是待預測資料的標籤.

我們分析KNN演算法的優缺點,

優點一:演算法思想簡單而有效,KNN演算法的思路可以說得上是非常簡單了,但是很有效,我對MNIST資料集上使用K值為20的KNN演算法,預測的正確率幾乎100%.有什麼優點能比演算法的有效性更重要呢?

優點二:該演算法對有outlier(離群值)的資料也有效.

缺點一:龐大的計算量與空間佔用,為了提高準確性,所有的機器學習演算法大多要求較高的訓練樣本以支援演算法的正確率,KNN也是如此,由於需要對所有訓練樣本計算帶預測與之的距離,這將耗費較高的時間成本和空間成本.

缺點二:對於訓練資料集的依賴性.訓練資料集的質量會影響KNN演算法的正確率,當資料集數量趨近於無窮,KNN的正確性趨近於100%,但是我們的訓練資料一定是有限的,甚至可能是不均勻的,這些會影響KNN演算法的正確性.當然,大多數機器學習演算法都依賴訓練資料集的質量.

此外,該有兩個點需要說明,一個是為了防止各維度資料對結果的影響不均勻(例如A維度的差值是100,B維度的差值是0.01,A維度對結果的影響程度明顯遠高於B維度,但是在事實上A,B維度應該是相同的),我們可以對所有維度的距離進行歸一化處理,使其取值範圍相同,這樣可以消除維度差值數量級差異的問題.

二是距離的測量,我們常用的是歐式距離,即兩點之間的直線距離,對距離的測量還有曼哈頓距離,名氏距離,切比雪夫距離等,這些距離有著各自的意義和計算方式,可根據問題的性質選擇.此外,如果各個維度因素真的對結果的影響是不同的,你也可以自己構造距離的計算方式.

以下附KNN演算法在python語言上的實現程式碼及對MNIST資料集使用KNN演算法的結果,MNIST資料集是一個手寫數字的灰度值矩陣的資料集,其資料為28*28的灰度值矩陣,標籤為0-9的是個數字.

# -*- coding: utf-8 -*-
# @Time    : 2018/10/27 16:45
# @Author  : Zy
# @Software: PyCharm

import numpy as np
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
xdata = mnist.train.images
ydata = mnist.train.labels
xd = mnist.test.images
yd = mnist.test.labels
xb, yb = mnist.test.next_batch(50)


# 該方法用於統計陣列中出現的所有元素及其數量
def element_num(narry):
    tu = sorted([(np.sum(narry == i), i) for i in set(narry.flat)])
    return tu


# 該方法用於獲取numpy陣列中出現次數最多的元素及其數量
# 該方法返回一個元組,元組第一位表示數量,第二位表示元素
def get_max_num_element(narry):
    tu = element_num(narry)
    return tu[-1]

class KNN(object):
    # sdata和slabel表示訓練資料及訓練標籤
    # adata表示帶預測資料
    # K_num表示k值,
    # 即取前k項距離最大的訓練資料的標籤
    # 作為待預測資料標籤的參考條件
    def __init__(self, sdata, slabel, adata=None, K_num=20):
        self.source_data = sdata
        self.source_label = slabel
        self.aim_data = adata
        self.K_num = K_num

    # 獲取標記資料集合


    # 獲取待預測資料
    def set_aim_data(self, data):
        self.aim_data = data

    # 資料預處理 如歸一化 本例子中不需要這個步驟

    # 計算距離陣列
    def distance(self):
        sdata = self.source_data
        slabel = self.source_label
        adata = self.aim_data
        adistance = np.empty((len(adata), len(sdata)))
        for ai in range(len(adata)):
            aitem = adata[ai]
            for si in range(len(sdata)):
                sitem = sdata[si]
                adistance[ai][si] \
                    = (np.square(aitem - sitem)) \
                    .sum()
        return adistance, slabel

    # 排序,少數服從多數分類
    def prediction(self):
        dis, label = knn.distance()
        ydi = 0
        pre_labels = np.array([])
        for i in dis:
            ind = np.argsort(i)
            order_dis_list = i[ind[0:self.K_num]]
            order_label_list = label[ind[self.K_num]]
            pre_label = get_max_num_element(order_label_list)[1]
            pre_labels = np.append(pre_labels, pre_label)
            ydi += 1
        return pre_labels


# 給出實驗結果
knn = KNN(xdata, ydata)
knn.set_aim_data(xd[0:10])
pre_labels = knn.prediction()
real_labels = yd[0:10]
print(pre_labels)
print(real_labels)