在第14章中,從基礎的決策樹分類與迴歸模型開始掌握如何使用不同的分割標準建立模型並進行預測,同時也學習如何視覺化決策樹,進一步了解模型內部的決策邏輯。對於隨機森林模型,可透過`Out Of Bags (OOB)`評估模型的能力,而對於從大量特徵中篩選出關鍵特徵,可有效提升模型的效能和解釋性。其他如面對資料不平衡的處理技巧、類別權重,以及如何透過設定樹的深度或節點數量來避免模型過擬合。進階的方法還有AdaBoost、XGBoost及LightGBM這些高效的演算法。
而在Exercise中,將第14章中的理論與技巧應用到MNIST這個資料集上,進一步理解各種演算法在實際大規模資料上的訓練時間與準確率表現差異,並從中分析不同模型的適用情境與優缺點。
OS: Windows 11 23H2
CPU: Intel Core i7-13700K
GPU: NVIDIA GeForce RTX 4070 SUPER 12G
RAM: DDR5 64G 5600MT/s
Python Package Manager: uv
Python Version: 3.10.16
Python Package: numpy, pandas, sklearn, rich, seaborn, matplotlib
IDE: Visual Studio Code
為方便查看運行結果,故額外使用rich函式庫建立兩個Function格式化印出指定內容。
14.1 Training a Decision Tree Classifier
Code:
Explanation:
此程式碼使用sklearn提供的DecisionTreeClassifier建立兩種不同的決策樹演算法對鳶尾花資料集進行分類。首先載入Iris資料集,並建立兩種決策樹模型(第一個使用預設設定,第二個設定criterion為entropy作為分割標準)。訓練完這兩個模型後,建立一個簡易的測試值進行預測測試值的分類結果與其判斷機率。
Discussion:
決策樹的可解釋性高,能夠簡易直觀地展現決策過程,且不需對數據進行預處理。然而,其容易過擬合訓練數據、對數據微小變化敏感,且單一決策樹的預測能力通常不如RandomForest強大。
Result:
14.2 Training a Decision Tree Regressor
Code:
Explanation:
此程式碼使用sklearn提供的DecisionTreeRegressor方法建立決策樹迴歸演算法來分析糖尿病資料集。首先載入糖尿病數據集,建立兩種決策樹迴歸模型(第一個使用預設的設定,第二個的criterion設定為absolute_error(MAE)作為分割標準)。訓練完這兩個模型後,使用這兩個模型來預測糖尿病資料集中的第一個觀測值。
Discussion:
決策樹迴歸能夠處理非線性的關係,且不需假設數據分佈。然而,其限制包括容易過擬合、對離群值敏感,且特別是在複雜的連續變數情況下表現可能不佳。
Result:
14.3 Visualizing a Decision Tree Model
Code:
Explanation:
此程式碼首先透過DecisionTreeClassifier建立一個簡單的決策樹分類模型,使用Iris資料集訓練一個決策樹分類器,然後再使用sklearn提供的tree.export_graphviz方法將決策樹結構轉換為DOT格式。接著利用pydotplus提供的graph_from_got_data方法將DOT格式轉為圖片,最後透過IPython的Image方法顯示建立好的PNG圖。
Discussion:
決策樹視覺化可以讓人直觀理解模型中的決策規則和特徵的重要性。然而,複雜的模型結構圖可能過於龐大難以閱讀,且視覺化需依賴額外套件如pydotplus,在某些環境中可能需要額外設置才能正常運作。
Result:
14.4 Training a Random Forest Classifier
Code:
Explanation:
此程式碼使用sklearn提供的RandomForestClassifier建立兩種不同的隨機森林模型,第一種使用預設的設定建立,第二個設定criterion為entropy作為分割標準。兩個模型經過訓練後,用來預測同一個測試值的類別。這邊第一個模型設定的n_jobs=-1參數可以讓演算法在訓練時使用所有可用的CPU核心進行平行運算,加速訓練過程。
Discussion:
隨機森林演算法透過多個決策樹集合以降低過擬合的風險,提高預測穩定性及準確度,且能夠自動評估特徵重要性。然而,其計算成本較高、模型不如單一決策樹直觀,且在處理高維稀疏數據或極度不平衡的分類問題時可能表現較差。
Result:
14.5 Training a Random Forest Regressor
Code:
Explanation:
此程式碼使用sklearn提供的RandomForestRegressor方法建立隨機森林迴歸模型,並設定random_state以確保結果可重現,同時指定n_jobs=-1來利用所有可用的CPU核心加速模型訓練。訓練完成後,程式使用此模型來預測資料集中第一個觀測值的目標數值。
Discussion:
隨機森林迴歸集合多個決策樹以減少過擬合的風險,提高預測穩定性及準確度,且能自動評估特徵重要性。然而,其計算資源需求較高,以及在處理高度非線性關係時可能會遇到預測準確度下降的問題。
Result:
14.6 Evaluating Random Forests with Out-of-Bag Errors
Code:
Explanation:
此程式碼使用隨機森林模型對Iris資料集進行分類,特別設定了較高的樹木數量(1000棵)並啟用了out of bags (oob_score=True),同時設定n_jobs=-1以加速運算。訓練完成後,透過oob_score_方法擷取模型的out of bags分數,這個分數代表模型在未訓練過的數據上的預測能力,提供了無需額外驗證集的模型性能預估。
Discussion:
隨機森林使用out of bags評分的優點是能夠在訓練過程中自動評估模型性能,節省了額外分割測試集的需求,並提供較為客觀的準確度估計。但其計算成本較高(特別是樹木數量大時),且out of bags評分只是模型性能的估計值,在數據量不足或類別極度不平衡的情況下可能不夠精確。
Result:
14.7 Identifying Important Features in Random Forests
Code:
Explanation:
此程式碼使用隨機森林分類器模型分析Iris資料集中個各特徵的重要性。首先載入Iris數據並訓練隨機森林模型,然後透過feature_importances_方法擷取特徵重要性分數。接著,使用np.argsort將特徵按重要性降序排列,並透過matplotlib繪製長條圖來顯示這些重要性的分數,呈現哪些特徵對分類任務貢獻最大。
Discussion:
隨機森林特徵重要性的分析是一個提供直觀易懂的特徵評估方式,有助於特徵選擇和模型解釋,不需額外計算成本。然而,這種方式可能高估分類性變數和較高數量的特徵的重要性,對特徵之間高度相關的情況較敏感,且無法辨識特徵之間交互作用的重要性,可能導致誤解特徵中複雜的關係。
Result:
14.8 Selecting Important Features in Random Forests
Code:
Explanation:
此程式碼使用圖片展示了特徵選擇的重要性。首先載入Iris資料集並訓練傳統隨機森林模型,透過sklearn提供的feature_importances_方法獲取原始的特徵重要性。接著使用SelectFromModel方法設定閾值0.3進行特徵篩選,僅保留重要性高於閾值的特徵,並用這些篩選後的特徵訓練新的隨機森林模型。
由於篩選後的特徵數量會減少,故特徵的shape已與原始特徵不同,這邊透過使用selector的get_support()方法獲取篩選後的特徵項目遮罩(一個布林陣列,True代表有選擇,False代表未選擇),並存於一個新的變數中以供繪製長條圖使用
最後使用對比長條圖並排顯示篩選前後各個特徵的重要性分數,呈現特徵選擇對重要性分布的影響。
Discussion:
特徵選擇可降低模型複雜度、減少過擬合風險、提高運算效率,並增加結果的可解釋性。然而,其閾值設定可能過度簡化問題,忽略特徵之間的互補關係,且在特徵之間高度相關時,重要的特徵可能被錯誤剔除,導致信息損失。
Result:
14.9 Handling Imbalanced Classes
Code:
Explanation:
此程式碼首先載入Iris資料集,接著移除前40個觀測值以製造不平衡的資料集,並將目標變數二元化(類別0保持為0,其他類別變為1)。然後創建一個設定class_weight為balanced的隨機森林分類器,讓模型自動調整不同類別的權重。最後手動計算各個類別的權重值,使用len方法計算樣本總數,在使用np.unique獲得唯一類別數的數量,以及使用np.bincount獲取各類別樣本數,再使用公式`樣本總數/(類別數*各類別樣本數)`計算平衡權重。
Discussion:
類別平衡權重能有效處理不平衡數據集,提高少數類別的識別能力,且無需改變原始數據分布。然而,其過度補償可能導致多數類別識別率下降,權重設定方法可能過於簡化,忽略類別之間的特徵分布差異,而且在極度不平衡或多類別的問題中效果可能不如數據採樣的方法好。
Result:
14.10 Controlling Tree Size
Code:
Explanation:
此程式碼使用各種自定義參數建立新的決策樹分類器。與之前的基本決策樹不同,這邊明確設定了多項參數,包括max_depth=None可不限制樹的深度、max_samples_split=2設定最小分割樣本數為2等。特別重要的是設定了max_leaf_nodes=6,這限制了決策樹最多只能有6個葉子節點,強制樹結構更加精簡。這邊同樣使用了graphviz和pydotplus將決策樹轉換為可視化的圖片。
Discussion:
自定義參數決策樹能夠對模型複雜度進行精確控制,特別是透過max_leaf_nodes限制葉子節點數量,可有效防止過擬合並提高模型泛化能力。然而,其參數選擇往往需要反覆實驗,參數間可能存在交互影響,且過度限制樹結構可能導致模型無法充分學習數據中的關鍵模式,降低分類準確率。
Result:
14.11 Improving Performance Through Boosting
Code:
Explanation:
程式碼比較決策樹與AdaBoost演算法在Iris資料集的分類任務上的效能差異。首先載入Iris資料集,並以75%,25%的比例分割為訓練集和評估集,使用stratify參數確保類別分布的一致性。接著分別訓練基本的決策樹和AdaBoost分類器,並使用score方式同時記錄兩者的訓練時間和準確率。最後比較兩種模型的性能指標。
Discussion:
AdaBoost本應能夠顯著提升基學習器的預測性能,特別擅長處理難分樣本,且較不易過擬合。然而,這邊測試的結果卻與基礎決策樹分類器沒有差別,有可能是資料集過小的問題。而其對異常值和帶噪數據較敏感,訓練時間較長,且訓練方式難以平行化,在處理大型數據集時可能面臨效率與記憶體消耗的問題。
Result:
14.12 Training an XGBoost Model
Code:
Explanation:
此程式碼使用XGBoost演算法處理Iris資料集的分類問題。首先載入鳶尾花資料集,並將其轉換為XGBoost特有的DMatrix格式,這個格式優化了資料的存取效率。接著設定模型參數,指定目標函數為多類別機率輸出`multi:softprob`並設定類別數量為3。然後使用xgb.train訓練梯度提升模型,對訓練資料進行預測,並使用argmax函數將模型輸出的機率分布轉換為類別標籤,確定每個樣本的最終分類結果。最後使用classification_report方法印出預測結果。
Discussion:
XGBoost透過梯度提升和正規化技術提供更好的預測性能,處理缺失值得能力更強,且運算效率較高。然而其超參數的調整過程複雜,需要大量實驗,對類別型特徵需要額外編碼,且在極度不平衡的資料上可能需要特殊的處理。
Result:
14.13 Improving Real-Time Performance with LightGBM
Code:
Explanation:
此程式碼使用LightGBM演算法進行Iris資料集的分類任務。首先載入Iris資料集,然後將特徵與目標變數轉換成LightGBM專用的Dataset格式,這種格式能夠優化記憶體的使用和訓練速度。接著設定模型參數,指定多類別分類目標函數`multiclass`、類別數量為3,以及關閉冗長輸出(verbose=-1)。然後使用lgb.train函數訓練梯度提升模型,對原始特徵資料進行預測,並使用argmax函數將輸出的概率矩陣轉換為類別標籤。最後使用classification_report方法印出預測結果。
Discussion:
LightGBM採用直方圖算法和葉子優先生長策略,大幅提升訓練速度和記憶體效率,同時保持高預測準確率,特別適合大型資料集。然而,其在小型資料集上容易過擬合、參數調整較為複雜,葉子優先的生長策略在某些情況下也可能導致生成不平衡的決策樹。
Result:
Settings
這邊使用MNIST作為主要資料集,對第14章中的每一種模型進行訓練與比較差異。
MNIST是機器學習領域中最經典的手寫數字識別資料集,包含60,000張訓練圖片和10,000張測試圖片。每張圖片大小為28×28像素,呈現0至9的手寫數字。因其簡單明確的目標和適中的複雜度,MNIST常作為深度學習入門教學的首選範例,可藉此熟悉基本模型的操作和評估方法。
Explanation:
首先透過fetch_openml方法下載MNIST資料集並存放於專案路徑下的data資料夾中。下載完成後載入資料集並轉換為對應的feature(data)與target(target),接著再對data進行正規化處理壓縮範圍至0 ~ 1之間以便進行訓練。最後再使用sklearn提供的train_test_split方法將MNIST資料即拆分為60000張圖片作為訓練集,10000張圖片作為評估集。
Code:
Explanation:
接著再透過sklearn提供的各種方法(DecisionTreeClassifier、DecisionTreeRegressor、RandomForestClassifier、RandomForestRegressor...)對各類模型進行訓練,這邊回歸類型的模型準確率透過計算相對誤差是否大於10%來判斷是否正確計算準確度(accuracy)。
Code:
Explanation:
接下來在隨機森林分類器中選擇重要特徵部分,透過獲取原始模型的特徵重要性,接著使用SelectFromModel挑選出重要性高於平均值的特徵來訓練新模型。這邊通過建立與原始特徵重要性相同大小的陣列,將新模型中的重要性值映射回原始特徵空間,並將結果重構為 28 x 28 的圖片。最後,使用seaborn提供的heatmap方法將熱圖並排顯示原始模型與特徵選擇後模型的重要性分布,直觀呈現特徵選擇的效果。
Code:
Result:
Discussion:
從圖片結果可以清楚觀察到,特徵選擇後的模型(右圖)保留了數字識別中最具辨別力的中心區域,同時過濾掉了周邊比較不重要的像素。特別是在數字的中央部分(約第10-16行和第12-14列)的高亮特徵仍被保留,但原始模型中的部分較弱特徵(邊緣區域的淺藍色部分)被移除。這表示選擇後的模型成功識別出了關鍵特徵,不僅使特徵分布更加集中,還增強了模型的可解釋性,讓我們能更清晰地看到哪些區域對分類任務真正重要。然而,注意到特徵選擇後的部分區域(如第18-20行的某些像素)被完全忽略,這可能導致某些邊緣情況下的識別能力下降。
Discussion:
這七種方法展現了準確率、訓練時間和可解釋性之間的不同權衡。決策樹模型訓練速度快但準確率較低,具有較佳可解釋性,適合理解特徵重要性。隨機森林透過集成學習明顯提升準確率,但需要更多的運算資源。從結果看出分類類型的模型通常比迴歸類型的模型在此任務表現更佳,因為MNIST資料集的數字辨識本質上是一種分類的問題。提升方法如AdaBoost、XGBoost和LightGBM透過序列化改進學習器,在準確率上領先許多。尤其是XGBoost和LightGBM採用梯度提升優化,表現最佳但調整參數卻變得相對複雜。而在執行效率方面,LightGBM設計更為輕量,在大規模應用時優勢會更加明顯。
Result:
Difficulties Encountered and Solutions
1. 使用Image方法印出建立完成的DecisionTree問題
Difficulties:在使用Image方法創建DecisionTree Model架構圖時,由於環境中未安裝GraphViz,故需要額外進行安裝。
Solution:
首先至GraphViz的官網中下載Windows的安裝包,並於安裝中選擇新增至PATH。接下來再透過pip安裝graphviz,就可以正常繪製出圖片了。
References
[1] Machine Learning with Python Cookbook-2nd, by Kyle Gallatin and Chris Albon, O'Reilly, 2023, ISBN: 9781098135720
[2] 機器學習入門:使用Scikit-Learn與TensorFlow, 黃建庭, 碁峰, 2021, ISBN: 9786263240285
[3] 精通機器學習:使用Scikit-Learn, Keras與TensorFlow(第三版), Aurélien Géron, 歐萊禮, 2024, ISBN: 9786263246676
[4] scikit-learn 機器學習實戰, 鄧立國 郭雅秋 陳子堯 鄧淇文, 清華大學, 2022, ISBN: 9787302604396
[5] Python資料科學學習手冊(第二版), Jake VanderPlas , 歐萊禮, 2023, ISBN: 9786263246843