Полный forward pass на маленькой сети

Урок соединяет слои в полный проход: от входа до итогового выхода сети.

Forward pass — это запуск сети «как есть»: с текущими (пусть даже случайными) весами посчитать, что она выдаёт на данном входе.

Что мы собираем

Возьмём сеть 2 → 3 → 1: два входа, скрытый слой из трёх нейронов с sigmoid, один выходной нейрон с sigmoid. Веса зададим вручную фиксированными числами, чтобы вывод был воспроизводимым. Наша задача — аккуратно прогнать вход через оба слоя и посмотреть на результат.

Считаем проход целиком

import math

def sigmoid(x):
    return 1.0 / (1.0 + math.exp(-x))

# Скрытый слой: 3 нейрона, у каждого 2 веса
W1 = [[0.5, -0.3],
      [0.8,  0.2],
      [-0.4, 0.7]]
b1 = [0.1, -0.2, 0.05]
# Выходной слой: 1 нейрон с 3 входами
W2 = [0.6, -0.9, 0.4]
b2 = -0.1

def forward(x):
    # скрытый слой
    h = []
    for j in range(3):
        s = W1[j][0]*x[0] + W1[j][1]*x[1] + b1[j]
        h.append(sigmoid(s))
    # выходной слой
    s_out = W2[0]*h[0] + W2[1]*h[1] + W2[2]*h[2] + b2
    out = sigmoid(s_out)
    return h, out

x = [1.0, 2.0]
h, out = forward(x)
print("Вход:", x)
print("Скрытый слой:", [round(v, 3) for v in h])
print("Выход сети:", round(out, 4))

Вывод:

Вход: [1.0, 2.0]
Скрытый слой: [0.5, 0.731, 0.741]
Выход сети: 0.4597

Что произошло по шагам

  1. Каждый из трёх скрытых нейронов взял оба входа, посчитал свою взвешенную сумму и пропустил её через sigmoid. Получили три числа — активации скрытого слоя.
  2. Выходной нейрон взял эти три числа как свой вход, посчитал взвешенную сумму и снова применил sigmoid.
  3. Итог — одно число 0.4597. Поскольку веса случайные (мы их не обучали), это пока бессмысленное предсказание.

Важная мысль: forward pass всегда работает одинаково, обучена сеть или нет. Обучение лишь подбирает удачные веса, но сама процедура прогона неизменна. Когда обученная нейросеть отвечает на ваш запрос — внутри происходит ровно такой forward pass, только из миллионов чисел.

Полезно отделять два режима. Обучение (training): forward pass + вычисление потери + обратный проход + шаг спуска; здесь обязательно хранить активации. Инференс (inference, предсказание): только forward pass — активации хранить не нужно, и потому он быстрее и легче по памяти. Именно инференс работает, когда модель уже обучена и просто отвечает пользователю. Многие приёмы (например, dropout) включаются на обучении и выключаются на инференсе — поэтому в реальных фреймворках сеть явно переключают между режимами train и eval.

Сохраняем промежуточные значения

Обратите внимание: функция возвращает не только выход, но и активации скрытого слоя h. Это не каприз — при обучении (обратное распространение) нам понадобятся все промежуточные значения. Поэтому forward pass перед обучением всегда «запоминает» активации каждого слоя.

Итог

  • Forward pass — последовательный прогон входа через все слои.
  • Выход одного слоя — вход следующего.
  • Промежуточные активации сохраняют для последующего backprop.
Проверьте себя
1. В каком порядке forward pass обрабатывает слои?
AОт выхода ко входу
BОт входа к выходу, выход слоя идёт на вход следующего
CВсе слои одновременно и независимо
DВ случайном порядке
2. Зачем forward pass сохраняет активации скрытых слоёв?
AДля экономии памяти
BОни понадобятся при обратном распространении для вычисления градиентов
CЧтобы вывести их на экран
DЭто требование sigmoid
3. Почему выход 0.4597 в примере не несёт смысла?
ASigmoid сломалась
BВеса заданы вручную/случайно и сеть не обучена
CВход был неверным
DСлоёв слишком мало
Поддержать проект