Mở đầu: bạn không thể viết rule cho mọi thứ
Hãy thử viết một hàm nhận diện chữ số 7 viết tay. Bạn bắt đầu: "nét ngang trên cùng, rồi nét
xiên xuống trái". Nhưng nét ngang dài bao nhiêu pixel? Nghiêng bao nhiêu độ thì vẫn là 7, bao
nhiêu thì thành 1? Người Pháp gạch ngang giữa thân số 7 thì sao? Mực đậm nhạt, giấy nhăn, chụp lệch góc?
Mỗi câu trả lời đẻ thêm mười trường hợp ngoại lệ — và đây mới là một chữ số, trong bài
toán được coi là "dễ nhất" của thị giác máy. Rule viết tay sụp đổ không phải vì lập trình viên kém, mà vì
bản thân tri thức "thế nào là số 7" không tồn tại dưới dạng quy tắc rõ ràng — nó nằm rải rác trong hàng
nghìn ví dụ.
Học máy đảo ngược cách làm: thay vì người viết rule, ta đưa máy các ví dụ và để nó tự tìm quy luật. Bài mở màn của lộ trình 19 bài này dạy phiên bản nhỏ nhất có thể của ý tưởng đó: hồi quy tuyến tính — fit một đường thẳng qua đám điểm dữ liệu. Nhỏ, nhưng chứa đủ bộ xương của mọi hệ thống AI bạn sẽ gặp trong 18 bài sau: model có tham số, hàm loss đo độ sai, và dữ liệu làm thầy. Cuối bài, bạn kéo thả điểm trên canvas và xem đường thẳng tự điều chỉnh theo — cùng công thức mà chính GPT dùng để học, chỉ khác quy mô.
Tài nguyên ngoài: Khan Academy — Statistics, PyTorch (cho snippet đối chiếu cuối bài, không bắt buộc).
1. Từ quy tắc viết tay đến học từ dữ liệu
Lập trình truyền thống là: quy tắc + dữ liệu vào → đáp án ra. Bạn viết
if (nhiet_do > 38) return 'sot'; — quy tắc do người nghĩ ra, máy chỉ thực thi.
ML lật ngược sơ đồ:
dữ liệu vào + đáp án mẫu → máy tự tìm quy tắc. Ta không bảo máy "số 7 có nét ngang" — ta
đưa 10.000 tấm ảnh kèm nhãn đúng, máy tự điều chỉnh các tham số bên trong cho tới khi dự đoán khớp nhãn.
Quy tắc học được không nằm dưới dạng if/else đọc được, mà nằm trong các con số —
chính là "tham số" ta sẽ gặp ngay ở Mục 2.
Học máy có 3 nhánh lớn, phân biệt bằng việc dữ liệu cho máy cái gì:
| Nhánh | Dữ liệu có gì | Ví dụ | Học ở đâu trong series |
|---|---|---|---|
| Học có giám sát (supervised) | Từng ví dụ kèm đáp án đúng (nhãn) | Ảnh → chữ số; diện tích nhà → giá | Bài này tới Bài 3, và hầu hết series |
| Học không giám sát (unsupervised) | Chỉ có dữ liệu, không nhãn | Gom khách hàng thành nhóm tự nhiên | Bài 4 (K-means, PCA) |
| Học tăng cường (reinforcement) | Không đáp án, chỉ có điểm thưởng thưa và đến trễ | Chơi game, robot, RLHF của ChatGPT | Bài 17 (Q-learning) |
Bài này thuộc nhánh thứ nhất — dạng bài "cho x, đoán y" với y là số liên tục (giá nhà, nhiệt độ, doanh thu), gọi là hồi quy (regression). Khi y là loại (spam/không spam, chó/mèo) thì gọi là phân loại — Bài 3 sẽ xử lý.
if giải xong.
2. Bộ ba Model – Loss – Data
Mọi hệ thống học máy — từ đường thẳng bài này tới GPT — đều gồm đúng 3 mảnh. Ta sẽ gọi tên chúng trên một bài toán cụ thể: dự đoán giá nhà (y, đơn vị trăm triệu) từ diện tích (x, đơn vị chục m²), với 4 căn đã bán được ghi lại: $(1, 2)$, $(2, 3)$, $(3, 5)$, $(4, 6)$.
Mảnh 1 — Model (giả thuyết): ta giả định quan hệ có dạng đường thẳng:
$$\hat{y} = w \cdot x + b$$
Trong đó $\hat{y}$ (đọc "y mũ") là dự đoán của model, $x$ là đầu vào, còn $w$ (độ dốc — weight) và $b$ (điểm cắt trục tung — bias) là 2 tham số học được. "Học", với model này, nghĩa đen là: tìm 2 con số $w, b$ sao cho đường thẳng đi sát đám điểm nhất. GPT-4 cũng chỉ là phiên bản phóng đại của câu này — thay vì 2 tham số, nó có hàng nghìn tỷ.
Mảnh 2 — Loss (hàm mất mát): "sát đám điểm nhất" phải đo được bằng một con số, nếu không thì không so sánh được hai đường thẳng với nhau. Chuẩn đo phổ biến nhất cho hồi quy là MSE:
$$L(w, b) = \frac{1}{n} \sum_{i=1}^{n} \left( \hat{y}_i - y_i \right)^2$$
Giải nghĩa: với từng điểm dữ liệu thứ $i$, lấy dự đoán $\hat{y}_i$ trừ đáp án thật $y_i$ (hiệu này gọi là residual — phần dư), bình phương lên, rồi lấy trung bình trên cả $n$ điểm. Loss càng nhỏ, đường càng khớp; loss bằng 0 nghĩa là đường xuyên qua đúng mọi điểm. Vì sao bình phương mà không lấy trị tuyệt đối? Hai lý do thật sự quan trọng: (1) bình phương phạt lỗi lớn nặng hơn hẳn — sai 2 bị phạt 4, sai 10 bị phạt 100, ép model ưu tiên sửa những chỗ sai to; (2) hàm bình phương khả vi tại mọi điểm (trơn tru, không gãy khúc như trị tuyệt đối tại 0) — điều kiện sống còn cho cả nghiệm giải tích ở Mục 3 lẫn gradient descent ở Bài 2.
// MSE — trung binh binh phuong sai so giua du doan (w*x + b) va dap an y
function mse(points, w, b) {
let s = 0;
for (const p of points) {
const err = w * p.x + b - p.y; // residual cua tung diem
s += err * err; // binh phuong: phat manh loi lon
}
return s / points.length; // trung binh tren ca n diem
}
Mảnh 3 — Data: 4 điểm $(x, y)$ ở trên. Dữ liệu là "thầy giáo" duy nhất: model không biết gì về nhà đất, nó chỉ biết kéo loss xuống thấp nhất trên những ví dụ được đưa. Hệ quả trực tiếp — chất lượng model bị chặn trên bởi chất lượng dữ liệu, và đây là nơi cạm bẫy đầu tiên của nghề xuất hiện:
Chính ưu điểm "phạt mạnh lỗi lớn" là con dao hai lưỡi: một điểm dữ liệu nhập sai tạo lỗi khổng lồ, bình phương lên nó lấn át mọi điểm đúng cộng lại. Thử bằng số thật (đã kiểm chứng bằng self-test trong file tải về): với 4 điểm ví dụ, đường fit là $w = 1{,}4$, $b = 0{,}5$, $R^2 = 0{,}98$ — gần hoàn hảo. Giờ thêm đúng một điểm gõ nhầm $(4, 60)$ — ai đó nhập 6,0 thành 60:
- $w$: từ $1{,}4$ nhảy lên $\approx 10{,}91$ — độ dốc tăng gần 8 lần;
- $b$: từ $0{,}5$ rơi xuống $\approx -15{,}35$;
- $R^2$: từ $0{,}98$ sụp còn $\approx 0{,}32$ — đường fit gần như vô dụng.
Bốn điểm đúng không cứu nổi một điểm sai. Bài học thực chiến: luôn nhìn dữ liệu trước khi train (vẽ nó ra!) và lọc/kiểm outlier — đừng tin mù quáng vào con số loss. Bấm nút "💥 Thêm outlier" trong demo Mục 5 để tận mắt xem đường thẳng bị giật khỏi đám điểm.
Hậu quả thực tế cụ thể ra sao? Thử dùng cả hai model để đoán giá một căn nhà mới 50m² ($x = 5$, đã kiểm chứng bằng số): model sạch cho $\hat{y} = 1{,}4 \times 5 + 0{,}5 = 7{,}5$ (750 triệu đồng); model bị outlier kéo lệch cho $\hat{y} = 10{,}91 \times 5 - 15{,}35 \approx 39{,}21$ (3,92 tỷ đồng) — chênh lệch hơn 3,1 tỷ đồng chỉ vì một ô dữ liệu gõ nhầm. Đây không phải sai số học thuật trừu tượng — đây là tiền thật bị định giá sai gấp hơn 5 lần.
3. Nghiệm giải tích: cho đạo hàm bằng 0
Vậy tìm $w, b$ tốt nhất bằng cách nào? Với model tuyến tính + MSE, may mắn hiếm hoi của toán học xảy ra: loss $L(w, b)$ là một mặt parabol lồi (hình cái bát) theo cả $w$ lẫn $b$ — có đúng một điểm thấp nhất, và tại đó đạo hàm theo cả hai tham số đều bằng 0. Giải hệ "đạo hàm = 0" cho ra công thức đóng (closed-form), gọi là nghiệm least squares:
$$w = \frac{\sum_{i} (x_i - \bar{x})(y_i - \bar{y})}{\sum_{i} (x_i - \bar{x})^2} \qquad b = \bar{y} - w \bar{x}$$
Giải nghĩa: $\bar{x}, \bar{y}$ là trung bình cộng của các $x_i$ và $y_i$; tử số của $w$ đo mức $x$ và $y$ cùng dao động quanh trung bình của chúng (hiệp phương sai), mẫu số đo độ trải của riêng $x$ (phương sai); còn $b$ được suy ra từ việc đường fit tốt nhất luôn đi qua điểm trung bình $(\bar{x}, \bar{y})$. Tính tay từng bước với 4 điểm dữ liệu của Mục 2:
| Bước | Tính | Kết quả |
|---|---|---|
| Trung bình | $\bar{x} = (1+2+3+4)/4$ ; $\bar{y} = (2+3+5+6)/4$ | $\bar{x} = 2{,}5$ ; $\bar{y} = 4$ |
| Tử số của $w$ | $(-1{,}5)(-2) + (-0{,}5)(-1) + (0{,}5)(1) + (1{,}5)(2)$ | $3 + 0{,}5 + 0{,}5 + 3 = 7$ |
| Mẫu số của $w$ | $(-1{,}5)^2 + (-0{,}5)^2 + (0{,}5)^2 + (1{,}5)^2$ | $2{,}25 + 0{,}25 + 0{,}25 + 2{,}25 = 5$ |
| $w$ và $b$ | $w = 7/5$ ; $b = 4 - 1{,}4 \times 2{,}5$ | $w = 1{,}4$ ; $b = 0{,}5$ |
| Kiểm lại MSE | residual: $0{,}1$; $-0{,}3$; $0{,}3$; $-0{,}1$ → $(0{,}01+0{,}09+0{,}09+0{,}01)/4$ | $L = 0{,}05$ |
Đường fit tốt nhất là $\hat{y} = 1{,}4x + 0{,}5$: mỗi chục m² thêm vào, giá tăng 140 triệu. Mọi con số
trong bảng đều tái lập được bằng file linear_regression.js tải về cuối bài — chạy
node linear_regression.js là thấy self-test đối chiếu đúng từng giá trị.
function fitLeastSquares(points) {
const n = points.length;
if (n < 2) return null; // 1 diem co vo so duong di qua — khong fit duoc
let sumX = 0, sumY = 0;
for (const p of points) { sumX += p.x; sumY += p.y; }
const meanX = sumX / n, meanY = sumY / n;
let sxy = 0, sxx = 0;
for (const p of points) {
sxy += (p.x - meanX) * (p.y - meanY); // tu so: x va y cung dao dong
sxx += (p.x - meanX) * (p.x - meanX); // mau so: do trai cua rieng x
}
if (sxx === 0) return null; // moi diem cung hoanh do — duong thang dung
const w = sxy / sxx;
return { w, b: meanY - w * meanX };
}
Đẹp như vậy, vì sao 18 bài còn lại không dùng tiếp công thức đóng? Vì nó là đặc sản chỉ có ở model tuyến tính + MSE. Chỉ cần model cong đi một chút (thêm hàm phi tuyến — Bài 6) hoặc tham số phình lên hàng triệu, hệ "đạo hàm = 0" không còn giải được bằng tay hay bằng công thức nào nữa. Khi đó ta cần chiến lược hoàn toàn khác: không giải phương trình nữa, mà dò dốc từng bước nhỏ — đứng ở điểm hiện tại, nhìn hướng nào làm loss giảm, bước một bước nhỏ về hướng đó, lặp lại nghìn lần.
4. Đánh giá model: R² và dữ liệu chưa thấy
MSE = 0,05 là tốt hay tệ? Không trả lời được — đơn vị của nó là "bình phương trăm triệu đồng", to nhỏ tuỳ thang đo dữ liệu. Để có một thang điểm không phụ thuộc đơn vị, ta so model với đối thủ ngây thơ nhất có thể: đường nằm ngang $\hat{y} = \bar{y}$ — tức "diện tích nào cũng đoán giá trung bình". Thang điểm đó là $R^2$:
$$R^2 = 1 - \frac{\sum_i (y_i - \hat{y}_i)^2}{\sum_i (y_i - \bar{y})^2}$$
Giải nghĩa: tử số là tổng bình phương sai số của model, mẫu số là tổng bình phương sai số của đường ngang ngây thơ; lấy 1 trừ đi tỉ lệ đó. $R^2 = 1$: model hoàn hảo (sai số 0). $R^2 = 0$: model chẳng hơn gì đoán trung bình. Với 4 điểm ví dụ: mẫu số $= 4 + 1 + 1 + 4 = 10$, tử số $= 0{,}2$, nên $R^2 = 1 - 0{,}02 = 0{,}98$ — model giải thích được 98% biến động của giá nhà.
$R^2$ âm chỉ xuất hiện khi đánh giá model trên dữ liệu KHÁC với dữ liệu đã dùng để fit — chính xác là tình huống train/test ở Bài 8. Ví dụ đã kiểm chứng: fit model trên 5 điểm có outlier $(4, 60)$ ra $w \approx 10{,}91$, $b \approx -15{,}35$; đem model đó đi chấm điểm trên một tập dữ liệu MỚI tuân theo đúng quan hệ tuyến tính sạch (không có outlier) thì $R^2 \approx -100{,}2$ — tệ hơn đường ngang tới hơn 100 lần. Đây là lý do "$R^2$ âm" gần như luôn là dấu hiệu model đang được đánh giá ngoài mẫu (out-of-sample), chứ hiếm khi là lỗi tính toán trong-mẫu.
function rSquared(points, w, b) {
const n = points.length;
let meanY = 0;
for (const p of points) meanY += p.y;
meanY /= n;
let ssRes = 0, ssTot = 0;
for (const p of points) {
const pred = w * p.x + b;
ssRes += (p.y - pred) * (p.y - pred); // sai so cua MODEL
ssTot += (p.y - meanY) * (p.y - meanY); // sai so cua duong ngang ngay tho
}
if (ssTot === 0) return 1; // moi y bang nhau — model nao cung "hoan hao"
return 1 - ssRes / ssTot;
}
Nhưng có một sự thật khó chịu mà cả series sẽ quay lại nhiều lần: $R^2 = 0{,}98$ ở trên được chấm trên chính 4 điểm dùng để fit — như học sinh tự chấm bài bằng đề đã lộ đáp án. Câu hỏi thật sự của học máy không phải "model khớp dữ liệu cũ tốt cỡ nào" mà là "model dự đoán dữ liệu CHƯA THẤY tốt cỡ nào" — căn nhà thứ 5 chưa bán mới là thứ ta cần đoán. Model càng phức tạp càng dễ "học thuộc lòng" tập train mà trượt thảm hại trên dữ liệu mới.
5. Thực hành: kéo thả điểm, đường thẳng tự fit
Toàn bộ lý thuyết trên gói trong demo dưới đây — chạy đúng 3 hàm
fitLeastSquares / mse / rSquared vừa học, không giả lập.
Bấm vào chỗ trống để thêm điểm, kéo điểm để di chuyển (cách kéo thả trên
canvas được giải thích ở Series Canvas Bài 9), và xem
đường fit + residual (đoạn đứt màu cam nối điểm với đường) + bảng số cập nhật theo thời gian thực:
Bấm chỗ trống để thêm điểm · kéo điểm để di chuyển · bấm "💥 Thêm outlier" rồi nhìn w, b và R² — đó chính là cạm bẫy MSE ở Mục 2 diễn ra trước mắt bạn.
Hai "trạng thái trống" của demo đều là hành vi đúng, không phải lỗi: dưới 2 điểm thì có vô số đường đi qua
nên không tồn tại đường fit duy nhất; mọi điểm cùng hoành độ thì đường fit tối ưu thẳng đứng — không viết
được dưới dạng $y = wx + b$ (hàm fitLeastSquares trả về null thay vì chia cho 0
ra NaN).
Cuối cùng, bản đối chiếu công nghiệp. Đoạn PyTorch dưới đây là bản 1-1 của bài này —
nn.Linear(1, 1) chính là $\hat{y} = wx + b$, MSELoss chính là công thức Mục 2 —
chỉ khác nó tìm $w, b$ bằng vòng lặp 2.000 bước (gradient descent — Bài 2) thay vì công thức đóng. Kết quả
hội tụ về đúng $w \approx 1{,}4$, $b \approx 0{,}5$ như ta tính tay (đã kiểm chứng bằng cách mô phỏng lại
đúng vòng lặp này trong Node, từ nhiều điểm khởi tạo khác nhau):
# Doi chieu 1-1 voi bai hoc — cai dat: pytorch.org/get-started/locally
import torch
import torch.nn as nn
# Cung 4 diem du lieu voi vi du tinh tay trong bai
x = torch.tensor([[1.0], [2.0], [3.0], [4.0]])
y = torch.tensor([[2.0], [3.0], [5.0], [6.0]])
model = nn.Linear(1, 1) # y_mu = w*x + b — dung model cua bai
loss_fn = nn.MSELoss() # dung cong thuc MSE cua Muc 2
optimizer = torch.optim.SGD(model.parameters(), lr=0.05)
for epoch in range(2000): # phuong phap LAP — Bai 2 giai thich co che
optimizer.zero_grad()
loss = loss_fn(model(x), y)
loss.backward() # Bai 7 se tu xay co che nay
optimizer.step()
print(model.weight.item(), model.bias.item()) # ~1.4 va ~0.5 — khop nghiem giai tich
Tóm lược
Bộ xương của mọi hệ thống học máy đã nằm gọn trong bài đầu tiên này:
- ✅ ML đảo ngược lập trình truyền thống: dữ liệu + đáp án mẫu → máy tự tìm quy tắc, dùng khi rule không viết nổi — và KHÔNG dùng khi 3 dòng if-else giải được.
- ✅ Bộ ba model–loss–data: giả thuyết $\hat{y} = wx + b$ với tham số học được; MSE đo độ sai (bình phương để phạt mạnh lỗi lớn + khả vi); dữ liệu là thầy duy nhất.
- ✅ Nghiệm giải tích: loss lồi → cho đạo hàm bằng 0 ra công thức đóng; tính tay được $w = 1{,}4$, $b = 0{,}5$ — nhưng đặc sản này không mở rộng cho model phi tuyến/triệu tham số.
- ✅ $R^2$: so model với đường ngang ngây thơ, không phụ thuộc đơn vị — và điểm chấm trên tập train luôn lạc quan hơn sự thật (chờ Bài 8).
- ✅ MSE nhạy outlier: 1 điểm nhập sai kéo $w$ tăng 8 lần, $R^2$ sụp từ 0,98 xuống 0,32 — luôn nhìn dữ liệu trước khi train.
Tải file code thực hành minh họa bài học
File JavaScript gồm 3 hàm fitLeastSquares / mse / rSquared của
bài kèm self-test đối chiếu đúng ví dụ tính tay (chạy node linear_regression.js, không cần
cài thêm gì):
Bình luận