Từ một perceptron đơn lẻ đến mạng nhiều tầng và thuật toán lan truyền ngược — đây là nền tảng của toàn bộ học sâu (deep learning) và là cầu nối sang thị giác máy tính (CV) cùng xử lý ngôn ngữ tự nhiên (NLP).
Perceptron là mô hình nơ-ron nhân tạo đơn giản nhất: lấy tổ hợp tuyến tính của đầu vào (cộng thêm độ chệch bias) rồi cho qua một hàm kích hoạt bước (step function) để ra quyết định nhị phân:
\[ \hat y = \text{step}(w^\top x + b) = \begin{cases} 1 & \text{nếu } w^\top x + b \ge 0 \\ 0 & \text{nếu } w^\top x + b \lt 0 \end{cases} \]
Trong đó \(x\) là vector đặc trưng đầu vào, \(w\) là vector trọng số, \(b\) là bias. Đại lượng \(z = w^\top x + b\) được gọi là tổng có trọng số (pre-activation).
Luật học perceptron. Với mỗi mẫu \((x, y)\), so sánh dự đoán \(\hat y\) với nhãn thật \(y\) rồi điều chỉnh trọng số theo hướng giảm sai số:
\[ w := w + \eta\,(y - \hat y)\,x, \qquad b := b + \eta\,(y - \hat y) \]
với \(\eta\) là tốc độ học (learning rate). Nếu dự đoán đúng thì \(y - \hat y = 0\) nên trọng số không đổi; nếu sai thì trọng số dịch về phía mẫu để lần sau dự đoán đúng hơn.
Giới hạn cốt tử. Một perceptron chỉ vẽ được một đường (siêu phẳng) phân chia — tức nó chỉ giải được bài toán phân tách tuyến tính. Nó không thể học hàm XOR, vì XOR không thể tách bằng một đường thẳng duy nhất. Muốn giải XOR bắt buộc phải xếp chồng nhiều tầng nơ-ron với hàm kích hoạt phi tuyến.
| x₁ | x₂ | XOR |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
Không có đường thẳng nào tách được cặp \(\{(0,1),(1,0)\}\) (nhãn 1) khỏi cặp \(\{(0,0),(1,1)\}\) (nhãn 0).
Logistic regression cho ra xác suất của bài toán 2 lớp. Khi có \(K\) lớp, ta dùng softmax regression (còn gọi là logistic đa thức). Mỗi lớp \(i\) có bộ trọng số riêng, sinh ra điểm số (logit) \(z_i = w_i^\top x + b_i\). Hàm softmax biến vector logit thành một phân phối xác suất hợp lệ (các thành phần dương và cộng lại bằng 1):
$$ \text{softmax}(z)_i = \frac{e^{z_i}}{\sum_{j=1}^{K} e^{z_j}} $$
Phép lấy mũ \(e^{z_i}\) bảo đảm mọi giá trị đều dương, còn việc chia cho tổng (chuẩn hóa) bảo đảm \(\sum_i \hat p_i = 1\). Lớp được dự đoán là lớp có \(\hat p_i\) lớn nhất.
Mất mát cross-entropy đa lớp. Với nhãn one-hot \(y\) (chỉ một thành phần bằng 1, còn lại 0), hàm mất mát cho một mẫu là:
\[ J = -\sum_{i=1}^{K} y_i \log \hat p_i \]
Vì \(y\) là one-hot nên thực chất \(J = -\log \hat p_{\text{lớp đúng}}\): mô hình bị phạt nặng khi gán xác suất thấp cho lớp đúng.
Gradient gọn đẹp. Một điểm tinh tế khiến softmax + cross-entropy rất được ưa dùng: đạo hàm của mất mát theo logit rút gọn về dạng cực kỳ đơn giản:
\[ \frac{\partial J}{\partial z} = \hat p - y \]
Chỉ là "xác suất dự đoán trừ nhãn thật". Chính nhờ gradient gọn này mà tầng output của hầu hết mạng phân loại đều dùng softmax + cross-entropy.
MLP xếp chồng nhiều tầng ẩn (hidden layers) giữa đầu vào và đầu ra. Tín hiệu lan truyền tiến (forward) qua từng tầng: kích hoạt của tầng \(l\) được tính từ kích hoạt của tầng trước \(l-1\):
\[ a^{(l)} = g\!\left(W^{(l)} a^{(l-1)} + b^{(l)}\right) \]
trong đó \(W^{(l)}\) là ma trận trọng số của tầng \(l\), \(b^{(l)}\) là bias, và \(g(\cdot)\) là hàm kích hoạt phi tuyến. Quy ước \(a^{(0)} = x\) (đầu vào), còn \(a^{(L)} = \hat y\) (đầu ra cuối cùng).
Tại sao phi tuyến là bắt buộc? Nếu mọi tầng đều tuyến tính, ghép \(L\) phép biến đổi tuyến tính lại với nhau vẫn chỉ là một phép tuyến tính: \(W^{(2)}(W^{(1)}x) = (W^{(2)}W^{(1)})x\). Nghĩa là dù xếp bao nhiêu tầng cũng không mạnh hơn một perceptron. Chính hàm kích hoạt phi tuyến \(g\) giữa các tầng mới cho mạng khả năng học các ranh giới cong, phức tạp.
Các hàm kích hoạt thông dụng:
| Hàm | Công thức | Ưu / Nhược |
|---|---|---|
| Sigmoid | \(\sigma(z)=\dfrac{1}{1+e^{-z}}\) | Ra (0,1), mượt — nhưng dễ bão hòa ở hai đầu khiến gradient tiêu biến (vanishing) |
| Tanh | \(\tanh(z)=\dfrac{e^{z}-e^{-z}}{e^{z}+e^{-z}}\) | Ra (−1,1), tâm 0 nên hội tụ tốt hơn sigmoid — vẫn bão hòa ở hai đầu |
| ReLU | \(\max(0,z)\) | Rẻ, không bão hòa phía dương, giảm vanishing — nhưng có thể "chết" (dying ReLU) khi đầu ra luôn bằng 0 |
| Leaky ReLU | \(\max(\alpha z, z),\ \alpha\!\approx\!0.01\) | Cho dòng gradient nhỏ ở vùng âm để tránh nơ-ron chết |
Định lý xấp xỉ phổ quát (Universal Approximation Theorem). Một MLP chỉ với một tầng ẩn đủ rộng và hàm kích hoạt phi tuyến có thể xấp xỉ bất kỳ hàm liên tục nào trên một miền đóng bị chặn, với độ chính xác tùy ý. Định lý này khẳng định sức biểu diễn lý thuyết của mạng — dù trong thực hành, mạng sâu (nhiều tầng) thường học hiệu quả hơn nhiều so với một tầng cực rộng.
Để huấn luyện MLP ta cần gradient của hàm mất mát theo mọi trọng số. Tính trực tiếp thì cực tốn kém. Back-propagation là thuật toán tính toàn bộ gradient đó một cách hiệu quả nhờ quy tắc chuỗi (chain rule) của đạo hàm, lan ngược sai số từ tầng output về tầng input. Mỗi vòng huấn luyện gồm hai lượt:
Forward pass. Đẩy dữ liệu qua mạng tầng này nối tầng kia, lưu lại các \(z^{(l)}\) và \(a^{(l)}\), rồi tính output \(\hat y\) và mất mát \(J\).
Backward pass. Lan ngược "sai số" \(\delta\) từ tầng cuối về đầu. Ký hiệu \(\delta^{(l)} = \partial J / \partial z^{(l)}\), \(\odot\) là nhân từng phần tử, \(g'\) là đạo hàm hàm kích hoạt.
Sai số tại tầng output \(L\):
\[ \delta^{(L)} = \nabla_a J \odot g'\!\left(z^{(L)}\right) \]
Lan ngược sai số xuống các tầng trước (đây chính là chain rule đang làm việc):
\[ \delta^{(l)} = \left(W^{(l+1)\top}\,\delta^{(l+1)}\right) \odot g'\!\left(z^{(l)}\right) \]
Từ sai số mỗi tầng, gradient theo trọng số và bias là:
\[ \frac{\partial J}{\partial W^{(l)}} = \delta^{(l)}\,a^{(l-1)\top}, \qquad \frac{\partial J}{\partial b^{(l)}} = \delta^{(l)} \]
Cập nhật trọng số. Có gradient rồi, ta dịch trọng số ngược hướng gradient bằng gradient descent:
\[ W^{(l)} := W^{(l)} - \eta\,\frac{\partial J}{\partial W^{(l)}}, \qquad b^{(l)} := b^{(l)} - \eta\,\frac{\partial J}{\partial b^{(l)}} \]
Tóm tắt một vòng huấn luyện:
Ví dụ NumPy — MLP một tầng ẩn (forward + backward):
import numpy as np def sigmoid(z): return 1 / (1 + np.exp(-z)) def sigmoid_grad(a): return a * (1 - a) # a = sigmoid(z) # Khởi tạo trọng số ngẫu nhiên (input 2 -> ẩn 4 -> output 1) W1 = np.random.randn(2, 4) * 0.5; b1 = np.zeros((1, 4)) W2 = np.random.randn(4, 1) * 0.5; b2 = np.zeros((1, 1)) eta = 0.1 # X: (n, 2) y: (n, 1) — ví dụ bài toán XOR for epoch in range(10000): # --- Forward pass --- a1 = sigmoid(X @ W1 + b1) # tầng ẩn a2 = sigmoid(a1 @ W2 + b2) # output # --- Backward pass (chain rule) --- d2 = (a2 - y) * sigmoid_grad(a2) # sai số tầng output d1 = (d2 @ W2.T) * sigmoid_grad(a1) # lan ngược về tầng ẩn # --- Update (gradient descent) --- W2 -= eta * (a1.T @ d2); b2 -= eta * d2.sum(0, keepdims=True) W1 -= eta * (X.T @ d1); b1 -= eta * d1.sum(0, keepdims=True)
Tương đương bằng Keras (cấp cao, tự lo back-prop):
from tensorflow.keras import Sequential from tensorflow.keras.layers import Dense model = Sequential([ Dense(4, activation='relu', input_shape=(2,)), # tầng ẩn Dense(1, activation='sigmoid'), # output nhị phân ]) model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) model.fit(X, y, epochs=200, verbose=0) # Keras tự forward + backward + update