Let .
| Activation | Note | ||
|---|---|---|---|
| Sigmoid | max slope , saturates both ends | ||
| Tanh | max slope , zero-centered | ||
| ReLU | undefined at , take | ||
| Leaky ReLU | if else | no dead units | |
| ELU | or | if else | reuse the forward value |
| GELU | is the normal pdf | ||
| SiLU / Swish | non-monotone near | ||
| Softplus | smooth ReLU |
Two useful identities
Both let you compute the derivative from the forward output, so you store one tensor instead of two:
From scratch
import torch
def check(f, df, x):
"""Compare an analytic derivative against autograd."""
x = x.clone().requires_grad_(True)
f(x).sum().backward()
return (x.grad - df(x.detach())).abs().max()
sigmoid = lambda x: torch.sigmoid(x)
d_sigmoid = lambda x: torch.sigmoid(x) * (1 - torch.sigmoid(x))
silu = lambda x: x * torch.sigmoid(x)
d_silu = lambda x: (s := torch.sigmoid(x)) * (1 + x * (1 - s))
gelu = lambda x: x * torch.distributions.Normal(0., 1.).cdf(x)
d_gelu = lambda x: (torch.distributions.Normal(0., 1.).cdf(x)
+ x * torch.exp(-x**2 / 2) / (2 * torch.pi)**0.5)
x = torch.randn(1000, dtype=torch.double)
assert all(check(f, d, x) < 1e-9 for f, d in
[(sigmoid, d_sigmoid), (silu, d_silu), (gelu, d_gelu)])