Сигмоида: число в вероятность
Вы узнаете
Как сигмоида переводит любой вход в вероятность и почему её наклон мешает глубоким сетям.
Сигмоида 1 / (1 + e^-x) сжимает всю числовую прямую в отрезок от 0 до 1, поэтому её читают как вероятность: 2 превращается в 0.8808, а -4 в 0.018. Её наклон равен s (1 - s) и считается по самому выходу s; он наибольший в 0, где равен 0.25, а при x = 8 равен 0.000335. Градиент, который идёт назад через стопку сигмоид, на каждой умножается не больше чем на 0.25, поэтому глубокие сети на сигмоидах учились медленно. Спека sigmoid_activation.t27 вычисляет кривую и наклон.
Попробовать
Найдите вход, при котором вероятность равна 0.5; затем перетащите к 6 и посмотрите, как мало осталось наклона.

Drag along the S-curve: every input lands between 0 and 1, and the slope peaks at 0.25 in the middle and dies away at both ends.
specs/ml/activation/sigmoid_activation.t27
// SPDX-License-Identifier: Apache-2.0
// t27/specs/
// Derivative (compute from output) | φ² + 1/φ² = 3 | TRINITY
module Sigmoid;
use base::types;
use math::constants;
// ═══════════════════════════════════════════════════════════
// 2. Types
// ═══════════════════════════════════════════════════════════
pub const SigmoidConfig = struct {
none : void,
};
// ═══════════════════════════════════════════════════════════
// 3. Core Functions
// ═══════════════════════════════════════════════════════════
// forward(x: f32) → f32
fn forward(x: f32) -> f32 {
// Numerically stable split to avoid overflow:
// For x >= 0: 1 / (1 + exp(-x))
// For x < 0: exp(x) / (1 + exp(x))
// Both branches are mathematically equivalent but the second avoids exp(large positive)
if (x >= 0.0) {
return 1.0 / (1.0 + @exp(-x));
} else {
return @exp(x) / (1.0 + @exp(x));
}
}
// forward_batch(input: []f32) → []f32
fn forward_batch(input: []f32) -> []f32 {
// In-place map over input: alias input and return it
// This is safe because we're only reading from input and writing to the same locations
let i = 0;
while (i < input.len) {
input[i] = forward(input[i]);
i += 1;
}
return input;
}
// derivative(sigmoid_x: f32) → f32
fn derivative(sigmoid_x: f32) -> f32 {
// Derivative computed from output: σ'(x) = σ(x) * (1 - σ(x))
return sigmoid_x * (1.0 - sigmoid_x);
}
// ═══════════════════════════════════════════════════════════
// TDD: Tests (from .tri behaviors)
// ═══════════════════════════════════════════════════════════
// #8980: through a function, so the check runs and is counted (it folded at compile
// time before, and the report counted it as a vacuous pass).
fn config_size() -> usize {
return @sizeOf(SigmoidConfig);
}
test sigmoid_has_no_tunable_parameter
given size = config_size()
then size == 0
// sigmoid_config_is_a_placeholder_not_an_empty_struct read std.meta.fields, which t27
// cannot lower; the parser discarded it. The test above already pins @sizeOf == 0.
test forward_at_zero_is_one_half
given y = forward(0.0)
then @abs(y - 0.5) < 1e-6
test forward_confined_to_unit_interval
given pos_large = forward(10.0)
given neg_large = forward(-10.0)
then (pos_large > 0.0) and (pos_large < 1.0) and (neg_large > 0.0) and (neg_large < 1.0)
test derivative_at_zero_is_maximum
given dy = derivative(forward(0.0))
then @abs(dy - 0.25) < 1e-6
test forward_batch_preserves_length
given input = []f32{0.0, 1.0, -1.0, 2.0, -2.0}
given result = forward_batch(input)
then result.len == input.len
// sigmoid(-x) = 1 - sigmoid(x); sigmoid(2) = 0.8807971
test forward_is_symmetric_about_one_half
given up = forward(2.0)
when down = forward(-2.0)
then @abs(up + down - 1.0) < 0.000001 and @abs(up - 0.8807971) < 0.000001
// The negative branch never computes exp(+x): no overflow at -100
test forward_far_left_is_zero_not_nan
given y = forward(-100.0)
then y >= 0.0 and y < 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, который учится на награде без рывков.