分類算法之k-近鄰算法(KNN)


一、k-近鄰算法概述

 1、什么是k-近鄰算法

如果一個樣本在特征空間中的k個最相似(即特征空間中最鄰近)的樣本中的大多數屬於某一個類別,則該樣本也屬於這個類別。

2、歐式距離

兩個樣本的距離可以通過如下公式計算,又叫歐式距離。比方說計算a(a1,a2,a3),b(b1,b2,b3)樣本之間的距離:

\[\sqrt {{{\left( {{a_1} - {b_1}} \right)}^2} + {{({a_2} - {b_2})}^2} + ({a_3} - {b_3})} \]

3、實例

 我們可以根據一部電影中的某些特征來判斷該電影屬於什么類別:

 我們可以計算未知電影與已知電影的歐式距離,從而判斷類別:

 按照歐式距離的計算公式計算,比如:

\[\sqrt {{{\left( {18 - 3} \right)}^2} + {{(90 - 104)}^2}}  = 20.5\]

根據距離的遠近,從而判斷未知樣本與哪個類別更近,就可以判斷未知樣本的類別。

二、案例

(一)k-近鄰算法API

1、sklearn.neighbors.KNeighborsClassifier(n_neighbors=5,algorithm='auto')

  • n_neighbors:int,可選(默認= 5),k_neighbors查詢默認使用的鄰居數
  • algorithm:{‘auto’,‘ball_tree’,‘kd_tree’,‘brute’},可選用於計算最近鄰居的算法。‘ball_tree’將會使用 BallTree;‘kd_tree’將使用 KDTree;‘auto’將嘗試根據傳遞給fit方法的值來決定最合適的算法。 (不同實現方式影響效率)

(二)k-近鄰算法實例(預測入住位置)

1、實例說明

  可進入https://www.kaggle.com/c/facebook-v-predicting-check-ins/data查看詳情,Facebook創建了一個人工世界,由10萬乘10公里的正方形中的100,000多個地方組成。對於給定的一組坐標,您的任務是返回最可能的位置的排名列表。

文件說明:

  • train.csv,test.csv 

          row_id:簽到事件的ID
          xy:坐標
          精度:位置精度
          時間:時間戳
         place_id:入住位置ID,這是您要預測的目標

其數據形式如下:

   row_id       x       y  accuracy    time    place_id
0       0  0.7941  9.0809        54  470702  8523065625
1       1  5.9567  4.7968        13  186555  1757726713
2       2  8.3078  7.0407        74  322648  1137537235
3       3  7.3665  2.5165        65  704587  6567393236
4       4  4.0961  1.1307        31  472130  7440663949
5       5  3.8099  1.9586        75  178065  6289802927
6       6  6.3336  4.3720        13  666829  9931249544
7       7  5.7409  6.7697        85  369002  5662813655
8       8  4.3114  6.9410         3  166384  8471780938
9       9  6.3414  0.0758        65  400060  1253803156
...

2、實例解析

顯然,xy坐標、位置精度、時間戳是特征值,入住位置是目標值;那么這就是一個分類問題。這個數據量是比較大的,我們可以對其進行如下處理:

  • 縮小數據范圍(0<x<2,0<y<2)
  • 將時間戳轉換成年、月、日、時等新的特征
  • 刪除少於指定位置的簽到人數位置刪除

然后,再進行特征選取與目標值選取,以及做下面的操作。

3、實現 

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
import numpy as np


def knn():
    """
    近鄰算法:預測入住位置
    :return:
    """
    # 讀取訓練集數據
    df = pd.read_csv("./data/k-近鄰算法數據/train.csv")
    # 讀取前5行數據
    # print(df.head(5))

    """ 一、進行數據處理:
    1、縮小數據范圍
    2、時間戳處理
    3、刪除少於指定位置的簽到人數位置刪除
    """
    # 1、縮小數據范圍
    df = df.query("x>0 & x<1.2 & y>0 & y<1.23")    # 2、時間戳處理
    df_time = pd.to_datetime(df["time"], unit='s')
        #把日期格式處理成字典格式
    time_dict = pd.DatetimeIndex(df_time)
    # 構造時間特征
    df["day"] = time_dict.day
    df["hour"] = time_dict.hour
    df["weekday"] = time_dict.weekday
        # 刪除時間戳這一列
    df = df.drop(['time'], axis=1)
    # 3、刪除少於指定位置的簽到人數位置刪除
    place_count = df.groupby('place_id').count()
    # 過濾出少於指定位置的簽到人數位置,通過reset_index將索引轉成列進行操作
    pf = place_count[place_count["row_id"] > 3].reset_index()
    # 根據指定place_id進行過濾
    df = df[df['place_id'].isin(pf['place_id'])]

    """ 二、獲取特征值、目標值
    """
    # 1、獲取特征值
    x = df.drop(['place_id'], axis=1)

    # 2、獲取目標值
    y = df['place_id']

    """
    三、進行數據集分割,分成訓練集和測試集
    """
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.25)
""" 四、特征工程:標准化 """ sd = StandardScaler() # 對訓練集進行標准化 x_train = sd.fit_transform(x_train.astype(np.float64)) # 對測試集進行標准化 x_test = sd.transform(x_test.astype(np.float64))
""" 五、進行KNN算法預測 fit predict score """ knn = KNeighborsClassifier(n_neighbors=5) knn.fit(x_train, y_train) # 預測位置 y_predict = knn.predict(x_test) # print('預測的位置:',y_predict) # 准確率 predict_accurate = knn.score(x_test, y_test) print(predict_accurate) if __name__ == '__main__': knn()
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
import numpy as np


def knn():
    """
    近鄰算法:預測入住位置
    :return:
    """
    # 讀取訓練集數據
    df = pd.read_csv("./data/k-近鄰算法數據/train.csv")

    # 讀取前5行數據
    # print(df.head(5))

    """
    進行數據處理:
    1、縮小數據范圍
    2、時間戳處理
    3、刪除少於指定位置的簽到人數位置刪除
    """
    # 1、縮小數據范圍
    df = df.query("x>0 & x<1.2 & y>0 & y<1.23")
    #
    # 2、時間戳處理
    df_time = pd.to_datetime(df["time"], unit='s')
    # print(df_time)
    """
    132        1970-01-08 19:14:45
    142        1970-01-02 00:41:22
    ...
    """
    #   #把日期格式處理成字典格式
    time_dict = pd.DatetimeIndex(df_time)
    # print(time_dict)
    """
    DatetimeIndex(
    ['1970-01-08 19:14:45', '1970-01-02 00:41:22',
   '1970-01-07 06:32:23', '1970-01-02 18:59:24',...], 
    dtype='datetime64[ns]', name='time', length=417477, freq=None
    )
    """
    # 構造時間特征
    df["day"] = time_dict.day
    df["hour"] = time_dict.hour
    df["weekday"] = time_dict.weekday

    # 刪除時間戳這一列
    df = df.drop(['time'], axis=1)
    # print(df)
    """
                    row_id       x       y   ...     day  hour  weekday
    132            132  0.1902  0.1510   ...       8    19        3
    142            142  0.1318  0.4975   ...       2     0        4
    149            149  0.0179  0.2321   ...       7     6        2
    ...
    """
    # 3、刪除少於指定位置的簽到人數位置刪除
    place_count = df.groupby('place_id').count()
    # print(place_count)
    """
                    row_id    x    y  accuracy  day  hour  weekday
    place_id                                                  
    1000213704      22   22   22        22   22    22       22
    1000842315       6    6    6         6    6     6        6
    1002574526       1    1    1         1    1     1        1
    1002803051       1    1    1         1    1     1        1
    ...
    """
    # 過濾出少於指定位置的簽到人數位置,通過reset_index將索引轉成列進行操作
    pf = place_count[place_count["row_id"] > 3].reset_index()
    # print(pf)
    """
               place_id  row_id    x    y  accuracy  day  hour  weekday
    0     1000213704      22   22   22        22   22    22       22
    1     1000842315       6    6    6         6    6     6        6
    ...
    """
    # 根據指定place_id進行過濾
    df = df[df['place_id'].isin(pf['place_id'])]
    # print(df)

    """
    獲取特征值、目標值
    """
    # 1、獲取特征值
    x = df.drop(['place_id'], axis=1)

    # 2、獲取目標值
    y = df['place_id']

    """
    進行數據集分割,分成訓練集和測試集
    """
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.25)
    """
    特征工程:標准化
    """
    sd = StandardScaler()
    # 對訓練集進行標准化
    x_train = sd.fit_transform(x_train.astype(np.float64))
    # 對測試集進行標准化
    x_test = sd.transform(x_test.astype(np.float64))
    """
    進行KNN算法預測
    fit predict score
    """
    knn = KNeighborsClassifier(n_neighbors=5)
    knn.fit(x_train, y_train)
    # 預測位置
    y_predict = knn.predict(x_test)
    # print('預測的位置:',y_predict)
    """
    [2013736336 4137191191 5861856288 ... 4223174852 8114087113 7163230644]
    """
    # 准確率
    predict_accurate = knn.score(x_test, y_test)
    print(predict_accurate)


if __name__ == '__main__':
    knn()
完整輸出

可以看出,上面的實現的大致步驟是:獲取數據與處理數據-->獲取特征值與目標值-->進行數據集切割-->特征工程(標准化、降維等)-->算法預測

三、k-近鄰算法的優缺點 

1、優點

k-近鄰算法的優點很明顯,那就是簡單、易於理解、易於計算。

2、缺點

  • 內存開銷大

可以看到,計算兩個樣本的歐式距離,如果樣本數量較大,這樣系統的開銷比較大。

  • k值的選擇需慎重

  k值在上面的實例過程中體現在KNeighborsClassifier方法的n_neighbors參數,如果這個參數過小,易受到異常點的影響;如果參數過大,那么容易受k值數量的波動。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM