一、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值數量的波動。