提升系統
前言叨B叨
大家是不是這會正在忙著搶券,忙著剁手呢? 好吧, 祝你們剁手愉快.
本章包含以下內容:
1. 擬合不足和過度擬合
2. 粗暴的解決方案:網格搜索
3. 特征選擇
正文
1. 擬合不足和過度擬合(Overfitting and underfitting)
構建機器學習模型的一個關鍵挑戰是學習如何處理欠擬合和過擬合問題。我們來看一個房價的圖表,
其中每個房子的價值只根據房子的大小來確定。一個好的模型可以按照平滑曲線預測價格。這條曲線是跟隨數據的趨勢的。
過度擬合(overfitting)是當你的模型記住你的確切的訓練數據,但實際上并沒有找出數據中的模式。過度擬合模型可能會像這樣預測價格。
換句話說,該模型太貼合訓練數據。對任何不在訓練數據集中的房屋都會做出不恰當的預測。如果是這樣,這個模型根本沒有把握住房價的套路。它只是記住這些確切的數據點。
而擬合不足(underfitting)恰恰相反, 也就是說你的模型太簡單,并沒有完全學習數據模式。下面是一個擬合不足例子。
在這種情況下,對于右側的值模型是正常的,但是左側的值卻相差很遠。因為你所使用的模型不夠復雜,無法匹配訓練數據,所以可能會發生擬合不足的情況。在這種情況下,我們的模型只是一條直線, 而沒有那一條直線能夠準確地跟蹤這些房價的趨勢。
不幸的是,當我們構建真實世界的機器學習模型時,我們不僅僅要關注像房子大小這樣的單一功能。我們的模型可能具有數百或數千個特征。因此,我們不能在圖表上繪制我們所有的數據,來查看我們的機器學習模型是否合適, 這個試圖化看起來太復雜了。因此,我們不直接看圖表,而是通過查看訓練數據和測試數據的錯誤率來間接計算出模型的好壞。
如果我們的訓練數據的錯誤率很低,但是我們的測試數據集的錯誤率非常高,這意味著我們的模型是過度擬合的。
我們知道,因為這個模型完全符合我們的訓練數據,但并沒有推廣到測試數據。太復雜的模型會導致過度擬合。我們需要減少模型的復雜性。通過使用較少的決策樹,使得每個決策樹更小,或者通過選擇簡單的決策樹而不是復雜決策樹,可以使您的梯度增強模型變得更加復雜。但即使這樣模型仍然可能擬合不足,原因可能是我們沒有足夠的訓練數據。如果降低模型的復雜度并不能解決問題,則可能是因為你沒有足夠的訓練數據來解決問題。
如果我們的訓練數據集和測試數據集的錯誤率都很高,那意味著我們的模型是擬合不足,它沒有很好地捕捉到數據集中的模式,太簡單的模型就會這樣。
所以您需要使模型更復雜一些。通過使用更多的決策樹,或者使每個決策樹的層級更深些,可以使梯度提升模型更加復雜。如果我們的訓練集和測試集的錯誤率都很低,這意味著我們的模型運行良好,而且訓練數據和測試數據都是準確的。這意味著模型已經學習到了數據背后的真實模式。
通過調整模型的超參數,我們可以修復擬合不足和過度擬合的問題,最終得到一個合適的模型。
2. 粗暴的解決方案:網格搜索( Grid search)
機器學習模型中的兩個常見問題是過度擬合和擬合不足。我們通常可以通過調整模型上的超參數來解決這個問題。但問題是機器學習模型有很多超參數需要調整。尋找最佳設置的最好方法通常是通過試驗和糾錯,但嘗試所有可能的組合需要花費大量的工作。
現在讓我們打開源碼中的train_model.py看下。
# Fit regression model
model = ensemble.GradientBoostingRegressor(
n_estimators=1000,
learning_rate=0.1,
max_depth=6,
min_samples_leaf=9,
max_features=0.1,
loss='huber'
)
當我們創建我們的模型時,我們傳入了這些參數。我們在這里有六個不同的參數,我們可以調整,大多數這些參數接受任何數字,所以我們可以嘗試無數的組合。
解決這個問題的方法是使用網格搜索(Grid Search)。網格搜索可以列出你想要調整的每個參數,然后都嘗試幾遍。用這個你可以訓練和測試每個參數組合的模型,最后生成最佳預測的參數組合是您應該用于真實模型的一組參數。幸運的是,scikit-learn完全自動化了這個過程。我們來打開grid_search.py??。這幾乎和train_model.py中的代碼完全一樣。
# Create the model
model = ensemble.GradientBoostingRegressor()
第一個區別是我們聲明了我們的模型而不傳入任何參數。
相反,我們在下面有一個參數網格。參數網格為每個參數都有一個數組。
# Parameters we want to try
param_grid = {
'n_estimators': [500, 1000, 3000],
'max_depth': [4, 6],
'min_samples_leaf': [3, 5, 9, 17],
'learning_rate': [0.1, 0.05, 0.02, 0.01],
'max_features': [1.0, 0.3, 0.1],
'loss': ['ls', 'lad', 'huber']
}
對于每個設置,我們添加我們想要嘗試的值的范圍。我們在這里設置的范圍是能夠很好覆蓋大多數問題的。
一個好的策略就是對每個參數嘗試一些值,在這個值上增加或減少一個很大的數值,比如1.0到0.3到0.1,就像我們這里所做的那樣。嘗試非常接近的值如1.0到0.95,沒有多大意義,因為結果可能不會有太大的不同。
接下來,使用網格搜索CV函數定義網格搜索。
# Define the grid search we want to run. Run it with four cpus in parallel.
gs_cv = GridSearchCV(model, param_grid, n_jobs=4)
這需要模型對象,參數表和我們想用來運行網格搜索的CPU數量。如果您的計算機具有多個CPU,則可以使用它們全部加速。接下來,我們調用CV的fit函數來運行網格搜索。
# Run the grid search - on only the training data!
gs_cv.fit(X_train, y_train)
這里我們只將訓練數據傳遞給網格搜索CV功能, 這一點是非常重要的。我們不給它訪問我們的測試數據集。網格搜索中的CV代表交叉驗證(Cross-Validation)。該功能將自動將訓練數據切片成較小的子集,并將部分數據用于訓練不同模型和不同部分數據以測試這些模型。
這意味著模型配置沒有看到我們的測試數據,這么做的目的是確保我們對最終模型進行完全的盲測。
運行網格搜索將需要很長時間,因為它實際上是在對齊網格中為每個可能的參數組合訓練一個模型。完成訓練后,將打印出效果最好的模型超參數。
# Print the parameters that gave us the best result!
print(gs_cv.best_params_)
當使用最佳參數時,它也會告訴我們兩個數據集的平均誤差。
# Find the error rate on the training set using the best parameters
mse = mean_absolute_error(y_train, gs_cv.predict(X_train))
print("Training Set Mean Absolute Error: %.4f" % mse)
# Find the error rate on the test set using the best parameters
mse = mean_absolute_error(y_test, gs_cv.predict(X_test))
print("Test Set Mean Absolute Error: %.4f" % mse)
3. 特征選擇
我們來打開feature_selection.py。在我們的房價模型中,
# These are the feature labels from our data set
feature_labels = np.array(['year_built', 'stories', 'num_bedrooms', 'full_bathrooms', 'half_bathrooms', 'livable_sqft', 'total_sqft', 'garage_sqft'...'])
如果我們包含18個原始特征,加上使用one-hot編碼創建的新特征,我們總共有63個特征。其中一些特征如房屋面積(以平方英尺計算),對確定房屋的價值可能非常重要。其他功能,如房子是否有壁爐,在計算最終價格時可能不太重要,但是有多不重要?也許有一些特征根本就沒有必要,我們可以從模型中刪除它們。
通過基于樹的機器學習算法(如梯度增強),我們可以查看訓練模型,并讓它告訴我們每個特征在決定最終價格中的使用頻率。
首先,我們使用joblib.load來加載模型。
# Load the trained model created with train_model.py
model = joblib.load('trained_house_classifier_model.pkl')
如果你還沒有生成train house classifier model.pkl文件,只需打開train_model.py并運行它來生成。現在我們可以從我們訓練的模型中獲得每個特征的重要性。為此,我們調用以下劃線結尾的model.feature重要性。
# Create a numpy array based on the model's feature importances
importance = model.feature_importances_
在scikit-learn中,這步將給我們一個包含每個特征的重要性的數組。所有特征的重要性的總和加起來為1,因此您可以將此視為百分比評定該特征用于確定房屋價值的頻率。
為了使特征列表更易于閱讀,讓我們根據重要性對它們進行排序。我們將使用numpy的argsort函數給出指向數組中每個元素的數組索引列表。然后,我們將使用一個前向循環打印出每個特征的名稱和它的重要性。
# Sort the feature labels based on the feature importance rankings from the model
feauture_indexes_by_importance = importance.argsort()
# Print each feature label, from most important to least important (reverse order)
for index in feauture_indexes_by_importance:
print("{} - {:.2f}%".format(feature_labels[index], (importance[index] * 100.0)))
讓我們來運行這個程序。結果如下:
city_Julieberg - 0.00%
city_New Robinton - 0.00%
city_New Michele - 0.00%
city_Martinezfort - 0.00%
city_Davidtown - 0.04%
city_Rickytown - 0.08%
...
stories - 2.06%
half_bathrooms - 2.14%
full_bathrooms - 3.60%
carport_sqft - 3.95%
num_bedrooms - 4.66%
year_built - 13.18%
garage_sqft - 13.44%
livable_sqft - 16.59%
total_sqft - 16.91%
我們可以看到,這些最后幾個特征是房子價格中最重要的。確定房屋價格的最重要的因素是規模,建筑年份,車庫的大小,臥室的數量和浴室的數量。
如果我們稍微向上滾動,我們可以看到其他因素,比如房子有多少個故事,或者有沒有一個游泳池,都會影響價格,但是它們往往不是那么重要。如果你一路走到名單的最前面,我們可以看到,有些因素,如房子是否在新米歇爾市,根本不會影響價格。但總的來說,在我們的例子中,這63個特征中的大部分都被使用了,但是如果你有一個非常大的模型,有成千上萬的特征,你可以用這種方法來選擇要保留哪些特征,哪些特征在下一次訓練的時候可以丟掉。即使你不排除模型中的任何特征,這也是了解你的模型實際在做什么的好方法。
例如,如果你看到模型認為最重要的特征是你認為并不重要的特征,也就意味著你的模型還不能很好地工作。
結語
下一章將會講解最后一章, 如何使用我們的模型來預測真實的數據, 歡迎關注.
你的 關注-收藏-轉發 是我繼續分享的動力!