Hãy tưởng tượng bạn dạy một đứa trẻ nhận biết chó. Bạn không nói "Nếu có 4 chân AND đuôi dài AND mõm nhọn THEN là chó". Thay vào đó, bạn cho trẻ xem hàng trăm ảnh chó khác nhau, và não bộ trẻ tự học pattern.
Neural Networks (Mạng nơ-ron nhân tạo) lấy cảm hứng từ cách não bộ hoạt động - hàng tỷ neurons kết nối với nhau, học từ kinh nghiệm. Nhưng đừng nhầm: Neural Networks không phải "bộ não thật" - chúng chỉ là mathematical functions cực kỳ phức tạp được tối ưu hóa để fit data.
Theo nghiên cứu của MIT, con người có khoảng 86 tỷ neurons trong não. GPT-3 có 175 tỷ parameters - nhưng vẫn không thể làm nhiều thứ mà một đứa trẻ 3 tuổi làm được. Tại sao? Vì kiến trúc và cách học hoàn toàn khác nhau.
Perceptron là neural network đơn giản nhất - chỉ có 1 neuron.
Inputs: x₁ ────w₁────┐
x₂ ────w₂────┤
x₃ ────w₃────┼──> Σ ──> Activation ──> Output
... │
xₙ ────wₙ────┘
↑
Bias (b)
Components:
Problem: Học logic AND
x₁ x₂ | Output
0 0 | 0
0 1 | 0
1 0 | 0
1 1 | 1
Solution với Perceptron:
# Sau khi training, perceptron học được:
w₁ = 0.5, w₂ = 0.5, b = -0.7
# Test:
x₁=1, x₂=1: z = 0.5*1 + 0.5*1 + (-0.7) = 0.3 > 0 → Output = 1 ✓
x₁=1, x₂=0: z = 0.5*1 + 0.5*0 + (-0.7) = -0.2 < 0 → Output = 0 ✓
x₁=0, x₂=1: z = 0.5*0 + 0.5*1 + (-0.7) = -0.2 < 0 → Output = 0 ✓
x₁=0, x₂=0: z = 0.5*0 + 0.5*0 + (-0.7) = -0.7 < 0 → Output = 0 ✓
Mục tiêu: Tìm weights (w) và bias (b) sao cho predictions đúng.
Algorithm:
1. Initialize weights randomly
2. For each training example:
- Predict: ŷ = activation(w·x + b)
- Calculate error: e = y_actual - ŷ
- Update weights: w_new = w_old + learning_rate * e * x
- Update bias: b_new = b_old + learning_rate * e
3. Repeat until converged
Ví dụ update:
Input: x = [1, 1], Actual: y = 1
Current weights: w = [0.2, 0.3], b = -0.1
Predict: z = 0.2*1 + 0.3*1 + (-0.1) = 0.4 → ŷ = 1 (if z > 0)
Error: e = 1 - 1 = 0 → No update (prediction đúng)
Input: x = [0, 1], Actual: y = 0
Predict: z = 0.2*0 + 0.3*1 + (-0.1) = 0.2 → ŷ = 1
Error: e = 0 - 1 = -1
Update:
w₁_new = 0.2 + 0.1*(-1)*0 = 0.2
w₂_new = 0.3 + 0.1*(-1)*1 = 0.2 ← Giảm weight vì x₂ gây ra sai prediction
b_new = -0.1 + 0.1*(-1) = -0.2 ← Giảm bias
Perceptron chỉ học được linear decision boundaries.
✓ Có thể học: AND, OR
✗ KHÔNG thể học: XOR
XOR problem:
x₁ x₂ | Output
0 0 | 0
0 1 | 1
1 0 | 1
1 1 | 0
Không có đường thẳng nào phân chia được!
x₂
1 | 1 0
|
0 | 0 1
└─────────── x₁
0 1
Đây là XOR crisis năm 1969 - gần như giết chết AI research trong 10+ năm.
Giải pháp: Multi-Layer Perceptron (MLP) - thêm hidden layers.
Input Hidden Output
Layer Layer Layer
x₁ ───┐ h₁ ───┐
├─── ├───> ŷ
x₂ ───┤ h₂ ───┘
│ ↑
x₃ ───┘ h₃
↑
Bias
3 loại layers:
Tại sao hidden layers quan trọng?
Mỗi hidden layer học higher-level features:
Ví dụ: Image Recognition
Input (pixels) → Hidden Layer 1 → Hidden Layer 2 → Hidden Layer 3 → Output
Raw pixels → Edges, lines → Shapes, textures → Object parts → Dog/Cat
Process:
1. Input Layer → Hidden Layer 1:
z₁ = W₁·x + b₁
a₁ = activation(z₁)
2. Hidden Layer 1 → Hidden Layer 2:
z₂ = W₂·a₁ + b₂
a₂ = activation(z₂)
3. Hidden Layer 2 → Output:
z₃ = W₃·a₂ + b₃
ŷ = activation_output(z₃)
Ví dụ cụ thể:
# Architecture: 3 inputs → 4 hidden → 1 output
import numpy as np
# Input
x = np.array([0.5, 0.3, 0.2])
# Weights (random khởi tạo)
W1 = np.random.randn(4, 3) # 3→4
b1 = np.random.randn(4)
W2 = np.random.randn(1, 4) # 4→1
b2 = np.random.randn(1)
# Forward pass
def sigmoid(z):
return 1 / (1 + np.exp(-z))
# Layer 1
z1 = W1.dot(x) + b1
a1 = sigmoid(z1) # [4 neurons]
# Layer 2 (output)
z2 = W2.dot(a1) + b2
y_pred = sigmoid(z2) # [1 neuron]
print(f"Prediction: {y_pred}")
Nếu không có activation functions, MLP chỉ là linear transformation - vô dụng!
Tại sao?
Layer 1: z₁ = W₁·x + b₁
Layer 2: z₂ = W₂·z₁ + b₂ = W₂·(W₁·x + b₁) + b₂
= (W₂·W₁)·x + (W₂·b₁ + b₂)
= W_combined·x + b_combined
→ Vẫn chỉ là linear function!
Activation functions làm network non-linear → học được complex patterns.
Công thức:
σ(z) = 1 / (1 + e^(-z))
Graph:
1.0 | ______
| /
0.5 | /
| /
0.0 |_/
└─────────── z
-5 0 5
Properties:
Ưu điểm:
Nhược điểm:
Khi nào dùng:
Công thức:
tanh(z) = (e^z - e^(-z)) / (e^z + e^(-z))
= 2σ(2z) - 1
Graph:
1.0 | ______
| /
0.0 | /
| /
-1.0 |_/
└─────────── z
-5 0 5
Properties:
Ưu điểm:
Nhược điểm:
Khi nào dùng:
Công thức:
ReLU(z) = max(0, z)
= z if z > 0
= 0 if z ≤ 0
Graph:
| /
| /
|/
────┴──────── z
0
Properties:
Ưu điểm:
Nhược điểm:
Khi nào dùng:
Ví dụ dying ReLU:
# Neuron với weights làm z luôn < 0
z = -5 # Always negative
ReLU(z) = 0
gradient = 0 # Không update được
→ Neuron "chết"
Fix dying ReLU bằng cách cho phép small gradient khi z < 0:
Công thức:
Leaky_ReLU(z) = z if z > 0
= 0.01z if z ≤ 0
Graph:
| /
| /
|/
___/┴──────── z
0
Ưu điểm:
Variants:
Dùng ở output layer cho multi-class classification.
Công thức:
softmax(z_i) = e^(z_i) / Σⱼ e^(z_j)
Ví dụ:
Logits (raw outputs): z = [2.0, 1.0, 0.1]
e^2.0 = 7.39
e^1.0 = 2.72
e^0.1 = 1.11
Sum = 11.22
Softmax:
Class 0: 7.39 / 11.22 = 0.659 (65.9%)
Class 1: 2.72 / 11.22 = 0.242 (24.2%)
Class 2: 1.11 / 11.22 = 0.099 (9.9%)
─────
1.000 ← Sum = 1
Properties:
Khi nào dùng:
Hidden Layers:
├─ Default: ReLU
├─ Dying ReLU issues: Leaky ReLU, PReLU, ELU
└─ Legacy/RNN: Tanh
Output Layer:
├─ Binary classification: Sigmoid
├─ Multi-class classification: Softmax
└─ Regression: Linear (no activation)
Forward propagation cho predictions. Nhưng làm sao update weights để predictions tốt hơn?
→ Backpropagation: Tính gradient của loss theo weights, sau đó update ngược từ output về input.
Goal: Minimize loss function L(ŷ, y)
Chain Rule:
∂L/∂W = ∂L/∂ŷ * ∂ŷ/∂z * ∂z/∂W
"Thay đổi W ảnh hưởng đến z, z ảnh hưởng đến ŷ, ŷ ảnh hưởng đến L"
Network:
x → z₁ = W₁x + b₁ → a₁ = σ(z₁) → z₂ = W₂a₁ + b₂ → ŷ = σ(z₂)
Loss: L = (y - ŷ)²
Backward pass:
# 1. Output layer gradient
dL_dyhat = -2 * (y - y_pred)
dyhat_dz2 = sigmoid_derivative(z2) # σ'(z) = σ(z)*(1-σ(z))
dz2_dW2 = a1
dz2_db2 = 1
# Gradient cho W2, b2
dL_dW2 = dL_dyhat * dyhat_dz2 * dz2_dW2
dL_db2 = dL_dyhat * dyhat_dz2 * dz2_db2
# 2. Hidden layer gradient
dz2_da1 = W2
da1_dz1 = sigmoid_derivative(z1)
dz1_dW1 = x
dz1_db1 = 1
# Gradient cho W1, b1
dL_dW1 = dL_dyhat * dyhat_dz2 * dz2_da1 * da1_dz1 * dz1_dW1
dL_db1 = dL_dyhat * dyhat_dz2 * dz2_da1 * da1_dz1 * dz1_db1
Pattern: Gradient "chảy ngược" từ output về input, multiply theo chain rule.
Sau khi có gradients, update weights:
learning_rate = 0.01
W2 = W2 - learning_rate * dL_dW2
b2 = b2 - learning_rate * dL_db2
W1 = W1 - learning_rate * dL_dW1
b1 = b1 - learning_rate * dL_db1
Intuition: Di chuyển weights theo hướng ngược với gradient (minimize loss).
Loss surface:
│ ╱╲
│ ╱ ╲
Loss │ ╱ ╲
│╱ ╲___
└────────────── Weight
↑
Gradient trỏ lên → Move xuống (negative gradient)
Update tất cả weights sau khi xem TOÀN BỘ training data.
for epoch in range(num_epochs):
# Forward pass trên TẤT CẢ training data
predictions = forward(X_train)
loss = compute_loss(predictions, y_train)
# Backward pass
gradients = backward(loss)
# Update
weights = weights - learning_rate * gradients
Nhược điểm:
Update weights sau MỖI sample.
for epoch in range(num_epochs):
for x, y in training_data: # Từng sample
prediction = forward(x)
loss = compute_loss(prediction, y)
gradients = backward(loss)
weights = weights - learning_rate * gradients
Ưu điểm:
Nhược điểm:
Best of both worlds: Update sau MỖI BATCH (VD: 32, 64, 128 samples).
batch_size = 32
for epoch in range(num_epochs):
for i in range(0, len(X_train), batch_size):
X_batch = X_train[i:i+batch_size]
y_batch = y_train[i:i+batch_size]
predictions = forward(X_batch)
loss = compute_loss(predictions, y_batch)
gradients = backward(loss)
weights = weights - learning_rate * gradients
Ưu điểm:
Batch size rules of thumb:
Vấn đề với GD: Oscillate (dao động) trong narrow valleys.
Loss surface với narrow valley:
│ ╱│╲
│ ╱ │ ╲ ← Bounces back and forth
│ ╱ │ ╲
│╱ ↓ ╲
Momentum: Tích lũy velocity từ previous updates.
velocity = 0.9 * velocity - learning_rate * gradients
weights = weights + velocity
Analogy: Quả bóng lăn xuống đồi - tích lũy momentum, không dừng ngay ở điểm gồ ghề nhỏ.
β (momentum coefficient): Thường 0.9
Vấn đề: Different features có scales khác nhau → cần learning rates khác nhau.
Solution: Adapt learning rate cho mỗi parameter dựa trên magnitude của gradients gần đây.
squared_gradients = 0.9 * squared_gradients + 0.1 * gradients²
weights = weights - learning_rate / sqrt(squared_gradients + ε) * gradients
Intuition:
Kết hợp Momentum + RMSprop → Best optimizer hiện tại!
# Momentum (first moment)
m = β1 * m + (1 - β1) * gradients
# RMSprop (second moment)
v = β2 * v + (1 - β2) * gradients²
# Bias correction
m_hat = m / (1 - β1^t)
v_hat = v / (1 - β2^t)
# Update
weights = weights - learning_rate * m_hat / (sqrt(v_hat) + ε)
Hyperparameters (thường dùng):
Tại sao Adam là default choice:
from tensorflow.keras.optimizers import Adam
model.compile(
optimizer=Adam(learning_rate=0.001),
loss='binary_crossentropy',
metrics=['accuracy']
)
Add penalty vào loss:
Loss_total = Loss_data + λ * Σ(weights²)
→ Buộc weights nhỏ → model không quá complex.
from tensorflow.keras import regularizers
model.add(Dense(64,
activation='relu',
kernel_regularizer=regularizers.l2(0.001) # λ = 0.001
))
Randomly "tắt" neurons trong training.
Training: Testing:
x₁ ──┐ x₁ ──┐
├── h₁ ├── h₁
x₂ ──┤ (ON) x₂ ──┤ (ON)
├── h₂ ├── h₂
x₃ │ (OFF) x₃ ──┤ (ON)
└── h₃ └── h₃
(ON) (ON)
Tại sao hiệu quả?
from tensorflow.keras.layers import Dropout
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5)) # Drop 50% neurons
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.3)) # Drop 30%
Best practices:
Normalize activations của mỗi layer.
Vấn đề: Internal Covariate Shift - distribution của inputs vào layer thay đổi liên tục khi weights update.
Solution: Normalize về mean=0, std=1 sau mỗi layer.
# For each mini-batch
z = W·x + b
z_normalized = (z - mean(z)) / sqrt(var(z) + ε)
a = γ * z_normalized + β # γ, β are learnable
Ưu điểm:
from tensorflow.keras.layers import BatchNormalization
model.add(Dense(128))
model.add(BatchNormalization()) # Trước activation
model.add(Activation('relu'))
Best practices:
Next steps:
Trong bài tiếp theo, chúng ta sẽ khám phá Computer Vision Architectures - các kiến trúc neural networks chuyên biệt cho xử lý ảnh, đặc biệt là CNNs (Convolutional Neural Networks).
Bài viết thuộc series "From Zero to AI Engineer" - Module 5: Deep Learning & Computer Vision