Moduł 05 · uczące się

Sieci neuronowe

MLP, MDN, IKFlow, diffusion — multi-modalne odwracanie f: Q → SE(3).

Trochę inaczej niż dotychczas

Do tej pory rozwiązywaliśmy IK przez jawne wzory matematyczne — albo zamknięte (moduł 1), albo iteracyjne (moduły 3 i 4). Teraz spróbujemy czegoś zupełnie innego: nauczyć komputer rozwiązywać IK przez pokazywanie mu tysięcy poprawnych przykładów. Zamiast programować algorytm, dostarczamy dane — a komputer sam odkrywa prawidłowości.

Wyobraź sobie, że uczysz dziecko rozpoznawać psy i koty. Nie tłumaczysz definicji („pies ma długi pysk i…") — pokazujesz tysiąc zdjęć z podpisami „pies" i „kot". Po jakimś czasie dziecko samo zaczyna rozpoznawać. Dokładnie tak działają sieci neuronowe: są to programy, które uczą się funkcji „wejście → wyjście" przez przykłady. Dla nas „wejście" = poza T, a „wyjście" = kąty q.

Co siedzi w środku sieci? — animacja krok po kroku

Zanim przejdziemy do skomplikowanych architektur, zobaczmy, jak działa najprostsza sieć neuronowa — wielowarstwowy perceptron (MLP). Animacja niżej przeprowadzi Cię przez jeden forward pass — czyli jedno przejście danych od wejścia do wyjścia. Naciśnij ▶ odtwórz albo klikaj dalej →:

krok 0/8: Start
WejścieWarstwa ukryta (tanh)Wyjście0.6x1 =-0.2x2 =0.5x3 =h1h2h3h4y1= q1y2= q2

Mała sieć MLP — 3 wejścia, 4 neurony ukryte, 2 wyjścia.

Naciśnij ▶ odtwórz albo dalej → żeby przejść przez forward pass krok po kroku. W każdym kroku zobaczysz, co dokładnie się dzieje.

Zauważ — w środku sieci nie ma żadnej magii. Każdy neuron to po prostu kalkulator, który robi trzy rzeczy:

  1. Bierze wartości z poprzedniej warstwy.
  2. Mnoży każdą przez przypisaną wagę i sumuje.
  3. Wynik puszcza przez prostą funkcję nieliniową (np. tanh).

Cała tajemnica sieci jest w tym, jakie dokładnie wagi siedzą przy strzałkach. Tych wag może być setki tysięcy. Nikt ich nie wpisuje ręcznie — uczą się same na danych treningowych. To jest właśnie uczenie (training).

Schemat sieci — dwa widoki

Po lewej — wersja specjalna dla naszego zadania (poza T → kąty q). Niżej — kanoniczny schemat z literatury. Obie pokazują to samo: warstwy neuronów połączone wagami.

xyz·······q₁q₂q₃q₄q₅q₆wejściepoza T (6 liczb)warstwa 164 neurony, tanhwarstwa 264 neurony, tanhwyjściekąty (6 liczb)Każda linia = jedna waga (parametr).waga dodatniawaga ujemnagrubość ≈ |waga|MLP: liczby wchodzą z lewej, wychodzą z prawej, w środku — mnożenia i dodawania
Klasyczny schemat MLP
Klasyczny diagram MLP — warstwa wejściowa, warstwy ukryte, warstwa wyjściowa. Każda strzałka to jeden parametr (waga).
Źródło: Wikimedia Commons · autor: Sky99 · licencja: CC BY-SA 3.0

Trening — jak komputer uczy się wag?

Trening sieci neuronowej to dokładnie ten sam problem optymalizacyjny co w module 4 — tylko że tu zmiennymi są wagi, a kosztem jest średni błąd predykcji na danych treningowych:

θ=argminθ  1Ni=1Nfθ(xi)yi2\theta^* = \arg\min_\theta \; \frac{1}{N} \sum_{i=1}^{N} \big\|f_\theta(x_i) - y_i\big\|^2

Procedura w pseudokodzie:

  1. Inicjuj wagi losowo (małe wartości z gaussowskiego rozkładu).
  2. Bierz przykład (xi,yi)(x_i, y_i) z datasetu.
  3. Forward pass: oblicz y^=fθ(xi)\hat{y} = f_\theta(x_i).
  4. Sprawdź błąd: y^yi2\|\hat{y} - y_i\|^2.
  5. Backpropagation — algorytm wyliczający, jak każdą wagę przesunąć, żeby błąd zmalał (to jest tylko gradient z modułu 4, sprytnie zorganizowany).
  6. Przesuń wagi w tym kierunku (SGD albo Adam).
  7. Wróć do 2. Powtarzaj miliony razy.

Po skończonym treningu sieć — przy odrobinie szczęścia — daje sensowne predykcje także dla nowych danych, których nie widziała. To zjawisko nazywa się generalizacją. Jeśli sieć nauczyła się tylko zapamiętać dane treningowe i nie umie odpowiadać na nowe — mówimy o przetrenowaniu (overfitting).

SGD i Adam — co to za algorytmy?

W kroku 6 procedury napisałem „przesuń wagi w tym kierunku (SGD albo Adam)". Co to dokładnie?

SGDStochastic Gradient Descent, stochastyczny gradient descent. To po prostu zwykły gradient descent z modułu 4, ale z jedną sprytną modyfikacją: zamiast liczyć gradient na całym datasecie (co dla milionów przykładów zajmuje wieczność), liczymy go na jednym losowo wybranym przykładzie albo małym mini-batchu (zazwyczaj 32, 64 lub 128 przykładów):

θk+1=θkαLi(θk),iUniform(1,N)\theta_{k+1} = \theta_k - \alpha\,\nabla L_i(\theta_k), \quad i \sim \text{Uniform}(1, N)

Gradient z jednego przykładu jest niedokładnym oszacowaniem prawdziwego gradientu (stąd „stochastic" — losowy), ale jest milion razy szybszy do policzenia. W praktyce: zaszumiona ścieżka mimo wszystko prowadzi do dobrego rozwiązania, a często nawet pomaga uciec z płytkich lokalnych minimów. „Wagi chodzą małymi krokami w mniej-więcej dobrym kierunku".

AdamAdaptive Moment Estimation (Kingma & Ba, 2014). Ulepszony SGD, który adaptuje krok osobno dla każdej wagi. Pomysł:

  1. Dla każdej wagi pamiętamy średnią ruchomą gradientów (kierunek dotychczasowych aktualizacji — momentum).
  2. Pamiętamy też średnią ruchomą kwadratów gradientów (jak duże były ostatnie aktualizacje).
  3. Krok dla danej wagi: kierunek z (1) podzielony przez pierwiastek z (2). Wagi, które otrzymują często duże gradienty, dostają mniejszy krok; te które rzadko się aktualizują — większy.
mt=β1mt1+(1β1)gt,vt=β2vt1+(1β2)gt2m_t = \beta_1 m_{t-1} + (1-\beta_1) g_t, \quad v_t = \beta_2 v_{t-1} + (1-\beta_2) g_t^2
θt+1=θtαm^tv^t+ε\theta_{t+1} = \theta_t - \alpha \cdot \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \varepsilon}

(z dodatkową korektą obciążenia m^t=mt/(1β1t)\hat{m}_t = m_t/(1-\beta_1^t), v^t=vt/(1β2t)\hat{v}_t = v_t/(1-\beta_2^t)). Domyślne hiperparametry: β1=0,9\beta_1 = 0{,}9, β2=0,999\beta_2 = 0{,}999, α=103\alpha = 10^{-3}.

Dlaczego Adam wyparł czysty SGD w deep learningu?

  • Mniej strojenia kroku — domyślne α=103\alpha = 10^{-3} zwykle działa od razu, bez szukania.
  • Stabilniejszy — adaptacja kroku dla każdej wagi tłumi oscylacje.
  • Szybciej zbiega — szczególnie na początku treningu (momentum przyspiesza ruch w stałym kierunku).
  • Działa „od razu" dla różnych architektur (CNN, transformerów, MLP itd.).

Kiedy SGD bywa lepszy? Dla bardzo dużych modeli i długich treningów SGD z momentum daje czasem lepszą generalizację (model „bardziej wygładzony", mniej overfitujący). Stąd np. ResNet'y w ImageNecie często trenuje się SGD-em, a transformerów (BERT, GPT) — Adamem (a właściwie Adam-W, wariantem z popraw lepszą regularyzacją).

W naszej aplikacji (sekcja „Laboratorium" niżej) używamy Adama — dzięki temu trening MLP w przeglądarce jest stabilny i kończy się w kilkunastu sekundach bez ręcznego strojenia α\alpha.

Krok 1

Naiwny MLP — najprostsza próba (i dlaczego nie wystarcza)

Pomysł: wytrenuj MLP, który dla danej pozy TT (6 liczb: pozycja + RPY) zwraca 6 kątów qq. Trenujesz na milionach par (T,q)(T, q) wygenerowanych losowo przez FK.

Brzmi sensownie — ale jest jeden zasadniczy problem.

Problem: jedna poza, kilka prawidłowych odpowiedzi

Pamiętasz z modułu 2, że dla Pumy ta sama poza może być osiągnięta na do 8 sposobów (shoulder L/R, elbow U/D, wrist flip)? W datasecie treningowym ta sama poza pojawia się więc kilka razy z różnymi kątami. A funkcja kosztu MSE każe sieci „minimalizuj średni kwadrat błędu" — więc sieć uczy się średniej wszystkich poprawnych odpowiedzi.

Tylko że średnia ośmiu różnych prawidłowych konfiguracji nie jest żadną prawidłową konfiguracją. To jak byś zapytał ośmiu osób o najkrótszą drogę do biura — jedna mówi „przez most", druga „przez tunel" — ich „średnia" („zjedź do rzeki i zatrzymaj się w połowie") to nonsens.

q (kąt przegubu)-2-1012q*1q*2MLPśrednia = błędna!Dla jednej pozy T istnieją DWIE poprawne wartości q. MLP uczy się ich średniej.prawdziwy rozkład p(q|T)predykcja MLP (jedna liczba)

Czerwona kropka to predykcja MLP — średnia dwóch niebieskich „dzwonków" prawdziwych odpowiedzi. Średnia trafia dokładnie tam, gdzie żadna z odpowiedzi nie była. To nie jest błąd implementacji ani za małej sieci — to fundamentalna pułapka uśredniania.

Praktyczny ratunek: hybryda NN → DLS

Mimo wad, naiwny MLP ma jedną zaletę — daje dobry punkt startowy (warm start). Pomysł hybrydy:

qseed=fθ(T)      kilka iteracji DLS      q (dokładne)q_{\text{seed}} = f_\theta(T) \;\;\xrightarrow{\;\text{kilka iteracji DLS}\;}\;\; q^* \text{ (dokładne)}

Sieć daje przybliżenie (błąd ~5 cm), DLS dopina do precyzji maszynowej w 2–3 iteracjach. Sieć daje szybkość, klasyczny solver — dokładność. Tak właśnie wygląda większość produkcyjnych systemów IK z neural-warmem.

Niżej — laboratorium. Trenujemy MLP od zera (bez zewnętrznych bibliotek ML, ~200 linii TypeScriptu) i porównujemy surową predykcję z hybrydą NN → DLS.

Krok 2

MDN — uczymy sieć, że poprawnych odpowiedzi może być wiele

Pomysł w jednym zdaniu: zamiast jednej liczby niech sieć zwraca listę możliwych odpowiedzi z prawdopodobieństwami.

Analogia: wyobraź sobie prognozę pogody. Możliwe podejścia:

  • Prognoza punktowa (jak naiwny MLP): „jutro będzie 10°C". Jeśli model się myli — masz pecha.
  • Prognoza probabilistyczna (jak MDN): „jutro: 70% szans 8°C ± 2°, 30% szans 15°C ± 2°" (dwa możliwe scenariusze). Może się okazać bardzo użyteczne — wiesz na co się przygotować.

Mixture Density Network (Bishop, 1994) to właśnie sieć, która zwraca prognozę probabilistyczną. Konkretnie — parametry kilku „dzwonków" (gaussianów):

  • Wagi αk(T)\alpha_k(T) — jak prawdopodobny jest k-ty „garb"
  • Środki μk(T)\mu_k(T) — gdzie ten garb leży
  • Szerokości Σk(T)\Sigma_k(T) — jak szeroki jest garb

Suma wszystkich garbów daje rozkład prawdopodobieństwa p(qT)p(q\,|\,T) — sieć mówi nie „q to 1.5", ale „q to z 50% szansy 1.5, z 50% szansy −1.5":

q (kąt przegubu)-2-1012komponent 1α₁ = 0.5, μ₁ = −1.5komponent 2α₂ = 0.5, μ₂ = +1.5MDN: zamiast zwracać jedną liczbę, zwraca DWA garby — po jednym na każdą poprawną odpowiedź.prawdziwy rozkład p(q|T)predykcja MDN (suma garbów)

Po wytrenowaniu sieć z K = 8 komponentami dla Pumy naturalnie odkrywa 8 gałęzi rozwiązania — bo to jest matematycznie optymalna liczba „garbów" dla tych danych.

Jak tego używać:

  • Najlepsze rozwiązanie — wybierz garb z największą wagą (α\alpha), weź jego środek (μ\mu).
  • Próbkowanie — losuj garb z prawdopodobieństwem proporcjonalnym do α\alpha, potem losuj punkt z gaussa wokół μ\mu. Daje różnorodne, ale poprawne odpowiedzi.
  • Ocena — dla danej kandydackiej wartości qq sieć zwraca jej prawdopodobieństwo. Można porównywać hipotezy.

Wady MDN: trening jest niestabilny (małe potknięcia inicjalizacji powodują, że wszystkie garby zlewają się w jeden); wybór K (ile garbów?) wymaga eksperymentów.

Krok 3

Normalizing Flows / IKFlow — sieć która umie losować

Pomysł w jednym zdaniu: nauczmy sieć przekształcać losowy szum w poprawne odpowiedzi.

Analogia: kreatywny artysta dostaje plamy atramentu z butelki (czyli losowy szum), patrzy na nie i rysuje z nich konkretne obrazy. Każdy losowy układ plam → inny prawidłowy obraz. Im lepszy artysta, tym ciekawsze i bardziej różnorodne wyniki.

IKFlow (Ames et al., 2022) jest takim artystą — siecią neuronową, która zamienia szum w poprawne kąty robota:

IKFlow: sieć uczy się odwracać losowy szum w poprawne rozwiązania IKz ~ N(0, I)gaussowski szumsieć IKFlowg_θ(z; T)(warunkowa na pozie T)q = g_θ⁻¹(z; T)poprawne rozwiązania IKshoulder Rshoulder L

Po lewej — chmurka punktów z prostego gaussowskiego szumu (każdy punkt to losowy 6-wymiarowy wektor z rozkładu normalnego). Po prawej — te same punkty po przejściu przez sieć IKFlow: wszystkie spadły do jednego z wyraźnych klastrów, każdy klaster odpowiada jednej gałęzi rozwiązania IK (shoulder R i shoulder L). Sieć „wie", którą gałąź ma trafić każdy konkretny punkt szumu — bo jest warunkowa na pozie TT.

Praktyczne użycie:

  1. Wylosuj zN(0,I)z \sim \mathcal{N}(0, I) — zwykły gaussowski wektor 6-wymiarowy.
  2. Podaj parę (zz, TT) do sieci.
  3. Sieć zwraca q=gθ1(z;T)q = g_\theta^{-1}(z; T) — jedną z gałęzi rozwiązania.
  4. Powtórz z innym zz — dostaniesz inną gałąź.

Siła tego podejścia: jedna sieć, dowolnie wiele różnych poprawnych rozwiązań. Wyniki publikowane (KUKA, Baxter, Atlas): próbkowanie w pojedynczych ms, błąd pozycji rzędu mm.

Konkretny przykład — manipulator 2R

Weźmy najprostszego robota, na którym można zobaczyć IKFlow w akcji: planarne ramię z dwoma ogniwami obrotowymi (dwa segmenty o długości 1, sterowane kątami q1,q2q_1, q_2). Cel — czerwona kropka — możemy osiągnąć na dwa sposoby: elbow up (łokieć w górę) albo elbow down (łokieć w dół).

Niżej: dwanaście niezależnych zapytań do (symulowanej) sieci IKFlow. Dla każdego losujemy 2-wymiarowy gaussowski szum, sieć przekształca go w parę kątów. Naciśnij 🎲 wylosuj — zobaczysz, że część sampli daje rozwiązanie „elbow up" (zielone), część — „elbow down" (fioletowe). Wszystkie trafiają końcówką w czerwoną kropkę:

seed = 10 elbow up · 0 elbow down

Cel TCP (x, y) = (1.2, 0.7) — czerwona kropka. Każdy mały robot poniżej to jeden sample z (uproszczonej) sieci IKFlow: bierzemy losowy gaussowski szum z, „sieć" przekształca go w parę kątów (q₁, q₂). Wszystkie roboty trafiają w ten sam cel, ale używają różnych gałęzi rozwiązania.

⚠ Uwaga: w tym demie „sieć" to skrót — używamy znaku z₁ żeby przypisać gałąź. Prawdziwa wytrenowana sieć IKFlow uczy się tych granic sama z danych. Czytaj wyjaśnienie pod demem.

To jest cała magia normalizing flow w jednym demie: jedna sieć, jedno wywołanie, ale za każdym razem inna poprawna gałąź. Z 8 gałęzi Pumy zrobiłoby się analogicznie 8 klastrów; tu mamy 2 (bo robot 2R ma tylko dwa rozwiązania). Każde losowanie z gaussa to jeden „zaczerpnięty z kapelusza" kandydat.

Praktyczne zastosowanie: w planowaniu ruchu robot sięga po przedmiot. Jeden klaster może być zablokowany kolizją z ścianą — dzięki IKFlow generujemy dziesięć kandydatów, sprawdzamy kolizje i wybieramy ten, który przejdzie. Algorytm nie musi „wiedzieć" o ścianie a priori — wystarczy że po prostu produkuje różnorodne rozwiązania.

Uczciwie — co upraszcza powyższe demo?

Mogłeś zauważyć, że w demie powyżej znak z1z_1 bezpośrednio decyduje o gałęzi: z[0] > 0 ? "up" : "down". Wygląda to jak gdybym z góry „kazał" sieci wybrać konkretne rozwiązanie. Tak — w demie tak jest. Symuluję jedynie efekt już wytrenowanej sieci, bez całego treningu.

Prawdziwa sieć IKFlow działa inaczej:

  1. Nikt jej z góry nie mówi „lewy obszar szumu prowadzi do elbow up, prawy do elbow down". Sieć ma wyjść z tym sama.
  2. Trening: sieć dostaje miliony par (T,q)(T, q) z datasetu (przez FK z losowych konfiguracji robota). W każdym kroku optymalizator przesuwa wagi tak, żeby rozkład wyjść sieci dla danej pozy TT pasował do rozkładu rzeczywistych qq w danych. Mówiąc dokładniej: minimalizujemy negatywny log-likelihood obserwowanych qq przy danym TT.
  3. Co z tego wynika geometrycznie: po treningu w przestrzeni szumu Rn\mathbb{R}^n wyłaniają się regiony. Każdy region trafia (po przejściu przez sieć) do jednej z gałęzi rozwiązania. Granica między regionami jest gładka i — kluczowe — zależy od pozy TT. Ten sam punkt szumu z=(0.3,0.7)z = (0.3, -0.7) dla pozy T1T_1 może trafić do elbow up, a dla pozy T2T_2 — do elbow down.
  4. Coupling layers (warstwy odwracalne) to techniczny trick, dzięki któremu sieć jest matematycznie odwracalna i ma policzalny jakobian. Pozwala to wytrenować ją przez maximum likelihood (potrzebujemy logp(qT)\log p(q|T), co przy odwracalnym mapowaniu liczy się jawnie).
  5. Proporcje gałęzi również wynikają z treningu. W danych mamy ~50% przykładów z elbow up i ~50% z elbow down (dla pozycji w obszarze, gdzie istnieją obie). Sieć dopasowuje wielkość regionów w przestrzeni szumu tak, żeby częstość trafień odpowiadała częstości w danych.

Inaczej mówiąc: nie programujemy podziału. Podział wyłania się jako efekt uboczny minimalizacji błędu na danych. Sieć „sama odkrywa", że czasem trzeba dwóch rozwiązań, czasem ośmiu — bo dane treningowe ją do tego zmuszają.

Dlaczego nie zrobiłem prawdziwego IKFlow w demie? Wymagałby wytrenowania sieci z coupling layers (~kilkaset linii kodu + kilkanaście minut treningu na GPU + przygotowane dane). W naszym module pokazujemy ideę — efekt, który student powinien zrozumieć — bez angażowania pełnej infrastruktury ML. Naiwny MLP w sekcji „Laboratorium" niżej jest natomiast prawdziwy i trenujemy go od zera w przeglądarce.

Magia matematyczna w środku — żeby sieć była odwracalna i żeby umiała przekształcać dowolny rozkład w inny, używa się specjalnych warstw (coupling layers) z obliczalnym wyznacznikiem jakobianu. Szczegóły są dla zaawansowanych — tu wystarczy intuicja: sieć uczy się gładkiego, odwracalnego mapowania szum ↔ rozwiązania.

Implementacja IKFlow wymaga GPU i bibliotek typu FrEIA albo nflows — w naszej aplikacji pokazujemy ideę, nie pełny model. Literatura: Ames, Limb & Srinivasa, „IKFlow: Generating Diverse Inverse Kinematics Solutions", IROS 2022.

Krok 4

Diffusion models — odzyskiwanie odpowiedzi z szumu

Pomysł w jednym zdaniu: zacznij od czystego szumu i stopniowo go „odszumiaj" w wiele małych kroków, aż wyłoni się prawidłowa odpowiedź.

Analogia: wyobraź sobie zaszumione, prawie nieczytelne zdjęcie. Aplikacja w telefonie powoli usuwa ziarno — krok po kroku obraz staje się wyraźniejszy. Po 50 krokach widać twarz. Diffusion models robią to samo, tylko zamiast obrazu odzyskują kąty robota.

Przesuń poniższy slider od 0 do 50. Zobaczysz, jak losowe punkty (szum) stopniowo przesuwają się ku swoim klastrom (rozwiązania IK):

krok dyfuzji:0/50

Krok 0: czysty losowy szum. Sieć dostaje te kropki na wejściu.

Diffusion: krok po kroku usuwamy szum, aż wyłonią się prawidłowe qq

Krok 0: czysty szum, kropki rozproszone wszędzie. Krok 50: kropki uformowały dwa wyraźne klastry — to są dwie gałęzie rozwiązania. Sieć diffusion, w każdym z 50 kroków, robi tylko jedno: zgaduje, jak trochę przesunąć każdy punkt, żeby było mniej szumu. Dziesiątki kolejnych takich małych kroków sumują się w pełny ruch z szumu do prawdziwej odpowiedzi.

Konkretny przykład — manipulator 2R z animacją

Trochę bardziej namacalna wersja — sześć egzemplarzy tego samego robota 2R (dwa ogniwa obrotowe). Każdy startuje z innego losowego ułożenia (czysty szum w kątach q1,q2q_1, q_2). Slider 0 → 50 to kroki dyfuzji. W każdym kroku model robi mały „krok odszumiania" — kąty zbliżają się do jednej z dwóch poprawnych konfiguracji.

krok dyfuzji:0/50

Krok 0: czysty losowy szum. Każdy z sześciu robotów ma losowe (q₁, q₂) — koniec ramienia jest gdzieś w przestrzeni, ale nie na celu.

Przesuń slider od 0 do 50 i obserwuj. Krok 0: każdy robot leży inaczej, końcówki rozproszone losowo, kolory blade (model nie wie jeszcze, w którą stronę pójdzie). Mniej więcej przy kroku 20–30 widać, że pewne roboty już idą w stronę elbow up (zielone), a inne — elbow down (fioletowe). Krok 50: wszystkie sześć trafiło końcówką w czerwoną kropkę.

Kluczowa obserwacja: diffusion nie zna celu z góry — tylko przez wiele małych kroków „szlifuje" odpowiedź. To jest jak rzeźba odsłaniana z bryły kamienia: każde uderzenie dłutem usuwa odrobinę zbędnego materiału. Po dziesiątkach uderzeń wyłania się postać.

Dla Pumy z 8 gałęziami wyglądałoby to identycznie, tylko z sześcioma kątami zamiast dwóch i ośmioma możliwymi „celami" zamiast dwóch. Czas inferencji: 50 forward passów × ~0.3 ms = 15 ms na zapytanie — wolniejsze niż IKFlow, ale wciąż w czasie rzeczywistym.

Plusy:

  • Bardzo wyraziste — radzą sobie z niezwykle skomplikowanymi rozkładami (multi-modalność wszelkiego rodzaju).
  • Stabilny trening — łatwiej niż MDN czy GAN-y.
  • State-of-the-art w generatywnym modelowaniu obrazów (DALL·E 2, Stable Diffusion), wideo (Sora) i robotyce (planowanie ruchu).

Minusy:

  • Sampling jest iteracyjny — ~50 forward passów na jedną odpowiedź. Pojedyncze IK trwa kilkanaście milisekund (vs ~mikrosekund dla IKFlow).
  • Dla zwykłego IK to zbyt drogie — IKFlow wystarcza.
  • Diffusion „odgrywa swoje" w planowaniu trajektorii, gdzie generujemy całą sekwencję ruchów q1:Tq_{1:T} naraz — wtedy iteracyjny sampling przestaje być wadą.

Literatura: Janner et al. „Planning with Diffusion", ICML 2022; Pearce et al. „Imitating Human Behaviour with Diffusion Models", ICLR 2023.

Laboratorium — MLP od zera

Trenujemy najprostszą sieć MLP w czystym TypeScripcie — bez zewnętrznych bibliotek ML. Cała implementacja (~200 linii kodu) w src/lib/ml/mlp.ts: forward pass, backpropagation, optymalizator Adam. Po treningu oceniamy predykcję na bieżącej pozie TT^* i porównujemy z wynikiem hybrydy NN → DLS.

Konfiguracja przegubów

θ₁2.8°
θ₂-77.7°
θ₃6.8°
θ₄8.5°
θ₅-19.3°
θ₆172.0°

Poza efektora (T₀⁶)

Pozycja [m]
x = 0.5000
y = 0.1500
z = 0.3000
Orientacja RPY [°]
R = 0.00
P = 90.00
Y = 0.00

Poza docelowa T*

Pozycja [m]
Orientacja RPY [°]

Hiperparametry treningu

Krótka ściągawka — która metoda do czego?

MetodaIdea w jednym zdaniuKiedy używać
Naiwny MLPSieć zwraca jedną odpowiedźGdy potrzebujesz tylko warm startu (potem dopinanie DLS-em)
MDNSieć zwraca rozkład gaussowski (kilka „garbów")Gdy chcesz znać wszystkie warianty + ich prawdopodobieństwa
IKFlowSieć przekształca szum w poprawne odpowiedziStandard nowoczesnego learning-based IK; szybkie, multi-modalne
DiffusionStopniowe odszumianie szumu w odpowiedźPlanowanie trajektorii; gdy IKFlow nie radzi sobie z trudnymi rozkładami

Praktyczne wnioski

  • Naiwny MLP sam w sobie jest edukacyjny, nie produkcyjny — błąd pozycji 102\sim 10^{-2} m po krótkim treningu. W praktyce nie używa się go samodzielnie.
  • Hybryda NN → klasyczny solver — łączy szybkość wnioskowania sieci z dokładnością iteracji. Warm start często zmienia trudne przypadki DLS w trywialne.
  • MDN / IKFlow — niezbędne, jeśli zależy nam na różnorodności rozwiązań (np. planowanie sięgania w ciasnych przestrzeniach, gdzie jedno rozwiązanie może być zablokowane kolizją).
  • Trening — jakość sieci w IK silnie zależy od jakości datasetu. Losowe konfiguracje qq dają pokrycie konfiguracji, ale nierównomierne pokrycie przestrzeniSE(3)SE(3). Profesjonalne podejścia uzupełniają: próbkowanie warstwowe, importance sampling, augmentacja syntetyczna (trajektorie, kolizje).

Limitacja modułu — co byłoby w wersji produkcyjnej

Moduł pokazuje trenowalnego MLP w przeglądarce bez GPU. W realnej dydaktyce dodatkowo:

  • Zbiór ewaluacyjny (holdout) z osobnych poz; mierzona success rate, nie tylko MSE.
  • Porównanie z IKFlow jako golden baseline (mały wstępnie wytrenowany model w ONNX, uruchamiany przez ONNX Runtime Web).
  • Trening architektur alternatywnych (transformer w wejściu poz, rezydualne bloki).