送你一份使用k近鄰演算法實現迴歸的實用指南(附程式碼、連結)

資料派THU發表於2018-11-28

簡介

在我遇到的所有機器學習演算法中,KNN是最容易學會的。儘管它很簡單,但事實證明它在某些任務中非常有效(我們將在本文中看到)。

甚至於在某種情況下它是更好的選擇,畢竟它可以同時用於分類和迴歸問題!不過,它更常用來解決分類問題,很少看到在迴歸任務中使用KNN。提起KNN可以被用於迴歸任務,只是想說明和強調一下當目標變數是自然連續的時候,KNN也會同樣有效。

送你一份使用k近鄰演算法實現迴歸的實用指南(附程式碼、連結)

在本文中,我們將首先理解KNN演算法背後的直觀解釋,看看計算點之間距離的不同方法,然後在Big Mart Sales資料集上用Python實現KNN演算法。讓我們開始吧!

目錄

1. 一個簡單的例子來理解KNN背後的直觀解釋

2. KNN演算法是如何工作的?

3. 點之間距離的計算方法

4. 如何選擇k因子?

5. 應用在一個資料集上

6. 額外的資源

1. 一個簡單的例子來理解KNN背後的直觀解釋

讓我們從一個簡單的例子開始。考慮下表——它包括10個人的身高、年齡和體重(目標)。如圖所示,ID11的體重值丟失了。下面,我們需要根據這個人的身高和年齡來預測他的體重。

注意:該表中的資料不代表實際值。它只是作為一個例子來解釋這個概念

送你一份使用k近鄰演算法實現迴歸的實用指南(附程式碼、連結)

為了更清楚地瞭解這一點,下面是從上表得出的身高與年齡的關係圖:

送你一份使用k近鄰演算法實現迴歸的實用指南(附程式碼、連結)

在上圖中,y軸代表一個人的身高(以英尺為單位),x軸代表年齡(以年為單位)。這些點是根據ID值編號的。黃色點(ID 11)是我們的測試點。

如果讓你根據上圖來確定編號ID11這個人的體重,你的答案會是什麼?你可能會說,因為ID11更接近於點5和點1,所以這個人的體重應該與這些id相似,可能在72-77公斤之間(表中ID1和ID5的體重)。這是有道理的,但是演算法是如何預測這些值的呢?我們會在這篇文章裡找到答案。

2. KNN演算法是如何工作的?

如上所述,KNN可以用於分類和迴歸問題。該演算法使用“特徵相似度”來預測任何新資料點的值。這意味著,根據與訓練集中點的相似程度為新點賦值。從我們的示例中,我們知道ID11的高度和年齡與ID1和ID5相似,所以重量也大致相同。

如果這是一個分類問題,我們會把眾數作為最終的預測。在本例中,我們有兩個體重值——72和77。誰能猜到最終值是如何計算的?我們會將兩個取值的平均值作為最終的預測結果。

下面是這個演算法的具體步驟:

首先,計算新點與訓練集中每一個點的距離。

送你一份使用k近鄰演算法實現迴歸的實用指南(附程式碼、連結)

選出與新點最接近的K個點(根據距離)。在這個例子中,如果K=3,點1,5,6將會被選擇。在本文後續部分,我們會進一步探索選擇正確K值的方法。

送你一份使用k近鄰演算法實現迴歸的實用指南(附程式碼、連結)

將所有點的均值作為新點的最終預測值。在這個例子中,我們可以得到ID11的體重=(77+72+60)/3 = 69.66kg。

接下來的幾個小節裡,我們將討論以上三個步驟的具體細節。

3. 點之間距離的計算方法

第一步是計算新點與訓練集中每個點之間的距離。計算這個距離的方法有很多種,其中最常見的方法是歐幾里得法、曼哈頓法(連續的)和漢明距離法(離散的)。

  • 歐幾里得距離歐幾里得距離是新點(x)和現有點(y)之間的平方差之和的平方根。

  • 曼哈頓距離:這是實向量之間的距離,用它們差的絕對值之和來計算。

送你一份使用k近鄰演算法實現迴歸的實用指南(附程式碼、連結)

  • 漢明距離:用於離散變數,如果(x)和(y)值相等,距離D就等於0。否則D = 1。

送你一份使用k近鄰演算法實現迴歸的實用指南(附程式碼、連結)

一旦計算完成新觀測點與訓練集中點之間的距離,下一步就是挑選最近的點。點的數量由K值決定。

4. 如何選擇k因子?

第二步是確定K值。在為新觀測點賦值時,K值決定了需要參考的鄰點數量。

在我們的例子裡,對於K=3,最近的點就是ID1、ID5和ID6。

送你一份使用k近鄰演算法實現迴歸的實用指南(附程式碼、連結)

ID11的預測體重是:

ID11 = (77+72+60)/3

ID11 = 69.66 kg

對於k=5,最近的點是ID1、ID4、ID5、ID6和ID10。

送你一份使用k近鄰演算法實現迴歸的實用指南(附程式碼、連結)

送你一份使用k近鄰演算法實現迴歸的實用指南(附程式碼、連結)

ID11的預測體重是:

ID 11 =  (77+59+72+60+58)/5 

ID 11 = 65.2 kg

我們注意到,基於k值,最終結果往往會改變。那麼如何求出k的最優值呢?讓我們根據訓練集和驗證集的誤差計算來決定(畢竟,最小化誤差是我們的最終目標!)

請看下面的圖表,不同k值的訓練錯誤和驗證錯誤。

送你一份使用k近鄰演算法實現迴歸的實用指南(附程式碼、連結)

送你一份使用k近鄰演算法實現迴歸的實用指南(附程式碼、連結)

K值很低時(假設k = 1),該模型過擬合訓練資料,從而導致驗證集的錯誤率很高。另一方面,k取較大值時,模型在訓練集和驗證集上表現都很差。如果你仔細觀察,驗證誤差曲線的值在k = 9時達到最小值,此時k值是模型的最優值(根據不同的資料集會有所不同)。這條曲線被稱為“手肘曲線”(因為它的形狀很像手肘),通常用於確定k值。

我們還可以使用網格搜尋技術來確定k值。在下一個小節裡我們將會介紹它。

5. 應用在一個資料集上

讀到現在,你應當對演算法有一個清晰的理解。如果你還有問題,請給我們的公眾號留言,我們很樂意回答。現在,我們將在資料集中實現該演算法。我已經使用了Big Mart sales資料集來展示演算法實現的過程,大家可以從這個連結下載它。

  • 讀取檔案

import pandas as pd

df = pd.read_csv('train.csv')

df.head()

  • 計算缺失值

df.isnull().sum()

#missing values in Item_weight and Outlet_size needs to be imputed

mean = df['Item_Weight'].mean() #imputing item_weight with mean

df['Item_Weight'].fillna(mean, inplace =True)

mode = df['Outlet_Size'].mode() #imputing outlet size with mode

df['Outlet_Size'].fillna(mode[0], inplace =True)

  • 處理分類變數,刪除id列

df.drop(['Item_Identifier', 'Outlet_Identifier'], axis=1, inplace=True)

df = pd.get_dummies(df)

  • 建立訓練和測試集

from sklearn.model_selection import train_test_split

train , test = train_test_split(df, test_size = 0.3)

 

x_train = train.drop('Item_Outlet_Sales', axis=1)

y_train = train['Item_Outlet_Sales']

 

x_test = test.drop('Item_Outlet_Sales', axis = 1)

y_test = test['Item_Outlet_Sales']

  • 預處理——擴充套件特徵

from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler(feature_range=(0, 1))

 

x_train_scaled = scaler.fit_transform(x_train)

x_train = pd.DataFrame(x_train_scaled)

 

x_test_scaled = scaler.fit_transform(x_test)

x_test = pd.DataFrame(x_test_scaled)

  • 看看不同K值的錯誤率

#import required packages

from sklearn import neighbors

from sklearn.metrics import mean_squared_error

from math import sqrt

import matplotlib.pyplot as plt

%matplotlib inline

rmse_val = [] #to store rmse values for different k

for K in range(20):

    K = K+1

    model = neighbors.KNeighborsRegressor(n_neighbors = K)

 

    model.fit(x_train, y_train)  #fit the model

    pred=model.predict(x_test) #make prediction on test set

    error = sqrt(mean_squared_error(y_test,pred)) #calculate rmse

    rmse_val.append(error) #store rmse values

    print('RMSE value for k= ' , K , 'is:', error)

輸出:

RMSE value for k = 1 is: 1579.8352322344945

RMSE value for k = 2 is: 1362.7748806138618

RMSE value for k = 3 is: 1278.868577489459

RMSE value for k = 4 is: 1249.338516122638

RMSE value for k = 5 is: 1235.4514224035129

RMSE value for k = 6 is: 1233.2711649472913

RMSE value for k = 7 is: 1219.0633086651026

RMSE value for k = 8 is: 1222.244674933665

RMSE value for k = 9 is: 1219.5895059285074

RMSE value for k = 10 is: 1225.106137547365

RMSE value for k = 11 is: 1229.540283771085

RMSE value for k = 12 is: 1239.1504407152086

RMSE value for k = 13 is: 1242.3726040709887

RMSE value for k = 14 is: 1251.505810196545

RMSE value for k = 15 is: 1253.190119191363

RMSE value for k = 16 is: 1258.802262564038

RMSE value for k = 17 is: 1260.884931441893

RMSE value for k = 18 is: 1265.5133661294733

RMSE value for k = 19 is: 1269.619416217394

RMSE value for k = 20 is: 1272.10881411344

 

#plotting the rmse values against k values

curve = pd.DataFrame(rmse_val) #elbow curve

curve.plot()

送你一份使用k近鄰演算法實現迴歸的實用指南(附程式碼、連結)

正如我們所討論的,當k=1時,我們得到一個非常高的RMSE值。RMSE值隨著k值的增加而減小。在k= 7時,RMSE約為1219.06,並進一步增加k值。我們可以有把握地說,在k=7這種情況下,會得到最好的結果。

這些是使用訓練資料集得到的預測結果。現在讓我們預測測試資料集的值並提交。

  • 在測試集上得到預測值

#reading test and submission files

test = pd.read_csv('test.csv')

submission = pd.read_csv('SampleSubmission.csv')

submission['Item_Identifier'] = test['Item_Identifier']

submission['Outlet_Identifier'] = test['Outlet_Identifier']

#preprocessing test dataset

test.drop(['Item_Identifier', 'Outlet_Identifier'], axis=1, inplace=True)

test['Item_Weight'].fillna(mean, inplace =True)

test = pd.get_dummies(test)

test_scaled = scaler.fit_transform(test)

test = pd.DataFrame(test_scaled)

#predicting on the test set and creating submission file

predict = model.predict(test)

submission['Item_Outlet_Sales'] = predict

submission.to_csv('submit_file.csv',index=False)

提交這個檔案,我得到了一個RMSE 1279.5159651297。

  • 實現網格搜尋(Gridsearch)

為了確定k值,每次繪製手肘曲線是一個繁瑣的過程。我們可以簡單地使用gridsearch來找到最佳值。

from sklearn.model_selection import GridSearchCV

params = {'n_neighbors':[2,3,4,5,6,7,8,9]}

knn = neighbors.KNeighborsRegressor(

model = GridSearchCV(knn, params, cv=5)

model.fit(x_train,y_train)

model.best_params

輸出:

{'n_neighbors': 7}

6. 額外的資源

在本文中,我們介紹了KNN演算法的工作原理及其在Python中的實現。這是最基本也是最有效的機器學習技術之一。對於在R中實現KNN,您可以瀏覽這篇文章:使用R的KNN演算法。

在本文中,我們直接使用sklearn庫中的KNN模型。您還可以從頭實現KNN(我建議這樣做!),這篇文章將對此進行介紹:KNN simplified。

如果你認為你很瞭解KNN,並且對該技術有紮實的掌握,在這個MCQ小測驗中測試你的技能:關於KNN演算法的30個問題。祝你好運!

譯者附:註冊下載資料集流程

1、註冊一個賬號,然後註冊這個比賽

送你一份使用k近鄰演算法實現迴歸的實用指南(附程式碼、連結)

2、點選data

送你一份使用k近鄰演算法實現迴歸的實用指南(附程式碼、連結)

就可以愉快的下載執行並測試啦~

送你一份使用k近鄰演算法實現迴歸的實用指南(附程式碼、連結)

譯者的分數是:

送你一份使用k近鄰演算法實現迴歸的實用指南(附程式碼、連結)

歡迎留言自己的分數和心得~

原文標題:

A Practical Introduction to K-Nearest Neighbors Algorithm for Regression (with Python code)

原文連結: 

https://www.analyticsvidhya.com/blog/2018/08/k-nearest-neighbor-introduction-regression-python/

相關文章