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, при котором провал на этом масштабе исчезает.

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
Все уроки
Модуль 1 · Один нейрон
Нейрон складывает входы и изгибает сумму: ReLU, сигмоида и tanh, и где каждая перестаёт пропускать градиент назад.
Модуль 2 · Новые изгибы
Гладкие кривые нынешних моделей: GELU с провалом, Swish с ручкой и ELU с полом вместо обрыва.
Модуль 3 · Слой
Что окружает нейроны: softmax превращает оценки в выбор, эмбеддинг превращает слово в числа, dropout выключает нейроны.
Модуль 4 · Картинки
Чтение картинки по участкам: окно, усреднённое по участку, max pooling и пакетная нормализация признаков разного масштаба.
Модуль 5 · Ошибка
Насколько неверна догадка, одним числом: среднеквадратичная ошибка, перекрёстная энтропия для вероятностей и функция Хьюбера для выбросов.
Модуль 6 · Под гору
Спуск по функции потерь: градиентный спуск с моментом, RMSProp без оглядки на величину градиента и Adam, который соединяет оба.
Модуль 7 · Обучение
Откуда берутся градиенты и чего они добиваются: обратное распространение через слой, сеть, которая учит XOR, и LAMB для больших пакетов.
Модуль 8 · Память и внимание
Сети, которые читают последовательность: рекуррентная ячейка и её затухающее эхо, вентили LSTM и внимание ко всем позициям сразу.
Модуль 9 · Трансформер и награда
Части трансформера и агента: позиции как часы, один блок энкодера и PPO, который учится на награде без рывков.