Mở đầu: bài toán 4 điểm làm đóng băng cả một ngành khoa học
Năm 1969, Marvin Minsky và Seymour Papert xuất bản một cuốn sách chứng minh rằng perceptron — mô hình neuron nhân tạo đời đầu, thứ đang được kỳ vọng sẽ dẫn tới trí tuệ máy — không thể học nổi một hàm logic đơn giản đến mức trẻ con cũng hiểu: XOR (hoặc loại trừ). Chỉ 4 điểm dữ liệu. Phát hiện này góp phần gây ra "mùa đông AI" đầu tiên — gần một thập kỷ tài trợ nghiên cứu AI gần như đóng băng, vì niềm tin sụp đổ rằng neuron nhân tạo chỉ là trò chơi không thể mở rộng.
Bài này xây dựng lại đúng khoảnh khắc lịch sử đó, bằng code bạn tự chạy. Bạn sẽ thấy 1 neuron (chính là model logistic regression của Bài 3) thật sự bất lực trước XOR, rồi thấy chỉ cần thêm một lớp ẩn — ý tưởng bị bỏ quên suốt "mùa đông" đó — là bài toán được giải gọn. Nhưng đây không phải chuyện cổ tích có hậu đơn giản: bạn sẽ tận mắt xem cùng một kiến trúc mạng, với hai cách khởi tạo ngẫu nhiên khác nhau, cho ra hai kết cục hoàn toàn khác — một bài học về giới hạn thật sự của gradient descent mà rất ít tài liệu nói thẳng.
1. Từ hồi quy logistic đến neuron
Tin tốt: bạn đã xây một neuron ở Bài 3, chỉ chưa gọi nó bằng cái tên đó. Một neuron nhân tạo chỉ là: nhận đầu vào $x$, tính tổ hợp tuyến tính $z = w \cdot x + b$, rồi áp một hàm phi tuyến $\sigma$ lên trên:
$$\text{neuron}(x) = \sigma(w \cdot x + b)$$
Đây chính xác là công thức logistic regression của Bài 3 — hồi quy logistic LÀ một neuron đơn, dùng sigmoid làm $\sigma$. Bài 3 đã chỉ ra decision boundary của nó luôn là một đường thẳng (hay mặt phẳng ở chiều cao hơn) — vì bên trong $z=w\cdot x+b$ chỉ là phép biến đổi tuyến tính, và $\sigma(0)=0{,}5$ là một hằng số cố định bất kể $\sigma$ là gì. Hệ quả trực tiếp: 1 neuron chỉ vẽ được đúng 1 đường ranh giới thẳng, bất kể chọn hàm phi tuyến nào. Muốn đường ranh giới cong — phải xếp chồng nhiều neuron lại. Câu hỏi của cả bài: xếp chồng thế nào?
2. Vì sao BẮT BUỘC phải có phi tuyến
Thử ý tưởng ngây thơ nhất: xếp 2 lớp tuyến tính thuần (bỏ hẳn $\sigma$), lớp 1 biến đổi $x$ bằng ma trận $W_1$, lớp 2 biến đổi kết quả đó bằng $W_2$. Kỳ vọng: 2 lớp thì mạnh hơn 1 lớp. Sự thật toán học phá vỡ kỳ vọng đó trong đúng 1 dòng:
$$W_2(W_1 x) = (W_2 W_1)\, x$$
Nhân ma trận có tính kết hợp — $W_2(W_1 x)$ luôn bằng $(W_2 W_1)x$, và $(W_2 W_1)$ tự nó CHỈ LÀ một ma trận khác. Xếp chồng bao nhiêu lớp tuyến tính cũng gộp lại thành đúng 1 phép biến đổi tuyến tính duy nhất — không mạnh hơn 1 lớp một chút nào. Kiểm chứng bằng số cụ thể (khớp self-test): $W_1 = \begin{pmatrix}1&2\\3&4\\5&6\end{pmatrix}$, $W_2 = \begin{pmatrix}1&0&1\\2&1&0\end{pmatrix}$, $x=(1,2)$: cả hai cách tính đều cho đúng $(22, 21)$.
Đây là lý do bắt buộc phải có hàm phi tuyến $\sigma$ xen giữa các lớp — không phải để "cho đẹp", mà vì thiếu nó, mọi kiến trúc dù sâu bao nhiêu tầng cũng suy biến về đúng 1 lớp tuyến tính. Ba lựa chọn phổ biến, cùng đạo hàm (bắt buộc phải biết để làm backward Mục 5):
| $z$ | sigmoid $\sigma(z)$ | tanh | ReLU | ReLU' |
|---|---|---|---|---|
| $-2$ | $0{,}1192$ | $-0{,}9640$ | $0$ | $0$ |
| $-1$ | $0{,}2689$ | $-0{,}7616$ | $0$ | $0$ |
| $0$ | $0{,}5000$ | $0{,}0000$ | $0$ | $0$ |
| $1$ | $0{,}7311$ | $0{,}7616$ | $1$ | $1$ |
| $2$ | $0{,}8808$ | $0{,}9640$ | $2$ | $1$ |
Nhìn vào $z=-2$: cả sigmoid lẫn tanh vẫn cho ra giá trị khác 0 (dù nhỏ), nhưng đạo hàm của chúng ($\sigma'(z)=\sigma(1-\sigma)$, $\tanh'(z)=1-\tanh^2$) đã gần bằng $0$ — đây là vùng bão hoà (saturation), gradient tắt dần nhưng KHÔNG BAO GIỜ tuyệt đối bằng $0$. ReLU khác hẳn: với mọi $z \leq 0$, cả giá trị lẫn đạo hàm đều đúng bằng $0$ tuyệt đối — không phải "gần 0", mà là $0$ chính xác. Khác biệt tưởng nhỏ này gây ra cạm bẫy nghiêm trọng nhất mục này.
- Tại $z=0$ (gần điểm cân bằng): $\tanh'(0) = 1{,}0$ so với $\sigma'(0) = 0{,}25$ — tanh dốc gấp đúng 4 lần sigmoid, học nhanh hơn hẳn ở vùng gần gốc toạ độ. Đây là lý do thực hành phổ biến "tanh hội tụ nhanh hơn sigmoid ở hidden layer".
- Tại $z=-4$ (đã hơi bão hoà): $\tanh'(-4) \approx 0{,}00134$ trong khi $\sigma'(-4) \approx 0{,}01766$ — lần này sigmoid lại có gradient lớn hơn tanh tới 13 lần.
- Tại $z=-6$ (bão hoà sâu): chênh lệch càng cực đoan hơn — $\sigma'(-6) \approx 0{,}00247$ so với $\tanh'(-6) \approx 0{,}000025$, gấp gần 100 lần.
Vì $\text{ReLU}'(z) = 0$ tuyệt đối khi $z \leq 0$, một khi cập nhật trọng số khiến neuron cho $z \leq 0$ trên MỌI điểm dữ liệu huấn luyện, gradient của nó theo mọi tham số cũng bằng $0$ tuyệt đối mãi mãi — GD thuần không còn cách nào cập nhật lại neuron đó. Kiểm chứng cụ thể: neuron $y=\text{ReLU}(wx+b)$ với $w=0{,}1$, $b=-5$, dữ liệu $x \in \{-2,-1,0,1,2\}$:
// w=0.1, b=-5: z tai moi x deu AM
[-2,-1,0,1,2].map(x => 0.1*x - 5);
// -> [-5.2, -5.1, -5.0, -4.9, -4.8] -- TAT CA < 0
// Vi ReLU'(z)=0 khi z<0, gradient dW va dB deu bang 0 tai day
// Chay THEM 50 buoc GD (voi learning rate binh thuong 0.1):
// w, b SAU 50 buoc == w, b BAN DAU het (0.1, -5) - khong nhich mot ly nao
Không phải "gradient rất nhỏ nên học chậm" — mà là gradient đúng bằng $0$, một điểm bất động tuyệt đối. Neuron này đã "chết": đầu ra của nó luôn là $0$, đóng góp $0$ vào mọi phép tính phía sau, và không thuật toán GD thuần nào lôi nó về vùng dương lại được — dù chạy thêm bao nhiêu epoch. Đây là lý do sigmoid/tanh (bão hoà nhưng không BAO GIỜ có gradient tuyệt đối $0$) đôi khi được chọn thay ReLU cho các mạng rất mỏng manh; và cũng là lý do các biến thể như Leaky ReLU (giữ một độ dốc nhỏ khác $0$ ở vùng âm) ra đời. Demo Mục 5 cho bạn tận mắt thấy hiện tượng này xảy ra trong lúc huấn luyện thật, không chỉ trên ví dụ dựng sẵn.
3. Kiến trúc MLP: width, depth, và cạm bẫy đọc nhầm universal approximation
Xếp nhiều neuron thành 1 hàng gọi là 1 layer; xếp nhiều layer nối tiếp gọi là
MLP. Hai trục mở rộng: width (số neuron mỗi layer — mạng "rộng" hơn) và
depth (số layer — mạng "sâu" hơn). Forward pass của MLP 2 lớp chỉ là 2 phép
matmul + add liên tiếp — dùng thẳng NeuroJS của
Bài 5, không viết lại vòng lặp nhân ma trận nào:
import { Tensor, matmul, add } from './ai-neuro.js';
function forward(params, X, activation) {
const z1 = add(matmul(Tensor.fromNested(X), Tensor.fromNested(params.W1)), Tensor.fromNested(params.b1));
const a1 = z1.toNested().map(row => row.map(activation)); // phi tuyen - BAT BUOC (Muc 2)
const z2 = add(matmul(Tensor.fromNested(a1), Tensor.fromNested(params.W2)), Tensor.fromNested(params.b2));
return z2.toNested().map(row => sigmoid(row[0])); // dau ra: xac suat
}
Đếm tham số theo đúng công thức "mỗi neuron có 1 trọng số cho mỗi input + 1 bias": layer với input chiều $n_{in}$, output chiều $n_{out}$ có $n_{in} \times n_{out} + n_{out}$ tham số. Ví dụ kiến trúc $2 \to 2 \to 1$ (mạng XOR tối giản Mục 4): layer 1 có $2 \times 2 + 2 = 6$, layer 2 có $2 \times 1 + 1 = 3$, tổng $9$ tham số. Mở rộng lên $2 \to 4 \to 1$ (rộng hơn): $2 \times 4 + 4 = 12$ cộng $4 \times 1 + 1 = 5$, tổng $17$ tham số — chỉ tăng width từ $2$ lên $4$ đã gần gấp đôi số tham số.
Một kết quả toán học nổi tiếng — universal approximation theorem — phát biểu rằng một MLP chỉ với 1 hidden layer đủ rộng có thể xấp xỉ bất kỳ hàm liên tục nào với sai số nhỏ tuỳ ý. Nghe như lời hứa vạn năng. Nhưng đây là cạm bẫy đọc nhầm phổ biến nhất của định lý này:
4. XOR: bài toán đóng băng cả ngành, và vì sao 1 hidden layer giải gọn
Bài toán XOR: 4 điểm $(0,0)\to 0$, $(0,1)\to 1$, $(1,0)\to 1$, $(1,1)\to 0$. Vẽ 4 điểm này ra giấy — thử tìm 1 đường thẳng tách 2 lớp. Không có đường nào cả: 2 điểm nhãn $1$ nằm chéo nhau, 2 điểm nhãn $0$ cũng nằm chéo nhau. Đây chính là ví dụ Minsky & Papert dùng năm 1969 để chứng minh giới hạn của perceptron. Kiểm chứng bằng logistic regression thật (Bài 3), không đoán bằng mắt:
// Logistic regression THUAN (1 neuron, khong hidden layer) tren XOR
// du chay bao nhieu epoch, du khoi tao tu dau (gan 0):
trainLogisticOnXOR([0.1, -0.05, 0.02], lr=0.5, epochs=3000);
// -> w1=0.0000, w2=0.0000, b=0.0000 (hoi tu VE DUNG 0, khong nhich)
// -> loss = 0.6931 = ln(2) (CHINH XAC bang loss cua "doan ngau nhien 50/50")
// -> accuracy = 2/4 (dung 1 nua, ngang tung dong xu)
Không phải learning rate sai hay thiếu epoch — vì tính đối xứng hoàn hảo của XOR (đảo bit đầu vào thì nhãn cũng đảo theo), gradient trung bình trên cả 4 điểm triệt tiêu chính xác về $0$ tại $w=0, b=0$, và logistic regression đứng yên mãi mãi ở điểm cân bằng vô dụng đó — loss đúng bằng $\ln(2) \approx 0{,}6931$, xác suất luôn $0{,}5$ cho mọi điểm, không hơn gì tung đồng xu. Đây là bằng chứng số học rõ ràng nhất cho "1 neuron chỉ vẽ được 1 đường thẳng" của Mục 1.
Giờ thêm đúng 1 hidden layer với 2 neuron tanh — kiến trúc $2 \to 2 \to 1$ tối giản (backward tính tay đầy đủ ở Mục 5), khởi tạo ngẫu nhiên với seed $42$:
| Epoch | Loss |
|---|---|
| 0 | $0{,}7018$ |
| 300 | $0{,}1452$ |
| 900 | $0{,}0102$ |
| 3000 (hội tụ) | $0{,}0023$ |
Kết quả cuối: cả 4 điểm được phân loại đúng tuyệt đối ($4/4$), xác suất dự đoán $0{,}0017 /0{,}9970/0{,}9970/0{,}0014$ — gần như hoàn hảo. Chỉ 1 hidden layer 2 neuron đã đủ giải bài toán từng đóng băng cả ngành khoa học suốt gần 1 thập kỷ.
Nhưng — và đây là điểm mà hầu hết tài liệu bỏ qua — không phải seed khởi tạo nào cũng giải được. Chạy đúng kiến trúc $2\to2\to1$, đúng thuật toán, chỉ đổi seed khởi tạo ngẫu nhiên, trên $30$ seed khác nhau:
| Kết cục | Số seed / 30 | Loss hội tụ |
|---|---|---|
| Giải đúng hoàn toàn ($4/4$) | $23$ ($77\%$) | $\approx 0{,}002$ |
| Mắc kẹt ở cực tiểu cục bộ ($2/4$) | $7$ ($23\%$) | $0{,}3475$ (đứng yên, không tiến thêm) |
Gần 1/4 số lần khởi tạo khiến mạng — dù về mặt lý thuyết HOÀN TOÀN có khả năng biểu diễn nghiệm đúng (universal approximation đảm bảo điều đó) — mắc kẹt ở một cực tiểu cục bộ và không bao giờ thoát ra được bằng GD thuần. Đây chính xác là khoảng cách giữa "tồn tại nghiệm" và "GD tìm ra nghiệm" của Mục 3, giờ đã có con số thật. Demo Mục 5 cho bạn bấm "khởi tạo lại" nhiều lần để tự chứng kiến cả hai kết cục.
- $H=2$: $23/30$ seed giải đúng ($77\%$) — khớp số liệu ở trên.
- $H=4$: $30/30$ seed giải đúng ($100\%$) — không còn seed nào mắc kẹt.
- $H=8$: vẫn $30/30$ ($100\%$).
5. Thực hành: Neural Playground (bản gọn) — train MLP thật trên XOR
Trước khi chạm demo, đây là toàn bộ backward pass tính tay cho kiến trúc $2\to H\to1$ — phép toán cuối cùng bạn phải tự đạo hàm trong series này:
// dL/dz2 = yhat - y -- KET QUA DEP giong het Bai 3, khong doi khi them hidden layer
const dz2 = yhat.map((p, i) => p - y[i]);
// Lan nguoc qua layer 2: dL/dW2 = a1^T . dz2 ; dL/db2 = mean(dz2)
const dW2 = /* a1^T @ dz2 / N */;
const db2 = /* mean(dz2) */;
// Lan tiep qua activation: dL/da1 = dz2 @ W2^T ; dL/dz1 = dL/da1 * activation'(z1)
const da1 = /* dz2 @ W2^T */;
const dz1 = da1.map((row, i) => row.map((v, h) => v * (1 - a1[i][h]**2))); // dao ham tanh
// Lan qua layer 1: dL/dW1 = X^T . dz1 ; dL/db1 = mean(dz1)
const dW1 = /* X^T @ dz1 / N */;
const db1 = /* mean(dz1) */;
Bấm Khởi tạo lại nhiều lần để tự thấy tỉ lệ $77\%/23\%$ ở Mục 4 — chọn ReLU để thấy dying ReLU xảy ra ngay trong huấn luyện thật (không chỉ ví dụ dựng sẵn của Mục 2):
9 tham số ($2\to2\to1$). Với tanh: ~77% seed hội tụ 4/4, ~23% mắc kẹt ở 2/4 (đúng tỉ lệ Mục 4). Với ReLU: thử vài seed để thấy "Neuron chết" xuất hiện — mạng vẫn chạy, không lỗi gì, chỉ học sai/kém hơn hẳn.
Đối chiếu công nghiệp: PyTorch định nghĩa đúng kiến trúc này bằng nn.Sequential:
# Doi chieu 1-1 voi kien truc 2->2->1 cua demo - cai dat: pytorch.org/get-started/locally
import torch
import torch.nn as nn
model = nn.Sequential(
nn.Linear(2, 2), # layer 1: 2 input -> 2 hidden (6 tham so: 4 weight + 2 bias)
nn.Tanh(), # phi tuyen BAT BUOC (Muc 2) - PyTorch tu tinh dao ham qua autograd
nn.Linear(2, 1), # layer 2: 2 hidden -> 1 output (3 tham so)
nn.Sigmoid(),
)
print(sum(p.numel() for p in model.parameters())) # 9 - khop dem tay Muc 3
X = torch.tensor([[0.,0.],[0.,1.],[1.,0.],[1.,1.]])
y = torch.tensor([[0.],[1.],[1.],[0.]])
opt = torch.optim.SGD(model.parameters(), lr=0.5)
loss_fn = nn.BCELoss()
for epoch in range(3000):
opt.zero_grad()
loss = loss_fn(model(X), y)
loss.backward() # autograd TU tinh toan bo backward - khong can Muc 5 nua
opt.step()
Tóm lược
- ✅ 1 neuron = logistic regression — chỉ vẽ được 1 đường ranh giới thẳng, bất kể chọn $\sigma$ nào.
- ✅ Chồng lớp tuyến tính vẫn là tuyến tính ($W_2(W_1x)=(W_2W_1)x$, verified) — phi tuyến là BẮT BUỘC, không phải tuỳ chọn.
- ✅ Dying ReLU là điểm bất động tuyệt đối: một khi $z\leq0$ trên mọi dữ liệu, gradient $=0$ chính xác, không GD thuần nào hồi sinh được — khác hẳn sigmoid/tanh (bão hoà nhưng gradient không bao giờ tuyệt đối $0$).
- ✅ Universal approximation là định lý về SỰ TỒN TẠI, không đảm bảo GD tìm ra — verified bằng số: cùng kiến trúc XOR, $77\%$ seed giải đúng, $23\%$ mắc kẹt cực tiểu cục bộ.
- ✅ XOR không tách tuyến tính được (logistic thuần hội tụ về loss$=\ln 2$, acc $2/4$) nhưng $1$ hidden layer $2$ neuron giải đúng $4/4$ — đúng bài toán làm đóng băng cả ngành AI năm 1969.
Tải file code thực hành minh họa bài học
File JavaScript gồm MLP 2→H→1 (forward dùng NeuroJS, backward tính tay), logistic regression trên XOR,
và ví dụ dying ReLU, với self-test đối chiếu mọi con số trích dẫn trong bài (chạy
node mlp_xor.js):
Bình luận