連載的上一篇文章,小魚為大家介紹了 KNN 算法的原理:選擇 K 個(gè)跟自己距離最近的樣本進(jìn)行預(yù)測(cè),KNN 可以用于分類也可以用于回歸。并且,我們也是用單特征來簡單地實(shí)現(xiàn)了一下 KNN 算法。
本節(jié),小魚將和大家一起來評(píng)估一下我們上一節(jié)的模型,內(nèi)容包含數(shù)據(jù)集劃分、均方根誤差(RMSE)。
數(shù)據(jù)集劃分
學(xué)校為了評(píng)估學(xué)生這個(gè)學(xué)期的學(xué)習(xí)成果,我們知道學(xué)校會(huì)進(jìn)行期末考試。為了讓學(xué)生更好的掌握所學(xué)知識(shí),不斷優(yōu)化自身,平時(shí)老師呢也會(huì)經(jīng)常性地安排小測(cè)驗(yàn):隨堂測(cè)驗(yàn)、月考、期中考試等等。
這個(gè)時(shí)候呢,小魚要問問大家:我們期末考試的試題是從平時(shí)的小測(cè)驗(yàn)中直接選題來進(jìn)行考試好呢?還是出學(xué)生之前沒做過的試題好呢?哪個(gè)更能反應(yīng)學(xué)生的真實(shí)水平呢?
其實(shí)毋庸置疑啦~當(dāng)時(shí)是要出學(xué)生沒見過的考題了。小魚初中的時(shí)候,記得每次開學(xué)老師都會(huì)來場(chǎng)考試,考試的題目全部選自寒假作業(yè)原題,這個(gè)時(shí)候一些平時(shí)成績一般但認(rèn)真完成了假期作業(yè)的同學(xué)就會(huì)考得非常好。小魚不喜歡寫寒暑假作業(yè),每次這個(gè)考試都很凄慘。

故事講完啦,我們繼續(xù)來看模型評(píng)估!為了評(píng)估模型的好壞,我們需要將原始數(shù)據(jù)集劃分為訓(xùn)練集和測(cè)試集,訓(xùn)練集用于模型的訓(xùn)練,測(cè)試集用于模型的評(píng)估,二者之間不重疊。

下面,以租金預(yù)測(cè)的數(shù)據(jù)集為例,來實(shí)操一下。首先,讀取數(shù)據(jù)集并進(jìn)行洗牌操作:
import pandas as pd
features = ['accommodates','bedrooms','bathrooms','beds','price','minimum_nights','maximum_nights','number_of_reviews']
df = pd.read_csv('listings.csv')[features]
df = df.sample(frac=1, random_state=0)
df.head()

由于原始數(shù)據(jù)集中價(jià)格為字符串類型,還需要進(jìn)行一下數(shù)據(jù)預(yù)處理,將其轉(zhuǎn)換為 float 類型:
df.price = df.price.str.replace('\$|,','').astype(float)
df.head()

將原始數(shù)據(jù)集進(jìn)行拷貝,取前 3/4 的數(shù)據(jù)為訓(xùn)練集,剩下的 1/4 為測(cè)試集:
>> train_df = df.copy().iloc[:2792]
>> test_df = df.copy().iloc[2792:]
>> train_df.shape, test_df.shape
((2792, 8), (931, 8))
接下來,我們定義一個(gè)函數(shù),該函數(shù)實(shí)現(xiàn) KNN 模型,接收單個(gè)特征以及該特征的值,返回值為預(yù)測(cè)的租金:
def predict_price(my_value, feature):
train_df['distance'] = np.abs(train_df[feature] - my_value)
knn_5 = train_df.sort_values('distance').price.iloc[:5]
return knn_5.mean()
使用該價(jià)格預(yù)測(cè)函數(shù),對(duì)測(cè)試集中的樣本進(jìn)行價(jià)格預(yù)測(cè):
test_df['predicted_price'] = test_df.accommodates.apply(predict_price, feature='accommodates')
test_df[['price','predicted_price']].head()
注:Series 結(jié)構(gòu)提供的
apply方法可以對(duì) Series 中的每個(gè)元素應(yīng)用指定的函數(shù)。

這樣,我們就得到了測(cè)試集中所有樣本的預(yù)測(cè)價(jià)格和真實(shí)價(jià)格了。怎么比較預(yù)測(cè)值與真實(shí)值之間的差距呢?我們接下來看均方根誤差!
均方根誤差 RMSE (root mean squared error)
均方根誤差 RMSE 的計(jì)算公式為:

RMSE 計(jì)算的是真實(shí)值與預(yù)測(cè)值的誤差,因此 RMSE 越小,我們的模型就越好。下面,我們來計(jì)算一下當(dāng)預(yù)測(cè)結(jié)果的 RMSE:
>> test_df['squared_error'] = (test_df.price - test_df.predicted_price) ** 2
>> mse = test_df.squared_error.mean()
>> rmse = mse ** (1/2)
>> print("單變量 KNN 模型評(píng)估: RMSE=", rmse)
單變量KNN模型評(píng)估: RMSE= 116.48813348009145
我們也可以多計(jì)算幾個(gè)單特征對(duì)模型準(zhǔn)確性的影響:
for feature in ['accommodates','bedrooms','bathrooms', 'minimum_nights', 'maximum_nights']:
test_df['predict_price'] = test_df[feature].apply(predict_price, feature=feature)
test_df['squared_error'] = (test_df['price'] - test_df['predict_price']) ** 2
mse = test_df['squared_error'].mean()
rmse = mse ** (1/2)
print("FEATURE {} RMSE:{} ".format(feature, rmse))
模型評(píng)估結(jié)果:
FEATURE accommodates RMSE:116.48813348009145
FEATURE bedrooms RMSE:115.20485075268974
FEATURE bathrooms RMSE:115.5295378414374
FEATURE minimum_nights RMSE:128.88879360548913
FEATURE maximum_nights RMSE:141.15756201746808
從上數(shù)結(jié)果來看臥室的數(shù)量、洗手間的數(shù)量以及可容納人數(shù)預(yù)測(cè)的結(jié)果更為準(zhǔn)確。在實(shí)際的機(jī)器學(xué)習(xí)任務(wù)中,特征的選取是非常重要的,好的特征可以極大地提高模型預(yù)測(cè)的準(zhǔn)確性。下一節(jié),小魚就帶大家一起進(jìn)行多變量的 KNN 價(jià)格預(yù)測(cè),我們明天見!