Mục lục
- Vì sao cần metric riêng cho regression
- MSE — định nghĩa và tính chất
- Vấn đề đơn vị của MSE
- RMSE — căn bậc hai của MSE
- Khi dùng MSE, khi dùng RMSE
- MSE và RMSE trong sklearn
- MAE — alternative robust
- Huber loss — kết hợp MSE và MAE
- MAPE — sai số theo phần trăm
- So sánh trên outlier
- Train error vs test error
- Sklearn convention — neg_* scorer
- Code Python — California housing
- Bài tập thực hành
- Bài tiếp theo
Vì sao cần metric riêng cho regression
Bài 16 đã train được Linear Regression với sklearn và model.predict() trả về một vector số thực. Câu hỏi tiếp theo: model đó tốt hay tệ?
Với classification, dễ trả lời: đếm số sample đúng / tổng → accuracy. Với regression không thể đếm "đúng" — prediction là số thực, gần như chẳng bao giờ trùng đúng target tới chữ số thập phân cuối. Cần thước đo khoảng cách giữa y_pred và y_true.
Các metric phổ biến cho regression đều xoay quanh residual \( r_i = y_i - \hat{y}_i \) (sai lệch của mỗi sample), khác nhau ở cách gộp các residual lại thành một con số. Lựa chọn cách gộp quyết định metric phạt loại sai số nào nặng hơn, có cùng đơn vị với target hay không, có robust với outlier hay không.
Bài này đi 4 metric: MSE, RMSE, MAE, MAPE. Bài 18 sẽ thêm \( R^2 \) — metric không có đơn vị, đo tỉ lệ phương sai mà model giải thích được.
MSE — định nghĩa và tính chất
Mean Squared Error (MSE) đã gặp ở Series 1 B26 và lặp lại ở Bài 15 — là loss function mặc định của Linear Regression. Công thức:
\[ \text{MSE} = \frac{1}{n} \sum_{i=1}^n (y_i - \hat{y}_i)^2 \]Tính chất:
- Luôn \( \geq 0 \) — vì bình phương. MSE = 0 khi và chỉ khi mọi prediction trùng target.
- Càng nhỏ càng tốt — không có upper bound; phụ thuộc scale của target.
- Phạt outlier nặng — sai số 10 đơn vị đóng góp 100 vào tổng, sai số 1 đơn vị chỉ đóng góp 1. Một outlier sai 100 đơn vị = 10.000 sai số 1 đơn vị về tổng phạt.
- Khả vi mọi nơi và lồi — gradient đẹp, dễ optimize bằng closed-form (OLS) hoặc Gradient Descent. Đây là lý do MSE được chọn làm loss cho training, không chỉ làm metric đánh giá.
Vấn đề đơn vị của MSE
MSE có một nhược điểm khi báo cáo: đơn vị là bình phương đơn vị target.
- Target là giá nhà tính bằng tỉ VND → MSE có đơn vị (tỉ VND)².
- Target là nhiệt độ °C → MSE có đơn vị °C².
- Target là điểm thi (số nguyên 0–100) → MSE có đơn vị điểm².
Nói "MSE = 0.09 tỉ VND bình phương" với business stakeholder không truyền được thông tin có ích. Họ muốn nghe "model sai trung bình bao nhiêu tỉ" — câu trả lời trực tiếp với cùng đơn vị target. Đó là vai trò của RMSE.
RMSE — căn bậc hai của MSE
Root Mean Squared Error (RMSE) đúng như tên — lấy căn của MSE:
\[ \text{RMSE} = \sqrt{\text{MSE}} = \sqrt{\frac{1}{n} \sum_{i=1}^n (y_i - \hat{y}_i)^2} \]Vì lấy căn bình phương, đơn vị của RMSE trùng với đơn vị target:
- Target tỉ VND → RMSE tỉ VND. RMSE = 0.3 có nghĩa "model sai lệch ~300 triệu trên một dự đoán".
- Target °C → RMSE °C. RMSE = 1.5 có nghĩa "sai trung bình ~1.5°C".
- Target điểm thi → RMSE điểm. RMSE = 5 có nghĩa "sai trung bình ~5 điểm".
Một cách nhớ: RMSE là độ lệch chuẩn của residual (khi residual có mean ≈ 0). Vì vậy RMSE đọc giống "sai số trung bình theo nghĩa căn quân phương" — không phải sai số trung bình tuyệt đối (đó là MAE), nhưng đủ gần để dùng trong báo cáo.
Lưu ý: RMSE \( \geq \) MAE luôn đúng (bất đẳng thức Jensen), và chỉ bằng nhau khi tất cả residual có cùng độ lớn tuyệt đối.
Khi dùng MSE, khi dùng RMSE
- Training (loss function): dùng MSE. Lý do: MSE smooth, gradient có dạng đóng và rẻ tính; căn bậc hai chỉ làm chậm và phức tạp hơn mà không thay đổi vị trí cực tiểu.
- Báo cáo / đánh giá (evaluation): dùng RMSE. Lý do: cùng đơn vị target, đọc hiểu được.
- Optimization / model selection: hai metric tương đương. Vì \( \sqrt{\cdot} \) là hàm đơn điệu tăng trên \( [0, \infty) \), model nào MSE nhỏ hơn thì RMSE cũng nhỏ hơn. Không cần lo lắng phải chọn MSE hay RMSE khi so sánh model.
Trong code production hay gặp cả hai: log MSE theo từng epoch khi train (vì đó là loss đang tối thiểu hoá), và in RMSE cuối cùng trên test set khi báo cáo.
MSE và RMSE trong sklearn
Sklearn cung cấp metric ở sklearn.metrics. Lịch sử API thay đổi qua các version, cần nhớ:
from sklearn.metrics import mean_squared_error, root_mean_squared_error
import numpy as np
y_true = [3.0, 5.0, 2.0, 7.0]
y_pred = [2.5, 5.5, 2.0, 6.0]
# MSE — luôn dùng được
mse = mean_squared_error(y_true, y_pred)
print(f"MSE = {mse:.4f}") # 0.3750
# RMSE — 3 cách
# (1) Hàm chuyên dụng — sklearn >= 1.4 (khuyên dùng):
rmse = root_mean_squared_error(y_true, y_pred)
print(f"RMSE = {rmse:.4f}") # 0.6124
# (2) Tự lấy căn — luôn chạy được, không phụ thuộc version:
rmse = np.sqrt(mean_squared_error(y_true, y_pred))
# (3) (DEPRECATED) — chỉ dùng nếu code legacy:
# rmse = mean_squared_error(y_true, y_pred, squared=False)
# Tham số squared= bị xoá từ sklearn 1.6, không dùng cho code mới.
Quy tắc: với code mới (sklearn ≥ 1.4) dùng root_mean_squared_error trực tiếp. Với code phải support nhiều version, np.sqrt(mse) là cách an toàn nhất.
MAE — alternative robust
Mean Absolute Error (MAE) dùng giá trị tuyệt đối thay cho bình phương:
\[ \text{MAE} = \frac{1}{n} \sum_{i=1}^n \lvert y_i - \hat{y}_i \rvert \]Tính chất:
- Cùng đơn vị target — không cần lấy căn như RMSE.
- Phạt sai số tuyến tính — sai 10 đơn vị bằng 10 lần sai 1 đơn vị (so với 100 lần của MSE).
- Robust với outlier — outlier không bị khuếch đại theo bình phương. Một sample sai 100 đơn vị chỉ thêm 100 vào tổng tuyệt đối.
- Không khả vi tại 0 — đạo hàm của \( \lvert x \rvert \) không xác định tại \( x = 0 \). Khi dùng MAE làm loss training, phải dùng subgradient hoặc smoothing. Đây là lý do sklearn dùng MSE làm loss mặc định, không dùng MAE.
Khi nào chọn MAE thay RMSE:
- Data có outlier nhiều và bạn không muốn model bị kéo về phía outlier.
- Domain quan tâm "sai trung bình bao nhiêu" theo nghĩa trực tiếp (mean absolute), không phải "căn quân phương".
- Forecasting tài chính / nhu cầu — thường báo MAE vì dễ giải thích với non-technical stakeholder.
Trong sklearn: from sklearn.metrics import mean_absolute_error.
Huber loss — kết hợp MSE và MAE
Huber loss ghép tính chất tốt của MSE (smooth, dễ tối ưu) với tính chất tốt của MAE (robust với outlier). Với threshold \( \delta \):
\[ L_\delta(r) = \begin{cases} \tfrac{1}{2} r^2 & \text{nếu } \lvert r \rvert \leq \delta \\ \delta \left( \lvert r \rvert - \tfrac{1}{2} \delta \right) & \text{nếu } \lvert r \rvert > \delta \end{cases} \]Trong đó \( r = y - \hat{y} \). Ý nghĩa: sai số nhỏ (\( \leq \delta \)) bị phạt theo bình phương như MSE; sai số lớn bị phạt theo tuyến tính như MAE. Threshold \( \delta \) quyết định ranh giới — thường tune bằng cross-validation.
Huber có cả 2 ưu điểm: khả vi mọi nơi (kể cả ranh giới \( \pm \delta \)) và không bị outlier kéo lệch. Sklearn có HuberRegressor trong sklearn.linear_model để fit Linear Regression với Huber loss. Bài 21 (Regularization) sẽ nhắc lại trong context của robust regression.
MAPE — sai số theo phần trăm
Mean Absolute Percentage Error (MAPE) đo sai số theo tỉ lệ phần trăm so với giá trị thực:
\[ \text{MAPE} = \frac{100\%}{n} \sum_{i=1}^n \left\lvert \frac{y_i - \hat{y}_i}{y_i} \right\rvert \]Ưu điểm:
- Không có đơn vị — chỉ là %. So sánh được giữa các dataset có scale khác nhau.
- Trực giác business — "model sai 8% trung bình" dễ hiểu với mọi audience.
Pitfall:
- Chia cho 0 — nếu \( y_i = 0 \), công thức không định nghĩa. Phải lọc bỏ hoặc thay bằng MAPE biến thể (sMAPE, MASE).
- Lệch khi y nhỏ — \( y_i \) gần 0 làm phân số nổ to, một vài sample nhỏ kéo MAPE lên rất cao.
- Bất đối xứng — sai số dưới (predict thấp hơn thực) bị phạt nặng hơn sai số trên cùng độ lớn tuyệt đối. Forecasting có thể dùng symmetric MAPE (sMAPE) để fix.
Sklearn: from sklearn.metrics import mean_absolute_percentage_error (trả về tỉ lệ, ví dụ 0.08 = 8%, không phải 8).
So sánh trên outlier
Để thấy sự khác biệt giữa MSE và MAE khi gặp outlier, dựng dataset 100 sample: 99 sample có sai số nhỏ ±1, 1 sample sai 50 (outlier).
import numpy as np
from sklearn.metrics import mean_squared_error, mean_absolute_error
rng = np.random.default_rng(0)
y_true = np.zeros(100)
y_pred = rng.uniform(-1, 1, size=100) # 99 sai số nhỏ
y_pred[0] = 50.0 # 1 outlier
mse = mean_squared_error(y_true, y_pred)
rmse = np.sqrt(mse)
mae = mean_absolute_error(y_true, y_pred)
print(f"MSE = {mse:7.3f}") # ~ 25.33
print(f"RMSE = {rmse:7.3f}") # ~ 5.03
print(f"MAE = {mae:7.3f}") # ~ 0.99
Quan sát:
- MAE ≈ 0.99 — phản ánh đúng tình hình "đa số sai trong khoảng ±1". Outlier sai 50 chỉ thêm 50/100 = 0.5 vào MAE.
- RMSE ≈ 5.03 — lớn gấp 5 lần MAE chỉ vì 1 sample. Sai số outlier bị bình phương lên 2500, đóng góp 25 vào MSE, kéo RMSE từ ~0.6 (nếu không có outlier) lên ~5.
Nếu domain cho phép outlier xuất hiện và muốn metric phản ánh "đa số case", chọn MAE. Nếu sai số lớn có hậu quả nghiêm trọng (vd dự báo tải hệ thống, sai 1 lần thật to là sập), chọn RMSE để metric nhạy với case đó.
Train error vs test error
Một metric đơn lẻ không nói được tình trạng model — cần so 2 con số:
- Train MSE thấp + Test MSE cao — model học thuộc train, không generalize. Dấu hiệu overfitting.
- Train MSE cao + Test MSE cao — model không đủ capacity để bắt pattern. Dấu hiệu underfitting (hoặc feature thiếu thông tin).
- Train MSE thấp + Test MSE thấp, hai gần nhau — trạng thái mong muốn.
- Train MSE cao + Test MSE thấp — hiếm gặp, thường do data leakage giữa train và test, hoặc test set quá dễ.
Bài 37 (Overfitting & Bias-Variance) sẽ đi sâu chẩn đoán và xử lý. Bây giờ chỉ cần thói quen: luôn in cả train metric và test metric sau mỗi lần fit.
Sklearn convention — neg_* scorer
Sklearn nhất quán dùng quy ước "higher is better" cho mọi scorer. Với accuracy / R² / F1, càng cao càng tốt — phù hợp tự nhiên. Nhưng với MSE / RMSE / MAE, càng thấp càng tốt — ngược quy ước.
Cách sklearn xử lý: gắn dấu âm vào trước. Khi truyền tên scorer cho cross_val_score, GridSearchCV, RandomizedSearchCV, phải dùng tên có prefix neg_:
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import cross_val_score
scores = cross_val_score(
LinearRegression(),
X, y,
scoring="neg_root_mean_squared_error", # KHÔNG phải "root_mean_squared_error"
cv=5,
)
# scores có giá trị âm — cần đảo dấu khi báo cáo
rmse_per_fold = -scores
print(f"RMSE per fold: {rmse_per_fold}")
print(f"RMSE mean : {rmse_per_fold.mean():.4f}")
Các tên scorer hay dùng cho regression:
"neg_mean_squared_error""neg_root_mean_squared_error"(sklearn ≥ 1.4)"neg_mean_absolute_error""neg_mean_absolute_percentage_error""r2"(đã là higher-is-better, không cầnneg_)
Quên prefix neg_ sẽ ra lỗi ValueError: ... is not a valid scoring value — bug khá thường gặp.
Code Python — California housing
Train Linear Regression trên dataset California housing (target = median house value đơn vị 100.000 USD), compute đủ 4 metric trên test set, sau đó tiêm 1 outlier vào prediction để thấy MSE phạt nặng outlier ra sao.
import numpy as np
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import (
mean_squared_error,
root_mean_squared_error,
mean_absolute_error,
mean_absolute_percentage_error,
)
data = fetch_california_housing(as_frame=True)
X, y = data.data, data.target # y đơn vị: 100.000 USD
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
model = LinearRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
mse = mean_squared_error(y_test, y_pred)
rmse = root_mean_squared_error(y_test, y_pred)
mae = mean_absolute_error(y_test, y_pred)
mape = mean_absolute_percentage_error(y_test, y_pred)
print(f"MSE = {mse:.4f} (đơn vị: (100k USD)^2)")
print(f"RMSE = {rmse:.4f} -> sai ~ {rmse * 100_000:,.0f} USD / nhà")
print(f"MAE = {mae:.4f}")
print(f"MAPE = {mape * 100:.2f}%")
# MSE = 0.5559 (đơn vị: (100k USD)^2)
# RMSE = 0.7456 -> sai ~ 74,556 USD / nhà
# MAE = 0.5332
# MAPE = 31.75%
# --- Demo: tiêm 1 outlier vào prediction ---
y_pred_bad = y_pred.copy()
y_pred_bad[0] += 50.0 # 1 prediction lệch 50 (đơn vị: 100k USD)
mse_bad = mean_squared_error(y_test, y_pred_bad)
mae_bad = mean_absolute_error(y_test, y_pred_bad)
print(f"\nSau khi thêm 1 outlier:")
print(f"MSE: {mse:.4f} -> {mse_bad:.4f} (tăng {mse_bad / mse:.1f}x)")
print(f"MAE: {mae:.4f} -> {mae_bad:.4f} (tăng {mae_bad / mae:.2f}x)")
Lưu ý cách báo cáo RMSE: nhân lại scale gốc (×100.000) để ra đơn vị USD thực tế. Đây là kiểu báo cáo mà stakeholder hiểu ngay.
MAPE ~32% trên California housing là cao — đa phần do segment giá thấp (\( y \) nhỏ làm mẫu số phình to). Đây cũng là minh hoạ cho pitfall của MAPE ở mục 9.
Quan sát phần outlier: MSE bị kéo lên nhiều lần (\( 50^2 = 2500 \) chia cho \( n_{test} \approx 4128 \) thêm ~0.6 vào MSE), MAE chỉ thêm \( 50 / 4128 \approx 0.012 \) — chênh lệch khẳng định lại bài học mục 10.
Bài tập thực hành
Bài 1 — Tính bằng tay. Cho:
y_true = [3, 5, 2, 7]
y_pred = [2.5, 5.5, 2, 6]
Yêu cầu:
- Tính residual của từng sample.
- Tính MSE, RMSE, MAE bằng tay (viết từng bước cộng).
- Verify bằng
sklearn.metrics. Đáp án mong đợi: MSE = 0.375, RMSE ≈ 0.6124, MAE = 0.5.
Bài 2 — Overfitting với polynomial. Sinh dữ liệu 1D:
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
rng = np.random.default_rng(0)
x = np.linspace(-1, 1, 40)
y = np.sin(3 * x) + 0.1 * rng.standard_normal(40)
X = x.reshape(-1, 1)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=0
)
Yêu cầu: fit 3 model với PolynomialFeatures(degree=d) + LinearRegression với \( d \in \{1, 5, 15\} \). In MSE train và MSE test cho từng degree. Chỉ rõ degree nào underfit, degree nào overfit, degree nào hợp lý nhất.
Bài 3 — MAPE với data dương. Trên California housing đã train ở mục 13, compute MAPE chỉ trên các sample có \( y_{true} > 1.0 \) (≥ 100.000 USD). So sánh với MAPE trên toàn test set. Tại sao chênh lệch?
Bài 4 — Cross-validate với neg_* scorer. Dùng cross_val_score với scoring="neg_root_mean_squared_error", cv=5 trên California housing. In RMSE mean và std qua 5 fold. Đảm bảo đảo dấu trước khi báo cáo.
Bài 5 — Robust với MAE. Tiêm 5% sample bất kỳ vào y_train với giá trị nhân 10 (outlier), refit LinearRegression, đo lại RMSE và MAE trên test set. So sánh với model fit trên data sạch. Quan sát: metric nào kéo lên nhiều hơn?
Bài tiếp theo
Bài 18: R-Squared (R²) — tỉ lệ phương sai giải thích được — metric không có đơn vị, đo tỉ lệ phương sai của target mà model giải thích được; cách tính, ý nghĩa, khi nào R² âm, và lý do nên báo cáo R² cùng RMSE thay vì chỉ một con số.
Tài liệu tham khảo
- scikit-learn — Regression metrics
- scikit-learn — mean_squared_error API
- scikit-learn — root_mean_squared_error API
- scikit-learn — mean_absolute_error API
- scikit-learn — mean_absolute_percentage_error API
- scikit-learn — HuberRegressor
- scikit-learn — The scoring parameter (neg_* convention)
- Wikipedia — Mean Squared Error
- Wikipedia — Root-mean-square deviation
- Wikipedia — Mean Absolute Percentage Error
- Wikipedia — Huber loss
