Mục lục
- Mục tiêu bài học
- Tanh — công thức
- Tính chất của tanh
- Quan hệ với sigmoid
- Đạo hàm tanh
- Pros so với sigmoid
- Cons — vẫn vanishing
- Tanh so với ReLU
- Khi nào dùng tanh trong DL hiện đại
- Numerical stability
- Bảng tóm tắt Tanh vs Sigmoid
- Code Python
- Tanh trong PyTorch
- Tanh và softmax — chuẩn bị Bài 8
- Bài tập
- Tóm tắt
Mục tiêu bài học
Sau bài học, bạn sẽ:
- Viết được công thức tanh \( \tanh(z) = \frac{e^z - e^{-z}}{e^z + e^{-z}} \) và đạo hàm \( \tanh'(z) = 1 - \tanh^2(z) \).
- Giải thích tính chất zero-centered của tanh và lợi thế so với sigmoid trong training.
- Chứng minh được đẳng thức \( \tanh(z) = 2 \sigma(2z) - 1 \) — tanh là phiên bản rescaled của sigmoid.
- Biết tanh vẫn saturate và vẫn bị vanishing gradient — không phải lời giải triệt để như ReLU (B6).
- Biết khi nào tanh vẫn xuất hiện trong DL hiện đại: RNN / LSTM / GRU (B36-38), GAN generator output, RL action bound.
- Implement tanh bằng NumPy và dùng
torch.tanhtrong PyTorch.
Bài này kế tiếp Bài 6 — ReLU và biến thể và đóng phần activation cho hidden layer trước khi sang Bài 8 — Softmax cho output layer multi-class.
Tanh — công thức
Tanh — viết tắt của hyperbolic tangent — định nghĩa qua hàm mũ:
\[ \tanh(z) = \frac{e^z - e^{-z}}{e^z + e^{-z}} \]
Nhân tử và mẫu với \( e^z \) thu được dạng tương đương hay dùng trong implementation stable:
\[ \tanh(z) = \frac{1 - e^{-2z}}{1 + e^{-2z}} \]
Đường cong S-shape giống sigmoid nhưng đối xứng quanh gốc toạ độ:
Một vài giá trị tham chiếu (làm tròn 4 chữ số):
- \( \tanh(-2) \approx -0.9640 \).
- \( \tanh(-1) \approx -0.7616 \).
- \( \tanh(0) = 0 \).
- \( \tanh(1) \approx 0.7616 \).
- \( \tanh(2) \approx 0.9640 \).
- \( \tanh(5) \approx 0.9999 \) — gần như chạm asymptote 1.
Tính chất của tanh
- Range: \( \tanh(z) \in (-1, 1) \) với mọi \( z \in \mathbb{R} \). Cận không bao giờ đạt nhưng tiến tới khi \( z \to \pm\infty \).
- Tâm: \( \tanh(0) = 0 \) — đi qua gốc toạ độ.
- Zero-centered: trung bình output gần 0 nếu input phân tán quanh 0 — khác sigmoid (output luôn dương, trung bình \( \approx 0.5 \) khi input chuẩn hoá quanh 0).
- Smooth, monotonic, khả vi mọi nơi: thuận tiện cho gradient-based optimization.
- Hàm lẻ: \( \tanh(-z) = -\tanh(z) \) — đối xứng qua gốc.
- Asymptote: tiệm cận \( y = -1 \) khi \( z \to -\infty \) và \( y = 1 \) khi \( z \to +\infty \).
Trong các tính chất trên, zero-centered là điểm tạo khác biệt thực tiễn so với sigmoid — phân tích kỹ ở mục 6.
Quan hệ với sigmoid
Tanh và sigmoid không phải hai hàm độc lập — tanh là phiên bản rescaled (và shifted) của sigmoid:
\[ \tanh(z) = 2 \sigma(2z) - 1 \]
Chứng minh ngắn. Từ \( \sigma(2z) = \frac{1}{1 + e^{-2z}} \):
\[ 2 \sigma(2z) - 1 = \frac{2}{1 + e^{-2z}} - 1 = \frac{2 - (1 + e^{-2z})}{1 + e^{-2z}} = \frac{1 - e^{-2z}}{1 + e^{-2z}} = \tanh(z) \]
Ý nghĩa: lấy sigmoid, kéo dãn input theo hệ số 2, ánh xạ output từ \( (0, 1) \) sang \( (-1, 1) \) bằng phép biến đổi affine \( y \mapsto 2y - 1 \). Vì tanh thực chất là sigmoid rescaled, nó kế thừa cùng cấu trúc S-shape và cùng vấn đề saturation — chỉ khác range và độ dốc.
Hệ quả thực tế: trong code, có thể implement tanh bằng 2 * sigmoid(2 * z) - 1 nếu đã có sigmoid stable. Tuy nhiên framework đã có np.tanh / torch.tanh implement trực tiếp và stable hơn — không cần tự build.
Đạo hàm tanh
Backpropagation cần \( \tanh'(z) \). Dùng quy tắc thương với \( f = e^z - e^{-z} \), \( g = e^z + e^{-z} \):
\[ \tanh'(z) = \frac{f' g - f g'}{g^2} = \frac{(e^z + e^{-z})^2 - (e^z - e^{-z})^2}{(e^z + e^{-z})^2} \]
Triển khai tử số: \( (e^z + e^{-z})^2 - (e^z - e^{-z})^2 = 4 \) (đẳng thức quen, có thể nhận ra qua hằng đẳng thức \( (a+b)^2 - (a-b)^2 = 4ab \) với \( ab = 1 \)). Vậy:
\[ \tanh'(z) = \frac{4}{(e^z + e^{-z})^2} = 1 - \tanh^2(z) \]
Bước cuối từ \( 1 - \tanh^2(z) = 1 - \left( \frac{e^z - e^{-z}}{e^z + e^{-z}} \right)^2 = \frac{(e^z + e^{-z})^2 - (e^z - e^{-z})^2}{(e^z + e^{-z})^2} = \frac{4}{(e^z + e^{-z})^2} \).
Một số giá trị đạo hàm:
- Tại \( z = 0 \): \( \tanh(0) = 0 \Rightarrow \tanh'(0) = 1 - 0 = 1 \) — giá trị tối đa, cao gấp 4 lần max của sigmoid (0.25).
- Tại \( z = \pm 1 \): \( \tanh'(\pm 1) = 1 - 0.7616^2 \approx 0.4200 \).
- Tại \( z = \pm 2 \): \( \tanh'(\pm 2) = 1 - 0.9640^2 \approx 0.0707 \).
- Tại \( z = \pm 5 \): \( \tanh'(\pm 5) \approx 1 - 0.9999^2 \approx 0.0002 \) — đã rất nhỏ.
- Khi \( |z| \to \infty \): \( \tanh(z) \to \pm 1 \Rightarrow \tanh'(z) \to 0 \).
Dạng \( \tanh'(z) = 1 - \tanh^2(z) \) tiện cho backprop: forward đã tính \( a = \tanh(z) \), khi backward chỉ cần \( 1 - a^2 \) — không phải gọi exp lần nữa.
Pros so với sigmoid
Tanh có 2 lợi thế cụ thể so với sigmoid:
1. Zero-centered → gradient không bias dấu.
Output sigmoid luôn dương, kéo theo dấu của \( \frac{\partial L}{\partial w_i} = \frac{\partial L}{\partial z} \cdot x_i \) chung cho mọi \( w_i \) của neuron (vì \( x_i > 0 \) luôn). Trajectory update phải đi zigzag — chỉ được di chuyển trong các hướng có dấu nhất quán (xem B5 mục "Not zero-centered").
Tanh output có cả dấu dương và âm, trung bình gần 0 nếu input đối xứng quanh 0 → dấu của \( x_i \) khác nhau giữa các neuron → update đi thẳng hơn theo hướng tối ưu. Tốc độ hội tụ thực tế tốt hơn sigmoid.
2. Max gradient = 1 (so với sigmoid 0.25) → vanishing nhẹ hơn.
Trong một network \( L \) layer dùng tanh, tích các \( \tanh'(z^{(\ell)}) \) bị chặn trên bởi \( 1^L = 1 \) — không tự nhân cho 1 hệ số \( \le 0.25 \) như sigmoid. Với \( L = 10 \), sigmoid cho tích \( \le 0.25^{10} \approx 10^{-7} \), tanh không có chặn cứng kiểu vậy. Network tanh học được sâu hơn network sigmoid với cùng setup.
Lưu ý: "nhẹ hơn" không nghĩa là "hết". Tanh vẫn vanishing — mục 7.
Cons — vẫn vanishing
Tanh không phải giải pháp triệt để cho vanishing gradient:
- Vẫn saturate: khi \( |z| > 3 \), \( \tanh'(z) < 0.01 \). Khi \( |z| > 5 \), \( \tanh'(z) \approx 0 \) trong float32. Neuron rơi vào vùng này gần như không cập nhật weight — y hệt vấn đề của sigmoid, chỉ là ngưỡng \( |z| \) hơi rộng hơn.
- Tốn computation hơn ReLU: tanh cần \( e^z \) và \( e^{-z} \) (hoặc \( e^{2z} \)) — 10-20 cycles trên CPU. ReLU chỉ là \( \max(0, z) \) — 1-2 cycles.
- Backprop vẫn nhân chuỗi \( \tanh'(z) \): nếu vài layer rơi vào saturate, tích tụt nhanh về 0.
Khoảng 1990s-2000s, tanh là mặc định cho hidden layer thay cho sigmoid trong các MLP — đặc biệt sau khi LeCun và cộng sự (1998, "Efficient BackProp") khuyến nghị tanh cùng với input chuẩn hoá. Nhưng deep network \( L > 10 \) layer vẫn không train nổi với tanh — đó là động lực để chuyển sang ReLU năm 2010-2011 (Glorot, Bordes, Bengio).
Tanh so với ReLU
Đặt cạnh ReLU (B6):
- ReLU \( = \max(0, z) \): với \( z > 0 \), đạo hàm bằng 1 (không saturate phía dương). Cheap (1-2 cycles). Không bị bão hoà ở vùng dương. Là mặc định cho hidden layer của CNN, MLP modern (ResNet, VGG, ViT MLP block, v.v.).
- Tanh: saturate cả 2 phía, gradient bị chặn. Đắt hơn ReLU. Nhưng output bounded \( (-1, 1) \) — đây là tính năng chứ không phải bug trong một số kiến trúc.
Quy tắc thực tế hiện đại:
- Hidden layer của feed-forward network (CNN, MLP, Transformer MLP block): ReLU hoặc biến thể (GELU, SiLU).
- RNN cổ điển / LSTM / GRU: tanh vẫn là choice mặc định cho cell update — vì cần output bounded để cell state không trôi (mục 9).
- Output layer cần range \( [-1, 1] \) (GAN generator cho ảnh đã normalize, RL action bound): tanh.
Khi nào dùng tanh trong DL hiện đại
Tanh không còn là default cho hidden layer của deep feed-forward network, nhưng giữ chỗ đứng cụ thể:
- RNN vanilla, LSTM, GRU (B36-38): cell state cần bounded để không tích luỹ vô hạn theo time-step. LSTM dùng tanh ở 2 vị trí: ứng viên cell state \( \tilde{c}_t = \tanh(W_c x_t + U_c h_{t-1} + b_c) \) và output gate cuối \( h_t = o_t \odot \tanh(c_t) \). GRU tương tự cho hidden state candidate. Sigmoid dùng cho gate (giá trị \( (0, 1) \) như "mở bao nhiêu phần trăm"), tanh dùng cho giá trị state — phân công này gần như đứng yên 25 năm nay.
- Transformer biến thể: một số positional encoding learnable, gated MLP variants vẫn dùng tanh; bản gốc Transformer (Vaswani 2017) dùng ReLU trong MLP block, các biến thể sau dùng GELU / SiLU.
- GAN generator: output layer mapping về \( [-1, 1] \) khi ảnh input được normalize sang khoảng đó (DCGAN, Radford 2015 đề xuất chuẩn này). Discriminator vẫn dùng leaky ReLU.
- Reinforcement Learning: action space continuous bounded (steering angle trong xe tự lái, joint angle robot) — output layer dùng tanh để bound chính xác, hoặc tanh + rescale tới khoảng action.
- Embedding normalization: vài kiến trúc gắn tanh lên embedding để giữ norm có trần — ít phổ biến hơn, layer-norm thường được chuộng hơn.
Đặc điểm chung của các use case: tanh đặt ở một layer cụ thể hoặc một thành phần của recurrent cell, không xếp chồng hàng chục layer tanh liên tiếp — nên vanishing gradient không phải vấn đề thực tế.
Numerical stability
Implementation ngây thơ \( (e^z - e^{-z}) / (e^z + e^{-z}) \) tràn số khi \( |z| \) lớn vì \( e^z \) overflow tại \( z \approx 700 \) (float64) hoặc \( z \approx 88 \) (float32).
Cách viết stable: chọn nhánh theo dấu \( z \), dùng đẳng thức tương đương:
\[ \tanh(z) = \operatorname{sign}(z) \cdot \left( 1 - \frac{2}{e^{2|z|} + 1} \right) \]
Hoặc viết lại theo dạng có \( e^{-2|z|} \) (luôn \( \in (0, 1] \)):
\[ \tanh(z) = \begin{cases} \displaystyle \frac{1 - e^{-2z}}{1 + e^{-2z}} & \text{nếu } z \ge 0 \\ \displaystyle -\frac{1 - e^{2z}}{1 + e^{2z}} & \text{nếu } z < 0 \end{cases} \]
NumPy (np.tanh), PyTorch (torch.tanh), TensorFlow (tf.tanh) đều dùng implementation stable dựa trên thư viện C math (tanh trong libm). Trong code production, gọi thẳng API có sẵn — không tự build từ np.exp.
Bảng tóm tắt Tanh vs Sigmoid
| Tiêu chí | Sigmoid | Tanh |
|---|---|---|
| Công thức | \( \sigma(z) = \frac{1}{1 + e^{-z}} \) | \( \tanh(z) = \frac{e^z - e^{-z}}{e^z + e^{-z}} \) |
| Range | \( (0, 1) \) | \( (-1, 1) \) |
| Giá trị tại \( z = 0 \) | 0.5 | 0 |
| Zero-centered | Không | Có |
| Đạo hàm | \( \sigma(z)(1 - \sigma(z)) \) | \( 1 - \tanh^2(z) \) |
| Max đạo hàm | 0.25 (tại \( z = 0 \)) | 1.0 (tại \( z = 0 \)) |
| Saturate khi \( |z| \) lớn | Có | Có |
| Vanishing gradient | Có (nặng) | Có (nhẹ hơn) |
| Cost tính toán | 1 lần \( \exp \) | 2 lần \( \exp \) (hoặc 1 lần \( e^{2z} \)) |
| Quan hệ | \( \tanh(z) = 2\sigma(2z) - 1 \) | |
| Use case chính | Output binary classification, gate LSTM/GRU | Cell state RNN/LSTM/GRU, GAN generator, RL action |
Code Python
Implement tanh, đạo hàm, kiểm tra đẳng thức với sigmoid:
import numpy as np
def sigmoid(z):
return np.where(
z >= 0,
1.0 / (1.0 + np.exp(-z)),
np.exp(z) / (1.0 + np.exp(z)),
)
def tanh(z):
# np.tanh đã stable cho mọi z trong float64
return np.tanh(z)
def tanh_derivative(z):
t = tanh(z)
return 1.0 - t * t
zs = np.array([-5.0, -2.0, -1.0, 0.0, 1.0, 2.0, 5.0])
for z in zs:
t = tanh(z)
dt = tanh_derivative(z)
print(f"z = {z: .1f} tanh(z) = {t: .6f} tanh'(z) = {dt:.6f}")
Output (làm tròn):
z = -5.0 tanh(z) = -0.999909 tanh'(z) = 0.000181
z = -2.0 tanh(z) = -0.964028 tanh'(z) = 0.070651
z = -1.0 tanh(z) = -0.761594 tanh'(z) = 0.419974
z = 0.0 tanh(z) = 0.000000 tanh'(z) = 1.000000
z = 1.0 tanh(z) = 0.761594 tanh'(z) = 0.419974
z = 2.0 tanh(z) = 0.964028 tanh'(z) = 0.070651
z = 5.0 tanh(z) = 0.999909 tanh'(z) = 0.000181
Verify đẳng thức \( \tanh(z) = 2 \sigma(2z) - 1 \):
zs = np.array([-3.0, -1.0, 0.0, 1.0, 3.0])
lhs = np.tanh(zs)
rhs = 2 * sigmoid(2 * zs) - 1
print(np.allclose(lhs, rhs)) # True
print(lhs - rhs) # gần như 0 mọi phần tử
So sánh max derivative sigmoid vs tanh:
grid = np.linspace(-10, 10, 1001)
sig_d = sigmoid(grid) * (1 - sigmoid(grid))
tanh_d = 1 - np.tanh(grid) ** 2
print(f"max sigmoid' = {sig_d.max():.4f}") # 0.2500
print(f"max tanh' = {tanh_d.max():.4f}") # 1.0000
print(f"tỷ số = {tanh_d.max() / sig_d.max():.1f}x")
Tỷ số \( 4\times \) — max gradient của tanh cao gấp 4 lần sigmoid, khớp với phép biến đổi \( \tanh'(z) = 2 \cdot 2 \cdot \sigma'(2z) = 4 \sigma'(2z) \) đạt max tại \( z = 0 \).
Demo bound output kiểu RNN preview — không build cell hoàn chỉnh, chỉ minh hoạ tại sao tanh giữ state trong tầm kiểm soát:
# Mô phỏng cell update kiểu vanilla RNN: h_t = tanh(W * h_{t-1} + b)
# với W = 2.0, b = 0.5 — input hệ số > 1, dễ blow-up nếu không bound
np.random.seed(0)
h = 0.1
W, b = 2.0, 0.5
for t in range(20):
h = np.tanh(W * h + b)
print(f"step {t:2d}: h = {h: .6f}")
# Quan sát: h hội tụ về một fixed point trong (-1, 1), không thoát.
Nếu thay np.tanh(W * h + b) bằng W * h + b (không activation), \( h \) sẽ phân kỳ vô hạn theo bước. Tanh đảm bảo \( |h| < 1 \) tại mọi thời điểm — đây là lý do tanh được giữ trong RNN dù vanishing tồn tại.
Tanh trong PyTorch
PyTorch 2.x cung cấp 3 dạng tương đương — functional, module, tensor method:
import torch
import torch.nn as nn
import torch.nn.functional as F
z = torch.tensor([-1000.0, -2.0, -1.0, 0.0, 1.0, 2.0, 1000.0])
# Dạng functional (khuyến nghị)
print(torch.tanh(z))
# tensor([-1.0000, -0.9640, -0.7616, 0.0000, 0.7616, 0.9640, 1.0000])
# Dạng tensor method
print(z.tanh())
# Dạng module — dùng trong nn.Sequential
act = nn.Tanh()
print(act(z))
# F.tanh là alias deprecated từ PyTorch 1.x — vẫn chạy, nhưng warning.
# Khuyến nghị: dùng torch.tanh trực tiếp.
Ví dụ dùng tanh ở output layer của một GAN generator đơn giản:
class Generator(nn.Module):
def __init__(self, z_dim=100, img_dim=784):
super().__init__()
self.net = nn.Sequential(
nn.Linear(z_dim, 256),
nn.ReLU(),
nn.Linear(256, img_dim),
nn.Tanh(), # output ảnh trong [-1, 1] để khớp normalize input
)
def forward(self, z):
return self.net(z)
Khi dùng cho LSTM / GRU, không cần khai báo tanh thủ công — nn.LSTM, nn.GRU đã hard-code tanh ở vị trí cell update và sigmoid ở gate (chi tiết B37-38). API người dùng chỉ cấu hình input_size, hidden_size, num_layers.
Tanh và softmax — chuẩn bị Bài 8
Sigmoid, tanh, ReLU đều là activation per-neuron — mỗi neuron tính output độc lập với neuron khác trong cùng layer. Tanh nhận một số thực, trả về một số thực.
Softmax (B8) khác hẳn: nhận vector logit \( \mathbf{z} \in \mathbb{R}^K \) (toàn bộ output layer cho \( K \) class), trả về vector xác suất \( \mathbf{p} \in \mathbb{R}^K \) tổng bằng 1:
\[ p_k = \frac{e^{z_k}}{\sum_{j=1}^{K} e^{z_j}} \]
Phân loại:
- Tanh / sigmoid / ReLU: element-wise, dùng ở hidden layer hoặc output bounded độc lập.
- Softmax: cross-neuron, dùng ở output layer khi cần phân phối xác suất K class (multi-class classification).
Bài 8 sẽ làm rõ softmax, đạo hàm Jacobian, mối quan hệ với cross-entropy loss, và lý do gộp Softmax + CrossEntropyLoss thành nn.CrossEntropyLoss trong PyTorch.
Bài tập
- Tính tay \( \tanh(z) \) cho \( z = -2, -1, 0, 1, 2 \) (dùng máy tính cầm tay cho \( e^z \)). Đối chiếu với bảng ở mục 2.
- Verify đẳng thức \( \tanh(z) = 2 \sigma(2z) - 1 \) tại \( z = -1, 0, 1 \) bằng cách tính riêng từng vế.
- Chứng minh đạo hàm \( \tanh'(z) = 1 - \tanh^2(z) \) bằng cách lấy đạo hàm trực tiếp công thức \( \tanh(z) = (e^z - e^{-z})/(e^z + e^{-z}) \).
- So sánh max gradient: chứng minh \( \max_z \tanh'(z) = 1 \) và \( \max_z \sigma'(z) = 0.25 \). Tỷ số \( 4 \) đến từ đâu trong phép biến đổi \( \tanh(z) = 2 \sigma(2z) - 1 \)?
- Chứng minh tính lẻ: \( \tanh(-z) = -\tanh(z) \) với mọi \( z \).
- Implement
tanh_from_sigmoid(z)dùng đẳng thức \( 2 \sigma(2z) - 1 \), so sánh vớinp.tanh(z)tại \( z = \pm 1, \pm 5, \pm 10 \). Sai số nằm ở mức float precision nào? - Vẽ tanh và sigmoid trên cùng plot với
matplotlibtrong khoảng \( z \in [-5, 5] \). (Mô tả code:plt.plot(z, np.tanh(z), label='tanh'); plt.plot(z, sigmoid(z), label='sigmoid'); plt.legend().) Quan sát: tanh dốc hơn quanh 0, biên độ gấp đôi. - Với network 10 layer dùng tanh, weight khởi tạo sao cho mỗi \( \tanh'(z^{(\ell)}) \approx 0.5 \). Ước tính bậc độ lớn gradient ở layer đầu so với layer cuối. So sánh với cùng network dùng sigmoid và \( \sigma'(z^{(\ell)}) \approx 0.2 \) (B5 mục 8).
Đáp án ngắn
- \( \tanh(-2) \approx -0.9640 \), \( \tanh(-1) \approx -0.7616 \), \( \tanh(0) = 0 \), \( \tanh(1) \approx 0.7616 \), \( \tanh(2) \approx 0.9640 \).
- Tại \( z = 1 \): \( 2 \sigma(2) - 1 = 2 \cdot 0.8808 - 1 = 0.7616 = \tanh(1) \). Tại \( z = 0 \): \( 2 \cdot 0.5 - 1 = 0 = \tanh(0) \). Tương tự \( z = -1 \).
- Xem mục 5 — quy tắc thương kết hợp hằng đẳng thức \( (a+b)^2 - (a-b)^2 = 4ab \).
- \( \tanh(z) = 2 \sigma(2z) - 1 \Rightarrow \tanh'(z) = 4 \sigma'(2z) \). Tại \( z = 0 \): \( 4 \sigma'(0) = 4 \cdot 0.25 = 1 \). Hệ số 4 = (2 từ rescale input) × (2 từ rescale output).
- \( \tanh(-z) = \frac{e^{-z} - e^z}{e^{-z} + e^z} = -\frac{e^z - e^{-z}}{e^z + e^{-z}} = -\tanh(z) \).
- Sai số khoảng \( 10^{-15} \) đến \( 10^{-16} \) (machine epsilon float64) — về cơ bản identical.
- Sigmoid: max 0.25 tại \( z = 0 \), nhanh tụt về 0. Tanh: max 1.0 tại \( z = 0 \), tụt chậm hơn, đối xứng âm-dương.
- Tanh: \( 0.5^{10} \approx 10^{-3} \) — 3 bậc độ lớn. Sigmoid: \( 0.2^{10} \approx 10^{-7} \) — 7 bậc. Tanh vanishing nhẹ hơn 4 bậc trong cùng setup.
Tóm tắt
- Tanh \( = (e^z - e^{-z}) / (e^z + e^{-z}) \), range \( (-1, 1) \), \( \tanh(0) = 0 \), hàm lẻ.
- Quan hệ với sigmoid: \( \tanh(z) = 2 \sigma(2z) - 1 \) — tanh là sigmoid rescaled input \( \times 2 \) và output từ \( (0, 1) \) sang \( (-1, 1) \).
- Đạo hàm: \( \tanh'(z) = 1 - \tanh^2(z) \), max \( = 1 \) tại \( z = 0 \) (gấp 4 lần max sigmoid).
- Pros so sigmoid: zero-centered → gradient không bias dấu, hội tụ nhanh hơn; max gradient lớn hơn → vanishing nhẹ hơn.
- Cons: vẫn saturate khi \( |z| > 3 \), vẫn vanishing trong deep network; đắt hơn ReLU.
- So với ReLU: ReLU là mặc định cho hidden layer của CNN/MLP modern. Tanh giữ vai trò ở: RNN/LSTM/GRU cell state, GAN generator output, RL action bound.
- Implementation stable: dùng
np.tanh/torch.tanh— không tự build từexp. - Tanh là activation per-neuron; bài kế (B8) chuyển sang softmax — activation cross-neuron cho phân phối xác suất.
- Wikipedia - Hyperbolic functions
- Wikipedia - Activation function
- Wikipedia - Vanishing gradient problem
- LeCun, Bottou, Orr, Müller (1998) - Efficient BackProp
- Glorot, Bengio (2010) - Understanding the difficulty of training deep feedforward neural networks
- Glorot, Bordes, Bengio (2011) - Deep Sparse Rectifier Neural Networks
- Hochreiter, Schmidhuber (1997) - Long Short-Term Memory
- Radford, Metz, Chintala (2015) - DCGAN (Unsupervised Representation Learning with Deep Convolutional GANs)
- Chung, Gulcehre, Cho, Bengio (2014) - Empirical Evaluation of Gated Recurrent Neural Networks
- PyTorch Docs - torch.tanh
- PyTorch Docs - torch.nn.Tanh
- PyTorch Docs - torch.nn.LSTM
- NumPy Docs - numpy.tanh
- Stanford CS231n - Neural Networks Part 1 (activation functions)
