Od pochodnych do propagacji wstecznej: teoria stojąca za Micrograd

Proces trenowania sieci neuronowej polega na wyszukiwaniu wag i odchyleń, dzięki którym jej przewidywania będą dokładniejsze. Porównujemy te przewidywania z oczekiwanymi odpowiedziami, korzystając z funkcji straty — formuły, która zamienia niedopasowanie na liczbę, przy czym niższa wartość wskazuje lepsze dopasowanie. Aby wybrać aktualizację, musimy wiedzieć, jak zmiana każdego parametru wpłynie na tę stratę. Moglibyśmy losowo zmienić parametr, ponownie obliczyć stratę i porównać wyniki. Jednak sprawdzenie w ten sposób milionów parametrów pojedynczo wymagałoby milionów dodatkowych ocen.

Aby poprowadzić to wyszukiwanie, używamy gradientu, który zbiera pochodne cząstkowe straty w odniesieniu do parametrów. Te pochodne pozwalają oszacować, jak małe zmiany parametrów wpłyną na stratę, bez jej ponownego przeliczania. Dla każdego parametru pochodna opisuje, jak wrażliwa jest strata na ten parametr, podczas gdy pozostałe pozostają stałe.

Matematycznie jest to problem optymalizacji, a konkretnie minimalizacji: szukamy wartości parametrów modelu, które sprawią, że strata będzie jak najmniejsza. Chociaż techniki zerowego rzędu, takie jak MeZO, mogą kierować aktualizacjami poprzez losowe zakłócanie wielu parametrów jednocześnie i ocenę strat, standardowe podejście polega na wykorzystaniu informacji pochodnych do kierowania wyszukiwaniem. Dzisiejsze sieci neuronowe są głównie trenowane przy użyciu gradientów obliczonych przez algorytm znany jako propagacja wsteczna — podejście, które tutaj rozwiniemy.

Aby zrozumieć propagację wsteczną, oprzemy się na kilku koncepcjach: pochodnych lokalnych, regule łańcuchowej i grafach obliczeniowych. Po drodze przyjrzymy się trzem metodom obliczania pochodnych: różniczkowaniu numerycznemu, różniczkowaniu symbolicznemu i różniczkowaniu automatycznemu. Zaczniemy od funkcji na tyle małych, że można je różniczkować ręcznie i porównać ich dokładne pochodne z szacunkami na podstawie różnic skończonych, a następnie przejdziemy do funkcji z kilkoma danymi wejściowymi. Użyjemy pochodnych cząstkowych, aby wyizolować efekt każdego sygnału wejściowego, utrzymując pozostałe na stałym poziomie i zbierając te współczynniki w postaci gradientu.

Następnie prześledzimy zmiany przez połączone operacje: reguła łańcuchowa mnoży pochodne wzdłuż ścieżki i sumuje wkłady, gdy jedno wejście wpływa na wynik kilkoma ścieżkami. Kluczowa idea brzmi: znając pochodne niewielkiego zestawu operacji podstawowych, możemy różniczkować zbudowane z nich funkcje. Nawet pochodną kwadratu można otrzymać z samego mnożenia. Powiążemy tę ideę z różniczkowaniem symbolicznym, a następnie prześledzimy przebieg w przód i wstecz, aby zobaczyć, jak automatyczne różniczkowanie w trybie odwrotnym wykorzystuje wyniki pośrednie do obliczania gradientów dla wielu wejść. Na tym opiera się propagacja wsteczna.

W tym artykule przedstawiono teoretyczne podstawy zrozumienia Micrograd Andreja Karpathy’ego, małego silnika autogradowego z interfejsem API podobnym do PyTorch. W swoim filmie instruktażowym buduje silnik i używa go do uczenia sieci neuronowej. Te same zasady leżą u podstaw torch.autograd.backward firmy PyTorch, który oblicza gradienty przy użyciu reguły łańcuchowej.

Od tempa zmian do pochodnych

Zacznijmy od prostej zależności liniowej z=3xz=3x i znajdź jej pochodną, badając nachylenie jej wykresu. Pochodna mówi nam, jak szybko zmienia się wynik funkcji, gdy zmieniamy jej dane wejściowe, przy określonej wartości wejściowej. W tym przypadku każdy wzrost xx powoduje trzykrotny wzrost zz. Używając Δ\Delta do reprezentowania zmiany, możemy napisać:

Δz=3 Δx.\Delta z=3\,\Delta x.

Kiedy wykreślamy xx poziomo i zz pionowo, to stałe tempo zmian daje linię prostą. W przypadku linii prostej pochodna jest równa jej nachyleniu, więc możemy ją znaleźć, obliczając stosunek zmiany sygnału wyjściowego do zmiany sygnału wejściowego, często nazywany „wzrostem po przebiegu”:

slope=ΔzΔx=riserun\text{slope}=\frac{\Delta z}{\Delta x}=\frac{\text{rise}}{\text{run}}

Na poniższym wykresie możemy zobaczyć, jak niebieska linia z=3xz=3x wznosi się o 33 dla przebiegu 11, dając nachylenie 3/1=33/1=3.

Compare slopes of 3, 1, and 0.5 on the same axesThe solid blue line z = 3x passes through (0, 0), (1, 3), and (2, 6). A green horizontal segment shows a run of 1, and an orange vertical segment shows a rise of 3. The dashed purple line z = x and dotted gray line z = 0.5x rise by 1 and 0.5 per unit of x. All three use the same axes: the larger slope gives the steeper line.1326390xzz = xz = 0.5xrun = 1rise = 3z = 3x

Szara przerywana linia z=0.5xz=0.5x i przerywana fioletowa linia z=xz=x rosną tylko o 0.50.5 i 11 dla tego samego przebiegu 11. Przy tych samych skalach osi większe nachylenie bezwzględne oznacza bardziej stromą linię. Wszystkie trzy nachylenia są dodatnie, więc linie rosną, gdy poruszamy się w prawo; nachylenie ujemne spowodowałoby spadek linii.

Odkryliśmy, że niebieska linia ma nachylenie 33, więc pochodna z=3xz=3x to 33. Zapisujemy to jako dzdx\frac{dz}{dx} i czytamy jako „pochodną zz względem xx”. Wyrażenie „w odniesieniu do xx” identyfikuje dane wejściowe, które zmieniamy: pochodna mierzy, jak szybko zmienia się sygnał wyjściowy zz wraz ze zmianami xx. Nasza funkcja ma tylko jedno wejście, xx, więc nie ma innych wejść, które można by ustawić na stałe. Dla naszej niebieskiej linii otrzymujemy:

dzdx=3\frac{dz}{dx}=3

Symbole dd oznaczają pochodną, natomiast Δz/Δx\Delta z/\Delta x opisuje średnią szybkość zmian w skończonym przedziale czasu. Dla z=3xz=3x tempo zmian jest stałe i niezależne od xx, więc pochodną jest 33, gdziekolwiek ją obliczymy.

Wykorzystaliśmy nachylenie linii do znalezienia pochodnej. Możemy to również obliczyć, korzystając z reguł różniczkowania. Od z=3xz=3x możemy zastąpić zz jego wyrażeniem i napisać:

dzdx=ddx(3x)\frac{dz}{dx}=\frac{d}{dx}(3x)

W tym przypadku operator różniczkowy ddx\frac{d}{dx} oznacza „różniczkowanie następującego wyrażenia w odniesieniu do xx”. W ddx(3x)\frac{d}{dx}(3x) wyrażenie to to 3x3x, traktowane jako funkcja xx. Notacja ta jest szczególnie przydatna, gdy poszczególne terminy w większym wyrażeniu traktujemy jako funkcje i różniczkujemy je osobno.

Stosując regułę stałego mnożnika, zachowujemy mnożnik 33 i różniczkujemy xx. Pochodną xx względem xx jest 11, ponieważ zwiększenie wartości wejściowej o dowolną wielkość powoduje zwiększenie wartości wyjściowej xx o dokładnie tę samą wielkość:

ddx(3x)=3ddx(x)=3×1=3.\frac{d}{dx}(3x)=3\frac{d}{dx}(x)=3\times1=3.

Reguły różniczkowania umożliwiają nam systematyczne obliczanie współczynników, które trudniej dostrzec w bardziej skomplikowanych wyrażeniach. Więcej tych reguł wprowadzimy, gdy połączymy kilka operacji.

Pochodna zależna od wartości wejścia

Przyjrzyjmy się teraz funkcji, której szybkość zmian zależy od jej wartości wejściowych. Dzięki temu lokalny charakter pochodnej jest jaśniejszy: opisuje, jak zmienia się wynik w pobliżu określonej wartości wejściowej, gdzie ją oceniamy. Aby to zademonstrować, użyjemy funkcji z jednym wejściem xx o nazwie LL:

L(x)=x2+3.L(x)=x^2+3.

Możemy zapisać tę pochodną jako dL/dxdL/dx, używając tej samej notacji, którą wprowadziliśmy powyżej, zwanej notacją Leibniza. Innym sposobem zapisania tego jest L′(x)L'(x), odczytywane jako „L liczba pierwsza x”, co nazywa się Lagrange notation lub notacją pierwszą. Obydwa oznaczają pochodną LL względem xx.

Aby znaleźć pochodną L(x)=x2+3L(x)=x^2+3, zróżniczkujemy jej dwa wyrazy oddzielnie, a następnie dodamy wyniki. Wykorzystuje trzy reguły:

ZasadaJak go używamy
Reguła potęgowaZróżniczkuj x2x^2
Stała zasadaZróżniczkuj człon stały 33
Reguła sumyDodaj pochodne obu terminów

Zastosujmy najpierw regułę potęgową do x2x^2: pomnóż przez wykładnik, a następnie zmniejsz wykładnik o jeden. Ogólnie rzecz biorąc,

ddx(xn)=nxn−1\frac{d}{dx}(x^n)=nx^{n-1}

W przypadku x2x^2 daje to 2x2x. Następnie stosujemy regułę stałej do 33. Jej pochodna względem xx wynosi zero, ponieważ nie zmienia się wraz ze zmianami xx. Na koniec używamy reguły sumy, aby dodać te pochodne. Zatem dla naszej funkcji pochodna wynosi:

L′(x)=2x+0=2xL'(x)=2x+0=2x

Aby zobaczyć, co nam mówi ten wzór, obliczmy wynik funkcji i jej pochodną przy trzech wartościach xx:

Wejście xxWyjście L(x)=x2+3L(x)=x^2+3Pochodna L′(x)=2xL'(x)=2x
−5-5(−5)2+3=28(-5)^2+3=282×(−5)=−102\times(-5)=-10
0002+3=30^2+3=32×0=02\times0=0
5552+3=285^2+3=282×5=102\times5=10

W przeciwieństwie do stałego tempa zmian 33 w z=3xz=3x, to tempo zależy od bieżącej wartości xx. Wynik funkcji jest taki sam w x=−5x=-5 i x=5x=5, ale pochodne mają przeciwne znaki. W przypadku x=5x=5 dodatnia pochodna oznacza, że ​​wartość wyjściowa wzrasta wraz z nieznacznym wzrostem xx. W przypadku x=−5x=-5 ujemna pochodna oznacza, że ​​wartość wyjściowa spada, gdy xx nieznacznie wzrasta.

Poniższy wykres skupia się na kierunku: dwa punkty mają tę samą wysokość, ale zielone strzałki pokazują różne sposoby poruszania się wzdłuż krzywej, aby zmniejszyć wartość wyjściową.

L(x) = x² + 3 against Parameter xThe function curve is U-shaped with a minimum at x = 0, where the output is 3. At x = −5 and x = 5, the output is 28. Arrows follow the curve downward toward zero: increase x from −5, but decrease x from 5.-60-310020330640L(x) = x² + 3Parameter x(−5, 28)(5, 28)Increase x →← Decrease xMinimum (0, 3)

Ten wykres pokazuje nam, że jeśli naszym celem jest zmniejszenie wyniku funkcji, znak pochodnej mówi nam, w którą stronę mamy się poruszać z każdego punktu początkowego. Zielone strzałki pokazują te kierunki, które możemy określić bez prób zwiększania i zmniejszania xx i ponownego obliczania wyniku funkcji. Obie strzałki prowadzą w stronę x=0x=0, gdzie funkcja ta osiąga minimum, a pochodna wynosi zero, chociaż pochodna zerowa nie zawsze oznacza minimum.

Powszechnym sposobem wizualizacji pochodnej jest nachylenie stycznej do wykresu w wybranym punkcie. W x=5x=5 pochodna to 1010, więc styczna ma nachylenie 1010. Zmień Parametr x poniżej, aby zobaczyć, jak nachylenie fioletowej stycznej zmienia się wzdłuż niebieskiej krzywej.

Output L(x) against Parameter xAt x=5, the output is 28 and the tangent slope is 10. -60-310.75021.5332.25643Output L(x)Parameter x
Exact derivative10L′(x) = 2x

Tangens pomaga nam również oszacować, jak bardzo zmienia się wynik funkcji w pobliżu. W x=5x=5, gdzie wynik to 2828, a pochodna to 1010, a mała zmiana w xx powoduje zmianę wyniku w przybliżeniu 1010 razy większą:

ΔL≈10 Δx.\Delta L\approx10\,\Delta x.

W powyższym wyrażeniu używamy ≈\approx, ponieważ szacujemy zmianę wartości wyjściowej w małym kroku. W x=5x=5 pochodna wynosi dokładnie 1010, ale nachylenie zmienia się w miarę przesuwania się wzdłuż krzywej. Ustawienie Parametr x na 4.94.9 w widżecie daje nachylenie 9.89.8; mniejszy ruch do 4.994.99 dałby 9.989.98. Im mniejszy krok, tym mniej zmienia się nachylenie, więc traktowanie go jako stałego w 1010 daje bliższe oszacowanie zmiany wartości wyjściowej.

Szacowanie pochodnej za pomocą różnic skończonych

Szacowanie pochodnych z wartości funkcji na pobliskich wejściach nazywa się różniczkowaniem numerycznym. Jednym ze sposobów osiągnięcia tego są różnice skończone. Pozostaje nam jedynie ocenić funkcję, nie znając jej wewnętrznych operacji ani nie wyprowadzając wzoru na jej pochodną.

Oszacowanie różnicy w przód dzieli zmianę sygnału wyjściowego przez małą, niezerową zmianę sygnału wejściowego hh:

L(x+h)−L(x)h\frac{L(x+h)-L(x)}{h}

Obliczmy oszacowanie różnicy skończonej za pomocą L(x)=x2+3L(x)=x^2+3. Zaczynając od x=5x=5, krok h=0.001h=0.001 prowadzi nas do 5.0015.001:

L(5.001)−L(5)0.001=28.010001−280.001=10.001\frac{L(5.001)-L(5)}{0.001} =\frac{28.010001-28}{0.001} =10.001

Jest to średnia szybkość zmian pomiędzy dwoma wejściami. Geometrycznie jest to nachylenie linii prostej łączącej ich punkty na krzywej, zwane secant. Pochodna podaje nachylenie stycznej w punkcie początkowym.

W poniższym widgecie ustaw Parametr x na 55, gdzie fioletowa styczna ma nachylenie 1010. Przy Mała zmiana h w 0.0010.001, pomarańczowa sieczna ma nachylenie 10.00110.001. Jest to oszacowanie metodą skończonej różnicy. Gdy zmniejszamy hh, pomarańczowe nachylenie zbliża się do 1010, dokładnej pochodnej.

L(x) = x² + 3 against Parameter xAt x=2, the output is 7 and the tangent slope is 4. With h=0.8, the secant slope is 4.8.-60-310.75021.5332.25643L(x) = x² + 3Parameter x
Tangent: exact derivativeSecant: finite difference
Exact derivative4L′(x) = 2x
Finite difference4.8(L(x + h) − L(x)) / h

Nazywamy to średnią, ponieważ dzielimy całkowitą zmianę sygnału wyjściowego przez całkowitą zmianę sygnału wejściowego w danym przedziale. Przybliżenie różnic skończonych wykorzystuje tylko dwie wartości punktów końcowych, więc nie mówi nam, jak funkcja zachowuje się między nimi. Jego wartość wyjściowa może zmieniać się wolniej w jednej części interwału i szybciej w innej.

Ponieważ znamy wzór, możemy wykreślić krzywą pomiędzy punktami końcowymi i zobaczyć to rozróżnienie. Poniższy wykres wykorzystuje szerszy przedział, od x=1x=1 do x=5x=5, aby różnica była widoczna. Linia przerywana łącząca punkty końcowe ma średnie nachylenie; zielona krzywa pokazuje, jak faktycznie zmienia się wartość wyjściowa. Zacieniony trójkąt pokazuje całkowitą zmianę sygnału wejściowego Δx=4\Delta x=4 i zmianę sygnału wyjściowego ΔL=24\Delta L=24, zatem średnie nachylenie wynosi 24/4=624/4=6.

The average slope uses only the endpoints
Average slope as change in output divided by change in parameterA dashed blue secant passes through (1, 4) and (5, 28). A shaded right triangle shows the parameter change Δx = 4 and output change ΔL = 24, giving an average slope of 6. The midpoint is (3, 12). An orange secant joins (1, 4) to (3, 12), with average slope 4. A purple secant joins (3, 12) to (5, 28), with average slope 8. The green curve is L(x) = x² + 3.Outputx(3, 12)3Δx = 4ΔL = 24(1, 4)1(5, 28)5
Whole intervalFirst halfSecond halfFunction curve

Możemy porównać średnią stopę w całym przedziale ze stopami w jego dwóch połówkach. Podział w x=3x=3, gdzie wyjście to 1212, daje:

InterwałZmiana wejściaZmiana wyjściaŚrednia stawka
Cały interwał1→51 \to 54→284 \to 28(28−4)/(5−1)=6(28-4)/(5-1)=6
Pierwsza połowa1→31 \to 34→124 \to 12(12−4)/(3−1)=4(12-4)/(3-1)=4
Druga połowa3→53 \to 512→2812 \to 28(28−12)/(5−3)=8(28-12)/(5-3)=8

Definicja granicy precyzuje ideę tempa zmian w jednym punkcie: jest to wartość, do której zbliża się średnia szybkość zmian, gdy przedział zmniejsza się do zera:

L′(x)=lim⁡h→0L(x+h)−L(x)hL'(x)=\lim_{h\to0}\frac{L(x+h)-L(x)}{h}

Pozwoliliśmy hh zbliżyć się do zera, zamiast zastępować h=0h=0, co dałoby 0/00/0. Biorąc tę granicę, możemy wyprowadzić regułę L′(x)=2xL'(x)=2x. Dla naszej funkcji najpierw uprośćmy stosunek, zachowując hh niezerowy:

L(x+h)−L(x)h=(x+h)2+3−(x2+3)h=2xh+h2h=2x+h\begin{aligned} \frac{L(x+h)-L(x)}{h} &=\frac{(x+h)^2+3-(x^2+3)}{h}\\ &=\frac{2xh+h^2}{h}\\ &=2x+h \end{aligned}

To sprawia, że rozróżnienie jest wyraźne: obliczenie różnicy skończonej w wybranym kroku daje 2x+h2x+h. Przyjęcie granicy, gdy hh zbliża się do zera, usuwa dodatkowe hh, dając nam regułę pochodną:

L′(x)=2x,L′(5)=10L'(x)=2x,\qquad L'(5)=10

To wyjaśnia regułę, którą zastosowaliśmy wcześniej symbolicznie. Kiedy już mamy formułę 2x2x, możemy ją obliczyć bezpośrednio na dowolnym wejściu bez oceniania funkcji na pobliskim wejściu.

Różnica w przód porównuje xx z x+hx+h. Próbkowanie za xx daje oszacowanie różnicy wstecznej, podczas gdy próbkowanie jednakowe po obu stronach daje oszacowanie różnicy centralnej. Są to inne typy aproksymacja różnic skończonych. Dla tej samej funkcji L(x)=x2+3L(x)=x^2+3 użyjemy teraz większego kroku h=0.1h=0.1:

MetodaFormułaOszacowanie na x=5x=5, h=0.1h=0.1
NaprzódL(x+h)−L(x)h\dfrac{L(x+h)-L(x)}{h}10.110.1
Do tyłuL(x)−L(x−h)h\dfrac{L(x)-L(x-h)}{h}9.99.9
CentralnyL(x+h)−L(x−h)2h\dfrac{L(x+h)-L(x-h)}{2h}10.010.0

W arytmetyce dokładnej, gdy zmniejszamy krok hh, oszacowania różnicy w przód, w tył i centralne zbiegają się do dokładnej pochodnej — dla naszej funkcji w x=5x=5 wartość ta wynosi 1010. Różnica środkowa jest dokładna dla tej funkcji kwadratowej w dokładnej arytmetyce, ale generalnie pozostaje przybliżeniem.

Oprogramowanie może oszacować pochodną przy użyciu różnic skończonych: ocenić funkcję na dwóch pobliskich wejściach, odjąć wyniki i podzielić przez różnicę wejściową. Wybór tej różnicy wymaga ostrożności. Skończony krok zazwyczaj wprowadza błąd obcięcia, ponieważ używamy przedziału do oszacowania szybkości w punkcie. Zmniejszenie kroku zmniejsza ten błąd, ale niezwykle mały krok stwarza kolejny problem: komputery przechowują liczby z ograniczoną precyzją. Odejmowanie prawie równych wartości funkcji może pozostawić różnicę zdominowaną przez błąd zaokrąglenia, który potęguje dzielenie przez maleńki hh.

Metody te są przydatne do sprawdzania pochodnych, ale ich dodatkowe oceny funkcji czynią je kosztownymi, gdy są stosowane oddzielnie do milionów parametrów.

Wiele wejść i pochodne cząstkowe

Do tej pory używaliśmy funkcji z=3xz=3x i L(x)=x2+3L(x)=x^2+3 z tylko jednym parametrem xx. Przyjrzyjmy się teraz z=x+yz=x+y i z=xyz=xy, z dwoma parametrami xx i yy, które możemy zmieniać oddzielnie. Aby zrozumieć wpływ każdego parametru na zz, zmieniamy jeden, utrzymując drugi na stałym poziomie. Zmiana obu na raz spowodowałaby zmieszanie ich efektów.

Wcześniej napisaliśmy dz/dxdz/dx, aby opisać, jak zmienia się wyjście zz przy pojedynczym wejściu xx. W przypadku naszych funkcji z dwoma wejściami musimy określić, które dane wejściowe będziemy zmieniać. Aby zmierzyć, jak zmienia się zz, gdy zmieniamy xx, trzymając yy na stałe, używamy pochodnej cząstkowej, zapisanej jako ∂z/∂x\partial z/\partial x. Czytamy to jako 'pochodną cząstkową zz względem xx'. Kolejność oznacza to samo co poprzednio; symbol ∂\partial wskazuje, że zmieniamy jedno wejście, utrzymując pozostałe na stałym poziomie.

Geometrycznie oba wejścia wyznaczają punkt na powierzchni z=f(x,y)z=f(x,y). Pochodna cząstkowa mierzy nachylenie w tym punkcie w kierunku jednego sygnału wejściowego, podczas gdy drugie wejście pozostaje stałe. Dlatego potrzebujemy obu wartości wejściowych, aby określić, gdzie mierzymy nachylenie, nawet jeśli badamy zmiany tylko w jednym kierunku.

Przy obliczaniu pochodnej cząstkowej stosujemy zasady różniczkowania, a pozostałe dane wejściowe traktujemy jako stałe.

Zacznijmy od dodatku z=x+yz=x+y. Trzymanie yy naprawiono, zwiększanie xx o 11 zwiększa zz o 11, więc ∂z/∂x=1\partial z/\partial x=1. Odwrócenie ról daje ∂z/∂y=1\partial z/\partial y=1.

W przypadku mnożenia z=xyz=xy współczynnik zależy od wartości wejściowych, które utrzymujemy na stałym poziomie. Poniższa tabela pokazuje jak zmienia się zz w miarę zwiększania xx przy jednoczesnym utrzymaniu yy na stałej wartości 55:

x\boldsymbol{x}y\boldsymbol{y}z=xy\boldsymbol{z=xy}
33551515
44552020
55552525
66553030

Widzimy, że za każdym razem, gdy xx wzrasta o 11, zz wzrasta o 55. W przypadku yy utrzymywanego na poziomie 55, tempo zmian w odniesieniu do xx wynosi zatem 55:

∂z∂x=y=5\frac{\partial z}{\partial x}=y=5

Jak widać, tempo zmian jest równe stałej wartości yy. Wykorzystuje tę samą regułę stałej-wielokrotności, którą zastosowaliśmy w przypadku z=3xz=3x. Mnożnik w z=3xz=3x wynosił 33. W z=xyz=xy mnożnikiem jest yy, który utrzymujemy na stałym poziomie podczas różniczkowania xx. Zatem jeśli utrzymujemy jedną zmienną na stałym poziomie, nadal uczestniczy ona w obliczeniach i wpływa na to, jak dane wyjściowe reagują na zmiany drugiej zmiennej.

Różniczkowanie względem yy działa w ten sam sposób: utrzymujemy xx jako stałą i stosujemy regułę stałej-wielokrotności, dając ∂z/∂y=x\partial z/\partial y=x. Obydwa wejścia pełnią role symetryczne, ale ich działanie zależy od bieżących wartości xx i yy. W przypadku x=3x=3 i y=5y=5 wynikiem jest 1515, a pochodne cząstkowe to odpowiednio 55 i 33.

Możemy teraz podsumować lokalne zasady dotyczące pochodnych dla obu operacji:

Operacja∂z/∂x\partial z/\partial x∂z/∂y\partial z/\partial y
z=x+yz=x+y1111
z=xyz=xyyyxx

Ponadto zmiana dowolnego wejścia powoduje zmianę sygnału wyjściowego o tę samą wielkość. W przypadku mnożenia każda pochodna cząstkowa jest równa drugiej wartości wejściowej. Reguły te nazywane są lokalnymi, ponieważ opisują, w jaki sposób dane wyjściowe pojedynczej operacji odpowiadają na dane wejściowe. Później połączymy je, aby śledzić zmiany poprzez kilka operacji.

Pochodne cząstkowe 55 i 33 opisują czułość, a nie części wyniku 1515. W przypadku równie małych zmian zmiana samego xx daje 5/35/3 razy większy efekt zmiany samego yy. Aby oszacować efekt zmiany obu danych wejściowych, potrzebujemy zarówno pochodnych, jak i proponowanych zmian wejściowych.

Aby to zrobić, zbieramy pochodne cząstkowe w wektor zwany gradientem. Oznaczamy go symbolem ∇\nabla, więc dla funkcji z=xyz=xy przy x=3x=3 i y=5y=5 piszemy ∇z=(5,3)\nabla z=(5,3). Wektor ten ma również znaczenie geometryczne w płaszczyźnie wejść: spośród przesunięć o tej samej małej długości euklidesowej wskazuje kierunek największego wzrostu wyniku w przybliżeniu pierwszego rzędu. Wektor przeciwny wskazuje kierunek najszybszego lokalnego spadku.

Poniższy wykres przedstawia te wskazówki dla z=xyz=xy. Możesz zobaczyć, jak fioletowa strzałka prowadzi w górę od (3,5)(3,5), gdzie wyjście to 1515, podczas gdy zielona strzałka prowadzi w dół. Śledzą ścieżki powierzchni, gdy przesuwamy dane wejściowe w kierunku gradientu lub odwrotnie. Wykres skaluje wysokość oddzielnie; gradient określa kierunek w płaszczyźnie wejściowej.

The surface z = xy and gradient directions at (3, 5)The horizontal axes are inputs x and y; height is output z. The saddle surface has height 15 at (3, 5). The purple surface path follows input direction (5, 3), and the green surface path follows (-5, -3), the directions of steepest local increase and decrease in output per unit input distance.xyz-36036(3, 5, 15)
∇z = (5, 3): local increase−∇z = (−5, −3): local decrease
Drag to rotate, or focus the graph and use the arrow keys. Height is scaled separately; the gradient describes direction in the input plane.

Znając pochodne cząstkowe, możemy skorzystać z a przybliżenie Taylora pierwszego rzędu, zwany także A przybliżenie liniowe. Pozwala nam to oszacować łączny wpływ małych zmian wejściowych na wynik: mnożymy każdą pochodną cząstkową przez odpowiednią zmianę wejściową i dodajemy wyniki.

Δz≈∂z∂xΔx+∂z∂yΔy\Delta z\approx\frac{\partial z}{\partial x}\Delta x+\frac{\partial z}{\partial y}\Delta y

Jest to iloczyn skalarny gradientu i wektora zmiany sygnału wejściowego. Dla naszej funkcji z=xyz=xy i (3,5)(3,5) daje to:

Δz≈5 Δx+3 Δy\Delta z\approx5\,\Delta x+3\,\Delta y

Na przykład zwiększenie obu wejść o 0.10.1 daje:

Δz≈5(0.1)+3(0.1)=0.8\Delta z\approx5(0.1)+3(0.1)=0.8

Zwiększanie xx o 0.10.1 przyczynia się do 0.50.5 do przewidywanej zmiany, podczas gdy zwiększanie yy o 0.10.1 przyczynia się do 0.30.3. Każdy udział jest pochodną cząstkową pomnożoną przez odpowiednią zmianę wejściową. Pochodne opisują wrażliwość; wybrane zmiany określają, jaka część tej wrażliwości wchodzi do prognozy.

Ocena funkcji na nowych wejściach daje rzeczywistą zmianę 3.1×5.1−15=0.813.1\times5.1-15=0.81. Pozostała część 0.010.01 to składnik interakcji Δx Δy\Delta x\,\Delta y, który jest pomijany w przybliżeniu liniowym. Terminy 0.50.5 i 0.30.3 opisują zatem wkład w przewidywaną zmianę, a nie udział pierwotnej wartości wyjściowej 1515. To samo rozróżnienie dotyczy propagacji wstecznej: oblicza ona wrażliwość straty na każdą wagę. Mnożąc te pochodne przez proponowane zmiany wagi, szacuje się ich udział w zmianie straty.

Łączenie reguł różniczkowania dla bardziej złożonej funkcji

W poprzedniej sekcji obliczyliśmy pochodne cząstkowe dodawania i mnożenia. Teraz połączymy reguły różniczkowania w wyrażeniu zawierającym kilka terminów i potęg:

f(x,y)=xy2+x3f(x,y)=xy^2+x^3

Porównamy dwa równoważne podejścia do obliczania pochodnych cząstkowych w wybranym punkcie: najpierw różniczkuj, a następnie podstawiaj wartości lub najpierw podstawiaj wartość drugiego wejścia, różniczkuj wynikową funkcję jednowyjściową, a następnie wyznaczaj jej pochodną. Obydwa podejścia dają wzory pochodne. Wykreślenie funkcji jednowejściowych w postaci przekrojów powierzchni pokaże, dlaczego każda pochodna cząstkowa jest również zwykłym nachyleniem stycznym.

Najpierw różniczkujemy, potem podstawiamy wartości

Zachowamy xx i yy jako symbole, znajdując pochodną cząstkową dla każdego wejścia, traktując drugie jako stałe. Następnie wycenimy obie pochodne w (x,y)=(3,2)(x,y)=(3,2).

Najpierw różniczkujemy w odniesieniu do xx, utrzymując yy na stałe i różnicując każdy człon. Współczynnik y2y^2 pełni rolę stałego mnożnika xx:

∂f∂x=y2∂∂x(x)+∂∂x(x3)=y2⋅1+3x2=y2+3x2\begin{aligned} \frac{\partial f}{\partial x} &=y^2\frac{\partial}{\partial x}(x) +\frac{\partial}{\partial x}(x^3)\\ &=y^2\cdot1+3x^2\\ &=y^2+3x^2 \end{aligned}

Przytrzymanie yy na stałe nie usuwa go z obliczeń: jego wartość wyznacza mnożnik y2y^2. Różnicujemy x3x^3 stosując regułę potęgową.

Następnie różniczkujemy względem yy, odwracając role i utrzymując xx na stałe. Teraz xx jest stałym mnożnikiem y2y^2, a cały wyraz x3x^3 jest stały:

∂f∂y=x∂∂y(y2)+∂∂y(x3)=x⋅2y+0=2xy\begin{aligned} \frac{\partial f}{\partial y} &=x\frac{\partial}{\partial y}(y^2) +\frac{\partial}{\partial y}(x^3)\\ &=x\cdot2y+0\\ &=2xy \end{aligned}

Termin x3x^3 wnosi zero, ponieważ zmiana yy podczas trzymania xx na stałe nie może zmienić jego wartości.

Daje nam to następujące pochodne cząstkowe na wybranych przez nas danych wejściowych:

Dane wejściowe zmieniamyWejście wstrzymaneCzęściowa pochodna w (3,2)(3,2)Przewidywana zmiana wartości wyjściowej
xxy=2y=222+3(32)=312^2+3(3^2)=31Δf≈31 Δx\Delta f\approx31\,\Delta x
yyx=3x=32(3)(2)=122(3)(2)=12Δf≈12 Δy\Delta f\approx12\,\Delta y

Najpierw ustalamy jedno wejście, potem różniczkujemy przekrój

Przyjrzyjmy się teraz drugiemu podejściu: najpierw ustalamy jedno wejście, a następnie różniczkujemy funkcję opisującą przekrój. Podstawiamy wartość jednego wejścia do f(x,y)=xy2+x3f(x,y)=xy^2+x^3, otrzymując zwykłą funkcję pozostałego wejścia.

Najpierw ustawiamy y=2y=2 i pozostawiamy xx dowolność:

f(x,2)=x(22)+x3=4x+x3f(x,2)=x(2^2)+x^3=4x+x^3

To jest przekrój oryginalnej funkcji, a nie pochodna: podstawiliśmy wartość tylko dla jednego wejścia.

W przypadku przekroju z y=2y=2 nie mamy już yy jako zmiennej w wyrażeniu, ponieważ podstawienie 22 zamieniło y2y^2 w stały mnożnik 44. Jednakże efekt yy jest nadal przenoszony przez mnożnik 44. Teraz, gdy zmienną pozostaje tylko xx, bierzemy pochodną zwykłą względem xx:

ddx(4x+x3)=4+3x2⟹4+3(32)=31at x=3\frac{d}{dx}(4x+x^3)=4+3x^2 \quad\Longrightarrow\quad 4+3(3^2)=31\quad\text{at }x=3

Następnie wracając do pierwotnej funkcji, ustawiamy x=3x=3 i pozostawiamy yy dowolność:

f(3,y)=3y2+33=3y2+27f(3,y)=3y^2+3^3=3y^2+27

A dla przekroju z x=3x=3 różniczkujemy w odniesieniu do yy:

ddy(3y2+27)=6y⟹6(2)=12at y=2\frac{d}{dy}(3y^2+27)=6y \quad\Longrightarrow\quad 6(2)=12\quad\text{at }y=2

Obydwa przekroje przechodzą przez ten sam wybrany punkt, gdzie f(3,2)=3(22)+33=39f(3,2)=3(2^2)+3^3=39. Zróżniczkowanie każdego przekroju daje te same pochodne cząstkowe, które obliczyliśmy powyżej:

∂f∂x(3,2)=31,∂f∂y(3,2)=12\frac{\partial f}{\partial x}(3,2)=31,\qquad \frac{\partial f}{\partial y}(3,2)=12

Oba podejścia utrzymują drugie wejście na stałym poziomie, więc możemy podstawić jego wartość przed różniczkowaniem albo zachować je jako symbol i podstawić wartość później. Pierwsze podejście daje ogólne wzory na pochodne, y2+3x2y^2+3x^2 i 2xy2xy, które można obliczyć dla dowolnej pary wejść. Drugie daje wzory dla konkretnych przekrojów: 4+3x24+3x^2, gdy y=2y=2, oraz 6y6y, gdy x=3x=3. Na przykład podstawienie x=3x=3 do 2xy2xy daje 6y6y, tak samo jak zróżniczkowanie funkcji przekroju 3y2+273y^2+27. Każdy przekrój otrzymujemy przez osobne podstawienie do pierwotnej funkcji.

Te pochodne mierzą raczej wrażliwość niż udziały w bieżącej wartości wyjściowej. Tak jak poprzednio, zbieramy pochodne cząstkowe do wektora gradientu. W przypadku (3,2)(3,2) daje to ∇f=(31,12)\nabla f=(31,12). Proponowane zmiany wejściowe tworzą wektor (Δx,Δy)(\Delta x,\Delta y). Ich iloczyn skalarny szacuje łączną zmianę wartości wyjściowej:

Δf≈∇f(3,2)⋅(Δx,Δy)=31Δx+12Δy\Delta f\approx\nabla f(3,2)\cdot(\Delta x,\Delta y) =31\Delta x+12\Delta y

W (x,y)=(3,2)(x,y)=(3,2) obie współrzędne określają punkt, w którym mierzymy nachylenia. Poruszanie się w kierunku xx przy trzymaniu y=2y=2 daje styczne nachylenie 3131; poruszanie się w kierunku yy przy trzymaniu x=3x=3 daje nachylenie 1212. Są to dwa kierunki przechodzące przez ten sam punkt na powierzchni. Każda stopa jest pochodną cząstkową pierwotnej funkcji dwóch danych wejściowych i pochodną zwyczajną odpowiadającego jej przekroju o jednym wejściu. Gradient łączy te szybkości w kierunku największego lokalnego wzrostu w płaszczyźnie wejściowej.

Poniższy widżet pokazuje to, co obliczyliśmy powyżej: jak zmienia się wynik w każdym kierunku wejściowym, mierzony za pomocą pochodnych cząstkowych, oraz jak te pochodne łączą się w kierunku gradientu. Fioletowe przekroje pokazują, że każde wejście zmienia się osobno, ich zielone styczne pokazują pochodne cząstkowe, a pomarańczowa strzałka podąża ścieżką powierzchniową w kierunku gradientu.

Surface and both fixed-input slicesBoth slices pass through (3, 2, 39). One varies x with y fixed at 2; the other varies y with x fixed at 3. Their tangent slopes are 31 and 12. Drag or use arrow keys to rotate.x varies; y stays fixedy varies; x stays fixedSurface path along the gradient direction at the selected point
f(x, 2) = 4x + x³-505-1450145xf

∂f/∂x = 31

f(3, y) = 3y² + 27-5052764.5102yf

∂f/∂y = 12

Począwszy od (3,2)(3,2), przesunięcie suwaka xx wpływa w różny sposób na oba wykresy. Po lewej stronie xx to zmienne wejście, więc wybrany punkt porusza się wzdłuż tej samej krzywej f(x,2)=4x+x3f(x,2)=4x+x^3, podczas gdy y=2y=2 pozostaje nieruchomy. Po prawej stronie xx to stałe wejście definiujące przekrój, więc zmiana go z 33 na 44 powoduje zmianę formuły z f(3,y)=3y2+27f(3,y)=3y^2+27 na f(4,y)=4y2+64f(4,y)=4y^2+64. Innymi słowy, przesuwanie xx przesuwa punkt wzdłuż lewego przekroju i zmienia, który przekrój jest wyświetlany po prawej stronie. Przenoszenie yy powoduje odwrotną sytuację.

Śledzenie pochodnych przez połączone operacje

Wcześniej zróżniczkowaliśmy dwa terminy oddzielnie i dodaliśmy ich pochodne, ponieważ oryginalne wyrażenie dodało te terminy. Mówiąc bardziej ogólnie, możemy postrzegać każde obliczenia pośrednie jako funkcję pierwotnych danych wejściowych. Sposób, w jaki łączą się ich wyniki, określa sposób łączenia ich pochodnych.

Poniższa tabela przedstawia zasady dodawania, mnożenia i zagnieżdżania. Będziemy używać g(x)g(x) i h(x)h(x) jako ogólnych nazw dwóch różniczkowalnych funkcji xx:

ZasadaJak funkcje łączą sięPochodna w odniesieniu do xx
Reguła sumyg(x)+h(x)g(x)+h(x)g′(x)+h′(x)g'(x)+h'(x)
Zasada iloczynug(x)h(x)g(x)h(x)g′(x)h(x)+g(x)h′(x)g'(x)h(x)+g(x)h'(x)
Reguła łańcuchag(h(x))g(h(x))g′(h(x))h′(x)g'(h(x))h'(x)

Reguła iloczynu uogólnia używaną wcześniej regułę stałej-wielokrotności na przypadki, w których zmieniają się oba czynniki. Mnożenie dwóch funkcji nie oznacza mnożenia ich pochodnych. Aby różniczkować poszczególne funkcje, możemy skorzystać z reguły takiej jak reguła potęgowa lub rozbić funkcje na prostsze operacje, których pochodne już znamy. Reguła łańcuchowa łączy ich lokalne pochodne, aby dowiedzieć się, jak oryginalne dane wejściowe wpływają na końcowe wyjście, co czyni je centralnymi dla propagacji wstecznej.

W poprzedniej sekcji zróżniczkowaliśmy terminy, traktując inne dane wejściowe jako stałe. Przyjrzymy się teraz bliżej tym terminom: każdą operację arytmetyczną można postrzegać jako funkcję jej danych wejściowych.

Najpierw podzielmy naszą funkcję f(x,y)=xy2+x3f(x,y)=xy^2+x^3 na poszczególne operacje z nazwanymi wynikami pośrednimi:

u=y2,v=xu,w=x3,f=v+wu=y^2,\qquad v=xu,\qquad w=x^3,\qquad f=v+w

Na przykład podnoszenie do kwadratu odwzorowuje yy do u=y2u=y^2 i odwzorowuje mnożenie xx i uu do v=xuv=xu. Wynik kwadratu staje się wejściem do mnożenia. Kiedy dane wyjściowe jednej funkcji stają się danymi wejściowymi innej, funkcje te tworzą relację zwaną kompozycją funkcji, pokazaną jako g(h(x))g(h(x)) w powyższej tabeli. Operacja ma swoje własne bezpośrednie dane wejściowe; łączenie operacji powoduje, że ich wynik zależy również od wcześniejszych danych wejściowych.

Podział następuje zgodnie z zależnościami wyrażenia: u=y2u=y^2 musi być dostępne przed v=xuv=xu, a zarówno vv, jak i ww muszą być dostępne przed f=v+wf=v+w. Ponieważ w=x3w=x^3 nie zależy od uu ani vv, możemy obliczyć to przed lub po tej gałęzi. Podział nie jest wyjątkowy: możemy wyrazić podnoszenie do kwadratu i sześcian jako mnożenie. Tutaj traktujemy je jako pojedyncze operacje i stosujemy regułę potęgową dla ich lokalnych pochodnych.

Wykorzystamy teraz regułę łańcuchową do obliczenia pochodnych poprzez pojedyncze operacje, mnożąc lokalne pochodne wzdłuż każdej ścieżki i dodając udziały z różnych ścieżek. Najpierw będziemy podążać jedną ścieżką, a następnie sprawdzimy dane wejściowe, które docierają do wyjścia dwiema ścieżkami.

Reguła łańcuchowa: jedna ścieżka

Zaczniemy od różniczkowania w odniesieniu do yy, utrzymując xx na stałe. Podział pokazuje, dlaczego yy jest przydatnym punktem wyjścia: zmiana ma wpływ na u=y2u=y^2, następnie na v=xuv=xu, podczas gdy w=x3w=x^3 pozostaje stała. Następnie sprawdzimy xx, który wpływa zarówno na vv, jak i ww. Na razie istnieje tylko jedna ścieżka od yy do końcowego wyniku:

y ⟶ u=y2 ⟶ v=xu ⟶ f=v+wy\ \longrightarrow\ u=y^2\ \longrightarrow\ v=xu\ \longrightarrow\ f=v+w

Aby zobaczyć różnicę między pochodną lokalną a regułą łańcuchową, skup się tylko na dwóch węzłach operacji: kwadracie u=y2u=y^2 i mnożeniu v=xuv=xu, połączonych przez u→vu\to v.

Patrząc osobno, mnożenie wymaga xx i uu jako danych wejściowych. Jego lokalne pochodne cząstkowe to:

∂v∂x=u,∂v∂u=x\frac{\partial v}{\partial x}=u, \qquad \frac{\partial v}{\partial u}=x

Aby wyprowadzić każdą pochodną lokalną, potrzebujemy jedynie wyrażenia operacji i danych wejściowych, ze względu na które różniczkujemy. W przypadku v=xuv=xu traktowanie xx jako stałej daje ∂v/∂u=x\partial v/\partial u=x niezależnie od tego, jak obliczono uu i gdzie zastosowano vv. W tym kroku nie potrzebujemy reszty wykresu. Kwadrat dostarczany jest osobno du/dy=2ydu/dy=2y.

Zwróć uwagę, że reguła łańcuchowa uwzględnia pochodną węzła u=y2u=y^2, du/dy=2ydu/dy=2y oddzielnie, mimo że węzeł v=xuv=xu wykorzystuje już wartość uu. Lokalna pochodna ∂v/∂u=x\partial v/\partial u=x mówi nam jedynie, jak vv reaguje na zmianę w uu. Nie obejmuje zmian uu w stosunku do yy. Stawka ta wynosi du/dy=2ydu/dy=2y, więc potrzebujemy obu czynników:

∂v∂y=∂v∂u⏟how v responds to u⋅dudy⏟how u responds to y=x⋅2y\frac{\partial v}{\partial y} =\underbrace{\frac{\partial v}{\partial u}}_{\text{how v responds to u}} \cdot\underbrace{\frac{du}{dy}}_{\text{how u responds to y}} =x\cdot2y

Po ustaleniu xx mnożenie jest funkcją zewnętrzną, a podnoszenie do kwadratu funkcją wewnętrzną. Sama pochodna zewnętrzna nie może nam powiedzieć, jak zmienia się funkcja wewnętrzna: mnożenie przyjmuje wartość uu, nie wiedząc, czy pochodzi ona z y2y^2, 2y2y, czy z innego obliczenia. W y=2y=2 zarówno y2y^2, jak i 2y2y wytwarzają u=4u=4, ale ich pochodnymi są odpowiednio 44 i 22. Ta sama wartość nie oznacza zatem takiej samej reakcji na zmianę w yy. To rozróżnienie ma zastosowanie zawsze, gdy różniczkujemy kompozycję.

Jeśli podstawimy u=y2u=y^2 do v=xuv=xu, pełne wyrażenie v=xy2v=xy^2 będzie zawierało tę wewnętrzną zależność. Zróżniczkowanie go bezpośrednio daje 2xy2xy, uwzględniając oba współczynniki. Wykres wyraźnie przedstawia te dwa czynniki.

Poniższa tabela przedstawia jedynie skutki zmiany yy, przy niezmienionym xx. Koncentruje się na dwóch operacjach u=y2u=y^2 i v=xuv=xu, zatrzymując się na vv w celu obliczenia ∂v/∂y\partial v/\partial y:

KrokForma symboliczna
Zdefiniuj operacjeu=y2u=y^2, następnie v=xuv=xu
Wyprowadź wzory na pochodne lokalnedu/dy=2ydu/dy=2y, ∂v/∂u=x\partial v/\partial u=x
Połącz formuły, korzystając z reguły łańcuchowej∂v/∂y=(∂v/∂u)(du/dy)=x⋅2y\partial v/\partial y=(\partial v/\partial u)(du/dy)=x\cdot2y

Połączenia mają znaczenie, gdy łączymy pochodne lokalne: ścieżka y→u→vy\to u\to v każe nam pomnożyć ∂v/∂u\partial v/\partial u przez du/dydu/dy, aby znaleźć ∂v/∂y\partial v/\partial y. Operacja dostarcza reguły lokalnej; wykres mówi nam, jak połączyć to z innymi regułami.

Teraz przejdź do ostatniego dodania. Oryginalna funkcja wykorzystuje xx i yy, ale każda operacja ma własne dane wejściowe. W przypadku ostatniego dodatku f=v+wf=v+w te wejścia to vv i ww. Jego lokalna pochodna ∂f/∂v\partial f/\partial v pyta, jak zmienia się wyjście dodatku, gdy zmienia się vv, podczas gdy ww pozostaje stałe. Jest to równe 11, ponieważ dodanie małej ilości do vv powoduje dodanie tej samej ilości do ff. Następnie reguła łańcuchowa łączy te lokalne odpowiedzi z pierwotnym wejściem yy.

W poniższej tabeli wymieniono lokalne pochodne na pełnej ścieżce:

OperacjaLokalna pochodna
Kwadrat yy daje uudu/dy=2ydu/dy=2y
Pomnóż uu przez stałą wartość xx, aby otrzymać vv∂v/∂u=x\partial v/\partial u=x
Dodaj stałą wartość ww, aby otrzymać ff∂f/∂v=1\partial f/\partial v=1

Aby otrzymać pochodną wyniku końcowego względem yy, , możemy teraz zastosować regułę łańcuchową, mnożąc pochodne lokalne przez y→u→v→fy\rightarrow u\rightarrow v\rightarrow f:

∂f∂y=∂f∂v⋅∂v∂u⋅dudy=1⋅x⋅2y=2xy\frac{\partial f}{\partial y} =\frac{\partial f}{\partial v}\cdot\frac{\partial v}{\partial u}\cdot\frac{du}{dy} =1\cdot x\cdot2y =2xy

Odpowiada to wzorowi, który otrzymaliśmy poprzez bezpośrednie różniczkowanie xy2+x3xy^2+x^3. Tutaj ∂f/∂y\partial f/\partial y równa się ∂v/∂y\partial v/\partial y, ponieważ ostatnia operacja mnoży stawkę przez 11.

Poniższy widget renderuje wykres połączonych obliczeń. Zaczyna się od xx i yy, wejść, od których zależy funkcja. Wyrażenia wewnątrz węzłów, takie jak u=y2u=y^2 i v=xuv=xu, definiują operacje. Strzałki ciągłe wskazują wynik operacji na operację, która go wykorzystuje. Zatem u→vu\to v oznacza, że ​​v=xuv=xu wykorzystuje wynik u=y2u=y^2, dzięki czemu ich kompozycja jest widoczna. Mnożenie wykorzystuje uu, a nie jego pochodną du/dydu/dy; etykiety pochodnych i przerywane strzałki prowadzące do pola reguł łańcuchowych pokazują, w jaki sposób różniczkujemy obliczenia. Podążaj pomarańczową ścieżką i pomnóż lokalne pochodne, aby otrzymać 2y⋅x⋅1=2xy2y\cdot x\cdot1=2xy.

One path from y through u and v to fHolding x constant, the derivative for y receives a contribution through one path: y → u → v → f. Multiply along this path: ∂f/∂y = 2y × x × 1 = 2xy.yu = y²du/dy = 2yxv = xu∂v/∂u = xw = x³f = v + w∂f/∂v = 1Chain rule∂f/∂y = 2y × x × 1 = 2xy

Scroll the graph sideways to see the chain-rule box.

Kiedy teraz weźmiemy wzór na pochodną 2xy2xy i podstawimy x=3x=3 i y=2y=2, otrzymamy 2(3)(2)=122(3)(2)=12, pasujący do obliczonej wcześniej pochodnej cząstkowej. Możemy ocenić ten wzór na dowolnej parze wejściowej bez uprzedniego obliczania wartości funkcji pośrednich.

W przypadku x=3x=3 i y=2y=2 niewielka zmiana hh w yy powoduje w przybliżeniu zmianę 4h4h w uu. Skale mnożenia zmieniają się o 33, dając w przybliżeniu 12h12h zmianę w vv. Dodanie niezmienionego ww pozostawia tę zmianę nietkniętą.

Wiele ścieżek: sumowanie wkładów do pochodnej

Prześledziliśmy, jak yy wpływa na sygnał wyjściowy jedną ścieżką. Znajdźmy teraz pochodną cząstkową f(x,y)=xy2+x3f(x,y)=xy^2+x^3 względem xx, przy stałym yy. Używamy tych samych wyników pośrednich:

u=y2,v=xu,w=x3,f=v+wu=y^2,\qquad v=xu,\qquad w=x^3,\qquad f=v+w

Wartość u=y2u=y^2 pozostaje stała, ale zarówno v=xuv=xu, jak i w=x3w=x^3 zmieniają się wraz z xx. Daje nam to dwie ścieżki od xx do końcowego wyniku:

x⟶v=xu⟶f=v+wandx⟶w=x3⟶f=v+wx\longrightarrow v=xu\longrightarrow f=v+w \qquad\text{and}\qquad x\longrightarrow w=x^3\longrightarrow f=v+w

Obliczyliśmy już ∂f/∂x=y2+3x2\partial f/\partial x=y^2+3x^2 różniczkując xy2+x3xy^2+x^3 względem xx, utrzymując stałą yy. Reguła łańcuchowa daje tę samą odpowiedź, używając lokalnych pochodnych wzdłuż obu ścieżek:

OperacjaLokalna pochodna
Pomnóż xx przez stałą wartość uu, aby otrzymać vv∂v/∂x=u=y2\partial v/\partial x=u=y^2
Kostka xx do uzyskania wwdw/dx=3x2dw/dx=3x^2
Dodaj vv i ww, aby otrzymać ff, biorąc pod uwagę wpływ vv∂f/∂v=1\partial f/\partial v=1
Dodaj vv i ww, aby otrzymać ff, biorąc pod uwagę wpływ ww∂f/∂w=1\partial f/\partial w=1

Każda lokalna pochodna dodania utrzymuje swoje inne dane wejściowe na stałym poziomie. Oba wejścia zmieniają się, gdy zmienia się xx, więc potrzebujemy obu wkładów.

Stosujemy teraz regułę łańcuchową wielu zmiennych: mnożymy lokalne pochodne wzdłuż każdej ścieżki, a następnie dodajemy oba składowe, aby otrzymać pochodną końcowego wyniku względem xx:

∂f∂x=∂f∂v⋅∂v∂x+∂f∂w⋅dwdx=1⋅y2+1⋅3x2=y2+3x2\frac{\partial f}{\partial x} =\frac{\partial f}{\partial v}\cdot\frac{\partial v}{\partial x} +\frac{\partial f}{\partial w}\cdot\frac{dw}{dx} =1\cdot y^2+1\cdot3x^2 =y^2+3x^2

Poniższy diagram przedstawia dwie ścieżki od xx do końcowego wyjścia: x→v→fx\to v\to f i x→w→fx\to w\to f. Obydwa śledzą wpływ zmiany xx, przy czym yy pozostaje stały. Węzły yy i u=y2u=y^2 są szare, ponieważ ta gałąź pozostaje niezmieniona; nadal dostarcza wartość uu używaną przy mnożeniu. Pomarańczowa ścieżka wnosi 1⋅y21\cdot y^2, a fioletowa ścieżka wnosi 1⋅3x21\cdot3x^2. Aktywne pola operacji pokazują ich lokalne pochodne. W powyższym ramce mnoży się pochodne wzdłuż każdej ścieżki, a następnie dodaje udziały, aby otrzymać y2+3x2y^2+3x^2.

The chain rule through two paths from x to fHolding y constant, the derivative for x receives contributions through two paths: x → v → f and x → w → f. Multiply along each path, then add: ∂f/∂x = 1 × y² + 1 × 3x² = y² + 3x².yu = y²xv = xu∂v/∂x = u = y²w = x³dw/dx = 3x²f = v + w∂f/∂v = 1∂f/∂w = 1Chain ruleMultiply along paths, then add(∂f/∂v) × (∂v/∂x)+(∂f/∂w) × (dw/dx)1 × y²+1 × 3x²∂f/∂x = y² + 3x²

Scroll the graph sideways to follow the paths.

Kiedy teraz weźmiemy wzór na pochodną y2+3x2y^2+3x^2 i podstawimy x=3x=3 i y=2y=2, otrzymamy 22+3(32)=4+27=312^2+3(3^2)=4+27=31. Odpowiada to pochodnej cząstkowej, którą obliczyliśmy wcześniej, bez obliczania najpierw wartości funkcji pośrednich.

Obliczanie pochodnych: różniczkowanie symboliczne i automatyczne

Powyższe przykłady łączą lokalne reguły różniczkowania, aby obliczyć pochodną większej funkcji. Możemy pójść dalej: podnoszenie do kwadratu i do sześcianu też da się zbudować z mnożenia, więc ich pochodne mogą wynikać z reguły mnożenia.

Pochodna kwadratu z samego mnożenia

Załóżmy, że nasz silnik obsługuje mnożenie, ale nie ma operacji potęgowania. Nadal możemy obliczyć s=x2s=x^2 jako s=x⋅xs=x\cdot x. Dla mnożenia s=abs=ab lokalne pochodne wynoszą:

∂s∂a=b,∂s∂b=a.\frac{\partial s}{\partial a}=b,\qquad \frac{\partial s}{\partial b}=a.

Obie pozycje wejściowe odnoszą się tutaj do tej samej zmiennej: a=xa=x i b=xb=x. Zmiana xx wpływa na wynik przez obie pozycje, więc sumujemy ich wkłady:

dsdx=∂s∂adadx+∂s∂bdbdx=x⋅1+x⋅1=2x.\frac{ds}{dx} =\frac{\partial s}{\partial a}\frac{da}{dx} +\frac{\partial s}{\partial b}\frac{db}{dx} =x\cdot1+x\cdot1 =2x.

Pochodna kwadratu wynika z mnożenia i reguły łańcuchowej; nie potrzebowaliśmy osobnej reguły dla kwadratu. W przebiegu wstecznym zaczynamy od gradientu ss równego 11. Każda pozycja wejściowa dodaje wartość drugiego argumentu do gradientu xx. Przy x=3x=3 otrzymujemy dwa wkłady: 3+3=63+3=6. Choć istnieje tylko jeden węzeł xx, trzeba uwzględnić oba jego użycia.

Ta sama idea działa dla sześcianu. Zapiszmy t=x⋅xt=x\cdot x, a następnie c=t⋅xc=t\cdot x. Łącząc pochodne, otrzymujemy:

dcdx=xdtdx+tdxdx=x(x+x)+x2=3x2.\frac{dc}{dx} =x\frac{dt}{dx}+t\frac{dx}{dx} =x(x+x)+x^2 =3x^2.

Silnik może więc różniczkować potęgi o dodatnich wykładnikach całkowitych, zapisane jako kolejne mnożenia, bez implementowania reguły potęgowej. Inne funkcje wymagają odpowiednich dodatkowych operacji podstawowych: samo mnożenie nie pozwala na przykład wyrazić sin⁡(x)\sin(x) ani dowolnych potęg ułamkowych. Ogólna zasada polega na tym, aby dostarczyć reguły różniczkowania operacji podstawowych, a następnie łączyć je w celu różniczkowania zbudowanych z nich funkcji.

Te same reguły mogą tworzyć wyrażenia lub obliczać liczby

W przykładzie kwadratu zachowaliśmy xx jako symbol, zbudowaliśmy wyrażenie x⋅1+x⋅1x\cdot1+x\cdot1 i uprościliśmy je do 2x2x. Takie tworzenie wyrażeń na pochodne to różniczkowanie symboliczne. Program może automatycznie budować te wyrażenia, korzystając z tych samych lokalnych reguł i zależności.

Micrograd stosuje te reguły do wartości liczbowych. Przy x=3x=3 pierwotne mnożenie daje s=9s=9, a jego reguła wsteczna sumuje 3+3=63+3=6 w gradiencie xx. To różniczkowanie automatyczne: wynik jest matematycznie równoważny obliczeniu 2x2x przy x=3x=3, choć nie konstruujemy tego symbolicznego wzoru. Propagacja wsteczna wykorzystuje automatyczne różniczkowanie w trybie odwrotnym do obliczania gradientów straty potrzebnych do uczenia.

Obie metody łączą te same reguły rachunku różniczkowego. W tym porównaniu zmienia się to, czy budujemy wyrażenia na pochodne, czy wykonujemy obliczenia pochodnych na liczbach:

MetodaNa czym działaCo daje
Różniczkowanie symboliczneWyrażenia symboliczne, w tym nazwane wyrażenia pośrednieWzory na pochodne, które można obliczyć dla wybranych wartości wejściowych
Różniczkowanie automatyczneOperacje programu i ich wartości dla wybranych wejśćWartości liczbowe pochodnych uzyskane przez połączenie pochodnych lokalnych

Od wzorów na pochodne do reguł wykonywanych przez program

Na wcześniejszym jednościeżkowym wykresie symbolicznym napisaliśmy du/dy=2ydu/dy=2y obok operacji u=y2u=y^2. Wzór ten mówi nam również, jak obliczyć pochodną za pomocą arytmetyki: weź wartość wejściową i pomnóż ją przez 22. W przypadku y=2y=2 wynikiem jest 44. To jest numeryczna pochodna lokalna: lokalna pochodna wyznaczana przy określonych wartościach wejściowych.

Autodiff nie wyprowadza reguł różniczkowania operacji podstawowych z samych wartości ani nie odkrywa ich symbolicznie podczas wykonywania programu. Twórca silnika dostarcza regułę dla każdej operacji podstawowej, zapisując znany wzór na pochodną jako kod. Jeśli wybierzemy podnoszenie do kwadratu jako operację podstawową, 2y2y staje się 2 * input_value. Wzór opisuje regułę matematycznie, a kod wykonuje ją na liczbach. Dla wejścia 2 otrzymujemy 4. Jeśli zamiast tego zapiszemy kwadrat jako y * y, dwa wkłady z mnożenia dadzą ten sam wynik: 2 + 2 = 4.

Silnik korzysta z grafu i reguły łańcuchowej, aby różniczkować kombinacje tych operacji. Każdą regułę lokalną implementujemy raz i wykorzystujemy ponownie przy każdym wystąpieniu danej operacji:

Działanie programuZnana reguła lokalnej pochodnejObliczenia numeryczne
u = y ** 2du/dy=2ydu/dy=2y2 * y.data
v = x * u∂v/∂x=u\partial v/\partial x=u, ∂v/∂u=x\partial v/\partial u=xu.data, x.data
w = x ** 3dw/dx=3x2dw/dx=3x^23 * x.data ** 2
f = v + w∂f/∂v=1\partial f/\partial v=1, ∂f/∂w=1\partial f/\partial w=11, 1

Tutaj .data przechowuje wartość liczbową węzła. Pochodną mnożenia względem uu oblicza się poprzez odczyt wartości drugiego argumentu, xx. Aby zastosować tę regułę lokalną, silnik nie musi wiedzieć, w jaki sposób obliczono ten operand.

Silnik rejestruje również, które dane wejściowe dały dany wynik, zachowując zależności pokazane na naszym wykresie. Zwykła arytmetyka oblicza wartości; autodiff dodaje śledzenie zależności i reguły pochodne. Może także śledzić operacje wykonywane w pętlach i gałęziach warunkowych, bez konieczności przepisywania całego obliczenia w postaci pojedynczej formuły.

W grafie używanym w naszym przykładzie wybieramy osobną operację podstawową potęgowania, dzięki czemu każdy kwadrat i sześcian pozostaje jedną operacją. Możemy je wtedy zapisać jako y.pow(2) i x.pow(3). To wybór implementacyjny; dla tych potęg sprawdziłoby się również powtarzane mnożenie. Metoda pow(exponent) stosuje ogólną regułę potęgową:

ddy(yn)=nyn−1\frac{d}{dy}(y^n)=ny^{n-1}

W przypadku użytych tutaj dodatnich wykładników całkowitych obliczenie wygląda następująco:

pow(exponent) {
  return new Value(
    this.data ** exponent,                         // function value
    [[this, exponent * this.data ** (exponent - 1)]] // input and local derivative
  );
}

Tutaj this jest węzłem wejściowym. Operacja oblicza wartość wyjściową i rejestruje połączenie z tym wejściem wraz z numeryczną pochodną lokalną. Przejście wsteczne wykorzysta zapisaną pochodną do propagacji wkładu gradientu na wejście.

Wywołanie y.pow(2) przekazuje wykładnik 22, więc lokalna pochodna wynosi 2y2−1=2y2y^{2-1}=2y. Wywołanie x.pow(3) daje 3x3−1=3x23x^{3-1}=3x^2. Silnik pobiera wykładnik z argumentu operacji, a programista dostarcza ogólną regułę różniczkowania. Silnik nie odkrywa reguły potęgowej podczas działania. Wyspecjalizowana metoda square() może używać tego samego obliczenia z wykładnikiem ustalonym na 22.

Wykres przedstawia połączenia; zaimplementowane reguły dostarczają lokalnych wartości pochodnych. Reguła łańcucha mówi następnie silnikowi, jak je połączyć.

Możemy teraz ponownie odwiedzić tabelę z symbolicznego przykładu, ponownie kierując się tylko y→u→vy\to u\to v z xx trzymanym na stałe w celu obliczenia ∂v/∂y\partial v/\partial y. Autodiff wykonuje te same operacje na wybranych wejściach, więc oblicza także ich numeryczne wartości wyjściowe. Nadal potrzebuje lokalnych pochodnych, ale ocenia reguły już wdrożone dla każdej operacji, zamiast wyprowadzać nowe formuły podczas wykonywania:

KrokW x=3x=3, y=2y=2, przy xx trzymanym nieruchomo
Użyj tych samych operacjiu=y2u=y^2, następnie v=xuv=xu
Oblicz wartości operacjiu=22=4u=2^2=4, następnie v=3⋅4=12v=3\cdot4=12
Oceń lokalne zasady dotyczące pochodnychdu/dy=2y=4du/dy=2y=4, ∂v/∂u=x=3\partial v/\partial u=x=3
Połącz pochodne liczbowe, korzystając z reguły łańcuchowej∂v/∂y=(∂v/∂u)(du/dy)=3⋅4=12\partial v/\partial y=(\partial v/\partial u)(du/dy)=3\cdot4=12

Kontynuacja ostatniego dodawania daje ∂f/∂y=1⋅12=12\partial f/\partial y=1\cdot12=12. W celu wyjaśnienia tabela ta wyodrębnia efekt jednego wejścia. Nasze symboliczne obliczenia połączyły 1⋅x⋅2y1\cdot x\cdot2y we wzorze 2xy2xy. Funkcja Autodiff ocenia poszczególne reguły na bieżących wejściach i zamiast tego łączy 1⋅3⋅41\cdot3\cdot4. Wykorzystuje znane reguły pochodne jako instrukcje numeryczne, bez konstruowania symbolicznego wzoru na pochodną dla całej funkcji.

Poniższy widget wykorzystuje ten sam wykres, co przykład z jedną ścieżką. Przełącz pomiędzy Różniczkowanie symboliczne i Różniczkowanie automatyczne w celu porównania łączenia wyrażeń pochodnych z łączeniem ich wartości liczbowych w x=3x=3 i y=2y=2.

One path from y through u and v to fHolding x constant, the derivative for y receives a contribution through one path: y → u → v → f. Multiply along this path: ∂f/∂y = 2y × x × 1 = 2xy.yu = y²du/dy = 2yxv = xu∂v/∂u = xw = x³f = v + w∂f/∂v = 1Chain rule∂f/∂y = 2y × x × 1 = 2xy

Scroll the graph sideways to see the chain-rule box.

W trybie symbolicznym ciągłe strzałki pokazują, w jaki sposób funkcje się łączą: każdy wynik pośredni staje się wejściem do innej operacji. W trybie automatycznego różniczkowania te same strzałki opisują również przepływ wartości numerycznych do przodu: podniesienie do kwadratu daje u=4u=4, a mnożenie z x=3x=3 daje v=12v=12. Zależności pozostają takie same; teraz oceniamy operacje na określonych wejściach. Ten widżet porównania nie animuje tego przepływu. Poniższy przewodnik krok po kroku pokaże te wartości w przód i propagację wsteczną wkładów pochodnych.

Obliczanie wartości operacyjnych różni się od obliczania lokalnych pochodnych. Każda reguła pochodnej używa dowolnych wartości, których potrzebuje: na tej ścieżce reguła kwadratu potrzebuje yy, a pochodna mnożenia względem uu potrzebuje xx. Wiersze w tabeli obliczeń numerycznych opisują różne zadania, a nie oddzielne przebiegi: implementacja może ocenić lokalne reguły pochodne podczas przebiegu w przód lub w tył. W tym przewodniku oceniamy je i zapisujemy podczas podania do przodu.

Widget porównawczy wyświetla obliczone pochodne; w późniejszym instruktażu zostaną również wyświetlone wartości przekazywane dalej. Tak się składa, że ​​wynik kwadratu u=y2u=y^2 i jego lokalna pochodna du/dy=2ydu/dy=2y są równe 44 w y=2y=2, ale są to różne wielkości: w y=3y=3 będą to 99 i 66.

Wyznaczanie wzoru na pochodną złożoną daje taki sam wynik, jak obliczanie najpierw pochodnych lokalnych, a następnie łączenie ich za pomocą reguły łańcuchowej. W przypadku (3,2)(3,2) obliczenie 2xy2xy daje 1212, podobnie jak mnożenie 4×3×14\times3\times1. Operacje i ich zależności pozostają takie same; zmienia się to, czy łączymy wyrażenia pochodne, czy wartości liczbowe.

Unikanie rozszerzonych formuł ma znaczenie, ponieważ wielokrotne podstawienie wyrażeń pośrednich może spowodować, że formuła pochodna będzie znacznie większa niż oryginalne wyrażenie — jest to problem zwany Zwiększenie wyrażenia. Rozwinięta formuła może zawierać wiele kopii tego samego podwyrażenia, zajmując więcej miejsca i powtarzając pracę, jeśli każda kopia jest oceniana osobno. Zachowanie nazwanych wyników pośrednich pozwala nam ponownie wykorzystać te obliczenia. Symboliczne zróżniczkowanie może również zachować i udostępnić te wyrażenia, więc wzrost ekspresji nie jest nieuniknionym ograniczeniem metody.

Tryb prosty i odwrotny

Widżet pokazuje, które pochodne lokalne łączymy, ale nie pokazuje kolejności, w jakiej je propagujemy. Połączenia przerywane łączą czynniki w iloczyn. Tryb do przodu i tryb do tyłu autodiff mogą używać tego samego wykresu obliczeń i oba oceniają funkcję od wejść do wyjść. Różnią się sposobem propagacji pochodnych: tryb do przodu przenosi je z wejść do wyjść, podczas gdy tryb odwrócony przenosi wkłady pochodne z wyjścia z powrotem do wejść.

Zaczynając od jednego wybranego wejścia, tryb forward może obliczyć, jak wszystkie wyjścia reagują na to wejście w jednym przebiegu różniczkującym. Zaczynając od jednego wybranego wyjścia, tryb odwrotny może obliczyć, jak to wyjście reaguje na wszystkie wejścia w jednym przejściu wstecz. Skoncentrujemy się na trybie odwrotnym, podejściu stosowanym w przypadku propagacji wstecznej. Pełne przejście wstecz obliczy zarówno ∂f/∂x\partial f/\partial x, jak i ∂f/∂y\partial f/\partial y, gromadząc wkład z obu ścieżek do xx.

Przejście pochodnej w trybie forward propaguje pochodne wzdłuż wartości funkcji. Zamiast tego przejście do przodu naszego obliczenia w trybie odwrotnym ocenia funkcję i rejestruje, czego potrzebuje późniejszy przebieg wstecz; pochodne wyniku końcowego są kumulowane podczas tego przejścia wstecz. Prześledzimy te obliczenia w dwóch przebiegach:

  • Przepust do przodu ocenia funkcję. W tym przewodniku każda operacja oblicza swoją wartość i zapisuje połączenia wejściowe oraz numeryczne pochodne lokalne.
  • Przejście wsteczne rozpoczyna się od pochodnej wyniku względem samego siebie, ∂f/∂f=1\partial f/\partial f=1, i wykorzystuje zapisane lokalne pochodne do propagowania wkładów liczbowych wstecz przy użyciu reguły łańcuchowej. Wkłady do tych samych danych wejściowych kumulują się.

Poniższa tabela pokazuje, co oblicza i przechowuje nasze przejście do przodu w x=3x=3 i y=2y=2. Typ operacji określa, która reguła pochodna zostanie uruchomiona; zapisane krawędzie łączą swoje wejścia z obliczonymi pochodnymi lokalnymi:

ObliczeniaWartość wyjściowa przy (x,y)=(3,2)(x,y)=(3,2)Przechowywane połączenia wejściowe i lokalne pochodne
u=y2u=y^244Wejście yy, z du/dy=4du/dy=4
v=xuv=xu1212Wejście xx, z ∂v/∂x=4\partial v/\partial x=4; wejście uu, z ∂v/∂u=3\partial v/\partial u=3
w=x3w=x^32727Wejście xx, z dw/dx=27dw/dx=27
f=v+wf=v+w3939Wejście vv, z ∂f/∂v=1\partial f/\partial v=1; wejście ww, z ∂f/∂w=1\partial f/\partial w=1

Wartości wyjściowe i pochodne lokalne pełnią różne role. Na przykład mnożenie wykorzystuje u=4u=4 do obliczenia wyniku v=12v=12 i ta sama wartość argumentu dostarcza lokalną pochodną względem xx. Wstecz łączy zapisane lokalne pochodne ze skumulowaną wrażliwością na wynik końcowy.

Model jest programem, więc funkcja autodiff ma dostęp do operacji, które wygenerowały jego wynik; sama skończona tabela obserwacji wejścia-wyjścia nie może dostarczyć takich informacji ani określić dokładnych pochodnych. W przypadku ustalonej partii szkoleniowej obliczenia przewidywania i strat w modelu mają wiele parametrów i jedną wartość straty. Tryb odwrotny ponownie wykorzystuje wyniki pośrednie do obliczenia wszystkich pochodnych parametrów w jednym przejściu wstecz, zamiast powtarzać całe obliczenia dla każdego parametru.

Ustaliliśmy, co rejestruje silnik i w jaki sposób wykorzystuje lokalne zasady dotyczące pochodnych. Następnie prześledzimy te same obliczenia w interaktywnym przebiegu do przodu i do tyłu, obserwując, jak rejestrowane są wartości i kumulują się wkłady pochodne.

Automatyczne różniczkowanie w trybie odwrotnym krok po kroku

Przeprowadźmy teraz pełne obliczenia automatycznej różnicy w trybie odwrotnym przy użyciu tej samej funkcji f(x,y)=xy2+x3f(x,y)=xy^2+x^3 i (x,y)=(3,2)(x,y)=(3,2). Ocenimy cztery operacje z powyższej tabeli, a następnie prześledzimy ich pochodne wstecz, aby otrzymać gradient (31,12)(31,12):

x = Value(3)
y = Value(2)

u = y ** 2
v = x * u
w = x ** 3
f = v + w

f.backward()

Przejście do przodu daje u=4u=4, v=12v=12, w=27w=27, a wyjście końcowe f=39f=39. Każdy wynik zachowuje swoje powiązania z danymi wejściowymi, które go wytworzyły, wraz z informacjami potrzebnymi do jego lokalnej reguły pochodnej. Przejście wstecz oblicza, jak końcowe wyjście reaguje na każdy węzeł; nie zmienia to wartości przekazywanych dalej.

Wartości forward nazywane są także liczbami podstawowymi. Pochodne zgromadzone podczas przejścia wstecz nazywane są sprzężonymi: każda mierzy, jak zmienia się końcowy wynik w odniesieniu do określonej wartości wejściowej lub wartości pośredniej. W naszym widgecie są one oznaczone odpowiednio jako value i grad. Sprzężenie to pochodna wyniku końcowego, różniąca się od lokalnej pochodnej operacji.

Przed przejściem wstecz każdy .grad zaczyna się od zera. Wysiewamy f.grad = 1, ponieważ ∂f/∂f=1\partial f/\partial f=1.

.grad każdego węzła skalarnego przechowuje pochodną końcowego wyniku w odniesieniu do tego węzła. Razem x.grad i y.grad tworzą wektor gradientu (31,12)(31,12). Przewodnik przechowuje lokalne pochodne numeryczne na połączeniach i wykorzystuje je podczas odtwarzania wstecz: parent.grad += node.grad * local_derivative.

Wcześniejszy widget porównawczy korzystał wyłącznie ze ścieżki z yy do obliczenia ∂f/∂y\partial f/\partial y. Poniższy widget przetwarza cały wykres w jednym przebiegu wstecznym, obliczając pochodne zarówno w odniesieniu do xx, jak i yy. Kroki te następują sekwencyjnie w ramach tego samego przebiegu. Każde pole pokazuje oddzielnie wartość wyjściową, numeryczne pochodne lokalne i skumulowany gradient.

Wejścia są ustawione na x=3x=3 i y=2y=2, co odpowiada utworzeniu x = Value(3) i y = Value(2). Wybierz opcję Oblicz w przód, aby zobaczyć, jak cztery operacje wygenerują wynik. Następnie wybierz Ustaw gradient wyjścia i wykonaj kroki wstecz, dodając, dzieląc, mnożąc i podnosząc do kwadratu. Każdy krok wstecz podkreśla operację i jej dane wejściowe, a następnie łączy symboliczne wyrażenie reguły łańcuchowej z jego podstawieniem numerycznym i odpowiednią aktualizacją .grad poniżej wykresu. Zobacz, jak te dwa wkłady kumulują się w xx.

The same operations, forward and backwardf(x, y) = xy² + x³
Computation graph of f = xy² + x³Start with x = 3 and y = 2, then evaluate u = y², v = xu, w = x³, and f = v + w. Local derivatives describe each operation. Each grad describes the final output’s derivative with respect to that node.yvalue 2grad —u = y²value ——grad —xvalue 3grad —v = xuvalue ———grad —w = x³value ——grad —f = v + wvalue ———grad —Chain rule∂f/∂x = —∂f/∂y = —
Stage 0 of 6. Start with x = 3 and y = 2, then evaluate u = y², v = xu, w = x³, and f = v + w.

W poniższej tabeli podsumowano kroki wstecz, które można wykonać w widgecie. Wartości forward pozostają niezmienione przez cały czas; zmieniają się tylko skumulowane gradienty.

Krok wsteczAktualizacja gradientu
Nasionaf.grad=1\mathtt{f.grad}=1
Dodatekv.grad=f.grad⋅1=1\mathtt{v.grad}=\mathtt{f.grad}\cdot1=1, w.grad=f.grad⋅1=1\mathtt{w.grad}=\mathtt{f.grad}\cdot1=1
Kostkax.grad=w.grad⋅3x2=1⋅27=27\mathtt{x.grad}=\mathtt{w.grad}\cdot3x^2=1\cdot27=27
Mnożenie, wkład do xxDodaj v.grad⋅u=1⋅4=4\mathtt{v.grad}\cdot u=1\cdot4=4, dając x.grad=27+4=31\mathtt{x.grad}=27+4=31
Mnożenie, wkład do uuu.grad=v.grad⋅x=1⋅3=3\mathtt{u.grad}=\mathtt{v.grad}\cdot x=1\cdot3=3
Kwadraty.grad=u.grad⋅2y=3⋅4=12\mathtt{y.grad}=\mathtt{u.grad}\cdot2y=3\cdot4=12

Dwie ścieżki z xx przyczyniają się oddzielnie, podczas gdy yy otrzymuje swój udział poprzez uu i vv:

∂f∂x=1×27⏟through w+1×4⏟through v=31,∂f∂y=1×3×4=12\frac{\partial f}{\partial x}=\underbrace{1\times27}_{\text{through }w}+\underbrace{1\times4}_{\text{through }v}=31, \qquad \frac{\partial f}{\partial y}=1\times3\times4=12

To zamówienie przetwarza gałąź kostki przed gałęzią mnożenia. Inne ważne zlecenia wstecz dają ten sam wynik, pod warunkiem, że węzeł otrzyma wszystkie dalsze wkłady, zanim przekaże zakumulowaną pochodną na swoje dane wejściowe.

Widżet pokazuje pełne podanie do tyłu. Połączmy teraz z kodem jego kroki mnożenia v=xuv=xu i kwadraty u=y2u=y^2. Krok wstecz mnożenia powoduje mnożenie dwóch różnych pochodnych:

∂f∂v⏟stored in v.grad×∂v∂x⏟u.data, the other operand’s value=1×4\underbrace{\frac{\partial f}{\partial v}}_{\text{stored in v.grad}} \times \underbrace{\frac{\partial v}{\partial x}}_{\text{u.data, the other operand's value}} =1\times4

Pierwszy czynnik mówi nam, jak końcowe wyjście ff reaguje na vv. Drugi mówi nam, jak vv reaguje na xx, utrzymując stałe uu. Mnożenie ich to reguła łańcuchowa: daje udział tej ścieżki w pochodnej względem xx.

W instrukcji zapisywane są numeryczne pochodne lokalne podczas przejścia do przodu. Inna implementacja może zapisać wartości argumentów i ocenić te same lokalne reguły podczas odtwarzania wstecz. W tej formie metoda mnożenia __mul__(self, other) tworzy węzeł wynikowy o nazwie out, a jej regułę wsteczną można zapisać jako:

self.grad += out.grad * other.data  # contribution to the left operand
other.grad += out.grad * self.data  # contribution to the right operand

Dla v = x * u, out to vv, self to xx, a other to uu. Po aktualizacjach mnożenia, zapisanych tymi nazwami, następuje aktualizacja kwadratu:

# Multiplication: v = x * u
x.grad += v.grad * u.data       # add 1 × 4 to the existing 27 → 31
u.grad += v.grad * x.data       # add 1 × 3 → 3

# Square: u = y ** 2
y.grad += u.grad * (2 * y.data) # add 3 × 4 → 12

Przepustka do przodu dostarczana z u=4u=4, x=3x=3 i y=2y=2. Mnożenie wykorzystuje wartość drugiego operandu dla każdej lokalnej pochodnej. Kwadrat wykorzystuje 2y=42y=4 i mnoży go przez skumulowany u.grad = 3.

KodZnaczenie
out.gradPochodna wyniku końcowego po wyniku tej operacji
other.dataWartość innego operandu, która jest równa lokalnej pochodnej mnożenia
+=Dodaj udział tej ścieżki do skumulowanej pochodnej danych wejściowych

other.data przechowuje wartość, a nie pochodną. Reguła mnożenia mówi, że ta wartość jest równa potrzebnej pochodnej lokalnej. Dla dodawania lokalna pochodna wynosi 11, więc odpowiednia aktualizacja to self.grad += out.grad * 1. Dla potęgi obliczamy regułę potęgową przy danym wejściu: lokalna pochodna sześcianu to 3x23x^2, a kwadratu — 2y2y.

W symbolicznym widżecie połączyliśmy lokalne wyrażenia pochodne wzdłuż ścieżki z yy:

∂f∂y=∂f∂v⋅∂v∂u⋅dudy=1⋅x⋅2y=2xy\frac{\partial f}{\partial y} =\frac{\partial f}{\partial v}\cdot\frac{\partial v}{\partial u}\cdot\frac{du}{dy} =1\cdot x\cdot2y =2xy

Kod opiera się na tych samych połączeniach, ale łączy liczby. Dodatek daje v.grad = 1; mnożenie daje u.grad = 1 × 3 = 3; kwadrat daje y.grad = 3 × 4 = 12. Zatem u.grad zawiera już dwa pierwsze czynniki wyrażenia symbolicznego. Autodiff otrzymuje tę samą pochodną bez konstruowania połączonego wzoru 2xy2xy.

Te kroki obliczają pochodne w (3,2)(3,2); nie aktualizują wejść, aby zminimalizować funkcję. Klasa Value automatyzuje kroki wstecz w jednym wywołaniu f.backward().

Od obliczeń do silnika

Pochodna mierzy lokalną wrażliwość. Pochodne cząstkowe izolują efekt każdego sygnału wejściowego, a gradient gromadzi te pomiary w wektorze. Propagacja wsteczna oblicza je, wykonując zarejestrowane operacje wstecz: mnożysz pochodne wzdłuż każdej ścieżki i dodajesz udziały w miejscach, gdzie ścieżki się spotykają.

Aby zobaczyć te pomysły zaimplementowane w Pythonie, zapoznaj się z przewodnikiem Andreja Karpathy’ego dotyczącym Microgradu. Lokalne reguły pochodne, akumulacja gradientów i przechodzenie wstecz, które tu zbadaliśmy, zapewniają podstawy do zrozumienia jego implementacji.

Aby uzyskać głębszy pogląd matematyczny, zapoznaj się z tym wykładem na temat automatycznego różniczkowania i przekształceń liniowych. Łączy rachunek różniczkowy z algebrą liniową, pokazując, jak pochodne zachowują się jak przekształcenia liniowe, jak reprezentują je macierze Jakobianu i jak łączy je reguła łańcuchowa — matematyczne podstawy automatycznego różniczkowania.

Podgląd obrazu

Zbliżaj palcami lub użyj + / −. Przeciągaj lub użyj strzałek. Esc zamyka podgląd.