Mục lục
Mục tiêu bài học
Sau bài học, bạn sẽ:
- Hiểu vì sao chỉ có mean là chưa đủ để mô tả dữ liệu.
- Tính được range, variance và standard deviation bằng tay và bằng Python.
- Phân biệt population variance (chia
n) và sample variance (chian-1) — Bessel correction. - Tính z-score và coefficient of variation, biết khi nào dùng cái nào.
- Liên hệ tới
StandardScaler, batch normalization và outlier detection trong AI/ML.
Bài này nối tiếp bài 21: mean / median / mode trả lời câu "tâm" ở đâu; variance / std trả lời câu "rộng" cỡ nào.
Vì sao mean chưa đủ
Xét điểm thi cuối kỳ của hai lớp, mỗi lớp 5 học sinh:
- Lớp A:
[7, 7, 7, 7, 7] - Lớp B:
[3, 5, 7, 9, 11]
Mean cả hai lớp đều là 7. Nhưng tình huống khác hẳn:
- Lớp A — trình độ đồng đều, mọi học sinh đều 7 điểm.
- Lớp B — chênh lệch lớn, có học sinh chỉ 3 điểm và có học sinh tới 11 điểm.
Nếu chỉ nhìn mean, ta không thấy sự khác biệt này. Cần một đại lượng đo "các giá trị rải rác quanh mean cỡ nào" — gọi chung là độ phân tán (dispersion / spread). Range, variance, standard deviation là ba cách phổ biến.
Range — đại lượng đơn giản nhất
Range = giá trị lớn nhất trừ giá trị nhỏ nhất:
\[ \text{range} = \max(x) - \min(x) \]
Với lớp A: 7 - 7 = 0. Với lớp B: 11 - 3 = 8. Range phân biệt được hai lớp ngay.
Hạn chế: range chỉ phụ thuộc vào 2 giá trị cực trị, nên rất nhạy với outlier. Một học sinh điểm 0 do bỏ thi sẽ kéo range vọt lên dù toàn lớp đồng đều. Range cũng không nói gì về phân bố ở giữa.
Ta cần một đại lượng dùng tất cả giá trị, không chỉ min và max — đó là variance.
Variance — phương sai
Ý tưởng: với mỗi giá trị \( x_i \), đo khoảng cách tới mean rồi lấy trung bình các khoảng cách đó.
Vấn đề là tổng của các sai số \( x_i - \mu \) luôn bằng 0 (do định nghĩa của mean), nên phải bình phương trước khi cộng:
Population variance (khi có toàn bộ tổng thể):
\[ \sigma^2 = \frac{1}{n} \sum_{i=1}^n (x_i - \mu)^2 \]
Sample variance (khi chỉ có một mẫu):
\[ s^2 = \frac{1}{n-1} \sum_{i=1}^n (x_i - \bar{x})^2 \]
Mẫu số n-1 được gọi là Bessel correction (sẽ giải thích ở bước 6).
Vì sao bình phương?
- Bỏ dấu: \( (x_i - \mu)^2 \) luôn không âm, lệch dương và lệch âm không triệt tiêu nhau.
- Phạt outlier nặng hơn: sai số 10 đóng góp 100, trong khi sai số 2 chỉ đóng góp 4. Một điểm nằm xa mean sẽ ảnh hưởng tới variance nhiều gấp bội một điểm gần mean.
- Khả vi trơn: hàm bình phương có đạo hàm dễ tính, hữu ích khi tính gradient (xem bài 25, 27). Hàm
|x - μ|không khả vi tại 0.
Tính tay cho lớp B
Dữ liệu [3, 5, 7, 9, 11], mean \( \bar{x} = 7 \).
- Sai số:
-4, -2, 0, 2, 4 - Bình phương:
16, 4, 0, 4, 16 - Tổng:
40 - Population variance:
40 / 5 = 8 - Sample variance:
40 / 4 = 10
Với lớp A toàn 7 điểm, mọi sai số đều bằng 0 nên variance = 0 — đúng với trực giác "không phân tán".
Nhược điểm về đơn vị
Nếu \( x_i \) tính bằng điểm, thì \( (x_i - \bar{x})^2 \) có đơn vị "điểm bình phương" — không có ý nghĩa thực tế. Đây là lý do ta thường dùng standard deviation thay vì variance khi muốn báo cáo.
Standard deviation
Standard deviation (độ lệch chuẩn) là căn bậc hai của variance:
\[ \sigma = \sqrt{\sigma^2}, \qquad s = \sqrt{s^2} \]
Lấy căn để đưa đơn vị về lại đơn vị gốc của dữ liệu. Với lớp B:
- Population std: \( \sigma = \sqrt{8} \approx 2.83 \) điểm.
- Sample std: \( s = \sqrt{10} \approx 3.16 \) điểm.
Ý nghĩa: trung bình các giá trị lệch khỏi mean khoảng 2.83 điểm (nếu coi 5 học sinh là toàn bộ tổng thể).
Quy ước ký hiệu thường gặp: \( \mu, \sigma \) cho population; \( \bar{x}, s \) cho sample. Khi đọc paper, để ý ký hiệu để biết tác giả dùng công thức nào.
Population n vs Sample n-1
Khi nào dùng cái nào
- Population (n): khi dữ liệu trong tay là toàn bộ tổng thể — ví dụ điểm thi của tất cả học sinh trong một lớp cụ thể, không cần suy ra cho ai khác.
- Sample (n-1): khi dữ liệu chỉ là mẫu rút ra từ một tổng thể lớn hơn — ví dụ 1000 user trong một A/B test đại diện cho hàng triệu user của sản phẩm.
Trong ML, dataset huấn luyện gần như luôn là sample. Vì vậy đa số thư viện thống kê dùng n-1.
Bessel correction — trực giác
Khi tính sample variance, ta dùng \( \bar{x} \) (mean của mẫu) thay vì \( \mu \) (mean thực của tổng thể) — mà \( \bar{x} \) được "fit" ngay vào mẫu, làm tổng \( \sum (x_i - \bar{x})^2 \) nhỏ hơn so với \( \sum (x_i - \mu)^2 \). Chia cho n-1 bù lại độ "co" này, cho ước lượng không chệch (unbiased estimator) của variance tổng thể.
Khi n lớn, sự khác biệt giữa chia n và n-1 rất nhỏ. Khi n nhỏ (chục mẫu trở xuống), sự khác biệt đáng kể.
Mặc định trong thư viện Python
numpy.var(x),numpy.std(x)— mặc địnhddof=0, chian(population).numpy.var(x, ddof=1)— chian-1(sample).pandas.Series.var(),pandas.Series.std()— mặc địnhddof=1, chian-1(sample).statistics.variance(x),statistics.stdev(x)— sample (chian-1).statistics.pvariance(x),statistics.pstdev(x)— population (chian).
Đây là điểm dễ nhầm khi so kết quả NumPy với Pandas hoặc với output mặc định của các tool thống kê khác.
Z-score — chuẩn hoá giá trị
Z-score trả lời câu hỏi: "Giá trị \( x \) cách mean bao nhiêu lần độ lệch chuẩn?"
\[ z = \frac{x - \mu}{\sigma} \]
Tính chất: nếu áp z-score cho toàn bộ dataset, kết quả mới có mean = 0 và std = 1 (chứng minh trực tiếp từ định nghĩa). Đây gọi là phân phối đã được "chuẩn hoá".
Ví dụ điểm thi: lớp có \( \mu = 7 \), \( \sigma = 2 \). Bạn được 9 điểm:
\[ z = \frac{9 - 7}{2} = 1 \]
Điểm của bạn cao hơn mean đúng 1 độ lệch chuẩn. Một bạn khác được 4 điểm: \( z = (4 - 7) / 2 = -1.5 \), thấp hơn mean 1.5 std.
Z-score giúp so sánh các giá trị nằm trên thang đo khác nhau. Một học sinh được 9/10 điểm Toán (z = 1) so với 85/100 điểm Văn (z = 1.5) — môn Văn em đó nổi bật hơn so với phần còn lại của lớp, dù điểm tuyệt đối lớn hơn ở môn Văn là điều đương nhiên do thang chấm khác nhau.
Trong bài 23 ta sẽ thấy: nếu dữ liệu xấp xỉ phân phối chuẩn, khoảng 68% giá trị có \( |z| \leq 1 \), 95% có \( |z| \leq 2 \), 99.7% có \( |z| \leq 3 \) (quy tắc 68–95–99.7).
Coefficient of variation
Coefficient of variation (CV) là tỷ số giữa std và mean:
\[ \text{CV} = \frac{\sigma}{\mu} \]
Đây là số không có đơn vị, dùng để so sánh độ phân tán giữa hai dataset có đơn vị hoặc thang đo khác nhau.
Ví dụ: so sánh độ biến động giữa giá Bitcoin (vài chục nghìn USD) và giá vàng (vài nghìn USD). Nói "std của BTC là 5000 USD" và "std của vàng là 100 USD" — con số tuyệt đối nói BTC biến động hơn, nhưng phải chia cho mean mới biết tỷ lệ. CV cho biết "biến động chiếm bao nhiêu phần trăm so với giá trị trung bình".
Lưu ý: CV chỉ có ý nghĩa khi mean dương và đủ xa 0. Với dữ liệu có mean gần 0 hoặc âm (như change trong giá), CV mất ý nghĩa.
Ứng dụng trong AI/ML
1. Standardization (StandardScaler)
Nhiều thuật toán ML (linear / logistic regression, SVM, KNN, neural network) hoạt động tốt hơn khi các feature có cùng thang đo. Trước khi train, ta thường biến đổi:
\[ x' = \frac{x - \bar{x}}{s} \]
Đây chính là z-score áp lên từng feature, sau biến đổi mỗi feature có mean = 0 và std = 1. Trong sklearn, class StandardScaler thực hiện đúng việc này. Quan trọng: chỉ "fit" trên train set, không fit trên test (để tránh data leakage).
2. Batch Normalization (preview Deep Learning)
Batch Normalization (Ioffe & Szegedy, 2015, arXiv:1502.03167) áp ý tưởng standardization lên activation giữa các layer trong mạng nơ-ron. Với mỗi mini-batch, lớp BatchNorm:
- Tính mean và variance theo batch.
- Chuẩn hoá activation về mean 0, variance 1.
- Áp tham số scale \( \gamma \) và shift \( \beta \) học được, để layer vẫn linh hoạt.
Lợi ích: giảm internal covariate shift, cho phép dùng learning rate lớn hơn, train ổn định hơn. Chi tiết sẽ trở lại trong series Deep Learning.
3. Outlier detection bằng z-score
Quy tắc đơn giản: điểm nào có \( |z| > 3 \) được coi là outlier (giả định phân phối xấp xỉ chuẩn — chỉ 0.3% giá trị nằm ngoài khoảng \( \pm 3\sigma \)).
def find_outliers(data, threshold=3):
mean = sum(data) / len(data)
var = sum((x - mean) ** 2 for x in data) / len(data)
std = var ** 0.5
return [x for x in data if abs((x - mean) / std) > threshold]
data = [10, 12, 11, 13, 12, 11, 10, 12, 95] # 95 là outlier
print(find_outliers(data)) # [95]
Cảnh báo: z-score outlier detection chỉ hợp lý khi dữ liệu xấp xỉ chuẩn và không bị chính các outlier "kéo" mean / std (xem bước 10).
Pitfall: nhạy với outlier
Vì bình phương khuếch đại sai số, variance và std rất nhạy với outlier. Một điểm xa mean 10 lần std đóng góp \( 100 \) vào tổng bình phương, trong khi điểm cách 1 std chỉ đóng góp 1.
So sánh hai dataset:
A = [10, 12, 11, 13, 12, 11, 10, 12]→ mean ≈ 11.4, std ≈ 1.1.B = [10, 12, 11, 13, 12, 11, 10, 100]→ mean ≈ 22.4, std ≈ 30.3.
Chỉ 1 outlier (100) đã làm std tăng ~28 lần. Khi nghi ngờ có outlier, có thể:
- Dùng median + IQR (interquartile range) thay cho mean + std.
- Dùng MAD (Median Absolute Deviation) — robust hơn std.
- Loại bỏ outlier dựa trên domain knowledge trước, rồi mới tính std.
- Trong sklearn, dùng
RobustScaler(dùng median và IQR) thayStandardScaler.
Code Python
Tự viết bằng list thuần
def mean(xs):
return sum(xs) / len(xs)
def variance(xs, sample=True):
m = mean(xs)
sq = sum((x - m) ** 2 for x in xs)
n = len(xs)
return sq / (n - 1) if sample else sq / n
def stdev(xs, sample=True):
return variance(xs, sample) ** 0.5
data = [3, 5, 7, 9, 11]
print(mean(data)) # 7.0
print(variance(data, sample=False)) # 8.0 (population)
print(variance(data, sample=True)) # 10.0 (sample)
print(stdev(data, sample=False)) # 2.828...
print(stdev(data, sample=True)) # 3.162...
Dùng module statistics (thư viện chuẩn)
import statistics as st
data = [3, 5, 7, 9, 11]
print(st.mean(data)) # 7
print(st.pvariance(data)) # 8 (population, chia n)
print(st.variance(data)) # 10 (sample, chia n-1)
print(st.pstdev(data)) # 2.828...
print(st.stdev(data)) # 3.162...
Preview NumPy (Module 4)
import numpy as np
data = np.array([3, 5, 7, 9, 11])
print(np.var(data)) # 8.0 (ddof=0, population)
print(np.var(data, ddof=1)) # 10.0 (sample)
print(np.std(data)) # 2.828...
print(np.std(data, ddof=1)) # 3.162...
Nhớ: NumPy mặc định population, Pandas và statistics mặc định sample. Khi so sánh số liệu, kiểm tra ddof trước.
Tính z-score
def zscore(xs):
m = mean(xs)
s = stdev(xs, sample=False)
return [(x - m) / s for x in xs]
scores = [60, 70, 80, 90, 100]
print(zscore(scores))
# [-1.414, -0.707, 0.0, 0.707, 1.414]
Bài tập
- Cho hai lớp điểm thi:
- Lớp X:
[6, 7, 7, 8, 7, 7, 6, 8] - Lớp Y:
[3, 9, 5, 10, 4, 9, 8, 8]
- Lớp X:
- Cho dữ liệu điểm
[55, 60, 65, 70, 75, 80, 85]. Chuẩn hoá thành z-score (dùng population std). - Một dataset có \( \bar{x} = 50 \), \( s = 10 \). Giá trị nào sau đây là outlier theo quy tắc \( |z| > 3 \): 70, 85, 25, 90?
- Hai sản phẩm có doanh thu:
- Sản phẩm A: mean = 1000, std = 50.
- Sản phẩm B: mean = 100, std = 20.
Đáp án
- Lớp X: mean = 7.0, sample std ≈ 0.756. Lớp Y: mean = 7.0, sample std ≈ 2.673. Cùng mean nhưng lớp X đồng đều hơn rõ rệt.
- Mean = 70, population variance = \( \tfrac{1}{7}(225+100+25+0+25+100+225) = 100 \), std = 10. Z-score:
[-1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5]. - Z-score: 70 → 2.0; 85 → 3.5; 25 → -2.5; 90 → 4.0. Chỉ 85 và 90 vượt ngưỡng \( |z| > 3 \), nên là outlier.
- CV(A) = 50 / 1000 = 5%. CV(B) = 20 / 100 = 20%. Tuy std tuyệt đối của A lớn hơn, B biến động tương đối mạnh hơn nhiều.
Tổng kết và bài tiếp theo
- Range = max - min: đơn giản, nhạy outlier, chỉ dùng 2 giá trị.
- Variance: trung bình của bình phương sai số quanh mean. Population chia
n, sample chian-1(Bessel correction). - Standard deviation = \( \sqrt{\text{variance}} \): cùng đơn vị với data, dễ interpret.
- Mặc định: NumPy
ddof=0(population); Pandas /statisticsddof=1(sample). - Z-score \( z = (x - \mu)/\sigma \): chuẩn hoá về mean 0 std 1, dùng cho StandardScaler, outlier detection.
- Coefficient of variation \( \sigma / \mu \): so sánh độ phân tán giữa dataset khác đơn vị.
- Variance / std rất nhạy với outlier do bình phương — khi cần, dùng median + IQR hoặc
RobustScaler.
Bài 23 tiếp tục: phân phối chuẩn — vì sao mean và std lại đặc biệt quan trọng đến vậy, và vì sao chúng xuất hiện ở khắp nơi trong AI.
