KNN算法的實現

K近鄰(KNN)算法簡介算法

  KNN是經過測量不一樣特徵值之間的距離進行分類。它的思路是:若是一個樣本在特徵空間中的k個最類似(即特徵空間中最鄰近)的樣本中的大多數屬於某一個類別,則該樣本也屬於這個類別,其中K一般是不大於20的整數。KNN算法中,所選擇的鄰居都是已經正確分類的對象。該方法在定類決策上只依據最鄰近的一個或者幾個樣本的類別來決定待分樣本所屬的類別。機器學習

     下面經過一個簡單的例子說明一下:以下圖,綠色圓要被決定賦予哪一個類,是紅色三角形仍是藍色四方形?若是K=3,因爲紅色三角形所佔比例爲2/3,綠色圓將被賦予紅色三角形那個類,若是K=5,因爲藍色四方形比例爲3/5,所以綠色圓被賦予藍色四方形類。ide

                      

由此也說明了KNN算法的結果很大程度取決於K的選擇。學習

     在KNN中,經過計算對象間距離來做爲各個對象之間的非類似性指標,避免了對象之間的匹配問題,在這裏距離通常使用歐氏距離或曼哈頓距離:測試

 

L1是曼哈頓距離:顧名思義,在曼哈頓街區要從一個十字路口開車到另外一個十字路口,駕駛距離顯然不是兩點間的直線距離。這個實際駕駛距離就是「曼哈頓距離」。曼哈頓距離也稱爲「城市街區距離」(City Block distance)。fetch

L2是咱們的歐氏距離:歐氏距離是最容易直觀理解的距離度量方法,咱們小學、初中和高中接觸到的兩個點在空間中的距離通常都是指歐氏距離。人工智能

 

咱們通常都經常使用歐氏距離。spa

 同時,KNN經過依據k個對象中佔優的類別進行決策,而不是單一的對象類別決策。這兩點就是KNN算法的優點。3d

   接下來對KNN算法的思想總結一下:就是在訓練集中數據和標籤已知的狀況下,輸入測試數據,將測試數據的特徵與訓練集中對應的特徵進行相互比較,找到訓練集中與之最爲類似的前K個數據,則該測試數據對應的類別就是K個數據中出現次數最多的那個分類。code

 其算法的描述爲:

1)計算測試數據與各個訓練數據之間的距離;

2)按照距離的遞增關係進行排序;

3)選取距離最小的K個點;

4)肯定前K個點所在類別的出現頻率;

5)返回前K個點中出現頻率最高的類別做爲測試數據的預測分類。

 概述:

KNN 算法自己簡單有效,它是一種 lazy-learning 算法。
分類器不須要使用訓練集進行訓練,訓練時間複雜度爲0。
KNN 分類的計算複雜度和訓練集中的文檔數目成正比,也就是說,若是訓練集中文檔總數爲n,那麼KNN 的分類時間複雜度爲O(n)。

   總結:

K 值的選擇,距離度量和分類決策規則是該算法的三個基本要素
問題:該算法在分類時有個主要的不足是,當樣本不平衡時,如一個類的樣本容量很大,而其餘類樣本容量很小時,有可能致使當輸入一個新樣本時,

   該樣本的K 個鄰居中大容量類的樣本佔多數。

解決:不一樣的樣本給予不一樣權重項。

    另外,KNN算法的超參數只有K一個,若是有其餘超參數,該怎麼肯定呢?事實上,模型的好壞跟參數的設定有很大的關係,當咱們須要調整參數時,最經常使用的額方式就是交叉驗證。

1.選取超參數的正確方法是:將原始訓練集分爲訓練集和驗證集,咱們在驗證集上嘗試不一樣的超參數,最後保留表現
最好那個
2.若是訓練數據量不夠,使用交叉驗證方法,它能幫助咱們在選取最優超參數的時候減小噪音。
3.一旦找到最優的超參數,就讓算法以該參數在測試集跑且只跑一次,並根據測試結果評價算法。

另外,KNN實現的時候注意點:

  1.預處理你的數據:對你數據中的特徵進行歸一化(normalize),讓其具備零平均值(zero mean)和單位方差(unit variance)。

  2.若是數據是高維數據,考慮使用降維方法,好比PCA

  3.將數據隨機分入訓練集和驗證集。按照通常規律,70%-90% 數據做爲訓練集

  4.在驗證集上調優,嘗試足夠多的k值,嘗試L1和L2兩種範數計算方式。

 

Python實現

# -*- coding: utf-8 -*-

"""
@Datetime: 2018/11/23
@Author: Zhang Yafei
"""
import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler


def maxminNormalization(data):
    """標準化
    data = (data - data.mean) / data.std
    :return data
    """
    mean_vals = data.mean(axis=0)
    std_val = data.std(axis=0)
    data = (data - mean_vals) / std_val
    return data


class KNN():
    def __init__(self,k=1):
        self.k = k

    def fit(self,x_train,y_train):
        self.x_train = x_train
        self.y_train = y_train

    def predict(self,x_test):
        dis_squar = (x_train - x_test)**2
        dis_squar_sum = dis_squar.sum(axis=1)
        distances = dis_squar_sum**0.5
        sortedIndics = distances.argsort()
        indices = sortedIndics[:self.k]
        labelCount = {}  # 存儲每一個label的出現次數
        for i in indices:
            label = self.y_train[i]
            labelCount[label] = labelCount.get(label, 0) + 1  # 次數加一
        # 排序方式一
        # sortedCount = list(zip(labelCount.values(),labelCount.keys()))
        # 對label出現的次數從大到小進行排序
        # sortedCount.sort()
        # return sortedCount[0][1]  # 返回出現次數最大的label
        # 排序方式二
        sortedCount = sorted(labelCount.items(), key=lambda k:k[1], reverse=True)
        return sortedCount[0][0]  # 返回出現次數最大的label


if __name__ == '__main__':
    # data = np.arange(24).reshape(4,6)
    train = pd.DataFrame({'age':[23,33,45],'income':[5000,12000,13000],'work':[1,2,3]})
    test = pd.DataFrame({'age':[22,30,40],'income':[9000,13000,14000],'work':[2,1,2]})
    x_train = maxminNormalization(train[['age','work']])
    y_train = train['income']
    x_test = maxminNormalization(test[['age','work']])
    y_test = test['income']
    knn = KNN()
    knn.fit(x_train,y_train)
    y_predict = knn.predict(x_test)
    print(y_predict)
底層代碼實現
from sklearn.datasets import load_iris,fetch_20newsgroups,load_boston
from sklearn.model_selection import train_test_split,GridSearchCV
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
import pandas as pd
from sklearn.metrics import classification_report

def knncls():
    """
    k-近鄰預測用戶簽到位置
    :return None
    """
    #讀取數據
    data = pd.read_csv('./data/FBlocation/train.csv')
    print(data.head(10))

    #處理數據
    #1.縮小數據,查詢數據篩選
    data = data.query('x>1.0 & x<1.25 & y>2.5 & y<2.75')

    #處理時間的數據
    time_value = pd.to_datetime(data.time,unit='s')
    print(time_value)

    #把日期格式轉化爲字典參數
    time_value = pd.DatetimeIndex(time_value)

    #構造一些特徵
    data.loc[:,'day'] = time_value.day
    data.loc[:,'hour'] = time_value.hour
    data.loc[:,'weekday'] = time_value.weekday

    #時間戳特徵刪除
    data.drop(['time'],axis=1)
    print(data)

    #把簽到數量少於n個目標位置刪除
    place_count = data.groupby('place_id').aggregate(np.count_nonzero)
    tf = place_count[place_count.row_id > 3].reset_index()
    data = data[data['place_id'].isin(tf.place_id)]

    # 2.4 清理無效特徵
    data = data.drop(['row_id'], axis=1)
    data = data.drop(['accuracy'], axis=1)

    #取出數據當中的特徵值和目標值
    y = data['place_id']
    x = data.drop(['place_id'],axis=1)

    #進行數據的分割 訓練集和測試集
    x_train,x_test,y_train,y_test = train_test_split(x,y,test_size=0.25)

    #特徵工程(標準化)
    std = StandardScaler()

    #對訓練集和測試機的特徵值進行標準化
    x_train = std.fit_transform(x_train)
    x_test = std.fit_transform(x_test)

    #特徵工程('標準化‘)

    #進行算法流程  #超參數
    knn = KNeighborsClassifier()
    # # fit,perdict,score
    # knn.fit(x_train,y_train)
    #
    # #獲得預測結果
    # y_predict = knn.predict(x_test)
    # print('預測的目標籤到位置爲:',y_predict)
    # # #獲得準確率
    # print('預測的準確率:',knn.score(x_test,y_test))

    #構造一些參數的值進行搜索
    param = {'n_neighbors':[3,5,10]}

    gc = GridSearchCV(knn,param_grid=param,cv=2)
    gc.fit(x_train,y_train)

    #預測準確率
    print('在測試集上的準確率:',gc.score(x_test,y_test))

    print('在交叉驗證中最好的結果:',gc.best_score_)
    print('選擇最好的模型是:',gc.best_estimator_)
    print('每一個超參數每次交叉驗證的結果:',gc.cv_results_)

    return None


def knncls_iris():
    li = load_iris()

    x_train,x_test,y_train,y_test = train_test_split(li.data,li.target,test_size=0.25)
    """
    knn = KNeighborsClassifier(n_neighbors=5)
    knn.fit(x_train,y_train)
    y_predict = knn.predict(x_test)
    print('鳶尾花種類預測值爲:',y_predict)
    print('準確率爲:',knn.score(x_test,y_test))
    print('每一個分類的精確率和召回率是:',classification_report(y_test,y_predict,target_names=li.target_names))
    """
    #模型選擇與調優
    knn = KNeighborsClassifier()
    param = {'n_neighbors':[3,5,10]}
    gc = GridSearchCV(knn,param_grid=param,cv=10)
    gc.fit(x_train,y_train)

    print('在測試集中的準確率:',gc.score(x_test,y_test))
    print('在交叉驗證中的最好的結果:',gc.best_score_)
    print('最好的模型是:',gc.best_estimator_)
    print('每一個超參數每次交叉驗證的結果:',gc.cv_results_)


if __name__ == '__main__':
    # knncls()
    knncls_iris()
sklearn實現

   KNN算法是一個經典的機器學習算法,同時也是最簡單的一個算法,應用它能夠解決一些很常見的分類問題。可是對於一個複雜的分類問題,好比圖像識別,它的效果就不是很好了,不過這也是機器學習算法和深度學習的分水嶺,深度學習能夠解決更復雜的分類問題,好比天然語言處理和計算機視覺中的圖像識別。因此,人工智能的快速發展得益於深度學習的突破,這也是將來額一個趨勢。咱們應該好好加油,咱們遇上了這個時代,使咱們的幸運,而可以投身於學習與這個世界上最前沿的技術,更是咱們的榮幸,咱們更應該好好加油!

相關文章
相關標籤/搜索