ML Atlas

06 · Sieci · 4 min czytania · Interaktywne · aktualizacja

Co to jest forward pass w sieci neuronowej i jak sieć liczy wynik?

W skrócie

Przejście w przód to obliczenie wyniku sieci: dane płyną warstwa po warstwie przez mnożenie przez wagi i funkcje aktywacji, aż do predykcji i straty.

Co to jest

Przejście w przód (ang. forward pass, też forward propagation) to obliczenie wyjścia sieci neuronowej dla danego wejścia: dane przechodzą kolejno przez wszystkie warstwy, a każda mnoży je przez swoje wagi, dodaje wyraz wolny i stosuje funkcję aktywacji. Na końcu powstaje predykcja, a w czasie treningu także wartość funkcji straty. To jedyna operacja potrzebna do używania gotowego modelu.

Można to porównać do linii produkcyjnej. Surowiec (piksele, słowa, liczby z tabeli) trafia na pierwsze stanowisko, które przetwarza go na półprodukt, i tak dalej. Żadne stanowisko nie widzi całości, każde dostaje tylko wynik poprzedniego. Ostatnie wydaje gotowy produkt: np. dziesięć prawdopodobieństw, po jednym dla każdej cyfry.

Podczas treningu przejście w przód jest pierwszą połową kroku uczenia. Drugą jest propagacja wsteczna, która idzie tą samą drogą w odwrotnym kierunku i liczy, jak zmienić każdą wagę. Dlatego w trakcie treningu przejście w przód zapamiętuje wyniki pośrednie: będą potrzebne do obliczenia gradientów.

Mechanizm — dlaczego tak działa

Dla jednej warstwy w pełni połączonej obliczenie ma dwa kroki: najpierw z = W·a + b (każdy neuron liczy sumę ważoną wyjść poprzedniej warstwy a), potem a' = f(z), gdzie f to aktywacja, np. ReLU, sigmoida albo tanh. Sieć to złożenie takich funkcji: wynik = f₃(W₃·f₂(W₂·f₁(W₁·x + b₁) + b₂) + b₃). Cały „model” to po prostu konkretne wartości macierzy W i wektorów b.

Ostatnia warstwa zależy od zadania. W regresji zwykle nie ma aktywacji i sieć zwraca liczbę. W klasyfikacji binarnej sigmoida σ(z) = 1 / (1 + e^(−z)) zamienia wynik na prawdopodobieństwo. W klasyfikacji wieloklasowej softmax zamienia wektor surowych wyników (logitów) na rozkład prawdopodobieństwa sumujący się do 1.

W praktyce nie przetwarza się przykładów pojedynczo. Cały mini-batch układa się w macierz X (wiersze to przykłady), a warstwa liczy Z = X·W + b dla wszystkich naraz. Mnożenie macierzy jest bardzo dobrze zoptymalizowane na procesorach i kartach graficznych, dlatego czas przejścia w przód rośnie z liczbą przykładów mniej niż liniowo, dopóki sprzęt nie jest w pełni wykorzystany.

Koszt obliczeń jest łatwy do policzenia: warstwa z n wejściami i m neuronami wykonuje n·m mnożeń z dodawaniem. Przejście w przód jest więc tanie w porównaniu z treningiem. Krok treningu (przejście w przód plus propagacja wsteczna) kosztuje typowo około trzy razy tyle, co samo przejście w przód, a trening powtarza go tysiące razy.

Dwie warstwy zachowują się inaczej w treningu i w predykcji. Dropout w trybie treningowym losowo zeruje neurony, w trybie predykcji nie. Normalizacja wsadowa w treningu używa statystyk bieżącego batcha, a w predykcji średnich zapamiętanych w trakcie treningu. Zapomnienie o przełączeniu trybu daje wyniki, które zmieniają się przy każdym wywołaniu albo zależą od tego, jakie inne przykłady są w batchu.

Na przykładzie

Mała sieć 2-2-1 z sigmoidą w obu warstwach. Wejście x = (1; 0,5), wagi pierwszego neuronu ukrytego (0,5; −0,3), drugiego (0,2; 0,8), wyrazy wolne zerowe, wagi wyjścia (1; −1). Neuron pierwszy: z₁ = 0,5·1 − 0,3·0,5 = 0,35, więc h₁ = σ(0,35) ≈ 0,587. Neuron drugi: z₂ = 0,2·1 + 0,8·0,5 = 0,60, więc h₂ = σ(0,60) ≈ 0,646. Wyjście: z = 0,587 − 0,646 ≈ −0,059, a y = σ(−0,059) ≈ 0,485. Jeśli prawidłowa odpowiedź to 1, entropia krzyżowa wynosi −ln(0,485) ≈ 0,723. Ten sam przykład kontynuuje hasło o propagacji wstecznej.

Na zbiorze Digits 8×8 (piksele podzielone przez 16, sieć 64-64-10 z ReLU, MLPClassifier, random_state=0) jedno przejście w przód to 64·64 + 64·10 = 4736 mnożeń z dodawaniem. Ręczne obliczenie Z = X·W + b na wagach z coefs_ daje prawdopodobieństwa identyczne z predict_proba. Dla pierwszego obrazka testowego (cyfra 2) aktywnych jest 45 z 64 neuronów ukrytych, a softmax daje 70,9% dla „2” i 28,3% dla „3”: logity 4,60 i 3,69 różnią się niecałą jednostką, a to wystarczy na wyraźną przewagę.

Ta ilustracja działa w przeglądarce z włączonym JavaScriptem: graf obliczeń jednego neuronu L = (σ(w₁x₁ + w₂x₂ + b) − y)² liczony krok po kroku: najpierw wartości w przód, potem gradienty wstecz z regułą łańcuchową; przy w₁ = 0,5 wychodzi ∂L/∂w₁ = −0,255, tyle samo co z różnicy skończonej.

Dane: Digits (ręcznie pisane cyfry 8×8)

W praktyce

  • PyTorch: wywołanie model(x) uruchamia metodę forward. Do predykcji używaj model.eval() i torch.no_grad() (albo torch.inference_mode()), żeby wyłączyć dropout, zamrozić statystyki normalizacji i nie zapisywać grafu obliczeń.
  • scikit-learn: predict_proba i predict w MLPClassifier to przejście w przód; wagi są w coefs_ i intercepts_, co pozwala policzyć je ręcznie dla zrozumienia.
  • Kształty tensorów to najczęstsze źródło błędów: nn.Linear(64, 10) oczekuje wejścia o ostatnim wymiarze 64; wypisuj x.shape po każdej warstwie przy debugowaniu.
  • Nie stosuj softmaxu przed nn.CrossEntropyLoss, bo ta funkcja sama go stosuje (w stabilnej numerycznie formie log-softmax); podwójny softmax spłaszcza gradienty.
  • Pamięć w treningu rośnie z rozmiarem batcha, bo przejście w przód przechowuje aktywacje każdej warstwy do propagacji wstecznej.

Najczęstsze pytania

Czym różni się przejście w przód od inferencji?
Inferencja to używanie wytrenowanego modelu do predykcji i składa się wyłącznie z przejść w przód. Przejście w przód występuje też w treningu, ale wtedy zapisuje wyniki pośrednie i jest poprzedzeniem propagacji wstecznej.
Dlaczego sieć daje różne wyniki dla tego samego wejścia?
Najczęściej model jest w trybie treningowym, więc dropout losowo wyłącza neurony, a normalizacja wsadowa używa statystyk bieżącego batcha. Po `model.eval()` wynik powinien być deterministyczny (z dokładnością do drobnych różnic numerycznych na GPU).
Co to są logity?
To surowe wyniki ostatniej warstwy, zanim zamieni je softmax lub sigmoida. Liczą się różnice między nimi: gdy logit jednej klasy rośnie o 1, stosunek jej prawdopodobieństwa do prawdopodobieństwa każdej innej klasy rośnie e ≈ 2,7 raza.

Źródła

  • Goodfellow I., Bengio Y., Courville A., „Deep Learning”, MIT Press, 2016, rozdz. 6, zwłaszcza podrozdz. 6.5.
  • Zhang A., Lipton Z. C., Li M., Smola A. J., „Dive into Deep Learning”, d2l.ai, rozdział „Forward Propagation, Backward Propagation, and Computational Graphs”.
  • Bishop C. M., „Pattern Recognition and Machine Learning”, Springer, 2006, podrozdz. 5.1 „Feed-forward Network Functions”.
  • Dokumentacja PyTorch, torch.nn.Module: https://pytorch.org/docs/stable/generated/torch.nn.Module.html

Zobacz też