【問題3】:Kaggle練習題《房價預測》----分別採用的嶺迴歸,隨機森林,bagging模型,AdaBoost,XgBoost等。

Shaw_Road發表於2019-02-13

第一步:匯入基本的模組, 並且載入資料。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# index_col=0  將第0列作為行索引
train_df = pd.read_csv('data/home_price/train.csv', index_col=0)
test_df = pd.read_csv('data/home_price/test.csv', index_col=0)
# print(train_df.head())
# print(train_df.shape)

   下面是讀取出來的資料       資料集下載地址:https://www.kaggle.com/c/house-prices-advanced-regression-techniques

MSSubClass MSZoning LotFrontage LotArea Street Alley LotShape LandContour Utilities LotConfig ... PoolArea PoolQC Fence MiscFeature MiscVal MoSold YrSold SaleType SaleCondition SalePrice
Id                                          
1 60 RL 65.0 8450 Pave NaN Reg Lvl AllPub Inside ... 0 NaN NaN NaN 0 2 2008 WD Normal 208500
2 20 RL 80.0 9600 Pave NaN Reg Lvl AllPub FR2 ... 0 NaN NaN NaN 0 5 2007 WD Normal 181500
3 60 RL 68.0 11250 Pave NaN IR1 Lvl AllPub Inside ... 0 NaN NaN NaN 0 9 2008 WD Normal 223500
4 70 RL 60.0 9550 Pave NaN IR1 Lvl AllPub Corner ... 0 NaN NaN NaN 0 2 2006 WD Abnorml 140000
5 60 RL 84.0 14260 Pave NaN IR1 Lvl AllPub FR2 ... 0 NaN NaN NaN 0 12 2008 WD Normal 250000

 5 rows × 80 columns

      kaggle平臺提供了兩張表,一張是帶標籤的資料(訓練資料), 一張是不帶標籤的資料(測試資料)。接下來,我想做的就是將帶有標籤資料的標籤列刪除,它也就變成不帶標籤的資料。在將測試資料與其進行合併。這麼做主要是為了統一進行資料預處理的時候更加方便。等所有的需要的預處理進行完之後,我們再把他們分隔開。

第二步:合併資料

prices = pd.DataFrame({'price': train_df['SalePrice'], 'log(price+1)': np.log1p(train_df['SalePrice'])})
prices.hist()   # 畫圖 看一下標籤是否平滑
plt.show()

y_train = np.log1p(train_df.pop('SalePrice'))  # 將原來的標籤刪除 剩下log(price+1)列的資料
all_df = pd.concat((train_df, test_df), axis=0) # 將train_df, test_df合併

     我們這裡除了將標籤取下來,還構造了一個新列log(price+1) 這樣主要是為了不讓資料傾斜。  也就是讓資料儘可能服從高斯分佈。log1p(x) 就是log(x+1)。  這裡我們將資料進行了平滑,那麼按“怎麼來的怎麼去”原則,最後的預測結果就需要expm1()。 也就是將預測結果變為真實的房價數。   log1p反運算就是expm1()

第三步:特徵工程(處理缺失值等)

# 有些資料的取值只有四五,或者可數個。這類資料我們轉為one_hot編碼

# 發現MSSubClass值應該是分類值
print(all_df['MSSubClass'].dtypes)  # int64
all_df['MSSubClass'] = all_df['MSSubClass'].astype(str)
print(all_df['MSSubClass'].value_counts())
# 我們將category的變數轉變為numerical表達形式
# 當我們用numerical來表達categorical的時候,要注意,數字本身有大小。
# 不能亂指定大小,我們採用one_hot編碼
# pandas自帶的get_dummies方法可以一鍵做到one_hot
print(pd.get_dummies(all_df['MSSubClass'], prefix='MSSubClass').head())
# 此刻MSSubClass被我們分成了12個column,每一個代表一個category。是就是1,不是就是0。

# 同理,我們把所有的category資料都轉化為One_hot
all_dummy_df = pd.get_dummies(all_df)


# 缺失值處理

# 統計每列缺失值情況
print(all_dummy_df.isnull().sum().sort_values(ascending=False).head())
# 可以看到,缺失最多的column是LotFrontage
# 處理這些缺失的資訊,得靠好好審題。一般來說,資料集的描述裡會寫的很清楚,這些缺失都代表著什麼。當然,如果實在沒有的話,也只能靠自己的『想當然』。。
# 在這裡,我們用平均值來填滿這些空缺

# 我們用均值填充
mean_cols = all_dummy_df.mean()
all_dummy_df = all_dummy_df.fillna(mean_cols)

# 再檢查一下是否有缺失值
print(all_dummy_df.isnull().sum().sum())


# 標準化數字型資料

# 標準化numerical資料¶
# 這一步並不是必要,但是得看你想要用的分類器是什麼。一般來說,regression的分類器都比較傲嬌,最好是把源資料給放在一個標準分佈內。不要讓資料間的差距太大。
# 這裡,我們當然不需要把One-Hot的那些0/1資料給標準化。我們的目標應該是那些本來就是numerical的資料:

# 先找出數字型資料
numeric_cols = all_df.columns[all_df.dtypes != 'object']
# print(numeric_cols)
# 對其標準化
numeric_col_mean = all_dummy_df.loc[:, numeric_cols].mean()
numeric_col_std = all_dummy_df.loc[:, numeric_cols].std()
all_dummy_df.loc[:, numeric_cols] = (all_dummy_df.loc[:, numeric_cols]-numeric_col_mean) / numeric_col_std

# 將合併的資料此時進行拆分  分為訓練資料和測試資料
dummy_train_df = all_dummy_df.loc[train_df.index]
dummy_test_df = all_dummy_df.loc[test_df.index]
   這一步我們分別進行了:將訓練資料和測試資料進行合併,主要是為了方便進行統一處理; 將category型的資料轉化為one_hot編碼; 對缺失值進行填充; 標準化資料; 將合併的資料又分開。

第四步:模型的建立,預測

  模型1:嶺迴歸

from sklearn.linear_model import Ridge
from sklearn.model_selection import cross_val_score

X_train = dummy_train_df.values
X_test = dummy_test_df.values

alphas = np.logspace(-3, 2, 50)
test_scores = []
for alpha in alphas:
    clf = Ridge(alpha)
    test_score = np.sqrt(-cross_val_score(clf, X_train, y_train, cv=10, scoring='neg_mean_squared_error'))
    test_scores.append(np.mean(test_score))

# 看那個alpha下 模型預測的更好
plt.plot(alphas, test_scores)
plt.title('Alpha vs CV Error')
plt.show()

  

        根據影象發現alpha值為15左右的時候,模型最佳。 均方誤差值為:0.135左右

模型二:隨機森林(基本模型我們設為嶺迴歸。一般預設為迴歸樹)

from sklearn.ensemble import RandomForestRegressor

max_features = [0.1, 0.3, 0.5, 0.7, 0.9, 0.99]
test_scores = []
for max_feat in max_features:
    clf = RandomForestRegressor(n_estimators=200, max_features=max_feat)
    test_score = np.sqrt(-cross_val_score(clf, X_train, y_train, cv=5, scoring='neg_mean_squared_error'))
    test_scores.append(np.mean(test_score))
plt.plot(max_features, test_scores)
plt.title('Max Features vs CV Error')
plt.show()

        max_features 代表的劃分是考慮的最大特徵數,可以是大於1的具體數字, 代表考慮多少個特徵。 如果是小於1的數字,代表的是特徵的百分之多少。

     從上圖中我們可以看到當max_feature為0.3左右的時候,均方誤差最小。大概也是0.135

模型三:Stacking整合 (用stacking的思維來汲取兩種或多種模型的優點)

# 我們用一個Stacking的思維來汲取兩種或多種模型的優點
ridge = Ridge(alpha=15)
rf = RandomForestRegressor(n_estimators=500, max_features=0.3)

ridge.fit(X_train, y_train)
rf.fit(X_train, y_train)

# 上面提到了,因為最前面我們給label做了個log(1+x), 於是這裡我們需要把predit的值給exp回去,並且減掉那個"1"
# 所以就是我們的expm1()函式。

y_ridge = np.expm1(ridge.predict(X_test))
y_rf = np.expm1(rf.predict(X_test))

y_final = (y_ridge + y_rf) / 2
submission_df = pd.DataFrame(data={'ID': test_df.index, 'SalePrice': y_final})
# print(submission_df)

   submission_df 就是我們這場比賽需要提交的結果。也就是測試樣本的預測值。

模型四: bagging模型

from sklearn.ensemble import BaggingRegressor
from sklearn.model_selection import cross_val_score

params = [1, 10, 15, 20, 25, 30, 40]
test_scores = []
for param in params:
    clf = BaggingRegressor(n_estimators=param, base_estimator=ridge)  # 基模型為嶺迴歸
    test_score = np.sqrt(-cross_val_score(clf, X_train, y_train, cv=10, scoring='neg_mean_squared_error'))
    test_scores.append(np.mean(test_score))
plt.plot(params, test_scores)
plt.title('n_estimator vs CV Error')
plt.show()

  bagging就是把多個小分類器放在一起,每個train隨機選取一部分,然後把他們的最終結果綜合起來(投票制)

     分類器個數為10個的時候  均方誤差最小,大概為0.132。 比前面的幾種模型好

 模型五:Adaboost

from sklearn.ensemble import AdaBoostRegressor

params = [10, 15, 20, 25, 30, 35, 40, 45, 50]
test_scores = []
for param in params:
    clf = AdaBoostRegressor(n_estimators=param, base_estimator=ridge)
    test_score = np.sqrt(-cross_val_score(clf, X_train, y_train, cv=10, scoring='neg_mean_squared_error'))
    test_scores.append(np.mean(test_score))

plt.plot(params, test_scores)
plt.title("n_estimator vs CV Error")

 這裡的基本分類器依舊是嶺迴歸  ,我們找的還是基分類器個數與均方誤差的關係

Adaboos+Ridge, 用35個弱分類器的情況下,均方誤差能降到0.141左右。。可以再增加基分類器個數 看影象的走勢

模型六:Kaggle神器:XgBoost  

from xgboost import XGBRegressor

# 用sklearn自帶的cross validation方法來測試模型
params = [1, 2, 3, 4, 5, 6]
test_scores = []
for param in params:
    clf = XGBRegressor(max_depth=param)
    test_score = np.sqrt(-cross_val_score(clf, X_train, y_train, cv=10, scoring='neg_mean_squared_error'))
    test_scores.append(np.mean(test_score))
plt.plot(params, test_scores)
plt.title('max_depth vs CV Error')
plt.show()

 

驚了,深度為5的時候,錯誤率縮小到0.127。。這就是為什麼,浮躁的競賽圈,人人都在用XGBoost

 

相關文章