TensorFlow Activation Functions

Activation functions introduce non-linearity into neural networks. Without them, stacking 100 Dense layers produces exactly the same result as a single linear equation — all that depth accomplishes nothing. An activation function takes the output of a neuron's linear calculation and transforms it before passing it forward. This non-linear transformation lets deep networks learn curves, decision boundaries, and complex patterns that no straight line could fit.

Why Non-Linearity Matters

The world's data is rarely linear. House prices do not increase proportionally with every extra square meter — small apartments are expensive per square foot, medium ones are cheaper, and enormous mansions are expensive again. Cancer detection requires recognizing complex pixel combinations, not just "more brightness = more likely cancer." Activation functions give neural networks the ability to model these real-world complexities.

Without activation (all linear):
  Layer 1: output = W1 × input + b1
  Layer 2: output = W2 × (W1 × input + b1) + b2
                  = (W2×W1) × input + (W2×b1 + b2)
                  = W_combined × input + b_combined
  → Just one linear equation, no matter how deep

With activation (non-linear):
  Layer 1: output = ReLU(W1 × input + b1)
  Layer 2: output = ReLU(W2 × ReLU(...) + b2)
  → Cannot be collapsed into one equation
  → Can approximate any function

ReLU — Rectified Linear Unit

ReLU is the most widely used activation function in hidden layers. It outputs the input if positive and zero if negative. Its simplicity makes it fast to compute and its gradient is either 0 or 1, which prevents the vanishing gradient problem that plagued earlier activations.

Formula: f(x) = max(0, x)

Input:   -3   -1    0    1    3    5
Output:   0    0    0    1    3    5

Graph:
         |        /
         |       /
    ─────|──────/─────── x
         |    /
         |   0
import tensorflow as tf

x = tf.constant([-3.0, -1.0, 0.0, 1.0, 3.0])
print(tf.nn.relu(x))         # [0.0, 0.0, 0.0, 1.0, 3.0]

# In a layer:
tf.keras.layers.Dense(64, activation='relu')

The Dying ReLU Problem

If many inputs are always negative, the neurons always output zero and their gradients are zero — the neuron "dies" and stops learning. Solutions: use He initialization, reduce the learning rate, or switch to Leaky ReLU.

Leaky ReLU

Leaky ReLU allows a small, non-zero gradient for negative inputs, preventing neurons from dying permanently.

Formula: f(x) = max(0.1x, x)

Input:   -3   -1    0    1    3
Output:  -0.3 -0.1  0    1    3
# As a layer
tf.keras.layers.LeakyReLU(alpha=0.1)

# As a separate layer in Sequential
model = tf.keras.Sequential([
    tf.keras.layers.Dense(64),
    tf.keras.layers.LeakyReLU(alpha=0.2)
])

Sigmoid

Sigmoid squashes any input to a value between 0 and 1. This makes it perfect for binary classification output layers where the output represents a probability.

Formula: f(x) = 1 / (1 + e^(-x))

Input:   -5     -2     0     2     5
Output:  0.007  0.119  0.5   0.880  0.993

Graph:
    1 |──────────────────────────╮
      |                      ╭──╯
  0.5 |────────────────────╮──
      |                ╭───╯
    0 |────────────────╯──────── x
x = tf.constant([-5.0, -2.0, 0.0, 2.0, 5.0])
print(tf.nn.sigmoid(x))
# [0.007, 0.119, 0.500, 0.880, 0.993]

# Used in binary classification output
tf.keras.layers.Dense(1, activation='sigmoid')

Sigmoid Drawback

For very large or very small inputs, the sigmoid gradient approaches zero. This creates the vanishing gradient problem in deep networks. Avoid sigmoid in hidden layers — use it only in binary classification output layers.

Tanh — Hyperbolic Tangent

Tanh is a shifted and scaled version of sigmoid. It squashes inputs to between -1 and 1, centering outputs around zero. Zero-centered outputs help gradient flow in RNNs.

Formula: f(x) = (e^x - e^(-x)) / (e^x + e^(-x))

Input:   -3    -1     0     1     3
Output:  -0.995 -0.762  0   0.762  0.995

# In a layer:
tf.keras.layers.Dense(64, activation='tanh')

# Common use: LSTM and GRU hidden states

Softmax

Softmax converts a vector of raw scores into a probability distribution. Every output value falls between 0 and 1, and all outputs sum to exactly 1. Use softmax in the output layer for multi-class classification where the model picks one class from many.

Formula: softmax(x_i) = e^(x_i) / Σ e^(x_j)

Example — 3-class classification:
  Raw scores (logits): [2.0, 1.0, 0.1]
  After softmax:       [0.659, 0.242, 0.099]
  Sum:                  1.000

The class with the highest probability (0.659, index 0) is the prediction.
x = tf.constant([[2.0, 1.0, 0.1]])
print(tf.nn.softmax(x))
# [[0.659, 0.242, 0.099]]

# Final layer for multi-class classification
tf.keras.layers.Dense(num_classes, activation='softmax')

ELU — Exponential Linear Unit

ELU allows negative outputs for negative inputs (unlike ReLU) but does so smoothly. This reduces the bias shift problem that occurs when ReLU neurons always output zero or positive values.

Formula: f(x) = x if x > 0 else α(e^x - 1)  where α=1.0

tf.keras.layers.Dense(64, activation='elu')

SELU — Scaled ELU

SELU is a self-normalizing activation: if you use it throughout a deep network with LeCun initialization, the network outputs tend to normalize automatically (mean near 0, variance near 1). This removes the need for BatchNormalization in many architectures.

tf.keras.layers.Dense(64, activation='selu',
                       kernel_initializer='lecun_normal')

Linear (No Activation)

Specifying no activation leaves the neuron outputs as raw numbers with no transformation. Use this in regression output layers where you want to predict an unbounded numerical value.

# Regression — predict house price (any positive number)
tf.keras.layers.Dense(1)           # No activation
tf.keras.layers.Dense(1, activation='linear')  # Same thing explicitly

Activation Function Quick Reference

Function    Output Range    Best Use
────────────────────────────────────────────────────────────────
ReLU        [0, +∞)        Hidden layers (most common)
Leaky ReLU  (-∞, +∞)      Hidden layers (avoids dying neurons)
Sigmoid     (0, 1)         Binary classification output
Tanh        (-1, 1)        RNN hidden states
Softmax     (0, 1), sum=1  Multi-class classification output
ELU         (-α, +∞)      Hidden layers (smooth negatives)
SELU        (-λα, +∞)     Deep fully-connected (self-normalizing)
Linear      (-∞, +∞)      Regression output
────────────────────────────────────────────────────────────────

Applying Activation as a Separate Layer

# Inline approach (most common)
tf.keras.layers.Dense(64, activation='relu')

# Separate layer approach (useful for inspecting intermediate values)
model = tf.keras.Sequential([
    tf.keras.layers.Dense(64),
    tf.keras.layers.Activation('relu'),
    tf.keras.layers.Dense(10),
    tf.keras.layers.Activation('softmax')
])

Custom Activation Functions

import tensorflow as tf

# Define a custom activation: swish = x × sigmoid(x)
def swish(x):
    return x * tf.nn.sigmoid(x)

# Use it in a layer
layer = tf.keras.layers.Dense(64, activation=swish)

# Or register it so it can be referenced by name
@tf.keras.utils.register_keras_serializable()
def my_activation(x):
    return tf.nn.relu(x) ** 2  # Squared ReLU

layer = tf.keras.layers.Dense(64, activation='my_activation')

Choosing the right activation function is as important as choosing the right architecture. ReLU handles most hidden layers well. Sigmoid and softmax handle outputs. Tanh handles sequential data. The next topic explains model compilation — where you wire together the loss function, optimizer, and metrics that guide the learning process.

Leave a Comment

Your email address will not be published. Required fields are marked *