Mục lục
- Vì sao cần thêm metric ngoài MSE
- R² là gì
- Công thức và các thành phần
- Cách diễn giải các giá trị R²
- Range trên train và test
- R² liên hệ với MSE
- Tính bằng sklearn
- Adjusted R² — penalize số feature
- Pitfall — R² cao không = model tốt
- Common values theo domain
- R² không đo causation
- R² trên model phi tuyến
- Code Python — California housing
- Bài tập thực hành
- Bài tiếp theo
Vì sao cần thêm metric ngoài MSE
Bài 17 đã giới thiệu MSE và RMSE — đo trực tiếp sai số dự đoán theo đơn vị target. Hai metric này có 1 hạn chế: không trả lời được câu hỏi "model có tốt không" ở mức độ tương đối.
Ví dụ: model dự đoán giá nhà với RMSE = 50,000 USD. Số này tốt hay tệ? Nếu giá nhà trung bình là 80,000 USD, sai số 50,000 là rất tệ. Nếu giá trung bình là 5 triệu USD, sai số 50,000 lại rất tốt. MSE/RMSE chỉ là con số tuyệt đối — phải có context của target để diễn giải.
R² giải bài toán này: nó là metric tương đối, so sánh model với một baseline đơn giản (predict mean). Kết quả nằm trên thang dễ đọc (thường \( [0, 1] \) trên train), không phụ thuộc đơn vị target.
Sau bài này, bạn sẽ:
- Viết và diễn giải được công thức R².
- Phân biệt range của R² trên train set và test set.
- Hiểu khi nào R² âm và nó nghĩa là gì.
- Tính được Adjusted R² và biết khi nào nên dùng nó.
- Nhận diện được các pitfall thường gặp khi đọc R².
R² là gì
R² (đọc là "R squared", còn gọi là coefficient of determination) đo tỉ lệ phương sai (variance) của target được model giải thích.
Cách hiểu đơn giản: target \( y \) có một độ biến động nội tại — variance của nó. Một phần biến động này được model "giải thích" qua các feature; phần còn lại không giải thích được (residual). R² trả lời câu hỏi: trong tổng variance của \( y \), model giải thích được bao nhiêu phần trăm?
Ví dụ: \( R^2 = 0.8 \) nghĩa là model giải thích được 80% variance của target; 20% còn lại là phần model không bắt được (do feature thiếu, noise, hoặc quan hệ phi tuyến).
Đây là metric tương đối theo nghĩa: so sánh với baseline "predict trung bình của \( y \)". Baseline này không nhìn vào feature — luôn output \( \bar{y} \) cho mọi input. Một model có R² > 0 nghĩa là nó đánh bại baseline; R² ≤ 0 nghĩa là nó tệ hơn hoặc bằng baseline.
Công thức và các thành phần
Công thức R²:
\[ R^2 = 1 - \frac{\text{SS}_{\text{res}}}{\text{SS}_{\text{tot}}} \]Trong đó:
- \( \text{SS}_{\text{res}} = \sum_{i=1}^n (y_i - \hat{y}_i)^2 \) — residual sum of squares, tổng bình phương sai số của model.
- \( \text{SS}_{\text{tot}} = \sum_{i=1}^n (y_i - \bar{y})^2 \) — total sum of squares, tổng bình phương lệch của \( y \) so với trung bình.
- \( \bar{y} = \frac{1}{n} \sum_{i=1}^n y_i \) — mean của target.
- \( \hat{y}_i \) — giá trị dự đoán của model cho sample \( i \).
- \( n \) — số sample.
Diễn giải hai SS:
- \( \text{SS}_{\text{tot}} \) là sai số tổng cộng của baseline predict mean. Nó cố định khi data cố định, không phụ thuộc model.
- \( \text{SS}_{\text{res}} \) là sai số tổng cộng của model thực tế. Càng nhỏ → model càng khớp data.
- Tỉ số \( \text{SS}_{\text{res}} / \text{SS}_{\text{tot}} \) là phần variance không giải thích được. Lấy 1 trừ đi → phần variance giải thích được = R².
Lưu ý notation: nhiều tài liệu viết \( \text{SSR} \) và \( \text{SST} \), một số khác đảo nghĩa \( \text{SSR} \) thành "sum of squares regression" — gây nhầm. Trong bài này dùng \( \text{SS}_{\text{res}} \) và \( \text{SS}_{\text{tot}} \) cho rõ ràng.
Cách diễn giải các giá trị R²
Bốn mốc cần thuộc:
- \( R^2 = 1 \) — model perfect. \( \text{SS}_{\text{res}} = 0 \), tức prediction trùng khít target trên mọi sample. Trong thực tế ML hầu như không xảy ra; nếu thấy, kiểm tra ngay xem có rò target vào feature không (data leakage).
- \( R^2 = 0 \) — model dự đoán không tốt hơn baseline predict mean. \( \text{SS}_{\text{res}} = \text{SS}_{\text{tot}} \). Feature không đóng góp gì cho prediction.
- \( R^2 < 0 \) — model tệ hơn baseline. Có thể do model predict random, model fit sai (vd dùng polynomial degree quá cao trên test set), hoặc model train trên distribution khác hẳn với test set.
- \( R^2 = 0.8 \) — model giải thích được 80% variance. Số còn lại (20%) là phần model chưa bắt được — có thể do feature thiếu, quan hệ phi tuyến chưa fit, hoặc noise nội tại không thể giảm.
Quy đổi nhanh: nếu R² là r, model giảm được \( r \times 100 \% \) sai số bình phương so với baseline predict mean.
Range trên train và test
Điểm dễ nhầm: range của R² khác nhau giữa train và test set.
Trên train set với Linear Regression fit bằng OLS: \( R^2 \in [0, 1] \).
Lý do: OLS tối thiểu hoá \( \text{SS}_{\text{res}} \) trên cùng tập train. Trong số mọi cách chọn parameter, baseline "predict mean" (tương ứng \( w = 0, b = \bar{y} \)) là một lựa chọn hợp lệ. Vì OLS chọn nghiệm tối ưu, sai số của nó không thể tệ hơn baseline → \( \text{SS}_{\text{res}} \le \text{SS}_{\text{tot}} \) → \( R^2 \ge 0 \).
Trên test set: \( R^2 \in (-\infty, 1] \).
Lý do: model được fit trên train, không phải trên test. Trên test set, model có thể dự đoán tệ hơn baseline predict mean (của test set) — đặc biệt khi overfit hoặc distribution shift. Khi đó \( \text{SS}_{\text{res}}^{\text{test}} > \text{SS}_{\text{tot}}^{\text{test}} \) → \( R^2 < 0 \).
Một số lưu ý nhỏ:
- R² âm trên test set là tín hiệu cảnh báo, không phải lỗi tính toán.
- Mean dùng để tính \( \text{SS}_{\text{tot}} \) trên test là mean của test set, không phải mean của train. Đây là quy ước của
sklearn.metrics.r2_score. - Với model phi tuyến (Random Forest, GBM) fit trên train, ngay cả trên train set R² vẫn có thể (về lý thuyết) âm nếu model lạ — nhưng cực hiếm trong thực tế.
R² liên hệ với MSE
Hai metric không độc lập. Chia cả tử và mẫu của \( \text{SS}_{\text{res}} / \text{SS}_{\text{tot}} \) cho \( n \):
\[ \frac{\text{SS}_{\text{res}}}{\text{SS}_{\text{tot}}} = \frac{\frac{1}{n} \sum (y_i - \hat{y}_i)^2}{\frac{1}{n} \sum (y_i - \bar{y})^2} = \frac{\text{MSE}}{\text{Var}(y)} \]Suy ra:
\[ R^2 = 1 - \frac{\text{MSE}}{\text{Var}(y)} \]Hai hệ quả trực tiếp:
- Khi \( \text{MSE} = 0 \) → \( R^2 = 1 \). Model perfect.
- Khi \( \text{MSE} = \text{Var}(y) \) → \( R^2 = 0 \). Model dự đoán tệ ngang baseline predict mean (vì MSE của baseline đúng bằng \( \text{Var}(y) \)).
Quan hệ này giải thích vì sao R² là metric chuẩn hoá theo variance của target: chia MSE cho \( \text{Var}(y) \) để loại bỏ đơn vị và scale của target. Bất kể giá nhà tính bằng USD hay VND, R² giữ nguyên giá trị.
Tính bằng sklearn
Sklearn có 2 cách tính R²:
from sklearn.metrics import r2_score
from sklearn.linear_model import LinearRegression
# Cach 1: dung r2_score truc tiep
r2 = r2_score(y_true, y_pred)
# Cach 2: dung score method cua model regressor
# (score cua moi regressor trong sklearn mac dinh tra R^2)
model = LinearRegression()
model.fit(X_train, y_train)
r2_train = model.score(X_train, y_train)
r2_test = model.score(X_test, y_test)
Hai cách cho cùng kết quả khi y_pred = model.predict(X). Quy ước: gọi r2_score(y_true, y_pred) theo thứ tự (true, pred); nhầm thứ tự với 2 đối số đều là array sẽ không báo lỗi nhưng ra kết quả sai vì R² không đối xứng theo tham số.
Tham số phụ đáng để ý của r2_score:
multioutput='uniform_average'(mặc định) — trung bình R² qua các target khi predict nhiều target song song.multioutput='variance_weighted'— trung bình có trọng số theo variance của từng target.multioutput='raw_values'— trả về array R² của từng target, không gộp.
Adjusted R² — penalize số feature
R² có một tính chất bất tiện: luôn tăng hoặc giữ nguyên khi thêm feature bất kỳ vào model — kể cả feature noise hoàn toàn không liên quan đến target.
Lý do toán học: thêm feature cho OLS thêm 1 chiều tự do; nghiệm tối ưu mới không thể tệ hơn nghiệm cũ (vì có thể đặt weight của feature mới = 0 để quay về model cũ). Nên \( \text{SS}_{\text{res}} \) giảm hoặc không đổi → R² tăng hoặc không đổi.
Hệ quả: dùng R² một mình để so sánh model có số feature khác nhau là không công bằng — model nhiều feature luôn được "credit" cao hơn dù feature thêm vào vô dụng.
Adjusted R² sửa vấn đề này bằng cách phạt theo số feature:
\[ R^2_{\text{adj}} = 1 - (1 - R^2) \cdot \frac{n - 1}{n - k - 1} \]Trong đó:
- \( n \) — số sample.
- \( k \) — số feature trong model.
Khi thêm feature, \( k \) tăng → mẫu số \( n - k - 1 \) giảm → hệ số \( \frac{n-1}{n-k-1} \) tăng → \( (1 - R^2) \) bị nhân lên nhiều hơn → \( R^2_{\text{adj}} \) có thể giảm nếu R² không tăng đủ để bù phần phạt.
Use case của Adjusted R²:
- So sánh model có số feature khác nhau trên cùng dataset.
- Quyết định có nên giữ một feature mới không: nếu thêm feature mà \( R^2_{\text{adj}} \) giảm, feature đó không đáng giữ.
Lưu ý:
- Adjusted R² có thể âm ngay cả khi R² dương — khi số feature lớn so với số sample.
- sklearn không có sẵn
adjusted_r2_score; thường tự tính từr2_scoretheo công thức trên. - Adjusted R² là heuristic cho linear regression; với model phi tuyến (RF, GBM) khái niệm "số feature" không tương đương "số tham số tự do" nên ý nghĩa không chính xác. Khi đó dùng cross-validation R² thay vì Adjusted R².
Pitfall — R² cao không = model tốt
R² cao trên train set là điều kiện cần, không phải điều kiện đủ, để khẳng định model tốt. Có 3 tình huống cổ điển dễ đánh lừa:
Pitfall 1 — Overfitting. Model phức tạp (polynomial degree cao, deep tree, NN không regularize) có thể đạt \( R^2_{\text{train}} = 0.99 \) nhưng \( R^2_{\text{test}} = -0.5 \). Train rất khớp, test rất tệ. Quy tắc: luôn báo cả R² train và R² test; chênh lệch lớn là dấu hiệu overfit.
Pitfall 2 — Spurious correlation. Hai biến không liên quan nhân quả vẫn có thể tương quan ngẫu nhiên trên dataset hữu hạn. Trang Spurious Correlations của Tyler Vigen có cả loạt ví dụ R² rất cao giữa các cặp biến hoàn toàn vô nghĩa (vd "số người chết đuối trong hồ bơi" vs "số phim Nicolas Cage"). Cách kiểm tra: chia data theo thời gian / theo nhóm và xem R² có giữ ổn định không.
Pitfall 3 — Wrong model với residual pattern. Linear Regression fit data parabola có thể vẫn cho R² ~ 0.9 nếu fit "tổng quát"; nhưng plot residual sẽ thấy hình cong rõ rệt — dấu hiệu quan hệ phi tuyến chưa được fit đúng. R² cao không phát hiện được pattern này. Quy tắc: luôn kèm residual plot khi đánh giá regression model, không chỉ nhìn R².
Checklist khi đọc R²:
- R² train và R² test có gần nhau không? (nếu lệch lớn → overfit).
- R² trên các fold CV có ổn định không? (nếu dao động lớn → data nhỏ hoặc noisy).
- Residual plot có ngẫu nhiên quanh 0 không? (nếu có pattern → wrong model).
- R² có còn ổn định khi loại 5–10% sample ngẫu nhiên? (nếu rớt mạnh → R² do vài sample outlier kéo).
Common values theo domain
"R² bao nhiêu là tốt" phụ thuộc domain — không có ngưỡng tuyệt đối. Tham chiếu tương đối:
- Vật lý, kỹ thuật, hoá học: hiện tượng deterministic, ít noise. R² > 0.95 thường mới được coi là khớp tốt. R² 0.8 ở đây có thể là tín hiệu thiếu biến.
- Khoa học xã hội, kinh tế, marketing: hành vi con người nhiều noise, nhiều biến ẩn. R² trong khoảng 0.3–0.6 đã được coi là tốt. Paper publish trong các tạp chí social science thường báo R² ~ 0.4 cho regression đa biến.
- Tài chính, dự đoán giá cổ phiếu: noise cực lớn, market hiệu quả. R² 0.05–0.20 đã được coi là rất tốt — đủ để xây strategy có alpha dương sau chi phí. R² > 0.5 trên price prediction thường là tín hiệu data leakage hoặc lookahead bias.
- Y học, dịch tễ học: tuỳ outcome. Sinh hoá có thể R² > 0.8; outcome lâm sàng (sống/chết, tái phát) R² 0.2–0.4 đã có giá trị lâm sàng nếu so với baseline.
Quy tắc thực hành: trước khi đánh giá R² của model mới, tìm trong literature R² baseline cho bài toán cùng domain. So sánh tương đối với baseline, không tuyệt đối hoá ngưỡng cố định.
R² không đo causation
R² cao chỉ nói "feature có thể dự đoán target", không nói "feature gây ra target". Ba lưu ý quan trọng:
- Correlation không phải causation. Feature \( x \) và target \( y \) có thể cùng phụ thuộc vào biến thứ ba \( z \) (confounder). R² cao của model \( y \sim x \) không chứng minh \( x \) gây \( y \); thực tế can thiệp vào \( x \) (cố tình tăng/giảm) có thể không thay đổi \( y \) chút nào.
- Reverse causation. Model fit \( y \sim x \) không phân biệt được \( x \to y \) hay \( y \to x \). Vd model "sức khoẻ ~ chi phí y tế hằng năm" có R² cao, nhưng quan hệ ngược: người sức khoẻ kém chi y tế nhiều, không phải chi y tế nhiều làm sức khoẻ kém.
- Selection bias. Khi dataset không random (vd chỉ thu thập khách hàng đã mua), quan hệ trong data có thể không tổng quát cho population.
Để khẳng định causation, cần experimental design: randomized controlled trial (RCT), A/B test, hoặc các kỹ thuật causal inference (instrumental variables, difference-in-differences, regression discontinuity). Một model regression chỉ với observational data không đủ — bất kể R² cao đến đâu.
Hệ quả khi báo cáo: tránh dùng từ "gây ra", "do", "khiến" khi diễn giải coefficient và R² trên observational data. Dùng từ "tương quan", "liên quan", "predict" cho chính xác.
R² trên model phi tuyến
R² thường được dạy gắn với Linear Regression, nhưng công thức \( R^2 = 1 - \text{SS}_{\text{res}}/\text{SS}_{\text{tot}} \) chỉ dựa vào \( y_i \) và \( \hat{y}_i \) — không quan tâm model sinh ra prediction là gì. Nên R² tính được cho mọi regressor: Random Forest, Gradient Boosting, SVR, Neural Network.
Tuy vậy, có 2 điểm khác biệt khi dùng R² cho model phi tuyến:
- Diễn giải khác. Với Linear Regression 1 feature, \( R^2 \) đúng bằng \( \text{corr}(x, y)^2 \) — bình phương Pearson correlation. Với model phi tuyến, đẳng thức này không còn đúng; R² chỉ giữ nghĩa "tỉ lệ variance giải thích bởi prediction", không quy về bình phương của một correlation cụ thể.
- Range trên train không bảo đảm ≥ 0. Linear Regression OLS bảo đảm R² train ∈ [0, 1] do tính chất tối ưu hoá. Model phi tuyến có thể (về lý thuyết) cho R² train âm nếu thuật toán fit không hội tụ tốt. Thực tế hiếm gặp nhưng cần biết.
Trong sklearn, mọi regressor đều có .score(X, y) trả về R² mặc định — tiện cho việc so sánh các model khác họ trên cùng tập test.
Code Python — California housing
Chạy đủ các kịch bản đã bàn ở trên trên dataset California housing.
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 r2_score, mean_squared_error
# 1. Load data
data = fetch_california_housing()
X, y = data.data, data.target # target: gia nha theo 100,000 USD
print(f"n_samples={X.shape[0]}, n_features={X.shape[1]}")
# n_samples=20640, n_features=8
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 2. Train Linear Regression
model = LinearRegression()
model.fit(X_train, y_train)
r2_train = model.score(X_train, y_train)
r2_test = model.score(X_test, y_test)
print(f"R^2 train: {r2_train:.4f}") # ~ 0.6126
print(f"R^2 test : {r2_test:.4f}") # ~ 0.5758
# 3. Baseline: predict mean
y_pred_baseline = np.full_like(y_test, y_train.mean())
r2_baseline = r2_score(y_test, y_pred_baseline)
print(f"R^2 baseline (predict mean): {r2_baseline:.4f}")
# Gan 0 (hoi am vi mean train != mean test)
# 4. Model toi (random prediction trong khoang [0, 5])
rng = np.random.default_rng(0)
y_pred_random = rng.uniform(0, 5, size=len(y_test))
r2_random = r2_score(y_test, y_pred_random)
print(f"R^2 random predict: {r2_random:.4f}")
# Am manh, vi du -2.5
# 5. Adjusted R^2
def adjusted_r2(r2, n, k):
return 1 - (1 - r2) * (n - 1) / (n - k - 1)
n_test, k = X_test.shape
adj_r2_test = adjusted_r2(r2_test, n_test, k)
print(f"Adjusted R^2 test: {adj_r2_test:.4f}")
Quan sát từ output:
- R² train (~0.61) và R² test (~0.58) gần nhau → model không overfit. Linear Regression vốn ít overfit vì chỉ học \( d + 1 = 9 \) parameter.
- R² baseline ~ 0 — như định nghĩa.
- R² random âm mạnh — predict bừa tệ hơn baseline predict mean rất nhiều.
- Adjusted R² xấp xỉ R² thường vì \( n \gg k \) (20,640 sample >> 8 feature).
R² ~ 0.58 trên test không "cao" — có nghĩa Linear Regression mới giải thích được 58% variance giá nhà. 42% còn lại cần model phức tạp hơn (Bài 24 sẽ thử Random Forest) hoặc feature engineering tốt hơn.
Bài tập thực hành
Bài 1 — Tính R² bằng tay. Dataset nhỏ:
y_true = [3, 5, 7, 9, 11]
y_pred = [2.8, 5.2, 6.9, 9.1, 10.8]
Yêu cầu: tính \( \bar{y} \), \( \text{SS}_{\text{tot}} \), \( \text{SS}_{\text{res}} \), và R² bằng máy tính (không dùng sklearn). Sau đó verify bằng r2_score.
Bài 2 — Demo R² âm. Tạo dataset target có mean = 50, predict tất cả = 0. Tính R² bằng r2_score:
import numpy as np
from sklearn.metrics import r2_score
rng = np.random.default_rng(42)
y_true = rng.normal(loc=50, scale=10, size=100)
y_pred = np.zeros(100)
print(r2_score(y_true, y_pred))
Câu hỏi: R² là bao nhiêu? Vì sao nó âm? Tính trực tiếp \( \text{SS}_{\text{res}} / \text{SS}_{\text{tot}} \) để kiểm chứng.
Bài 3 — Overfit với polynomial degree 15. Sinh data noisy:
import numpy as np
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
rng = np.random.default_rng(0)
x = np.linspace(0, 1, 30).reshape(-1, 1)
y = np.sin(2 * np.pi * x.ravel()) + rng.normal(0, 0.2, 30)
X_train, X_test, y_train, y_test = train_test_split(
x, y, test_size=0.3, random_state=42
)
Fit Linear Regression trên feature gốc, trên polynomial degree 3, và polynomial degree 15. Báo cáo R² train và R² test cho cả 3. Quan sát: degree 15 có R² train rất cao nhưng R² test thế nào?
Bài 4 — Adjusted R² với số feature khác nhau. Trên California housing, fit Linear Regression hai lần:
- Lần 1: chỉ dùng 5 feature đầu tiên.
- Lần 2: dùng cả 8 feature + 5 feature noise (sinh từ
np.random.normal, total = 13 feature).
So sánh R² và Adjusted R² của hai lần. R² của lần 2 có cao hơn lần 1 không? Adjusted R² thì sao? Giải thích kết quả.
Gợi ý đáp án Bài 1: \( \bar{y} = 7 \), \( \text{SS}_{\text{tot}} = 4 + 4 + 0 + 4 + 16 = 40 \), \( \text{SS}_{\text{res}} = 0.04 + 0.04 + 0.01 + 0.01 + 0.04 = 0.14 \), \( R^2 = 1 - 0.14/40 = 0.9965 \).
Bài tiếp theo
Bài 19: Multiple Linear Regression — nhiều feature, multicollinearity và diễn giải coefficient — mở rộng từ 1 feature sang \( n \) feature: cách đọc coefficient khi feature có scale khác nhau, vấn đề multicollinearity (feature tương quan cao), Variance Inflation Factor (VIF), và lưu ý khi diễn giải "ảnh hưởng" của từng feature trong model đa biến.
Tài liệu tham khảo
- scikit-learn — r2_score API reference
- scikit-learn — Model evaluation: R² score
- Wikipedia — Coefficient of determination
- Wikipedia — Adjusted R-squared
- Tyler Vigen — Spurious Correlations
- Gareth James et al. — An Introduction to Statistical Learning (Chương 3.1.3: Assessing Model Accuracy)
- Jim Frost — How to Interpret R-Squared in Regression Analysis
