Activation Functions
Hàm kích hoạt
Nếu xếp chồng 100 lớp tuyến tính (chỉ nhân ma trận, không có gì khác), kết quả sẽ tương đương với bao nhiêu lớp?
Hình minh họa
ReLU: Khoảng giá trị: [0, +∞) · Xuất hiện: 2010 / 2012
Cú hích của deep learning hiện đại (AlexNet, 2012).
Hình minh họa
So sánh 6 hàm kích hoạt song song
Cùng một đầu vào x đi qua cả 6 hàm — kéo thanh trượt để thấy chúng phản ứng khác nhau như thế nào. Đường liền là f(x), đường nét đứt là đạo hàm f'(x).
f(0.0) = 0.500
f(0.0) = 0.000
f(0.0) = 0.000
f(0.0) = 0.000
f(0.0) = 0.000
f(0.0) = 0.000
Giá trị đầu ra của từng hàm tại x = 0.0:
Hàm kích hoạtlà "công tắc" của nơ-ron — nó quyết định tín hiệu có được chuyển tiếp hay không, và chuyển bao nhiêu. Trong perceptron cổ điển dùng hàm bước đơn giản; mạng hiện đại dùng ReLU hoặc GELU để gradient có thể lan truyền qua hàng trăm lớp mà không bị triệt tiêu hay bùng nổ.
Điểm then chốt: chính đạo hàm của hàm kích hoạt mới là thứ quyết định mạng có học được hay không — backpropagation nhân đạo hàm này qua hàng chục lớp; nếu nó quá nhỏ, gradient biến mất; nếu quá lớn, gradient bùng nổ.
Hình minh họa
Dying ReLU — Nơ-ron chết vĩnh viễn
Khi pre-activation (z = Wx + b) luôn âm, ReLU trả về 0 và gradient cũng bằng 0. Không có gradient → không cập nhật trọng số → nơ-ron đứng im mãi mãi. Trượt để xem trạng thái sau mỗi epoch.
| Epoch | Pre-activation z | ReLU(z) | ∂/∂z | Trạng thái |
|---|---|---|---|---|
| 0 | -1.200 | 0.000 | 0.000 | chết — không học |
| 1 | -1.200 | 0.000 | 0.000 | chết — không học |
| 2 | -1.200 | 0.000 | 0.000 | chết — không học |
| 3 | -1.200 | 0.000 | 0.000 | chết — không học |
Gradient luôn bằng 0 → trọng số không đổi → nơ-ron chết cho mọi epoch tiếp theo. Không có cách nào "hồi sinh" nó bằng cùng dữ liệu đó nữa.
Đây là lý do sigmoid gặp vấn đề ở lớp ẩn. Khi tín hiệu đi qua sigmoid nhiều lần, nó bị "nén" dần về 0.5 — gradient gần bằng 0, mạng không thể học!
Xem giá trị thay đổi khi đi qua sigmoid 8 lần liên tiếp
| Đầu vào | x | σ×1 | σ×2 | σ×3 | σ×4 | σ×5 | σ×6 | σ×7 | σ×8 |
|---|---|---|---|---|---|---|---|---|---|
| x = 2 | 2.000 | 0.881 | 0.707 | 0.670 | 0.661 | 0.660 | 0.659 | 0.659 | 0.659 |
| x = -1 | -1.000 | 0.269 | 0.567 | 0.638 | 0.654 | 0.658 | 0.659 | 0.659 | 0.659 |
| x = 0.5 | 0.500 | 0.622 | 0.651 | 0.657 | 0.659 | 0.659 | 0.659 | 0.659 | 0.659 |
Mọi giá trị đều hội tụ về 0.5 — gradient gần bằng 0! Đây là vấn đề "triệt tiêu gradient" (vanishing gradient).
Phép tính nhanh:
σ'(x) đạt cực đại 0.25 tại x = 0. Khi backprop qua 10 lớp sigmoid, gradient bị nhân với . Mạng sâu hơn 10 lớp dùng sigmoid gần như không huấn luyện được. ReLU khắc phục: gradient là 1 ở vùng dương, không bị nén.
Nơ-ron dùng ReLU luôn nhận đầu vào âm (ví dụ: z = -3). Điều gì xảy ra?
Bạn đang huấn luyện một LSTM để sinh văn bản. Lớp cổng (gate) nào bên trong LSTM dùng sigmoid, và vì sao?
Bảng so sánh tức thời (cùng một x)
Kéo để đổi x, xem output và gradient của cả 6 hàm cùng lúc.
x = 0.80
| Hàm | f(x) | f'(x) |
|---|---|---|
| Sigmoid | 0.6900 | 0.2139 |
| Tanh | 0.6640 | 0.5591 |
| ReLU | 0.8000 | 1.0000 |
| Leaky ReLU | 0.8000 | 1.0000 |
| ELU | 0.8000 | 1.0000 |
| GELU | 0.6304 | 1.0196 |
Giải thích
Hàm kích hoạt (Activation Function) thêm tính phi tuyến vào mạng. Không có nó, dù mạng bao nhiêu lớp cũng chỉ tương đương một phép biến đổi tuyến tính. Trong quá trình lan truyền ngược, đạo hàm của hàm kích hoạt đóng vai trò then chốt trong việc truyền gradient qua các lớp.
Bảng so sánh hàm kích hoạt phổ biến:
| Hàm | Khoảng | Dùng ở | Nhược điểm |
|---|---|---|---|
| ReLU | [0, +∞) | Lớp ẩn (mặc định CNN) | Dying neuron |
| Leaky ReLU | (-∞, +∞) | Lớp ẩn | Hệ số alpha cần chọn |
| ELU | (-α, +∞) | Lớp ẩn, muốn mean ≈ 0 | Tốn e^x ở vùng âm |
| GELU | ≈(-0.17, +∞) | Transformer (BERT, GPT) | Đắt hơn ReLU |
| Sigmoid | (0, 1) | Đầu ra nhị phân, LSTM gate | Triệt tiêu gradient |
| Tanh | (-1, 1) | RNN, LSTM candidate | Triệt tiêu gradient |
| Softmax | (0, 1), tổng = 1 | Đầu ra đa lớp | Chỉ dùng ở lớp cuối |
import torch
import torch.nn.functional as F
x = torch.tensor([-2.0, -1.0, 0.0, 1.0, 2.0])
print(F.relu(x)) # [0, 0, 0, 1, 2]
print(torch.sigmoid(x)) # [0.12, 0.27, 0.50, 0.73, 0.88]
print(torch.tanh(x)) # [-0.96, -0.76, 0.00, 0.76, 0.96]
print(F.leaky_relu(x, 0.01)) # [-0.02, -0.01, 0, 1, 2]
print(F.elu(x, 1.0)) # [-0.865, -0.632, 0, 1, 2]
print(F.gelu(x)) # [-0.045, -0.159, 0, 0.841, 1.955]
# Trong mạng nơ-ron:
model = torch.nn.Sequential(
torch.nn.Linear(784, 256),
torch.nn.ReLU(), # hàm kích hoạt sau mỗi lớp ẩn
torch.nn.Linear(256, 10),
# Không cần Softmax ở đây — CrossEntropyLoss tự xử lý log-softmax
)
criterion = torch.nn.CrossEntropyLoss()import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def sigmoid_grad(x):
s = sigmoid(x)
return s * (1 - s)
def relu(x):
return np.maximum(0, x)
def relu_grad(x):
return (x > 0).astype(float)
def leaky_relu(x, alpha=0.01):
return np.where(x > 0, x, alpha * x)
def leaky_relu_grad(x, alpha=0.01):
return np.where(x > 0, 1.0, alpha)
def elu(x, alpha=1.0):
return np.where(x > 0, x, alpha * (np.exp(x) - 1))
def elu_grad(x, alpha=1.0):
return np.where(x > 0, 1.0, alpha * np.exp(x))
def gelu(x):
# Hendrycks-Gimpel tanh approximation
return 0.5 * x * (1 + np.tanh(
np.sqrt(2 / np.pi) * (x + 0.044715 * x ** 3)
))
# So sánh 6 hàm trên cùng đầu vào
xs = np.linspace(-3, 3, 7)
for name, fn in [("sigmoid", sigmoid), ("relu", relu),
("leaky", leaky_relu), ("elu", elu),
("gelu", gelu), ("tanh", np.tanh)]:
print(name, np.round(fn(xs), 3))- Hàm kích hoạt thêm tính phi tuyến — nền tảng của mọi kiến trúc mạng nơ-ron hiện đại. Không có nó, một mạng bao nhiêu lớp cũng chỉ tương đương một phép biến đổi tuyến tính.
- ReLU là mặc định cho CNN/MLP: nhanh, gradient ổn định ở vùng dương, nhưng có thể gây dying neuron. GELU là mặc định cho Transformer.
- Sigmoid (0-1) cho đầu ra nhị phân và LSTM gate; Softmax cho phân loại đa lớp; Tanh cho RNN candidate state.
- Triệt tiêu gradient: sigmoid/tanh nén gradient về ~0 ở vùng bão hoà — trong mạng sâu, điều này nhân qua nhiều lớp thành số cực nhỏ, không học được.
- Dying ReLU: khi pre-activation luôn âm, gradient bằng 0 và trọng số không cập nhật. Giải pháp: Leaky ReLU, ELU, He init, learning rate nhỏ.
- Lịch sử: từ step của Rosenblatt (1958) → sigmoid (1986) → ReLU của AlexNet (2012) → GELU của BERT/GPT (2016+). Mỗi bước nhảy tương ứng với một thế hệ mô hình mới.
Kiểm tra hiểu biết
Tại sao ReLU là lựa chọn mặc định cho lớp ẩn trong hầu hết mạng nơ-ron hiện đại?