Trong học máy truyền thống, mô hình "Random Forest" (Cây quyết định kết hợp - RQĐK) thường được ví như "vũ khí đa năng" nhờ tính linh hoạt và hiệu quả cao.
Nhiều lý do làm nên điều này:
- Ít yêu cầu tiền xử lý dữ liệu, dễ triển khai thực tế.
- Có thể dùng cho cả bài toán phân lớp và hồi quy.
- Có khả năng chống quá khớp tốt hơn nhiều thuật toán đơn lẻ.
- Cung cấp đo lường "độ quan trọng của đặc trưng" (feature importance) một cách tự nhiên.
Bài này phân tích chi tiết cấu trúc bên trong của RQĐK——từ những bước đầu tiên cho đến cơ chế xây dựng cây con, nhằm giúp bạn hình dung rõ hơn về cách mô hình hoạt động. Ví dụ minh họa tập trung vào phân lớp, nhưng nguyên tắc cốt lõi cũng áp dụng cho hồi quy.
1. Triển khai nhanh với scikit-learn
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
X = df.drop(columns=['target'])
y = df['target']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
model = RandomForestClassifier(n_estimators=150, random_state=42)
model.fit(X_train, y_train)
print("Độ chính xác:", model.score(X_test, y_test))
Đây là cách phổ biến để chạy mô hình.dfunding tham số chính là n_estimators (số lượng cây con) và random_state (hạt giống để tái lập)..fc giá trị .93 có thể khiến người dùng hài lòng, nhưng tranh luận sâu hơn sẽ cho thấy cần hiểu rõ cơ chế dẫn đến kết quả này.
2. Đa dạng hóa: Học từ mẫu có hoàn lại (bootstrap sampling)
Một RQĐK là một ensemble của nhiều cây quyết định khác biệt, không đơn thuần chỉ là chạy từng cây trên cùng tập huấn luyện.
Chạy từng cây quyết định độc lập trên toàn bộ dữ liệu sẽ dẫn đến các cây rất giống nhau, làm mất đi ưu điểm hợp nhất. Do đó, RQĐK sử dụng kỹ thuật bootstrap sampling — lấy mẫu có hoàn lại — để tạo nên sự đa dạng giữa các cây.
Giả sử ta có tập dữ liệu gồm 5 điểm: [A, A, B, B, B]. Khi lấy mẫu có hoàn lại, một mẫu có thể là [B, B, A], thậm chí [A, A, A]. Mỗi cây được huấn luyện trên một phiên bản khác nhau của tập huấn luyện — ở đây, mỗi cây dùng khoảng 67% số mẫu gốc (theo mặc định).
Ví dụ minh họa cho n_estimators = 3:
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
samples = []
for _ in range(3):
sampled_df = df.sample(frac=0.67, replace=True)
X_s = sampled_df.drop(columns=['target'])
y_s = sampled_df['target']
samples.append((X_s, y_s))
trees = []
for X_s, y_s in samples:
tree = DecisionTreeClassifier()
tree.fit(X_s, y_s)
trees.append(tree)
# Dự đoán trên dữ liệu kiểm thử
preds = [t.predict(X_test) for t in trees]
# Hợp nhất kết quả qua quy tắc đa số
import numpy as np
final_pred = np.round(np.mean(preds, axis=0)).astype(int)
Tuy nhiên, chỉ sử dụng bootstrap vẫn chưa đủ để đảm bảo cây đủ độc lập — do đó, cần thêm bước tiếp theo.
3. Lọc đặc trưng ngẫu nhiên (feature subsampling)
RQĐK còn áp dụng cơ chế feature subsampling: mỗi cây chỉ sử dụng một tập con các đặc trưng ngẫu nhiên khi phân tách từng nút.
Với bài toán phân lớp, mặc định scikit-learn dùng √d đặc trưng (d = số đặc trưng). Thao tác này diễn ra trên mỗi nút — không phải chỉ một lần cho cây.
Ví dụ minh họa với kỹ thuật sqrt:
import math
d = X_train.shape[1]
k = int(math.sqrt(d))
subsampled_X_train = X_train.sample(n=k, axis=1, replace=False)
subsampled_X_test = X_test[subsampled_X_train.columns]
clf = DecisionTreeClassifier()
clf.fit(subsampled_X_train, y_train)
y_pred = clf.predict(subsampled_X_test)
Việc kết hợp bootstrap sampling và feature subsampling giúp các cây trong rừng thực sự không tương quan, đồng thời kiểm soát tốt độ thiên lệch và phương sai.
4. Gắn kết: Bagging (Bootstrap Aggregating)
Khi ánh xạ lại các bước trên, bạn sẽ thấy RQĐK thực chất là một dạng mở rộng của BaggingClassifier.
from sklearn.ensemble import BaggingClassifier
bag = BaggingClassifier(
estimator=DecisionTreeClassifier(),
n_estimators=100,
max_samples=0.67,
max_features=0.8,
bootstrap=True,
bootstrap_features=False
)
bag.fit(X_train, y_train)
RQĐK khác baseBagging ở chỗ: max_features=sqrt(n_features) (hoặc log2, v.v.) được áp dụng tự động tại mỗi nút phân tách — còn baseBagging chỉ áp dụng nếu được cấu hình thủ công. Vì thế, Random Forest ≡ Bagging + Subspace Randomness.
5. Cấu trúc cây quyết định cơ bản
Cây quyết định hoạt động bằng cách tìm điểm phân chia tối ưu tại mỗi nút——tức tìm đặc trưng và ngưỡng sao cho độ "bất định" giảm mạnh nhất.
Ví dụ cấu trúc cây:
- Gốc: "Tuổi > 30?"
- Có → "Thu nhập > 50 triệu?"
- Có → lớp A
- Không → lớp B
- Không → lớp C
6. Đo lường bất định: Entropy và Information Gain
Entropi là thước đo mức độ hỗn loạn tại một nút:
$$ H(S) = -\sum_{c} p_c \log_2 p_c $$
Trong đó p_c là tỷ lệ mẫu thuộc lớp c.
Thông tin thu được từ một phép phân chia (IG) được tính như:
$$ IG(S, a) = H(S) - \sum_{v \in Values(a)} \frac{|S_v|}{|S|} H(S_v) $$
from collections import Counter
from math import log2
def entropy(labels):
counts = Counter(labels)
total = len(labels)
return -sum((cnt / total) * log2(cnt / total) for cnt in counts.values())
def info_gain(parent_labels, left_labels, right_labels):
p_left = len(left_labels) / len(parent_labels)
p_right = 1 - p_left
return entropy(parent_labels) - p_left * entropy(left_labels) - p_right * entropy(right_labels)
Cây sẽ chọn phân tách có IG lớn nhất để tiếp tục phát triển.
7. Xây dựng cây từ đầu: Bộ mã-minh họa
Giới hạn độ sâu và dừng khi nút đã thuần nhất (entropy=0):
def build_tree(df, features, target, max_depth=4, depth=0):
labels = df[target]
if depth >= max_depth or len(labels.unique()) == 1:
return labels.mode()[0] # trả về lớp đa số (hoặc nút thuần nhất)
best_gain, best_feat = -1, None
for f in features:
vals = df[f].unique()
for val in vals:
left = df[df[f] == val][target]
right = df[df[f] != val][target]
gain = info_gain(labels, left, right)
if gain > best_gain:
best_gain, best_feat, best_val = gain, f, val
if best_gain <= 0:
return labels.mode()[0]
left_sub = df[df[best_feat] == best_val]
right_sub = df[df[best_feat] != best_val]
return {
'split_on': best_feat,
'value': best_val,
'left': build_tree(left_sub, features, target, max_depth, depth+1),
'right': build_tree(right_sub, features, target, max_depth, depth+1)
}
Dự đoán: bắt đầu từ nút gốc, lần lượt đi theo cành phù hợp dựa trên giá trị đặc trưng, cho đến khi đạt đến lá.
8. Một số khía cạnh quan trọng khác
- Độ quan trọng đặc trưng: Tính trung bình độ giảm Entropy mà thưc hiện qua tất cả các cây. Cao hơn → càng có ảnh hưởng mạnh lên mô hình.
- OOB Error (Out-of-Bag): Vì mỗi cây chỉ dùng ~63.2% mẫu gốc, phần không được chọn (OCB) được dùng như tập kiểm thử nội bộ — giúp đánh giá mô hình mà không cần rời tập huấn luyện.
- Tự động chọn ngưỡng tối ưu: Không cần chuẩn hóa; xử lý tốt cả giá trị thiếu (nếu dùng pandas/sklearn 1.4+).
RQĐK không hoàn hảo, nhưng là công cụ mạnh mẽ để bắt đầu phân tích dữ liệu——đặc biệt khi bạn cần cân bằng giữa hiệu suất, độ phức tạp và khả năng diễn giải.