t27.aiEnglish

Swish и SiLU

Вы узнаете

Как x, умноженный на sigmoid(beta x), открывает сам себя и как один параметр переводит его от гладкой кривой к ReLU.

Swish это x, умноженный на sigmoid(beta x): нейрон, который сам себе вентиль. При beta 1 он называется SiLU, и его нижняя точка -0.2785 около x = -1.28. Увеличьте beta, и вентиль становится резче: при beta 8 кривая это ReLU со скруглёнными углами, при beta 0.5 пологий склон через ноль. Один параметр переводит нейрон от гладкой кривой к переключателю, и сеть может его выучить. Спека silu_swish_activation.t27 вычисляет кривую и её наклон при любом beta, а также свой пакет при -2, 0 и 2: -0.238, 0 и 1.762.

Попробовать

Держите x на -1.28 и поднимайте beta от 1 до 8; затем найдите beta, при котором провал на этом масштабе исчезает.

Открыть интерактивный урок →

Swish: one knob between a smooth curve and ReLU
Swish: one knob between a smooth curve and ReLU ↗

Drag along x times sigmoid(beta x). At beta 1 it is SiLU, with its dip of -0.28; turn beta up and the curve closes on ReLU, turn it down and it flattens toward x / 2.

specs/ml/activation/silu_swish_activation.t27

// SPDX-License-Identifier: Apache-2.0
// t27/specs/
// SiLU derivative | φ² + 1/φ² = 3 | TRINITY

// #8980: forward_batch returned its input unchanged, beta was never read, and the
// invariants asserted `true` over an undeclared `valid_input`. SiLU is now
// x * sigmoid(beta * x) (Swish; beta = 1 is SiLU) on scalars and in place on slices.

module SiluSwish;
    use base::types;
    use math::constants;

    // ═══════════════════════════════════════════════════════════
    // 1. Constants
    // ═══════════════════════════════════════════════════════════

    const DEFAULT_BETA : f32 = 1.0;

    // ═══════════════════════════════════════════════════════════
    // 2. Types
    // ═══════════════════════════════════════════════════════════

    pub const SiLUConfig = struct {
        beta : f32,
    };

    // ═══════════════════════════════════════════════════════════
    // 3. Core Functions
    // ═══════════════════════════════════════════════════════════

    // sigmoid(z) = 1 / (1 + exp(-z))
    fn sigmoid(z: f32) -> f32 {
        return 1.0 / (1.0 + exp(-z));
    }

    // Swish(x) = x * sigmoid(beta * x); beta = 1 is SiLU
    fn forward(x: f32, beta: f32) -> f32 {
        return x * sigmoid(beta * x);
    }

    // forward on each element IN PLACE; returns the same slice
    fn forward_batch(input: []f32, beta: f32) -> []f32 {
        var i : usize = 0;
        while (i < input.len) {
            input[i] = forward(input[i], beta);
            i = i + 1;
        }
        return input;
    }

    // d/dx x sigmoid(beta x) = s + beta x s (1 - s), s = sigmoid(beta x)
    fn derivative(x: f32, beta: f32) -> f32 {
        const s = sigmoid(beta * x);
        return s + beta * x * s * (1.0 - s);
    }

    fn batch_at(a: f32, b: f32, c: f32, index: usize) -> f32 {
        var input : [3]f32 = [a, b, c];
        const output = forward_batch(&input, DEFAULT_BETA);
        return output[index];
    }

    fn valid_input() -> f32 {
        return 2.0;
    }

    // ═══════════════════════════════════════════════════════════
    // TDD: Tests (from .tri behaviors)
    // ═══════════════════════════════════════════════════════════

    test "forward_basic_case"
        given input = 0.0, beta = 1.0
        when result = forward(input, beta)
        then result == 0.0  // SiLU(0) = 0 * sigmoid(0) = 0

    test "forward_positive_case"
        given input = 1.0, beta = 1.0
        when result = forward(input, beta)
        then result > 0.0  // SiLU(1) = 1 * sigmoid(1) > 0

    test "forward_negative_case"
        given input = -1.0, beta = 1.0
        when result = forward(input, beta)
        then result < 0.0  // SiLU(-1) = -1 * sigmoid(-1) < 0

    test "forward_batch_basic_case"
        given beta = 1.0
        when result = batch_at(1.0, 2.0, 3.0, 2)
        then @abs(result - forward(3.0, beta)) < 0.0000001 and @abs(batch_at(1.0, 2.0, 3.0, 0) - 0.7310586) < 0.000001

    test "forward_batch_zero_case"
        given beta = 1.0
        when result = batch_at(0.0, 0.0, 0.0, 0)
        then result == 0.0 and batch_at(0.0, 0.0, 0.0, 1) == 0.0 and batch_at(0.0, 0.0, 0.0, 2) == 0.0  // SiLU(0) = 0

    test "derivative_basic_case"
        given input = 0.0, beta = 1.0
        when result = derivative(input, beta)
        then result > 0.0  // SiLU'(0) = sigmoid(0) + 0 * sigmoid(0) * (1 - sigmoid(0)) = sigmoid(0) > 0

    test "derivative_positive_case"
        given input = 1.0, beta = 1.0
        when result = derivative(input, beta)
        then result > 0.0  // SiLU derivative should be positive for positive inputs

    test "derivative_negative_case"
        given input = -1.0, beta = 1.0
        when result = derivative(input, beta)
        then result > 0.0  // SiLU derivative should be positive for negative inputs too

    // ═══════════════════════════════════════════════════════════
    // TDD: Invariants (from .tri constraints)
    // ═══════════════════════════════════════════════════════════

    // beta > 0
    invariant silu_swish_constraint_0
        assert DEFAULT_BETA > 0.0

    // name: silu_formula -- x * sigmoid(x), so SiLU(x) - SiLU(-x) = x
    invariant silu_swish_constraint_1
        assert @abs(forward(valid_input(), DEFAULT_BETA) - forward(-valid_input(), DEFAULT_BETA) - valid_input()) < 0.000001

    // name: derivative -- sigmoid(0) = 0.5 at the origin
    invariant silu_swish_constraint_2
        assert derivative(0.0, DEFAULT_BETA) == 0.5

    // "Elfwing et al. (2017) - Sigmoid-Weighted Linear Units for Neural Network Function Approximation":
    // SiLU dips below zero left of the origin
    invariant silu_swish_constraint_3
        assert forward(-valid_input(), DEFAULT_BETA) < 0.0

    // "Ramachandran et al. (2017) - Searching for Activation Functions (Swish)": a large beta
    // approaches ReLU
    invariant silu_swish_constraint_4
        assert @abs(forward(valid_input(), 50.0) - valid_input()) < 0.000001

Открыть спеку урока в плеере ↗

Все уроки