Modele dyfuzyjne: jak z czystego szumu powstaje obraz
Proces w przód dodający szum, sieć szacująca szum, DDPM i DDIM napisane od zera, osiem skupisk bez kolapsu modów, ile kroków próbkowania potrzeba, porównanie z VAE i GAN na cyfrach oraz droga do obrazów generowanych według tekstu.
Seria Podstawy AI Część 46 z 52
- Mapa sztucznej inteligencji: co jest czym i jak to do siebie pasuje
- Krótka historia sztucznej inteligencji: od Turinga do modeli językowych
- Dane, model, trening, inferencja: podstawowe pojęcia na jednym przykładzie
- Jak czytać zapis matematyczny: symbole, indeksy, sumy i kwantyfikatory
- Zbiory i relacje: klocki, z których składają się definicje w AI
- Funkcje i odwzorowania: dlaczego każdy model AI jest funkcją
- Logika i dowody: reguły, według których rozumuje AI
- Grafy i drzewa: struktura, na której opierają się przeszukiwanie i sieci neuronowe
- Algebra liniowa dla uczenia maszynowego: wektory, macierze i dlaczego AI głównie mnoży
- Pochodne, gradient i reguła łańcuchowa: jak model ustala, w którą stronę się uczyć
- Spadek gradientu: jak uczy się model
- Czym jest prawdopodobieństwo: od rzutu kostką do aksjomatów Kołmogorowa
- Prawdopodobieństwo warunkowe i twierdzenie Bayesa: jak zmienić ocenę, gdy dowiesz się czegoś nowego
- Zmienna losowa i dystrybuanta: jedna funkcja, która mówi o rozkładzie wszystko
- Funkcja gęstości i funkcja prawdopodobieństwa: dlaczego zmienna ciągła ma w punkcie prawdopodobieństwo zero
- Wartość oczekiwana, wariancja i kowariancja: gdzie leży rozkład, jak jest szeroki i co z czym się wiąże
- Przegląd rozkładów prawdopodobieństwa: Bernoulliego, dwumianowy, Poissona, jednostajny, normalny i wykładniczy
- Prawo wielkich liczb i centralne twierdzenie graniczne: dlaczego średnie zachowują się tak przewidywalnie
- Statystyka i estymacja parametrów: największa wiarygodność, MAP i przedziały ufności
- Teoria informacji: entropia, entropia krzyżowa i dywergencja KL — skąd się biorą funkcje straty
- Przeszukiwanie przestrzeni stanów: BFS, DFS, UCS i A* z animacjami
- Gry i przeciwnik: minimax, przycinanie alfa-beta i Monte Carlo tree search
- Spełnianie ograniczeń: backtracking, propagacja ograniczeń i AC-3 na sudoku i mapie czeskich krajów
- Logika i systemy z bazą wiedzy: wnioskowanie w przód i wstecz, rezolucja i systemy ekspertowe
- Planowanie: STRIPS, świat klocków, heurystyki z relaksacji i planowanie w agentach AI
- Przeszukiwanie lokalne i symulowane wyżarzanie: komiwojażer przez czeskie miasta wojewódzkie
- Algorytmy genetyczne: populacja, krzyżowanie, mutacja i selekcja na plecaku na wycieczkę
- Strategie ewolucyjne i programowanie genetyczne: od reguły jednej piątej do prawa Keplera
- Optymalizacja kolonią mrówek: jak ślad feromonowy znajduje krótką drogę
- Optymalizacja rojem cząstek: bezwładność, własne doświadczenie i doświadczenie roju
- Neuroewolucja: ewolucja wag i kształtu sieci neuronowych oraz algorytm NEAT
- Zbiory rozmyte i funkcje przynależności: jak zapisać „trochę ciepło”
- Logika rozmyta a prawdopodobieństwo: dlaczego „trochę ciepło” nie znaczy „prawdopodobnie ciepło”
- Logika rozmyta: t-normy, implikacje i reguły JEŻELI–TO
- Sterowanie rozmyte: Mamdani i Sugeno od wentylatora po wahadło na wózku
- Klasteryzacja rozmyta: algorytm fuzzy c-means i punkty między skupieniami
- Systemy neuro-rozmyte (ANFIS): reguły rozmyte, które uczą się z danych
- Łańcuchy Markowa: macierz przejścia, rozkład stacjonarny i PageRank
- Ukryte modele Markowa: algorytm Viterbiego i nieuczciwe kasyno
- Sieci bayesowskie: dlaczego trawa jest mokra i co z tego wynika dla zraszacza
- Filtr Kalmana: jak z niedokładnych pomiarów oszacować, gdzie jest samochód i jak szybko jedzie
- Rodzaje uczenia maszynowego: nadzorowane, nienadzorowane, półnadzorowane i inne
- Prompty, kontekst i RAG: jak dać modelowi właściwe informacje
- Wariacyjne autoenkodery (VAE): nowe obrazki z ciągłej przestrzeni latentnej
- Generatywne sieci przeciwstawne (GAN): fałszerz kontra detektyw
- Modele dyfuzyjne: jak z czystego szumu powstaje obraz
- Procesy decyzyjne Markowa: stany, akcje, nagrody i równania Bellmana
- Q-learning i DQN: uczenie się wartości akcji od tabeli po sieć neuronową
- Policy gradient i PPO: uczenie się bezpośrednio polityki
- Jak działa agent AI: model językowy w pętli z narzędziami
- Programowanie z agentami AI: zadanie, testy i kontrola
- Ocena i bezpieczeństwo systemów AI: jak mierzyć i jak pilnować
Pełny program serii Gotowe: 52 z 83 tematów
- Jak działa proces dyfuzji w przód i proces odwrotny.
- Dlaczego dyfuzja uczy się stabilniej niż GAN i ile kosztuje liczba kroków.
- Jak z dyfuzji powstaje generator obrazów według tekstu.
Warto wiedzieć wcześniej: Generatywne sieci przeciwstawne (GAN): fałszerz kontra detektyw, Wariacyjne autoenkodery (VAE): nowe obrazki z ciągłej przestrzeni latentnej
Stable Diffusion, Midjourney, DALL·E 3 i modele generujące wideo opierają się na tym samym pomyśle, a jest on zaskakująco prosty. Zniszczyć obraz jest łatwo — wystarczy stopniowo dodawać do niego szum, aż zostanie sam szum. Gdybyśmy nauczyli się cofać każdy mały krok, moglibyśmy zacząć od czystego szumu i krok po kroku wyłuskać z niego obraz. W tej części zaprogramujemy to od zera i porównamy z VAE i GAN na tych samych danych.
Nawiązuje do części Generatywne sieci przeciwstawne i Wariacyjne autoenkodery.
1. Proces w przód: obraz rozpuszcza się w szumie
Definicja — proces w przód (dyfuzyjny)
Wybiera się liczbę kroków T i harmonogram małych wariancji β1, …, βT. W każdym kroku dodaje się trochę szumu: xt = √(1 − βt) · xt−1 + √βt · ε, ε ~ N(0, I). Złożenie kroków daje postać zamkniętą xt = √ᾱt · x0 + √(1 − ᾱt) · ε, gdzie ᾱt = ∏s ≤ t (1 − βs). Dla dostatecznie dużego t jest ᾱt ≈ 0, a xt to czysty szum.
Kropla atramentu w szklance wody: przez chwilę widać kształt kropli, potem się rozmywa, w końcu woda jest równomiernie zabarwiona i pierwotnego kształtu nie da się rozpoznać. Wzór zamknięty mówi, że do dowolnej chwili można skoczyć bezpośrednio — nie musisz symulować wszystkich kroków.
xt = √ᾱt · x0 + √(1 − ᾱt) · ε
- xt
- zaszumiony obraz w kroku t — dokładnie to, co sieć dostaje na wejściu podczas uczenia
- √ᾱt
- ile zostało z pierwotnego obrazu; ᾱt to iloczyn wszystkich (1 − βs) do kroku t, więc z każdym krokiem maleje
- x0
- pierwotny obraz z danych
- √(1 − ᾱt)
- ile jest w nim szumu; pierwiastki są dlatego, że sumują się wariancje: (√ᾱ)² + (√(1 − ᾱ))² = 1, więc dane o wariancji 1 mają wariancję 1 w każdym kroku, a na końcu płynnie przechodzą w N(0, I)
- ε
- jedna próbka szumu z N(0, I) — właśnie ją sieć będzie zgadywać; to, że ją znamy, czyni z uczenia zwykłą regresję
Twierdzenie — dlaczego wystarczy jeden skok
Jeśli xt−1 = √ᾱt−1 x0 + √(1 − ᾱt−1) ε′ z ε′ ~ N(0, I), to również xt = √ᾱt x0 + √(1 − ᾱt) ε z nowym ε ~ N(0, I).
Dowód. Podstawiamy do jednego kroku: xt = √(1 − βt) √ᾱt−1 x0 + √(1 − βt) √(1 − ᾱt−1) ε′ + √βt ε″. Pierwszy składnik to √ᾱt x0, ponieważ ᾱt = (1 − βt) ᾱt−1. Pozostałe dwa to niezależne szumy normalne o średniej 0; ich suma jest znów normalna, a wariancje się sumują: (1 − βt)(1 − ᾱt−1) + βt = 1 − (1 − βt) ᾱt−1 = 1 − ᾱt. Dla t = 1 teza zachodzi wprost (ᾱ0 = 1), dalej przez indukcję. ∎
Przykład — nasz harmonogram szumu w liczbach
β rośnie liniowo: βt = β1 + (βT − β1) · (t − 1)/(T − 1), czyli o 0,00101 na krok.
| t | βt | ᾱt | √ᾱt (sygnał) | √(1 − ᾱt) (szum) |
|---|---|---|---|---|
| 1 | 0,00010 | 0,9999 | 1,000 | 0,010 |
| 2 | 0,00111 | 0,9988 | 0,999 | 0,035 |
| 25 | 0,02432 | 0,7351 | 0,857 | 0,515 |
| 37 | 0,03643 | 0,5045 | 0,710 | 0,704 |
| 38 | 0,03744 | 0,4856 | 0,697 | 0,717 |
| 50 | 0,04955 | 0,2830 | 0,532 | 0,847 |
| 100 | 0,10000 | 0,0056 | 0,075 | 0,997 |
Drugi wiersz sprawdzimy ręcznie: ᾱ2 = (1 − β1)(1 − β2) = 0,9999 · 0,99889 = 0,99879. Sygnał i szum zrównują się (ᾱ = 0,5) między krokami 37 i 38; w kroku 100 pierwotny obraz ma wagę tylko 0,075 wobec 0,997 dla szumu.
U nas T = 100, a β rośnie liniowo od 0,0001 do 0,1. Udział pierwotnego sygnału √ᾱt wynosi w kroku 25 0,86, w kroku 50 0,53, a w kroku 100 już tylko 0,07.
2. Proces odwrotny: sieć, która zgaduje szum
Definicja — DDPM (denoising diffusion probabilistic model)
DDPM (Jonathan Ho, Ajay Jain i Pieter Abbeel, 2020; pomysł Jaschy Sohl-Dicksteina i in., 2015) uczy sieć εθ(xt, t), by na podstawie zaszumionego obrazu i numeru kroku oszacowała, jaki szum ε został dodany. Funkcja straty to zwykły błąd średniokwadratowy E ‖ε − εθ(√ᾱt x0 + √(1 − ᾱt) ε, t)‖² z losowym t. Próbkuje się od xT ~ N(0, I) wstecz: xt−1 = (xt − βt/√(1 − ᾱt) · εθ) / √(1 − βt) + √βt · z.
Sieć dostaje rozmazane, zaszumione zdjęcie i pytanie „ile jest w nim szumu i jak on wygląda?”. Jeśli umie go oszacować, umie go też trochę odjąć. Powtarzamy to sto razy, za każdym razem z odrobiną świeżej losowości — i z szumu wyłania się obraz. Uczenie jest przy tym zwykłą regresją: żadnego przeciwnika jak w GAN, żadnych kompromisów jak w VAE.
L(θ) = Ex0, t, ε ‖ε − εθ(√ᾱt x0 + √(1 − ᾱt) ε, t)‖²
- ε
- szum, który sami dodaliśmy do obrazu — poprawna odpowiedź, dlatego znamy ją bez jakiegokolwiek etykietowania danych
- εθ(…, t)
- oszacowanie szumu przez sieć; dostaje ona też numer kroku t, bo przy t = 2 ma do znalezienia mało szumu, a przy t = 100 w xt jest prawie sam szum
- √ᾱt x0 + √(1 − ᾱt) ε
- zaszumiony obraz xt wytworzony jednym skokiem — dzięki wzorowi zamkniętemu dla każdego przykładu wystarczy wylosować t i ε, niczego nie symuluje się krok po kroku
xt−1 = 1/√(1 − βt) · (xt − βt/√(1 − ᾱt) · εθ(xt, t)) + √βt · z
- xt
- bieżący zaszumiony stan — zaczyna się od czystego szumu xT ~ N(0, I)
- εθ(xt, t)
- oszacowanie szumu, który jest w xt
- βt/√(1 − ᾱt)
- jak duży kawałek oszacowanego szumu odjąć — tylko część odpowiadająca jednemu krokowi (βt), przeliczona na całkowity poziom szumu √(1 − ᾱt); u nas 0,01 w kroku 1 i 0,10 w kroku 100
- 1/√(1 − βt)
- cofa zmniejszenie z kroku w przód (tam mnożyło się przez √(1 − βt)); w kroku 100 to 1,054
- √βt · z
- świeży szum z ~ N(0, I) (w ostatnim kroku z = 0) — proces odwrotny jest losowy jak proces w przód; bez niego próbki ściągałyby do uśrednionych obrazów. Wielkość √βt to jeden z dwóch wariantów, które wypróbowali Ho i in.
# uczenie (DDPM)
powtórz 16 000×:
dla każdego z 64 przykładów partii:
x₀ = losowy przykład z danych
t = losowo z 1 … 100
ε = wektor liczb losowych z N(0, 1)
x_t = √ᾱ_t · x₀ + √(1 − ᾱ_t) · ε
błąd = ‖ε − ε_θ(x_t, kod(t))‖² / wymiar
krok Adama na średnim błędzie
# próbkowanie (DDPM, 100 kroków)
x = wektor z N(0, 1)
dla t = 100, 99, …, 1:
ε̂ = ε_θ(x, kod(t))
z = wektor z N(0, 1), w kroku t = 1 zero
x = (x − β_t / √(1 − ᾱ_t) · ε̂) / √(1 − β_t) + √β_t · z
zwróć x
Definicja — sinusoidalny kod czasu
Sieć nie dostaje liczby t wprost, lecz osiem liczb kod(t) = (sin(fπt/T), cos(fπt/T)) dla f = 1, 2, 4, 8. Wolne częstotliwości mówią „mniej więcej gdzie w procesie jesteśmy”, szybkie odróżniają sąsiednie kroki; wszystkie liczby leżą między −1 a 1, więc żadne wejście nie przeważa nad pozostałymi.
Dla t = 25 (ćwierć procesu, kąty π/4, π/2, π i 2π): kod(25) = (0,707; 0,707; 1,000; 0,000; 0,000; −1,000; 0,000; 1,000).
Nasza sieć to zwykła sieć wielowarstwowa z części o GAN: na wejściu xt i osiem liczb sinusoidalnego kodu czasu t (jak kodowanie pozycyjne w transformerze), na wyjściu oszacowanie szumu. Na okręgu ma dwie warstwy ukryte po 64 neurony, na cyfrach po 128. Uczy się Adamem przez 16 000 kroków.
3. Osiem skupisk bez kolapsu
Te same dane co w poprzedniej części: osiem skupisk na okręgu. Tak wygląda 400 próbek w trakcie procesu odwrotnego:
Jeszcze w kroku 25 punkty są rozrzucone prawie równomiernie (jakość 3 %), w kroku 10 zarysowuje się okrąg, a w ostatnich dziesięciu krokach punkty zbiegają się w skupiska. Na końcu pokrytych jest wszystkich 8 skupisk, a każde dostało od 28 do 50 z 500 próbek. Jakość (odsetek w odległości do 3σ od środka) wynosi 63 %.
Różnica względem GAN nie leży w jakości, lecz w niezawodności. GAN pokrył wszystkie osiem skupisk tylko w jednym z czterech przebiegów; dyfuzja zrobiła to w jedynym przebiegu, jaki uruchomiliśmy (jeden przebieg to nie dowód, ale jest to zgodne z tym, co wiadomo o dyfuzji). Funkcja straty dyfuzji nie jest bowiem grą, lecz regresją, a sieć jest karana za każdą próbkę danych — żadnego skupiska nie może „olać”.
4. Ile kroków potrzeba
Definicja — DDIM
DDIM (Jiaming Song, Chenlin Meng i Stefano Ermon, 2020) używa tej samej wyuczonej sieci, ale próbkuje deterministycznie i większymi skokami: z oszacowania szumu oblicza oszacowanie czystego obrazu x̂0 = (xt − √(1 − ᾱt) εθ) / √ᾱt, a z niego od razu xs = √ᾱs x̂0 + √(1 − ᾱs) εθ dla dowolnego wcześniejszego czasu s < t.
Zamiast stu drobnych kroczków robimy dwadzieścia większych. W każdym sieć pyta „jak mniej więcej wyglądałby gotowy obraz?” i na tej podstawie przeskakuje kawałek drogi. Gdy skoki są za duże, sieć chybia.
xs = √ᾱs · x̂0 + √(1 − ᾱs) · εθ(xt, t), x̂0 = (xt − √(1 − ᾱt) εθ) / √ᾱt
- xt
- stan, w którym właśnie jesteśmy
- εθ
- oszacowanie szumu — ta sama sieć co w DDPM, niczego nie trenuje się od nowa
- x̂0
- oszacowanie gotowego obrazu: wzór zamknięty procesu w przód odwrócony względem x0; dzieli się przez √ᾱt, które na początku jest małe, więc tu oszacowanie jest najmniej dokładne
- √ᾱs
- ile obrazu ma być w docelowym czasie s — ten sam czynnik co w procesie w przód, tylko dla innego czasu
- √(1 − ᾱs)
- ile szumu ma pozostać; DDIM używa przy tym tego samego oszacowanego szumu i nie losuje nowego — dlatego jest deterministyczne
Przykład — dlaczego dwa kroki nie wystarczą (w uproszczeniu)
DDIM z n krokami idzie po równomiernie rozłożonych czasach: przy 5 krokach 100 → 80 → 60 → 40 → 20 → 0, przy 2 krokach 100 → 50 → 0. Pierwszy skok jest zawsze z t = 100, gdzie √ᾱ100 = 0,075.
Jeśli sieć pomyli się w oszacowaniu szumu o δ, oszacowanie x̂0 pomyli się o δ · √(1 − ᾱ100)/√ᾱ100 = 13,3 δ — błąd wzmacnia się ponad dziesięciokrotnie. Po podstawieniu do skoku do czasu s nowy stan różni się od poprawnego o δ · (√(1 − ᾱs) − √ᾱs · 13,3):
- 5 kroków, skok 100 → 80: czynnik −1,59,
- 2 kroki, skok 100 → 50: czynnik −6,23,
Przy małym skoku złe oszacowanie x̂0 domiesza się z małą wagą √ᾱ80 = 0,19, a kolejne kroki je poprawiają. Przy skoku do połowy procesu wchodzi z wagą √ᾱ50 = 0,53 i zostaje jeden jedyny krok. Rozumowanie śledzi tylko jeden błąd w jednym kroku i zakłada, że poza tym sieć szacuje dokładnie; pokazuje jednak, dlaczego pierwsze, najbardziej zaszumione kroki są najbardziej wrażliwe.
DDIM z 50 krokami osiąga jakość 79 % — więcej niż DDPM ze 100 krokami (63 %), najpewniej dlatego, że nie dodaje losowego szumu, którego nasza niedokładna sieć nie zdąży usunąć w ostatnich krokach. Jeszcze przy 10 krokach to 70 %, przy 5 krokach 60 %. Przy 2 krokach próbkowanie się rozpada (0 %). Liczba kroków to główny koszt dyfuzji: każdy krok to jedno przejście przez sieć, podczas gdy GAN generuje obraz jednym przejściem.
5. Cyfry: porównanie trzech generatorów
Na cyfrach 8 × 8 (piksele przeskalowane do zakresu od −1 do 1) sieć uczy się przez 16 000 kroków.
| model | odległość do najbliższej cyfry | najsłabiej reprezentowana cyfra (z 200) | cyfr z mniej niż 10 próbkami |
|---|---|---|---|
| VAE (k = 8) | 0,054 | 10 | 0 |
| GAN, ziarno 1 | 0,049 | 3 | 3 |
| GAN, ziarno 2 | 0,042 | 0 | 6 |
| dyfuzja (100 kroków) | 0,063 | 12 | 0 |
Tu trzeba być uczciwym: nasza dyfuzja ma odległość 0,063, gorzej niż VAE (0,054) i GAN (0,042). Na obrazkach widać szczątkowy szum. Reprezentacja cyfr jest jednak wyrównana — najrzadsza cyfra ma 12 próbek, podczas gdy GAN z ziarnem 2 całkiem pomija dwie cyfry.
Dlaczego ostrość jest gorsza? Mała sieć wielowarstwowa musi odszumiać obrazy na wszystkich stu poziomach szumu naraz, a to trudniejsze zadanie niż jedno odwzorowanie szum → obraz w GAN. Za to rzetelnie poprawia się wraz z obliczeniami: odległość próbek spadała w trakcie uczenia 0,096 (krok 4 000), 0,082 (krok 8 000), 0,068 (krok 12 000), 0,063 (krok 16 000) (szybkie oszacowanie ze 100 próbek DDIM z 20 krokami). Żadnego kolapsu ani wahań. Prawdziwe modele dyfuzyjne używają splotowych sieci U-Net albo transformerów z setkami milionów parametrów i uczą się tygodniami — a wtedy wygrywają także ostrością.
6. Od cyfr do obrazów według tekstu
- Warunek. Sieć εθ(xt, t, c) dostaje dodatkowo opis c — embedding tekstu z modelu językowego, dołączony przez mechanizm uwagi (część o mechanizmie uwagi). Uczy się na miliardach par obraz–podpis.
- Naprowadzanie bez klasyfikatora (classifier-free guidance, Jonathan Ho i Tim Salimans, 2021). Sieć uczy się z opisem i bez niego, a przy próbkowaniu oszacowanie przesuwa się w kierunku „z opisem minus bez opisu”, wzmocnionym współczynnikiem. Obraz lepiej wtedy odpowiada poleceniu kosztem mniejszej różnorodności.
- Dyfuzja latentna. Dyfuzja działa w małej przestrzeni latentnej autoenkodera, a nie na pikselach — zobacz część o VAE. Dzięki temu Stable Diffusion (2022) działa nawet na karcie do gier.
- Mniej kroków. Lepsze samplery i destylacja (sieć uczy się naśladować wiele kroków w jednym lub kilku) skróciły generowanie z setek kroków do kilku.
ε̃ = εθ(xt, t, ∅) + w · (εθ(xt, t, c) − εθ(xt, t, ∅))
- εθ(…, ∅)
- oszacowanie szumu bez opisu (pusty warunek ∅) — „jak wygląda dowolny obraz”; sieć uczy się go dzięki temu, że podczas uczenia opis jest czasem losowo odrzucany
- εθ(…, c) − εθ(…, ∅)
- o ile zmienia się oszacowanie, gdy sieć zna opis — kierunek, w którym opis ciągnie obraz
- w
- siła naprowadzania: w = 0 ignoruje opis, w = 1 to zwykły model warunkowy, w > 1 przesadnie wzmacnia wpływ opisu — obraz lepiej odpowiada poleceniu, ale próbki są do siebie bardziej podobne
7. Podsumowanie
- Proces w przód stopniowo dodaje szum; do dowolnego kroku można skoczyć wzorem √ᾱ x₀ + √(1 − ᾱ) ε.
- Sieć uczy się szacować dodany szum — zwykła regresja. Generuje się wstecz, od czystego szumu.
- Na ośmiu skupiskach dyfuzja pokryła wszystkie w jedynym przebiegu (DDIM z 50 krokami 79 %); GAN tylko w jednym z czterech przebiegów.
- Mniej kroków (DDIM) przyspiesza próbkowanie; przy 10 krokach jakość wynosi 70 %, przy 2 krokach próbkowanie się rozpada.
- Na cyfrach dyfuzja pokrywa wszystkie cyfry, ale z naszą małą siecią ustępuje pod względem ostrości; poprawia się jednak stabilnie wraz z obliczeniami.
Chcesz to wypróbować sam? Poproś agenta AI, by nauczył mały DDPM na danych dwuwymiarowych (na przykład punktach w kształcie twoich inicjałów) i narysował, jak punkty w trakcie procesu odwrotnego układają się w litery.
Masz inne zdanie? Napisz na support@coden-app.com.