iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Activations

Activation functions inject non-linearity, which is what lets neural nets learn anything beyond a linear map. The choice affects gradient flow, training speed, and whether neurons die.

ReLU, GELU, SiLU, Softmax, custom

EXAMPLE
import torch
import torch.nn as nn
import torch.nn.functional as F

# 1) ReLU — the workhorse
relu = nn.ReLU()
relu(torch.tensor([-2.0, 0.0, 3.0]))      # tensor([0., 0., 3.])

# Functional form (no parameters, no module)
F.relu(x)

# 2) Leaky ReLU + Parametric ReLU — fixes 'dying ReLU'
lrelu = nn.LeakyReLU(negative_slope=0.01) # leaks 1 percent of negative
prelu = nn.PReLU(num_parameters=1)        # learns the slope

# 3) GELU — used in Transformers (BERT, GPT)
gelu = nn.GELU()
F.gelu(x)
# Smoother than ReLU; better empirically for attention models.

# 4) SiLU / Swish — used in EfficientNet, Llama, modern CNNs
silu = nn.SiLU()                          # x * sigmoid(x)
F.silu(x)

# 5) Tanh — bounded (-1, 1)
F.tanh(x)
# Good for RNNs, regression outputs in a fixed range.
# Saturates → vanishing gradients in deep nets.

# 6) Sigmoid — bounded (0, 1)
F.sigmoid(x)
# Use only at output for binary classification or gating; don't stack in hidden layers.

# 7) Softmax — turns logits into probabilities
logits = torch.tensor([[2.0, 1.0, 0.1]])
probs  = F.softmax(logits, dim=-1)        # [0.659, 0.242, 0.099]
logp   = F.log_softmax(logits, dim=-1)    # numerically stabler

# Common bug: applying softmax then CrossEntropyLoss (which expects logits)
# → double softmax. Pass raw logits to nn.CrossEntropyLoss directly.

# 8) Where to place activations
class MLP(nn.Module):
    def __init__(self, d_in=784, d_hidden=256, d_out=10):
        super().__init__()
        self.fc1 = nn.Linear(d_in, d_hidden)
        self.fc2 = nn.Linear(d_hidden, d_hidden)
        self.fc3 = nn.Linear(d_hidden, d_out)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        return self.fc3(x)                 # raw logits — no softmax

# 9) Custom activation
class Mish(nn.Module):
    def forward(self, x):
        return x * torch.tanh(F.softplus(x))

mish = Mish()

# 10) Gating — modern Transformer FFN (GLU variants)
class SwiGLU(nn.Module):
    def __init__(self, d_model, d_ff):
        super().__init__()
        self.w1 = nn.Linear(d_model, d_ff, bias=False)
        self.w2 = nn.Linear(d_model, d_ff, bias=False)
        self.w3 = nn.Linear(d_ff, d_model, bias=False)

    def forward(self, x):
        return self.w3(F.silu(self.w1(x)) * self.w2(x))

# 11) Initialization matters
linear = nn.Linear(256, 256)
nn.init.kaiming_normal_(linear.weight, nonlinearity='relu')   # He init for ReLU
nn.init.xavier_uniform_(linear.weight)                          # Xavier for tanh/sigmoid

# 12) Diagnostics — watch dead neurons
def relu_alive_fraction(model, x):
    activations = []
    def hook(m, i, o): activations.append((o > 0).float().mean().item())
    handles = [m.register_forward_hook(hook) for m in model.modules() if isinstance(m, nn.ReLU)]
    model(x); [h.remove() for h in handles]
    return activations
# < 0.1 alive fraction → many dead neurons → use LeakyReLU or lower LR

# 13) Quick reference — what to use
#   Hidden layers (general):  ReLU
#   Transformers FFN:          GELU or SwiGLU
#   CNNs (modern):             SiLU/Swish
#   RNN/LSTM gates:            sigmoid + tanh
#   Output: classification:    softmax (or none + CrossEntropyLoss)
#   Output: regression:         no activation (linear)
#   Output: bounded regression: tanh or sigmoid

# 14) Common bugs
#   • Softmax then CrossEntropyLoss → use raw logits
#   • ReLU on output of a regression model → can't predict negatives
#   • Sigmoid everywhere → vanishing gradients, slow training
#   • Forgetting dim= in softmax → softmaxes over the wrong axis
#   • Putting activation before, not after, batchnorm → known suboptimal

Why it matters

For most modern hidden layers, ReLU is fine; GELU and SiLU win in Transformers and large CNNs. The output layer is different — emit raw logits and let the loss function (e.g. CrossEntropyLoss) handle the softmax internally for numerical stability.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
import torch.nn.functional as F
F.relu(x)
F.gelu(x)
F.silu(x)
F.softmax(x, dim=-1)
Try it Yourself »

Discussion

Loading…