選擇並訓練模型
至此,我們已明確了問題,並對數據進行了預處理。現在我們選擇並訓練一個機器學習模型。
在訓練集上訓練模型
這個過程相對來說較為簡單,我們首先訓練一個線性回歸模型:
from sklearn.linear_model import LinearRegression lin_reg = LinearRegression() lin_reg.fit(housing_prepared, housing_labels)
這樣就已完成了一個線性回歸模型的訓練,非常簡單。我們從訓練集里抓幾條數據驗證一下:
sample_data = housing.iloc[:5] sample_labels = housing_labels.iloc[:5] sample_data_prepared = full_pipeline.transform(sample_data) lin_reg.predict(sample_data_prepared) >array([210644.60459286, 317768.80697211, 210956.43331178, 59218.98886849, 189747.55849879]) list(sample_labels) >[286600.0, 340600.0, 196900.0, 46300.0, 254500.0]
可以看到預測的精准度並不高,我們可以看一下這個模型的RMSE(均方誤差)的大小:
from sklearn.metrics import mean_squared_error
housing_predictions = lin_reg.predict(housing_prepared) lin_mse = mean_squared_error(housing_labels, housing_predictions) lin_rmse = np.sqrt(lin_mse) >68628.19819848923
從均方誤差的大小來看,這並不是一個很好的結果,說明模型存在欠擬合。在遇到欠擬合時,說明:
- 這些特征沒有提供足夠的信息給模型,導致無法學習到准確的預測模型
- 模型本身不適用這個數據集
一般我們可以采取以下方式解決欠擬合問題:
- 選擇一個更強大更合適的模型
- 給訓練集構造更好的屬性特征
- 減少模型的限制(例如加了正則化導致欠擬合)
在這個場景下,模型沒有使用L1/L2 正則,所以先排除第3點。對於第二點,我們可以嘗試加更多的屬性(例如population的對數),不過這里我們首先嘗試第一個方法,選擇另一個更復雜的模型試試。
下面我們試試決策樹,決策樹模型擅長發現復雜的非線性關系:
from sklearn.tree import DecisionTreeRegressor tree_reg = DecisionTreeRegressor() tree_reg.fit(housing_prepared, housing_labels) housing_predictions = tree_reg.predict(housing_prepared) tree_mse = mean_squared_error(housing_labels, housing_predictions) tree_rmse = np.sqrt(tree_mse) tree_rmse >0.0
可以看到此模型做出預測后,RMSE的結果是0.0。說明這個模型在訓練集工作良好,但也有過擬合的可能。為了進一步判斷是否過擬合,我們需要用測試集對它進行評估與驗證。
使用交叉驗證
其中一種評估決策樹模型的方法是用train_test_split() 將訓練集划分為一個更小的訓練集以及一個驗證集(validation set)。然后使用訓練集訓練模型,並使用驗證集評估模型。
不過另一個更好的辦法是使用K-折交叉驗證(K-fold cross-validation),這個在sklearn 中也提供了。下面的代碼會將訓練集隨機划分成10個互斥子集(每個子集稱為一個fold,這里k=10,也是默認常用的k值),然后訓練並評估模型10次,每次均取9個子集的並集作為訓練數據,剩下的1個子集作為驗證集。最終結果會包含10次validation 的分值:
from sklearn.model_selection import cross_val_score scores = cross_val_score(tree_reg, housing_prepared, housing_labels, scoring="neg_mean_squared_error", cv=10) tree_rmse_scores = np.sqrt(-scores)
tree_rmse_scores >array([67669.73816437, 67362.61970456, 70229.34328631, 68299.62160307, 71291.36213287, 75577.59634532, 72668.53981039, 70226.23940832, 77010.86953522, 69438.29976359])
這里我們在scoring 參數那里指定了一個neg_mean_squared_error,而不是我們之前提到的正均方誤差。這是因為sklearn的交叉驗證方法需要傳入的是一個效應函數(utility function,值越大越好)而不是一個損失函數(cost function,值越小越好)。所以scoring function 這里指定的是均方誤差的負數。
我們看一下結果:
def display_scores(scores): print("Scores:", scores) print("Mean:", scores.mean()) print("Standard deviattion:", scores.std()) display_scores(tree_rmse_scores) >Scores: [69462.00181424 67609.32626575 70422.57362602 69319.8112669 71825.7583015 75264.10640903 72395.84019687 71141.89336673 76759.56875965 68894.22449779]
Mean: 71309.51045044875 Standard deviattion: 2729.4780010832424
可以看到決策樹現在的rmse 的結果並沒有之前好了(之前在訓練集上的 rmse 為0),並且結果比線性回歸的結果還要差。通過交叉驗證我們可以獲得多次評估的平均值,並且!還能知道這個評估的精准度(也就是標准差)。隨機數的rmse大約為 71309,一般是±2729(這個變動區間里)。
接下來我們計算一下線性回歸的交叉驗證結果:
lin_scores = cross_val_score(lin_reg, housing_prepared, housing_labels, scoring="neg_mean_squared_error", cv=10) lin_rmse_scores = np.sqrt(-lin_scores) display_scores(lin_rmse_scores) >Scores: [66782.73843989 66960.118071 70347.95244419 74739.57052552 68031.13388938 71193.84183426 64969.63056405 68281.61137997 71552.91566558 67665.10082067] Mean: 69052.46136345083 Standard deviattion: 2731.674001798344
可以看到線性回歸的效果確實略好於決策樹。而決策樹在第一次訓練集上的rsme 的值為0的現象,充分說明了決策樹存在嚴重過擬合。
最后我們試試另一個模型:隨機森林。隨機森林的方法是:在多個隨機子集上訓練多個決策樹,然后取它們預測值的平均值。根據多個模型而構造一個機器學習模型的方法稱為集成學習,這是一個非常有效的手段,可以讓機器學習算法更強大。下面是隨機森林的表現:
from sklearn.ensemble import RandomForestRegressor forest_reg = RandomForestRegressor() forest_reg.fit(housing_prepared, housing_labels) forest_prediction = forest_reg.predict(housing_prepared) forest_rmse = np.sqrt(mean_squared_error(housing_labels, forest_prediction)) forest_rmse >22260.03021330574 forest_scores = cross_val_score(forest_reg, housing_prepared, housing_labels, scoring='neg_mean_squared_error', cv=10) forest_rmse_scores = np.sqrt(-forest_scores)
display_scores(forest_rmse_scores) >Scores: [52997.8760788 49958.86106325 52721.44943885 55406.53413082 51444.10657159 55939.86344122 50784.56562956 50979.4567054 56421.26248695 53300.44123253] Mean: 52995.44167789745 Standard deviattion: 2167.186371913895
可以看到效果明顯好了很多,隨機森林的效果看起來很不錯。不過,需要注意的是:在訓練集上的均方誤差仍遠小於驗證集的均方誤差,說明這個模型在訓練集上仍存在過擬合的問題。在這個問題中,我們可以采用以下方式緩解過擬合:
- 簡化模型,減少模型復雜度
- 添加限制,例如正則項
- 獲取更多的訓練數據
當然,在進一步調優隨機森林模型之前,我們仍應該嘗試一下其他機器學習算法(例如SVM+不同的kernel、神經網絡等等),看看這些模型的表現結果(並不需要調參)。我們的目標是:找出幾個(2到5個)可能效果還不錯的模型。再進行下一步調優。
建議大家每次都要保存訓練好的模型,這樣之后可以很快的找到想要的模型。在保存時,需要確保保存了超參數、訓練時的參數、以及交叉驗證的所有結果、(可能)以及預測后的結果。這樣我們在之后進行不同模型對比時,節省不少時間。我們可以通過 Python 的 pickle 模塊保存 sk-learn 的模型。或是使用更高效的sklearn.extenals.joblib,它的性能更好(會序列化大型NumPy 數組):
from sklearn.externals import joblib joblib.dump(forest_reg, "my_model.pkl") #load model if necessary my_model_loaded = joblib.load("my_model.pkl")
至此,我們已介紹了機器學習模型的訓練,下面我們會繼續介紹模型的調優。