Bài 7

Machine Learning có giám sát

Học có giám sát (supervised learning) là nhóm thuật toán học từ dữ liệu đã có nhãn — mỗi mẫu gồm đặc trưng đầu vào X và đáp án đúng y. Bài này đi qua các thuật toán nền tảng: Linear/Logistic Regression, k-NN, Decision Tree, Random Forest, Boosting, SVM và Naive Bayes — kèm trực giác, khi nào dùng, các hyperparameter quan trọng và code sklearn thực chiến.

Supervised learning là gì
X → y · regression vs classification

Học có giám sát: ta có tập huấn luyện gồm nhiều cặp (X, y)X là ma trận đặc trưng (features, mỗi dòng một mẫu, mỗi cột một biến), y là nhãn/đáp án. Mô hình học một hàm f(X) ≈ y để khi gặp X mới (chưa từng thấy) có thể dự đoán y. "Có giám sát" nghĩa là quá trình học được "chấm điểm" bằng đáp án đúng đã biết.

Hai bài toán chính, phân biệt bởi kiểu của y:

Loạiy là gìVí dụVí dụ thuật toán
Regression (hồi quy)Số thực liên tụcDự đoán giá nhà, doanh thu, nhiệt độLinear Regression, Random Forest Regressor, XGBoost
Classification (phân loại)Nhãn rời rạc (2 lớp hoặc nhiều lớp)Spam/không spam, chẩn đoán bệnh, nhận dạng chữ sốLogistic Regression, k-NN, SVM, Random Forest

Mọi mô hình sklearn đều theo một API thống nhất: model.fit(X_train, y_train) để học, rồi model.predict(X_test) để dự đoán. Điều then chốt là đánh giá trên dữ liệu chưa từng thấy (test set) — mô hình học thuộc lòng train nhưng dự đoán tệ trên dữ liệu mới gọi là overfitting.

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
Luôn có baseline trước: với classification hãy so với DummyClassifier(strategy="most_frequent"), với regression so với dự đoán trung bình. Một mô hình phức tạp không thắng nổi baseline là dấu hiệu có gì đó sai (leak, feature yếu, hoặc bài toán khó hơn tưởng).
Linear Regression
Nền tảng của hồi quy

Trực giác: giả sử y phụ thuộc tuyến tính vào các đặc trưng — vẽ một đường/mặt phẳng khớp nhất với đám điểm dữ liệu. Mô hình: ŷ = w₀ + w₁x₁ + w₂x₂ + … + wₙxₙ, trong đó w₀ là intercept (hệ số chặn) và wᵢ là trọng số của từng đặc trưng.

Hàm mất mát (cost) — MSE: trung bình bình phương sai số MSE = (1/m) Σ (yᵢ − ŷᵢ)². Ta tìm bộ w tối thiểu hoá MSE. Nghiệm có công thức đóng (Normal Equation): w = (XᵀX)⁻¹ Xᵀy; với dữ liệu lớn sklearn dùng phân rã ma trận / gradient descent thay vì nghịch đảo trực tiếp.

Giả định quan trọng: quan hệ tuyến tính; phần dư (residual) độc lập, phương sai đồng nhất (homoscedasticity) và xấp xỉ phân phối chuẩn; ít đa cộng tuyến.

Đa cộng tuyến (multicollinearity): khi hai đặc trưng tương quan mạnh, XᵀX gần suy biến → hệ số w trở nên bất ổn, dấu và độ lớn vô nghĩa (khó diễn giải). Kiểm tra bằng VIF; xử lý bằng bỏ bớt biến hoặc dùng regularization (mục sau).

from sklearn.linear_model import LinearRegression
lr = LinearRegression().fit(X_train, y_train)
# lr.coef_ = trọng số wᵢ, lr.intercept_ = w₀
from sklearn.metrics import mean_squared_error, r2_score
print(r2_score(y_test, lr.predict(X_test)))  # R² càng gần 1 càng tốt
Regularization: Ridge, Lasso, Elastic Net
Chống overfit & chọn đặc trưng

Ý tưởng: thêm một "hình phạt" vào cost để giữ trọng số w nhỏ → mô hình đơn giản hơn, bớt overfit, ổn định hơn khi có đa cộng tuyến. Tham số alpha (hay λ) điều khiển mức phạt: alpha lớn = phạt mạnh = mô hình đơn giản hơn (bias tăng, variance giảm).

Phương phápHình phạtĐặc điểm
Ridge (L2)+ α Σ wᵢ²Co nhỏ các hệ số nhưng hiếm khi về đúng 0. Rất tốt khi có đa cộng tuyến; giữ lại toàn bộ đặc trưng.
Lasso (L1)+ α Σ |wᵢ|Đẩy một số hệ số về đúng 0 → tự động feature selection. Hữu ích khi nghi ngờ nhiều đặc trưng vô dụng.
Elastic Nettrộn L1 + L2Kết hợp cả hai (l1_ratio). Tốt khi nhiều đặc trưng tương quan theo nhóm.
from sklearn.linear_model import Ridge, Lasso, ElasticNet
Ridge(alpha=1.0).fit(X_train, y_train)
Lasso(alpha=0.1).fit(X_train, y_train)       # hệ số =0 bị loại
ElasticNet(alpha=0.1, l1_ratio=0.5).fit(X_train, y_train)
Bắt buộc scale trước: regularization phạt theo độ lớn hệ số nên các đặc trưng phải cùng thang đo. Dùng StandardScaler trong một Pipeline để tránh phạt thiên lệch.
Logistic Regression
Phân loại tuyến tính, dễ diễn giải

Bất chấp cái tên "regression", đây là thuật toán phân loại. Nó tính tổ hợp tuyến tính z = w·x + b rồi bóp qua hàm sigmoid σ(z) = 1 / (1 + e⁻ᶻ) để ra một xác suất trong khoảng [0, 1].

Hàm mất mát — log-loss (cross-entropy): −(1/m) Σ [ y·log(p) + (1−y)·log(1−p) ]. Phạt rất nặng khi mô hình tự tin nhưng sai.

Ngưỡng (threshold): mặc định dự đoán lớp 1 nếu p ≥ 0.5. Có thể chỉnh ngưỡng để đánh đổi precision/recall (vd bài toán y tế hạ ngưỡng để bắt nhiều ca dương tính hơn).

Đọc hệ số / odds: mỗi hệ số wᵢ là log-odds. exp(wᵢ)odds ratio: nếu exp(w)=1.5 thì tăng 1 đơn vị đặc trưng làm odds thuộc lớp 1 tăng 50% (giữ nguyên các biến khác). Đây là lý do Logistic Regression được ưa dùng khi cần giải thích (tín dụng, y tế). Có tham số C (nghịch đảo mức regularization) và penalty.

from sklearn.linear_model import LogisticRegression
clf = LogisticRegression(C=1.0, max_iter=1000).fit(X_train, y_train)
proba = clf.predict_proba(X_test)[:, 1]   # xác suất lớp 1
pred  = (proba >= 0.5).astype(int)          # tự chỉnh ngưỡng
k-Nearest Neighbors (k-NN)
"Cho tôi biết bạn của bạn là ai"

Trực giác: không "học" gì cả (lazy learning) — lưu toàn bộ dữ liệu train. Khi dự đoán một điểm mới, tìm k điểm gần nhất (theo khoảng cách Euclid) rồi lấy nhãn đa số (classification) hoặc trung bình (regression).

Chọn k: k nhỏ (vd 1) → bám nhiễu, variance cao, dễ overfit; k lớn → mượt hơn nhưng có thể bỏ qua cấu trúc cục bộ (underfit). Thường thử k lẻ (tránh hòa) bằng cross-validation.

Cần scaling: vì dựa trên khoảng cách, đặc trưng có thang đo lớn sẽ áp đảo → bắt buộc chuẩn hoá. Chi phí: train gần như miễn phí nhưng dự đoán chậm (phải so với mọi điểm) và tốn bộ nhớ; kém với dữ liệu nhiều chiều (curse of dimensionality).

from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
knn = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=5))
knn.fit(X_train, y_train)
Decision Tree
Chuỗi câu hỏi if/else

Trực giác: chia dữ liệu bằng một loạt câu hỏi ngưỡng ("tuổi > 30?", "thu nhập > 50tr?"), tạo cây nhị phân; mỗi lá là một dự đoán. Rất trực quan, không cần scaling, xử lý được cả biến số và biến hạng.

Chia theo tiêu chí nào: tại mỗi nút, chọn đặc trưng + ngưỡng làm "trong sạch" nhất các nhánh con. Đo độ vẩn đục bằng Gini impurity 1 − Σ pᵢ² hoặc entropy −Σ pᵢ log pᵢ (information gain). Hai tiêu chí thường cho kết quả tương tự.

Vấn đề overfit: cây để mọc tự do sẽ nhớ từng mẫu train (lá thuần khiết) → dự đoán tệ trên dữ liệu mới. Kiểm soát bằng các tham số:

from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier(max_depth=5, min_samples_leaf=20,
                            criterion="gini").fit(X_train, y_train)
# dt.feature_importances_ cho biết biến nào quan trọng
Cây đơn ít khi dùng một mình vì variance cao — nhưng là "viên gạch" xây nên Random Forest và Boosting cực mạnh dưới đây.
Ensembles: Bagging, Random Forest & Boosting
Gộp nhiều cây yếu thành mô hình mạnh

Ý tưởng chung: kết hợp nhiều mô hình để thắng một mô hình đơn. Có hai trường phái đối lập về cách kết hợp:

Bagging (Random Forest)Boosting (XGBoost…)
Cách họcSong song, độc lậpTuần tự, cây sau sửa lỗi cây trước
Mục tiêu chínhGiảm varianceGiảm bias (và variance)
Cây thành phầnCây sâu (khác nhau nhờ bootstrap + random features)Cây nông (weak learners)
Rủi roKhó overfit, an toànDễ overfit nếu chỉnh sai, cần tinh chỉnh

Bagging & Random Forest: huấn luyện nhiều cây, mỗi cây trên một mẫu bootstrap (lấy có hoàn lại) của dữ liệu; Random Forest bổ sung: mỗi lần chia chỉ xét một tập con ngẫu nhiên các đặc trưng → các cây ít tương quan → gộp (vote/trung bình) cho kết quả ổn định. Tham số chính: n_estimators (số cây, càng nhiều càng ổn), max_depth, max_features. Là lựa chọn "mặc định mạnh" cho dữ liệu bảng.

Boosting: xây cây tuần tự, mỗi cây tập trung vào lỗi còn lại của cả đội trước đó.

Tham số chính của boosting: n_estimators (số cây), learning_rate (bước học — nhỏ thì cần nhiều cây hơn nhưng ổn định hơn; đánh đổi với n_estimators), max_depth (thường nông 3–8). Tinh chỉnh cặp learning_rate ↔ n_estimators là quan trọng nhất.

from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
rf = RandomForestClassifier(n_estimators=300, max_depth=None,
                            n_jobs=-1).fit(X_train, y_train)

# XGBoost (thư viện riêng: pip install xgboost)
from xgboost import XGBClassifier
xgb = XGBClassifier(n_estimators=400, learning_rate=0.05,
                    max_depth=4, subsample=0.8).fit(X_train, y_train)
Support Vector Machine (SVM)
Tìm ranh giới có lề rộng nhất

Trực giác: tìm siêu phẳng (hyperplane) phân tách hai lớp sao cho margin (khoảng cách tới điểm gần nhất của mỗi lớp) là lớn nhất. Các điểm nằm sát lề gọi là support vectors — chỉ chúng quyết định ranh giới.

Kernel giúp phân tách dữ liệu không tuyến tính bằng cách ánh xạ lên không gian cao chiều mà không tính tường minh (kernel trick): linear (dữ liệu tách tuyến tính, nhiều đặc trưng như text), rbf (mặc định, biên cong linh hoạt).

Hai tham số cốt lõi: C (đánh đổi lề rộng ↔ ít lỗi train: C lớn = phạt lỗi nặng = lề hẹp, dễ overfit), gamma (với RBF: tầm ảnh hưởng của một điểm — gamma lớn = biên uốn sát từng điểm = overfit). SVM cần scaling và chậm trên dữ liệu rất lớn.

from sklearn.svm import SVC
svm = make_pipeline(StandardScaler(),
        SVC(kernel="rbf", C=1.0, gamma="scale"))
svm.fit(X_train, y_train)
Naive Bayes
Xác suất theo định lý Bayes

Dựa trên định lý Bayes với giả định "ngây thơ" (naive): các đặc trưng độc lập với nhau khi biết lớp. Dù giả định hiếm khi đúng, nó chạy cực nhanh, cần ít dữ liệu và mạnh bất ngờ cho phân loại văn bản / lọc spam. Biến thể: MultinomialNB (đếm từ), GaussianNB (đặc trưng liên tục), BernoulliNB (nhị phân).

from sklearn.naive_bayes import MultinomialNB
nb = MultinomialNB().fit(X_train, y_train)
Bảng tổng hợp: chọn thuật toán nào?
Ưu · nhược · khi nào dùng
Thuật toánƯu điểmNhược điểmKhi nào dùng
Linear/Logistic RegressionNhanh, dễ diễn giải hệ số, baseline tốtChỉ bắt quan hệ tuyến tínhBaseline; khi cần giải thích; dữ liệu tuyến tính
Ridge/LassoChống overfit, Lasso chọn đặc trưngVẫn tuyến tính, cần scaleNhiều đặc trưng / đa cộng tuyến
k-NNĐơn giản, không giả định, phi tuyếnDự đoán chậm, cần scale, kém khi nhiều chiềuDữ liệu nhỏ, biên phức tạp
Decision TreeTrực quan, không cần scale, đọc được luậtOverfit, variance caoCần giải thích luật; làm nền cho ensemble
Random ForestMạnh, ít chỉnh, chống overfit, đo importanceNặng bộ nhớ, kém diễn giải, không extrapolateLựa chọn "mặc định" cho dữ liệu bảng
Gradient Boosting / XGBoost / LightGBMĐộ chính xác cao nhất trên dữ liệu bảngNhạy hyperparameter, dễ overfit, huấn luyện tuần tựKhi cần accuracy tối đa và chịu tinh chỉnh
SVMHiệu quả khi nhiều chiều, biên rõ ràngChậm với dữ liệu lớn, khó chỉnh, cần scaleDữ liệu vừa, nhiều đặc trưng (text)
Naive BayesCực nhanh, ít dữ liệu vẫn chạyGiả định độc lập phi thực tếPhân loại văn bản, lọc spam
Quy trình thực chiến: (1) Chạy baseline (Logistic/Linear Regression) trước. (2) Nếu cần mạnh hơn, thử Random Forest — ít chỉnh mà đã tốt. (3) Ép accuracy tối đa thì dùng XGBoost/LightGBM và tinh chỉnh. Nhớ: các mô hình cây (tree/RF/boosting) KHÔNG cần scaling; còn Linear/Logistic, k-NN, SVM thì bắt buộc scale đặc trưng.
← Quay lại
Bài 6: Feature Engineering