ML 4 — Cây quyết định, Random Forest & Gradient Boosting

13 thg 7, 2026 7 lượt xem
#machine-learning
#ai
#xgboost
#decision-tree
#random-forest
#gradient-boosting

ML 4 — Cây quyết định, Random Forest & Gradient Boosting

Nếu chỉ được chọn một họ mô hình để mặc định thử đầu tiên trên dữ liệu bảng (tabular) — bảng khách hàng, log giao dịch, hồ sơ vay — thì câu trả lời của phần lớn giới thực chiến là: tree ensembles (cây quyết định + tổ hợp cây). Không phải deep learning, không phải hồi quy tuyến tính. Trong hầu hết các cuộc thi Kaggle về dữ liệu bảng và trong vô số hệ thống production (scoring tín dụng, phát hiện gian lận, dự báo churn), XGBoost / LightGBM là mô hình chiến thắng.

Bài này đi từ viên gạch nền — cây quyết định — rồi ghép chúng lại theo hai triết lý khác nhau: bagging (Random Forest) và boosting (Gradient Boosting, XGBoost, LightGBM). Cuối cùng ta bàn về diễn giải mô hình bằng feature importance và SHAP — thứ gần như bắt buộc khi triển khai mô hình tín dụng chịu ràng buộc tuân thủ.

Bài này nối tiếp Linear & Logistic Regression. Nếu bạn cần đo lường mô hình cho đúng, đọc Đánh giá mô hình; nếu cần tinh chỉnh siêu tham số và xử lý mất cân bằng, đọc Imbalanced & Tuning.

Cây quyết định — viên gạch nền

Một cây quyết định (decision tree) học bằng cách chia không gian đặc trưng thành các vùng chữ nhật, mỗi lần đặt một câu hỏi ngưỡng trên một đặc trưng: thu_nhập > 15 triệu?, số_ngày_trễ ≤ 3?. Đi từ gốc xuống, mỗi câu trả lời đưa mẫu sang nhánh trái/phải, cho tới khi chạm (leaf) — nơi cây đưa ra dự đoán (nhãn phổ biến nhất cho phân loại, giá trị trung bình cho hồi quy).

Chọn câu hỏi chia như thế nào?

Tại mỗi nút, thuật toán duyệt qua mọi đặc trưng và mọi ngưỡng khả dĩ, chọn phép chia làm cho hai nhánh con "thuần" nhất có thể. Độ thuần được đo bằng:

  • Phân loại: Gini impurity hoặc entropy. Gini = 1 − Σ pₖ² (với pₖ là tỷ lệ lớp k trong nút). Gini = 0 nghĩa là nút chỉ chứa một lớp — hoàn hảo. Cây chọn phép chia làm giảm Gini/entropy nhiều nhất (information gain lớn nhất). Gini và entropy cho kết quả gần như giống nhau; Gini rẻ hơn nên là mặc định trong scikit-learn.
  • Hồi quy: MSE (hoặc MAE). Cây chọn ngưỡng làm giảm phương sai của target trong hai nhánh con nhiều nhất.

Đây là thuật toán tham lam (greedy): tại mỗi nút chọn phép chia tốt nhất tại chỗ, không quay lui. Không tối ưu toàn cục, nhưng nhanh và đủ tốt.

Ưu điểm khiến cây được yêu thích

  • Dễ giải thích: đường đi từ gốc tới lá là một chuỗi luật if-then mà cả người không kỹ thuật cũng đọc được. Rất quý cho ngân hàng, y tế.
  • Không cần scale/chuẩn hóa: cây chỉ so sánh ngưỡng nên thu_nhập (đơn vị triệu) và tuổi (đơn vị năm) sống chung hòa bình — khác hẳn KNN, SVM, hồi quy có regularization.
  • Bắt được phi tuyến và tương tác đặc trưng một cách tự nhiên: một nhánh có thể nói "nếu thu nhập cao dư nợ thấp thì...", tức là tương tác giữa hai biến, mà không cần ta tạo feature thủ công.
  • Xử lý được cả biến số lẫn biến hạng mục, ít nhạy với outlier.

Nhược điểm chí mạng

  • Cực dễ overfit: nếu để cây mọc tự do, nó sẽ tạo lá riêng cho gần như từng mẫu huấn luyện — thuộc lòng nhiễu, tổng quát hóa kém.
  • Không ổn định (high variance): đổi vài dòng dữ liệu, phép chia ở gốc có thể đổi, kéo theo toàn bộ cây khác hẳn.
  • Ranh giới quyết định là các bậc thang vuông góc trục, khó khớp quan hệ tuyến tính chéo.

Kiểm soát overfit

Vũ khí chính là giới hạn độ phức tạp của cây: max_depth (độ sâu tối đa), min_samples_split / min_samples_leaf (số mẫu tối thiểu để chia / để làm lá), max_leaf_nodes. Ngoài ra có cắt tỉa (pruning) — trong scikit-learn là cost-complexity pruning qua tham số ccp_alpha: mọc cây đầy rồi cắt bớt các nhánh đem lại ít lợi ích.

# (minh hoạ) Cây quyết định với kiểm soát độ sâu
from sklearn.tree import DecisionTreeClassifier, export_text
from sklearn.model_selection import train_test_split

X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2, random_state=42)

tree = DecisionTreeClassifier(
    criterion="gini",
    max_depth=4,            # giới hạn sâu để tránh thuộc lòng
    min_samples_leaf=50,    # mỗi lá phải đủ mẫu mới đáng tin
    random_state=42,
)
tree.fit(X_tr, y_tr)

print("Train acc:", tree.score(X_tr, y_tr))
print("Test  acc:", tree.score(X_te, y_te))
print(export_text(tree, feature_names=list(X.columns)))  # in ra luật if-then

Một cây đơn hiếm khi là mô hình cuối cùng — nó là thành phần để xây những thứ mạnh hơn nhiều.

Ensembles — sức mạnh của số đông

Ý tưởng cốt lõi: nhiều mô hình yếu, kết hợp lại, thành một mô hình mạnh. Có hai triết lý ghép hoàn toàn khác nhau.

Bagging & Random Forest — giảm variance

Bagging (Bootstrap Aggregating): lấy mẫu bootstrap (lấy có hoàn lại) từ tập huấn luyện để tạo N tập con, huấn luyện một cây trên mỗi tập, rồi trung bình/vote kết quả. Vì mỗi cây thấy dữ liệu hơi khác nhau, sai số ngẫu nhiên của chúng triệt tiêu lẫn nhau khi lấy trung bình — variance giảm mạnh trong khi bias gần như giữ nguyên. Đây chính là thuốc chữa cho căn bệnh "không ổn định" của cây đơn.

Random Forest thêm một thủ thuật nữa: tại mỗi phép chia, chỉ xét một tập con ngẫu nhiên các đặc trưng (thường sqrt(số_đặc_trưng) cho phân loại). Điều này khiến các cây bớt giống nhau (bớt tương quan) — trung bình của những cây ít tương quan cho variance thấp hơn nữa. Đó là lý do "random" trong tên gọi.

Hai món quà đi kèm rất tiện:

  • OOB (Out-Of-Bag): mỗi cây bootstrap bỏ sót ~1/3 mẫu; dùng chính các mẫu bị bỏ sót đó để đánh giá cây, ta có ước lượng lỗi gần như validation mà không cần tách tập riêng.
  • Feature importance: đo mỗi đặc trưng đóng góp bao nhiêu vào việc giảm impurity trên toàn rừng.

Random Forest nổi tiếng vì mạnh, ổn định và ít cần tune — dùng gần như "hộp đen bật sẵn" đã cho kết quả tốt. Đổi lại, nó thường không đạt đỉnh cao tuyệt đối bằng boosting được tinh chỉnh kỹ.

# (minh hoạ) Random Forest + OOB + feature importance
from sklearn.ensemble import RandomForestClassifier
import pandas as pd

rf = RandomForestClassifier(
    n_estimators=400,
    max_features="sqrt",
    min_samples_leaf=20,
    oob_score=True,        # ước lượng lỗi miễn phí
    n_jobs=-1,
    random_state=42,
)
rf.fit(X_tr, y_tr)

print("OOB score:", rf.oob_score_)
importances = pd.Series(rf.feature_importances_, index=X.columns)
print(importances.sort_values(ascending=False).head(10))

Boosting — giảm bias

Boosting đi theo triết lý ngược lại: xây tuần tự, cây sau sửa lỗi cây trước. Bắt đầu bằng một dự đoán thô; cây thứ hai học để dự đoán phần dư (residual — phần cây đầu làm sai); cây thứ ba sửa tiếp phần còn lại; và cứ thế. Kết quả cuối là tổng có trọng số của tất cả các cây.

Gradient Boosting khái quát ý tưởng này: thay vì chỉ khớp phần dư, mỗi cây khớp gradient âm của hàm mất mát — cho phép dùng với bất kỳ hàm mất mát khả vi nào (log-loss cho phân loại, MSE cho hồi quy...). Do mỗi bước tập trung vào cái đang sai, boosting giảm bias hiệu quả và thường đạt độ chính xác cao nhất trên tabular. Cái giá phải trả: dễ overfit hơn nếu chạy quá nhiều vòng, và huấn luyện tuần tự nên khó song song hơn bagging.

Ba thư viện boosting hiện đại thống trị thực tế:

  • XGBoost — chuẩn công nghiệp một thời và vẫn cực mạnh. Mọc cây level-wise (theo tầng, cân đối), có regularization L1/L2 tích hợp, xử lý missing value tự động, ổn định và tài liệu phong phú.
  • LightGBM — của Microsoft, tối ưu cho tốc độ và bộ nhớ. Điểm khác biệt cốt lõi: mọc cây leaf-wise (best-first — luôn tách chiếc lá đem lại giảm mất mát lớn nhất, bất kể độ sâu), cộng với histogram-based splitting (gom giá trị vào các bin thay vì duyệt từng ngưỡng), GOSS (ưu tiên giữ các mẫu có gradient lớn — khó học) và EFB (gom các đặc trưng thưa loại trừ lẫn nhau). Bộ ba tối ưu này cho tốc độ nhanh gấp nhiều lần trên dữ liệu triệu dòng. Đánh đổi: leaf-wise dễ overfit hơn trên tập nhỏ nếu không giới hạn num_leaves.
  • CatBoost — của Yandex, mạnh nhất ở việc xử lý biến hạng mục tự động (ordered target encoding, chống rò rỉ) mà không cần one-hot thủ công; mặc định thường tốt ngay.
# (minh hoạ) XGBoost với early stopping
from xgboost import XGBClassifier

xgb = XGBClassifier(
    n_estimators=1000,       # nhiều vòng, nhưng để early stopping tự dừng
    learning_rate=0.05,      # bước nhỏ -> cần nhiều cây, tổng quát tốt hơn
    max_depth=5,
    subsample=0.8,           # mỗi cây thấy 80% mẫu
    colsample_bytree=0.8,    # và 80% cột
    reg_lambda=1.0,          # regularization L2
    eval_metric="auc",
    early_stopping_rounds=50,
    n_jobs=-1,
    random_state=42,
)
xgb.fit(X_tr, y_tr, eval_set=[(X_te, y_te)], verbose=False)
# (minh hoạ) LightGBM — nhanh trên dữ liệu lớn
from lightgbm import LGBMClassifier

lgbm = LGBMClassifier(
    n_estimators=1000,
    learning_rate=0.05,
    num_leaves=31,           # kiểm soát độ phức tạp cây leaf-wise
    min_child_samples=50,    # số mẫu tối thiểu ở lá -> chống overfit
    subsample=0.8,
    colsample_bytree=0.8,
    reg_lambda=1.0,
    n_jobs=-1,
    random_state=42,
)
lgbm.fit(X_tr, y_tr)

Bagging vs Boosting — bảng so sánh

Tiêu chíBagging / Random ForestBoosting (XGBoost/LightGBM)
Mục tiêu chínhGiảm varianceGiảm bias
Cách xây câySong song, độc lậpTuần tự, cây sau sửa cây trước
Rủi ro overfitThấp (rất khó overfit khi thêm cây)Cao hơn (thêm cây quá đà thì overfit)
Công sức tuneÍt, mặc định đã tốtNhiều, cần tune kỹ để đạt đỉnh
Đỉnh cao độ chính xácTốtThường tốt nhất trên tabular
Song song hóaDễKhó hơn (tuần tự)

Quy tắc ngón tay cái: Random Forest là baseline mạnh, ổn định, cắm là chạy. Khi cần vắt kiệt độ chính xác và sẵn sàng tune, chuyển sang XGBoost/LightGBM.

Các siêu tham số quan trọng

Nắm được nhóm này là nắm được 90% việc tune (chi tiết ở Imbalanced & Tuning):

  • n_estimators — số cây. Boosting: càng nhiều càng khớp, nhưng cặp đôi với learning_rateearly stopping để không overfit.
  • learning_rate (chỉ boosting) — mỗi cây đóng góp bao nhiêu. Nhỏ (0.01–0.1) + nhiều cây → tổng quát tốt hơn nhưng chậm hơn. Đây là đánh đổi trung tâm của boosting.
  • max_depth / num_leaves — độ phức tạp mỗi cây. XGBoost dùng max_depth; LightGBM dùng num_leaves (nhớ giữ num_leaves < 2^max_depth).
  • subsample / colsample_bytree — lấy mẫu ngẫu nhiên hàng/cột cho mỗi cây → thêm tính ngẫu nhiên, chống overfit.
  • min_child_weight (XGBoost) / min_child_samples, min_data_in_leaf (LightGBM) — số mẫu tối thiểu ở lá; tăng lên để cây bớt "thuộc lòng".
  • reg_lambda / reg_alpha — regularization L2/L1 trên trọng số lá.

Feature importance & giải thích mô hình

Importance kiểu cổ điển và cái bẫy của nó

Boosting/RF cho ta importance theo gain (đặc trưng giảm được bao nhiêu mất mát) hoặc theo split (được dùng để chia bao nhiêu lần). Tiện, nhưng có thiên lệch cần cảnh giác: importance kiểu impurity thường thổi phồng các đặc trưng liên tục và đặc trưng có nhiều mức (high cardinality), vì chúng có nhiều ngưỡng để chia. Đừng ra quyết định lớn chỉ dựa vào bảng này.

SHAP — chuẩn vàng cho diễn giải cục bộ

SHAP (SHapley Additive exPlanations) mượn khái niệm giá trị Shapley từ lý thuyết trò chơi để trả lời câu hỏi quan trọng nhất: với một dự đoán cụ thể, mỗi đặc trưng đẩy kết quả lên hay kéo xuống bao nhiêu? Với mô hình cây, TreeSHAP tính được các giá trị này chính xác và nhanh. Ưu điểm lớn của SHAP:

  • Diễn giải cục bộ: giải thích được từng ca một — vì sao khách hàng X bị từ chối vay (dư nợ/thu nhập cao đẩy điểm rủi ro lên, thâm niên làm việc ổn định kéo xuống).
  • Nhất quán: gộp các giá trị cục bộ lại cho bức tranh toàn cục đáng tin hơn importance kiểu impurity.

Lưu ý giới hạn: khi các đặc trưng tương quan mạnh (ví dụ thu nhập và trình độ học vấn), SHAP có thể phân bổ công lao lệch giữa chúng — cần đọc kết quả cùng kiến thức nghiệp vụ.

# (minh hoạ) TreeSHAP giải thích một mô hình boosting
import shap

explainer = shap.TreeExplainer(xgb)
shap_values = explainer.shap_values(X_te)

# Toàn cục: đặc trưng nào quan trọng nhất
shap.summary_plot(shap_values, X_te)

# Cục bộ: vì sao KHÁCH HÀNG THỨ 0 bị chấm điểm rủi ro cao
shap.force_plot(explainer.expected_value, shap_values[0], X_te.iloc[0])

Use case thực tế: chấm điểm tín dụng bằng GBM + SHAP

Bài toán: dự đoán xác suất một hồ sơ vay sẽ vỡ nợ (default) trong 12 tháng, từ dữ liệu bảng — thu nhập, dư nợ, lịch sử trễ hạn, thâm niên, sản phẩm vay. Đây đúng là địa hình sở trường của tree ensembles.

Vì sao chọn GBM (XGBoost/LightGBM) thay vì hồi quy logistic? Quan hệ giữa các biến tài chính và rủi ro thường phi tuyến và có tương tác (thu nhập cao chỉ an toàn khi dư nợ/thu nhập thấp) — cây bắt được những mẫu này mà không cần feature engineering thủ công. Trên tabular, GBM thường cho AUC/KS cao hơn logistic một khoảng đáng kể.

Vì sao vẫn cần SHAP? Ngân hàng chịu ràng buộc tuân thủ: khi từ chối một hồ sơ, luật ở nhiều nơi yêu cầu nêu lý do cụ thể (adverse action). Một điểm số "0.87 rủi ro" trần trụi là không đủ. TreeSHAP biến hộp đen thành lời giải thích: "Điểm rủi ro cao chủ yếu vì tỷ lệ dư nợ/thu nhập 0.75 (+0.22) và 3 lần trễ hạn gần đây (+0.15); được bù một phần bởi thâm niên 6 năm (−0.08)." Nghiên cứu về credit scoring cho thấy dùng Shapley values có thể tạo ra scorecard dễ diễn giải như logistic trong khi vẫn giữ độ chính xác vượt trội của GBM.

Quy trình gợi ý:

  1. Baseline nhanh bằng Random Forest để có mốc và bảng importance sơ bộ.
  2. Chuyển sang LightGBM/XGBoost, tune learning_rate + n_estimators (early stopping) + độ sâu/regularization.
  3. Xử lý mất cân bằng lớp (default thường hiếm) — xem Imbalanced & Tuning.
  4. Đánh giá đúng bằng AUC-ROC, KS, precision-recall — xem Đánh giá mô hình, tuyệt đối không chỉ nhìn accuracy.
  5. Chạy TreeSHAP để kiểm định mô hình (feature nào chi phối có hợp lý về nghiệp vụ không?) và để phục vụ giải thích cho từng quyết định.

Khi nào dùng tree ensembles — và khi nào không

  • Dữ liệu bảng (tabular) → tree ensembles gần như luôn là lựa chọn tốt nhất. Scoring, gian lận, churn, dự báo nhu cầu, giá — cứ dữ liệu dạng cột số/hạng mục là chúng thắng.
  • Ảnh, âm thanh, văn bản, chuỗi tuần tựdeep learning vượt trội, vì cần học biểu diễn (representation) từ dữ liệu thô — xem Deep Learning. Cây không "nhìn" được pixel hay ngữ nghĩa từ.
  • Cần cực kỳ dễ giải thích hoặc quan hệ thực sự tuyến tínhhồi quy tuyến tính/logistic vẫn có chỗ đứng: hệ số minh bạch, cực nhanh, là baseline bắt buộc và đôi khi là mô hình cuối được cơ quan quản lý ưa chuộng.

Ghi nhớ

  • Cây quyết định = chuỗi câu hỏi ngưỡng; dễ hiểu, không cần scale, bắt phi tuyến/tương tác, nhưng dễ overfit và không ổn định. Kiểm soát bằng max_depth, min_samples_leaf, pruning.
  • Random Forest = bagging + random featuregiảm variance, mạnh, ổn định, ít cần tune, có OOB và importance miễn phí. Baseline mặc định tuyệt vời.
  • Boosting (XGBoost/LightGBM/CatBoost) xây cây tuần tự sửa lỗi, giảm bias, thường thắng trên tabular. LightGBM leaf-wise + histogram nhanh nhất; CatBoost mạnh với categorical.
  • Bagging song song giảm variance vs boosting tuần tự giảm bias; boosting rủi ro overfit cao hơn nên cần early stopping + regularization.
  • Siêu tham số cốt lõi: learning_rate + n_estimators, độ sâu/num_leaves, subsample/colsample, min_child_*, regularization.
  • Feature importance kiểu impurity có thiên lệch — ưu tiên SHAP/TreeSHAP cho diễn giải cục bộ, đặc biệt trong tín dụng/tuân thủ.
  • Tabular → tree ensembles; ảnh/text/chuỗi → deep learning; cần siêu minh bạch/tuyến tính → linear.

Bài viết liên quan

Phân biệt AI/ML/DL, các kiểu học máy, quy trình ML end-to-end và thuật ngữ nền tảng.

13 thg 7, 2026 15

Chất lượng dữ liệu, xử lý thiếu/ngoại lai, mã hoá, chuẩn hoá, tạo đặc trưng và tránh data leakage.

13 thg 7, 2026 13

Mô hình ngôn ngữ lớn hoạt động ra sao, token, context window, tham số sinh, và kỹ thuật viết prompt.

13 thg 7, 2026 13

Retrieval-Augmented Generation, vector database, tool calling, agent và MCP để xây ứng dụng LLM thực tế.

13 thg 7, 2026 11