iT邦幫忙

2024 iThome 鐵人賽

DAY 27
0
AI/ ML & Data

征服機器學習的終極秘技系列 第 27

Day 27 - 應用實作練習:模型優化

  • 分享至 

  • xImage
  •  

📖前言

在前一篇文章中,我們對多種機器學習分類演算法進行了評估,發現 Random Forest 🌲模型在準確度、精確率和 F1 Score 🎯等指標上表現最佳。這篇文章將深入探討如何基於 Random Forest 模型進行優化,以提高其在醫療數據分析中的預測能力。優化的核心是平衡泛化誤差與模型複雜度,以最大化模型的準確性。泛化誤差反映了模型在未知數據上的表現,而模型複雜度則關係到模型的靈活性和過擬合風險

🌟常見的 Random Forest 超參數

在進行 🌲Random Forest 的參數調整時,以下是一些常見的參數及其含義:

1️⃣ n_estimators:控制隨機森林中樹的數量(即子樹數量🌲)。通常,增加樹的數量可以提高模型的穩定性和準確性,但過多的樹可能會導致訓練時間過長⏳,且在某個點後,增加樹的數量對模型性能的提升有限

2️⃣ max_depth:限制每棵樹的最大深度。這個參數有助於控制模型的複雜性⚙️,從而降低過擬合的風險📉。若深度過小,模型可能擬合不足;若深度過大,則可能導致過擬合。常用的深度選擇包括 3、5 或 7。

3️⃣ max_features:每次分裂時考慮的特徵數量📑。這個參數決定了每棵決策樹在進行分裂時所能選擇的特徵數,適當的特徵數量可以提升模型性能🔧,並增加模型的多樣性。

⚠️需要注意的是在一定範圍內,子樹數量越多🌲,模型效果越好。但當子樹數量越來越大,準確率會發生波動📉📈。因此找到適當的平衡點非常重要⚖️。

📉學習曲線的繪製

學習曲線可以幫助我們了解模型在不同樣本數量下的表現,從而判斷模型是否存在過擬合或欠擬合的問題⚠️。在這裡,我們將通過調整 n_estimators 的範圍,繪製學習曲線,以找到最佳的子樹數量,從而最大化模型的性能。在初始階段,我們會擴大數據範圍🌍,觀察模型在不同 n_estimators 值下的表現,當準確率開始穩定並出現輕微波動時,便可縮小參數範圍🔍,進一步尋找最佳子樹數量。在模型評估過程中,我們的目標是優化 Random Forest 的性能,而使用交叉驗證可以確保評估結果的穩定性和可靠性,雖然這個過程可能會導致準確度的輕微變化,但它有助於我們更全面地理解模型的表現💡。程式碼如下:

from sklearn.model_selection import cross_val_score, cross_val_predict
score_lt = []

for i in range(0,500,10):
    rfc = RandomForestClassifier(n_estimators=i+1
                                ,random_state=42)
    score = cross_val_score(rfc, X_train, y_train, cv=5).mean()
    score_lt.append(score)
score_max = max(score_lt)
print('highest_score:{}'.format(score_max),
      'subtree_num:{}'.format(score_lt.index(score_max)*10+1))
x = np.arange(1,501,10)
plt.subplot(111)
plt.plot(x, score_lt, 'r-')
plt.show()

得到的輸出如下:
https://ithelp.ithome.com.tw/upload/images/20240927/20168116AfuDqc50gR.jpg
可以看到線圖在 60 個子樹以內有慢慢趨緩平滑的趨勢,後面都是一些波動且目前看起來是子樹設定在 56 時,準確度最高,達到 54.7978%。因此可以在找到平滑的範圍後,我們嘗試縮小範圍,並在小範圍內一個一個點去找到準確度最高點的位置和數值。程式碼如下:

# 缩小n_estimators的範圍從 10-60
score_lt = []
for i in range(10,60):
    rfc = RandomForestClassifier(n_estimators=i
                                ,random_state=42)
    score = cross_val_score(rfc, X_train, y_train, cv=5).mean()
    score_lt.append(score)
score_max = max(score_lt)
print('highest_score:{}'.format(score_max),
      'subtree_num:{}'.format(score_lt.index(score_max)+10))

# 繪製學習曲線
x = np.arange(10,60)
plt.subplot(111)
plt.plot(x, score_lt,'o-')
plt.show()

得到的結果如下圖:
https://ithelp.ithome.com.tw/upload/images/20240927/20168116Q0Fzsz4CVr.jpg
可以看到我們的 n_estimators 一個一個點去找時,已經不是找到 56,而是在子樹為 55 時,準確度提升到了54.8679%,這就是微調的用處 💡。

找到最佳子樹數量後,我們可以用 Grid Search 方法來調整 max_depthmax_features。通常會基於已找到的最佳 n_estimators,再進一步微調其他超參數。

Grid Search 調整 max_depth

此步驟基於找到的 n_estimators=55 去搜尋最佳的 max_depth。程式碼如下:

from sklearn.model_selection import GridSearchCV
rfc = RandomForestClassifier(n_estimators=55, random_state=42)
param_grid = {'max_depth':np.arange(1,20)}
GS = GridSearchCV(rfc, param_grid, cv=5)
GS.fit(X_train, y_train)

best_param = GS.best_params_
best_score = GS.best_score_
print(best_param, best_score)

輸出結果為:{'max_depth': 18} 0.5472002050554043
結果顯示最佳深度為 18,準確度為 54.72%。與之前僅調整 n_estimators 時的準確度相比,雖然看起來有些波動,但差異並不大。因此,可以先保留這個超參數,繼續進行下一步的 max_features 優化。

Grid Search 調整 max_features

此步驟基於找到的 n_estimators = 55 、max_depth = 18 去尋找最佳 max_features。程式碼如下:

# 用grid_search 調整 max_features
param_grid = {'max_features':np.arange(5,35)}

rfc = RandomForestClassifier(n_estimators=55, random_state=42
                            ,max_depth=18)
GS = GridSearchCV(rfc, param_grid, cv=5)
GS.fit(X_train, y_train)
best_param = GS.best_params_
best_score = GS.best_score_
print(best_param, best_score)

通常情況下,若未特別設定 max_features,其預設值為總特徵數量的平方根。但建議大家可以多方嘗試不同組合。此程式碼得到的輸出結果為:{'max_features': 20} 0.552184825900075
結果顯示最佳 max_features 為 20,準確度提升至 55.21%📈。這意味著在每次決策樹的分裂過程中,考慮 20 個特徵能夠達到最佳效果,這不僅提高了模型的準確度,也代表了特徵的選擇對於隨機森林模型的性能影響是顯著的🚀。

在模型優化階段,需要不斷測試不同的參數組合。有時候,調整後的結果反而會更差,這可能意味著當前的參數組合不適合,或者模型過於複雜⚠️;但如果僅有輕微的落差,則不必過於擔心,因為這對整體性能影響不大。因此,對於不理想的調整結果,可以選擇不進行調整。最終在完成所有參數調整後,我們便可使用這個調整後的模型進行測試並保存🔍。

在完成所有參數調整後,我們發現在這個隨機森林模型中,n_estimators=55, max_depth=18, max_features=20 時結果最佳🌟。經過微調後,我們需要進行最終的模型測試。以下程式碼呈現如何使用 KFold 交叉驗證來評估模型性能,包括計算常見的醫療指標 (計算準確度、靈敏度、特異性、PPV、NPV、正負似然比(LR+ 和 LR-)等性能指標,以及 ROC AUC 🩺指標。) 這些指標提供了全面的性能評估,幫助我們確定模型的效能✔️:

from sklearn.model_selection import KFold
from sklearn.metrics import roc_auc_score
X = cleaning_data_filtered.drop('Test Results', axis=1) # Since we are predicting Type: Normal/Abnormal, those are the output
y = cleaning_data_filtered['Test Results'] # output is the label
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

rf = RandomForestClassifier(n_estimators=55, max_depth=18, max_features=20
                            )

kfold = KFold(n_splits=5, random_state=42, shuffle=True)
cm_holder = []
for train_index, valid_index in kfold.split(X_train):
    train_x, X_valid = X_train.iloc[train_index].values, X_train.iloc[valid_index]
    train_y, y_valid = y_train.iloc[train_index].values, y_train.iloc[valid_index]

    rf.fit(train_x, train_y)
    cm_holder.append(confusion_matrix(y_valid, rf.predict(X_valid.values)))

print("Confusion matrix: ", cm_holder)
accuracy = []
Sen = []
Spe = []
ppv = []
npv = []
LR_Pos = []
LR_Neg = []
for cm in cm_holder:
    TN = cm[0][0]
    FN = cm[1][0]
    TP = cm[1][1]
    FP = cm[0][1]
    PPV = TP/(TP+FP)
    NPV = TN/(TN+FN)
    Sensitivity = TP/(TP+FN)
    Specificity = TN/(TN+FP)
    ACC = (TP+TN)/(TP+FP+FN+TN)
    FPR = FP/(FP+TN)
    TPR = TP/(TP+FN)
    TNR = TN/(TN+FP)
    FNR = FN/(FN+TP)
    LR_pos = TPR/FPR
    LR_neg = FNR/TNR
    accuracy.append(ACC)
    Sen.append(Sensitivity)
    Spe.append(Specificity)
    ppv.append(PPV)
    npv.append(NPV)
    LR_Pos.append(LR_pos)
    LR_Neg.append(LR_neg)
print("mean_acc: ", np.mean(accuracy))
print("mean_sen: ", np.mean(Sen))
print("mean_spe: ", np.mean(Spe))
print("mean_ppv: ", np.mean(ppv))
print("mean_npv: ", np.mean(npv))
print("mean_LR+: ", np.mean(LR_Pos))
print("mean_LR-: ", np.mean(LR_Neg))
roc_auc = roc_auc_score(y_valid, rf.predict(X_valid.values))
auc_score = cross_val_score(rf, X_valid, y_valid, scoring='roc_auc')
print("AUC", auc_score.mean())

我們可以得到結果如下圖:
https://ithelp.ithome.com.tw/upload/images/20240927/20168116Kl35vrCIDW.jpg
我們使用 5 折交叉驗證,最終測試結果的平均值顯示,平均準確度達到 54.36%,平均靈敏度為 52.21%,平均特異度為 56.54% 等。

這些結果仍需詢問專業人士(例如醫療人員)🤝,以確定這樣的準確度是否可以接受。如果認為這些結果不可接受或低於預期,我們將需要嘗試更進階的演算法🔍、調整超參數或其他參數,甚至返回數據前處理步驟進行修正。不過,這系列文章的主要目的是向讀者介紹機器學習的流程及其應用實作📚,而並非追求特定水準的預測準確度。因此,我們這系列文章將不再進行持續優化,而是進入後續步驟🔄,保存已優化的模型並開始進行預測。這樣的做法不僅能幫助讀者了解如何在實際應用中使用這些模型,還能展示它們的實用性💡。如果有興趣的讀者,可以自行嘗試模型的建立與優化,以進一步提升結果的準確度,深入了解機器學習的過程與技術喔~✨

保存模型

完成這些測試後,我們可以選擇將最佳模型保存,以便在未來使用新的數據進行預測:

import pickle 
from google.colab import files

# 假設 'best_model' 是我們找到的最佳 Random Forest 模型 
best_model = rf # 這裡填入您的最佳模型程式碼 
# 將模型保存為 Pickle 檔  
with open('best_random_forest_model.pkl', 'wb') as file:
  pickle.dump(best_model, file)  
# 顯示成功訊息
print("模型已成功保存為 best_random_forest_model.pkl")
# 下載文件到本地
files.download(filename)

結論

本文深入探討了如何優化 Random Forest 模型以提高醫療數據分析中的預測能力。我們評估了多個超參數,發現 n_estimatorsmax_depthmax_features 是關鍵因素。在調整過程中,我們通過學習曲線找到最佳的子樹數量,並使用 Grid Search 確定最佳的樹深度和特徵數量。最終,模型的準確度和其他性能指標得到了顯著提升。這一過程展示了在醫療領域中,合理的超參數調整能顯著增強模型的預測效能,為未來的醫療數據分析提供了有力支持。


上一篇
Day 26 - 應用實作練習:演算法建立、模型評估與比較
下一篇
Day 28 - 應用實作練習:模型預測
系列文
征服機器學習的終極秘技30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言