KNN回歸算法


1.算法原理

1.分類和回歸

  分類模型和回歸模型本質一樣,分類模型是將回歸模型的輸出離散化。

  一般來說,回歸問題通常是用來預測一個值,如預測房價、未來的天氣情況等等,例如一個產品的實際價格為500元,通過回歸分析預測值為499元,我們認為這是一個比較好的回歸分析。回歸是對真實值的一種逼近預測。

  分類問題是用於將事物打上一個標簽,通常結果為離散值。例如判斷一幅圖片上的動物是一只貓還是一只狗。分類並沒有逼近的概念,最終正確結果只有一個,錯誤的就是錯誤的,不會有相近的概念。

簡言之:

  定量輸出稱為回歸,或者說是連續變量預測,預測明天的氣溫是多少度,這是一個回歸任務

  定性輸出稱為分類,或者說是離散變量預測,預測明天是陰、晴還是雨,就是一個分類任務

2.KNN回歸

  KNN算法不僅可以用於分類,還可以用於回歸。通過找出一個樣本的k個最近鄰居,將這些鄰居的某個(些)屬性的平均值賦給該樣本,就可以得到該樣本對應屬性的值。

3.原理

問題引入

  我有個3個卧室的房子,租多少錢呢?

  不知道的話,就去看看別人3個卧室的房子都租多少錢吧!

  其中,K代表我們的候選對象個數,也就是找和我房間數量最相近的K個房子的價格,做一定的處理后(例如平均),作為我們房子的出租價格。

那么,如何衡量和我的房子最相近呢?如何評估我們得到的出租價格的好壞呢?

K近鄰原理

假設我們的數據源中只有5條信息,現在我想針對我的房子(只有一個房間)來定一個價格。

在這里假設我們選擇的K=3,也就是選3個跟我最相近的房源。

再綜合考慮這三個只有房子的價格,就得到了我的房子大概能值多錢啦!

如何才能知道哪些數據樣本跟我最相近呢?

歐氏距離公式:

其中p1到pn是一條數據的所有特征信息,q1到qn是另一條數據的所有特征信息。

4.舉例說明

假設我們的房子有3個房間

單變量下的距離定義簡化為:

讀取數據:

 
import pandas as pd

features = ['accommodates','bedrooms','bathrooms','beds','price','minimum_nights','maximum_nights','number_of_reviews']

dc_listings = pd.read_csv('listings.csv')

dc_listings = dc_listings[features]
print(dc_listings.shape)

dc_listings.head()
 

 

僅取以單個指標accommodates的個數來計算每個樣本到我們的距離:

代碼實現如下:

 
import numpy as np

# 定義我們的accomodates個數為3
our_acc_value = 3

# 新增一列distance,記錄每個樣本到3的距離
# np.abs函數用於計算絕對值
# 通過dc_listings.accommodates取出accommodates列的所有數據
# 可通過dc_listings.accommodates取值
# 也可通過字典的形式取值,dc_listings['accommodates']或dc_listings.get('accommodates')
dc_listings['distance'] = np.abs(dc_listings.accommodates - our_acc_value)

# 取出結果列distance
# value_counts()統計個數
# sort_index()按照索引distance排序
dc_listings.distance.value_counts().sort_index()
 

 

輸出結果:

 
0      461
1     2294
2      503
3      279
4       35
5       73
6       17
7       22
8        7
9       12
10       2
11       4
12       6
13       8
Name: distance, dtype: int64
 

 

  從結果中可以看出,以房間個數來衡量的話,同樣有3個房間的樣本一共461個

假設K=5,即取距離我們最近的五個樣本的價格取平均值,作為我們的出租價格。

 

 
# 使用sample函數,進行洗牌操作,將數據隨機打亂
# farc = 1 表示選擇100%的數據
# random_stare-0 表示設置隨機種子
dc_listings = dc_listings.sample(frac=1, random_state=0)

# 以instance為索引,進行升序排序
dc_listings = dc_listings.sort_values('distance')

# 取出價格列
price = dc_listings.price
# 對價格進行一定的處理,去掉$和,兩個符號,並轉化為float類型dc_listings['price'] = price.str.replace(r'\$|,', '').astype(float)

# 取K=5時,預測的出租價格
pre_price = dc_listings.price.iloc[:5].mean()
print(pre_price)
 

  得到了平均價格,也就是我們的房子大致的價格了。這個就是KNN回歸預測的整個過程。這里僅做一個例子,旨在說明KNN回歸的過程。

實際情況下,我們有很多個樣本,而且每個樣本不僅僅只有一個accomodate屬性。

2.基於單變量預測價格

實際情況下,一般將樣本划分為兩部分,一部分用作訓練(稱之為訓練集)用於訓練模型;一部分用作測試(稱之為測試集)對訓練出的模型進行評估。

 

繼續上面的數據進行演示,此時的數據長這個樣子:

輸入以下代碼,構造訓練集和測試集。

 
# 刪除distance列, axis=1表示按列刪除
dc_listings.drop('distance', axis=1)

# 訓練集,取前2792行作為訓練集
df_train = dc_listings.copy().iloc[:2792]
# 測試集,剩下的作為測試集
df_test = dc_listings.copy().iloc[2792:]
 

 

 僅考慮accommodates一個衡量指標下,對測試集中的所有樣本的價格進行預測,並與真實值進行對比。

 
def predict_price(new_listing_value, fea_col):
    """
    對房子出租價格進行預測,K取值5
    new_listing_value :  
    fea_col: 特征列,參考的特征
    """
    df_temp = df_train  # 導入訓練集數據
    
    # 在df_temp中添加一列,用於記錄距離
    df_temp['distance'] = np.abs(df_train[fea_col] - new_listing_value)
    # 按照distance索引進行升序排序
    df_temp = df_temp.sort_values('distance')
    # 取距離最近的前5行數據
    KNN_5 = df_temp.price.iloc[:5]
    # 取平均值作為預測值
    predict_price = KNN_5.mean()
    
    return predict_price
 

 

例如,上文中的預測accomodates為3的價格,那么fea_col = 'accomodates', new_listing_value=3;

那么,對於測試集中的樣本,accomodates屬性值(即new_listing_value)是不同的,使用apply函數,循環調用預測函數,並得到測試樣本中每個樣本的預測值。

 
# 取測試集中,每個樣本的accomodates值
# 應用predice_price函數,得到預測值
# 並新增一列predict_price記錄預測值
df_test['predict_price'] = df_test.accommodates.apply(predict_price, fea_col='accommodates')

# 取出預測值與真實值這兩列,對比
df_test[['predict_price', 'price']]
 

 

部分輸出結果:

那么,如何評判預測結果的好壞呢?

3.誤差評估

一般采用均方根誤差(root mean squared error,RMSE)作為誤差評估指標,誤差越大,說明預測效果越差。

因此,基於上文內容,計算測試集總的均方根誤差:

 
# 求出預測值與真實值差值的平方
df_test['squared_error'] = (df_test.predict_price - df_test.price)**2
# 求差值均值
mse = df_test.squared_error.mean()
# 求均方差誤差
rmse = mse ** (1/2)

print(rmse)
 

 

如此,就得到了對於一個變量的模型評估分。

結果輸出:

顯然,此誤差值較大,可見僅僅用一個指標,結果不一定靠譜。

那么,如何能降低這個預測的誤差呢?顯然需要利用多個指標對房子的價格進行評估。但是不同指標的單位不同,且相差較大。

因此,考慮將數據進行一定的處理,將所有數據都處理成不受單位影響的指標。 

4.數據標准化與歸一化

一般將數據進行標准化或歸一化處理,使其不受單位影響。

z-score標准化

z-score標准化是將數據按比例縮放,使之落入一個特定區間。 要求:均值 μ = 0 ,σ = 1

標准差公式:

z-score標准化轉換公式:

歸一化

歸一化:把數變為(0,1)之間的小數

 歸一化公式:

這里利用sklearn的MinMaxScaler和StandardScaler兩個類,對所有數據進行歸一化處理。

 
import pandas as pd
from sklearn import preprocessing
from sklearn.preprocessing import MinMaxScaler
from sklearn.preprocessing import StandardScaler

# 讀取數據
features = ['accommodates','bedrooms','bathrooms','beds','price','minimum_nights','maximum_nights','number_of_reviews']
dc_listings = pd.read_csv(r'D:\codes_jupyter\數據分析_learning\課件\05_K近鄰\listings.csv', engine='python')
dc_listings = dc_listings[features]

# 對price列進行一定的處理,使其變成float型
dc_listings['price'] = dc_listings.price.str.replace(r'\$|,', '').astype(float)

# 對缺失值進行處理,刪除有缺失值的數據
dc_listings = dc_listings.dropna()

# 歸一化
dc_listings[features] = MinMaxScaler().fit_transform(dc_listings)

# 標准化
# dc_listings[features] = StandardScaler().fit_transform(dc_listings)

print(dc_listings.shape)
dc_listings.head()
 

 

輸出結果如下:

得到標准化的數據后,就可以利用多個指標對房租價格進行預測了。

例如,增加一個Bathrooms指標,對房租價格進行預測。

兩個指標計算距離,相當於計算平面上兩個點的距離:

也可以利用scipy中的已有工具對距離進行計算。

 
from scipy.spatial import distance

first_listing = dc_listings.iloc[0][['accommodates', 'bathrooms']]
second_listing = dc_listings.iloc[20][['accommodates', 'bathrooms']]

# 利用euclidean函數計算兩個點間的距離
# 點可以是n維,但只能計算兩個點的距離
# 結果返回一個數值
distance = distance.euclidean(first_listing, second_listing)

distance
 

結果輸出:

5.多變量的KNN模型

這里選取所有特征進行預測,並對預測結果進行評估。

 
import pandas as pd
from sklearn import preprocessing
from sklearn.preprocessing import MinMaxScaler
from scipy.spatial import distance

# 數據讀取
features = ['accommodates','bedrooms','bathrooms','beds','price','minimum_nights','maximum_nights','number_of_reviews']
dc_listings = pd.read_csv(r'D:\codes_jupyter\數據分析_learning\課件\05_K近鄰\listings.csv', engine='python')
dc_listings = dc_listings[features]

# 數據預處理
dc_listings['price'] = dc_listings.price.str.replace(r'\$|,', '').astype(float) # 對price列進行一定的處理,使其變成float型
origin_listings = dc_listings.dropna()  # 對缺失值進行處理,刪除有缺失值的數據
norm_dc_listings = origin_listings.copy()
norm_dc_listings[features] = MinMaxScaler().fit_transform(norm_dc_listings)  # 歸一化

# 構造訓練集和測試集
train_set = norm_dc_listings[:2792]
test_set = norm_dc_listings[2792:]

# 價格預測函數
def predict_price_multivariate(new_listing_value, features_cols):
    
    temp = train_set
    
    # distance.cdist是計算兩個集合的距離
    # [new_listing_value[features]是使其滿足array結構
    temp['distance'] = distance.cdist(temp[features], [new_listing_value[features_cols]])
    
    # 以distance以索引,從小到大排序
    temp = temp.sort_values('distance')
    # 取價格的前5行
    KNN_5 = temp.price.iloc[:5]
    
    # 取平均值進行預測
    predict_price = KNN_5.mean()
    
    return predict_price

# 利用測試集進行預測
test_set['predict_price'] = test_set[features].apply(predict_price_multivariate, features_cols=features, axis=1)

# 數據結果預處理,化簡為標准化數據,因此預測出來的值也是標准化后的值
# 因此,需要將預測值進行反歸一化處理,轉化為真實值
# 反歸一化處理
scaler = MinMaxScaler()
norm_price = scaler.fit_transform(origin_listings['price'].values.reshape(-1, 1))
orig_price = scaler.inverse_transform(test_set['predict_price'].values.reshape(-1, 1))

# 對預測結果進行評估
test_set['square_error'] = (orig_price.ravel() - origin_listings['price'][2792:]) ** 2
mse = test_set['square_error'].mean()
rmse = mse ** (1/2)
print(rmse)
 

 

結果輸出如下:

6.KNN模型的sklearn實現

 

 
import pandas as pd
from sklearn import preprocessing
from sklearn.preprocessing import MinMaxScaler
from sklearn.neighbors import KNeighborsRegressor
from sklearn.metrics import mean_squared_error

# 數據讀取
features = ['accommodates','bedrooms','bathrooms','beds','price','minimum_nights','maximum_nights','number_of_reviews']
dc_listings = pd.read_csv(r'D:\codes_jupyter\數據分析_learning\課件\05_K近鄰\listings.csv', engine='python')
dc_listings = dc_listings[features]

# 數據預處理
dc_listings['price'] = dc_listings.price.str.replace(r'\$|,', '').astype(float) # 對price列進行一定的處理,使其變成float型
origin_listings = dc_listings.dropna()  # 對缺失值進行處理,刪除有缺失值的數據
norm_dc_listings = origin_listings.copy()
norm_dc_listings[features] = MinMaxScaler().fit_transform(norm_dc_listings)  # 歸一化

# 構造訓練集和測試集
train_set = norm_dc_listings[:2792]
test_set = norm_dc_listings[2792:]

# 實例化一個KNN回歸模型並制定K為14
KNN = KNeighborsRegressor(n_neighbors = 14)  # 默認的K為15
KNN.fit(train_set[features], train_set['price'])

# 預測
prediction = KNN.predict(test_set[features])
# 評估
mean_squared_error(test_set['price'], prediction) ** 0.5
 

輸出結果如下:

這里利用數據歸一化,求出的rmse為上述值。將MinMaxScaler()改為StandardScaler(),利用標准化后的數據求解得到的rmse為:

 

摘自:https://www.cnblogs.com/pythoner6833/p/9296035.html


免責聲明!

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



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