EasyActivation Functions

Implement Swish Activation

Activation Functions

Easy

Problem

Apply the Swish activation elementwise:

\operatorname{Swish}(x) = x\,\sigma(x)

\sigma(x) = \frac{1}{1+e^{-x}}

Here, x is each input value and \sigma is the sigmoid function. Compute sigmoid without overflow and return a NumPy array with the same shape as the input.

Theory

ReLU (\max(0, x)) is the most popular activation function in deep learning, but it is not optimal in every setting. Its sharp corner at zero and completely dead negative region leave room for improvement.

Researchers have tried many alternatives (Leaky ReLU, ELU, PReLU, etc.), each designed by hand. In 2017, Google Brain took a different approach: they used automated search to discover new activation functions by combining basic mathematical operations. The best function they found was Swish.


What Swish Does

Swish is defined as:

\text{Swish}(x) = x \cdot \sigma(x)

where \sigma(x) = \frac{1}{1 + e^{-x}} is the sigmoid function.

The formula multiplies the input x by its own sigmoid. Since sigmoid outputs values between 0 and 1, Swish acts like a smooth gate that controls how much of $$ passes through:

Some concrete values:


The Non-Monotonic "Bump"

One of Swish's most interesting properties: it is non-monotonic. Unlike ReLU (always increasing or flat) or sigmoid (always increasing), Swish dips slightly below zero before coming back up.

The minimum occurs at approximately x \approx -1.28, where \text{Swish}(x) \approx -0.278.

This means:

This non-monotonicity is unusual for an activation function, and it is part of what makes Swish work well. It allows the network to produce small negative activations for moderately negative inputs while still suppressing very negative ones. This provides more information to downstream layers compared to ReLU's hard zero.


Swish vs. ReLU

The key differences:

Smoothness: Swish is infinitely differentiable (smooth everywhere). ReLU has a sharp corner at zero where the derivative is undefined. Smooth functions create smoother loss landscapes, which can make optimization easier.

Negative values: Swish allows small negative outputs. ReLU outputs exactly zero for all negative inputs. The small negative values in Swish:

Asymptotic behavior: for large positive x, both approach f(x) \approx x. For large negative x, both approach 0. The difference is in the transition region around zero.

Computational cost: Swish requires computing sigmoid (e^{-x}, addition, division) plus a multiplication. ReLU is just a comparison with zero. Swish is more expensive per operation, but the overall impact on training time is small because the matrix multiplications dominate.


The Gradient

The derivative of Swish is:

\text{Swish}'(x) = \sigma(x) + x \cdot \sigma(x)(1 - \sigma(x))

This can be simplified to:

\text{Swish}'(x) = \sigma(x) + x \cdot \sigma'(x) = \text{Swish}(x) + \sigma(x)(1 - \text{Swish}(x))

Key properties of the gradient:


Swish and SiLU

Swish is also known as SiLU (Sigmoid Linear Unit). The names refer to the same function:

\text{SiLU}(x) = \text{Swish}(x) = x \cdot \sigma(x)

In PyTorch, it is called `torch.nn.SiLU`. In some papers and frameworks, you will see either name. They are interchangeable.


Where Swish Shows Up

Examples

Example 1

Input
x = [0, 1, -1, 3]
Output
[0.0, 0.731059, -0.268941, 2.857722]
Explanation
Each value is multiplied by its sigmoid gate.

Example 2

Input
x = [[1, -1], [2, -2]]
Output
[[0.731059, -0.268941], [1.761594, -0.238406]]

Hints

  1. Use np.exp(-np.logaddexp(0.0, -x)) for a stable sigmoid.
  2. Multiply the sigmoid array elementwise by x.

Requirements

Constraints

Starter Code

import numpy as np

def swish(x: list) -> np.ndarray:
    """
    Returns a NumPy array with the same shape as x.
    """
    # Write code here
    pass

Test Cases

CaseMatches
Mixed valuesExample 1public
2D arrayExample 3public