- Je onderscheidt voorwaartse ruis, cumulatief signaal en de posteriorvariantie.
- Je verstoort tweedimensionale punten met correcte wortelgewichten en tijdindices.
Een generator die uit ruis een bruikbaar voorbeeld maakt, klinkt alsof hij informatie uit het niets haalt. Diffusie begint juist met een proces dat we volledig begrijpen: steeds wat signaal verzwakken en onafhankelijke ruis toevoegen. Daarna leren we een netwerk welke ruis bij zo'n verstoord voorbeeld hoort.
Denk aan vier kleine wolkjes stippen op papier. Met iedere stap worden de wolkjes waziger en hun oorspronkelijke plaats minder duidelijk. In deze module gebruiken we tweedimensionale punten, zodat je de hele verdeling kunt zien; dit is geen model dat foto's genereert.
Het voorwaartse proces heeft vaste coëfficiënten en leert niets. De omgekeerde generator krijgt straks wel getrainde parameters. Houd die rollen uit elkaar: een formule voor ruis toevoegen is nog geen geleerd model dat die ruis kan verwijderen.
1. Leg vast hoeveel signaal overblijft
Bij stap t krijgt een nieuwe onafhankelijke standaardnormale ruisterm amplitude √β_t. Het vorige punt wordt vermenigvuldigd met √α_t, waarbij α_t gelijk is aan 1−β_t. Vermenigvuldig alle α-waarden tot en met t om ᾱ_t te krijgen: de streep betekent een cumulatief product, geen gemiddelde.
Neem twee stappen met β gelijk aan 0,1 en 0,2. Dan zijn de α-waarden 0,9 en 0,8; na twee stappen is het cumulatieve product 0,72. Na meer stappen wordt de oorspronkelijke bijdrage dus kleiner, ook als iedere afzonderlijke stap maar weinig verandert.
betas = np.array([0.1,0.2])
schedule = diffusion_schedule(betas)
print(schedule['alpha'].tolist())
print(schedule['bar'].round(3).tolist())
# [0.9, 0.8]
# [0.9, 0.72]
Maak diffusion_schedule met beta, alpha, bar, previous en variance als onafhankelijke arrays. Previous bevat het cumulatieve product vóór iedere stap: bij de eerste stap is dat één. Variance is de posteriorvariantie voor de omgekeerde stap, berekend met de volgende vaste formule; de betekenis ervan werken we uit bij de sampler.
De β met tilde heet deze posteriorvariantie; ᾱ met index t−1 is previous. In ons tweede stapje wordt dat 0,2×0,1/0,28, ongeveer 0,071. Bij de eerste stap is previous één en wordt de teller nul.
print(schedule['previous'].tolist())
print(schedule['variance']
.round(3).tolist())
# [1.0, 0.9]
# [0.0, 0.071]
Ons oefenschema heeft twee tot en met 128 stappen en β tussen 0,0001 en 0,2. Deze grenzen houden de kleine berekeningen overzichtelijk en numeriek bruikbaar. Controleer de volledige invoer voordat je het schema teruggeeft; geef bij ongeldige waarden ValueError.
012. Spring rechtstreeks naar een verstoord punt
Je hoeft niet alle voorafgaande ruistrekkingen uit te voeren om een voorbeeld op stap t te krijgen. Trek een standaardnormale ruisvector ε met dezelfde vorm als het schone punt x_0. Combineer beide met de wortels van ᾱ_t en 1−ᾱ_t.
De wortels zijn belangrijk: de formule schaalt standaardafwijkingen, terwijl de kwadraten van die gewichten de variantiebijdragen bepalen. Voor het punt [2;−1] en ruis [0,5;1] op stap twee gebruiken we dus √0,72 en √0,28. Dat geeft ongeveer [1,962;−0,319].
clean = np.array([[2.,-1.]])
noise = np.array([[0.5,1.]])
noisy = forward_diffusion(
clean,noise,[1],betas)
print(noisy.round(3).tolist())
# [[1.962, -0.319]]
De array-index in Python begint op nul: index 1 betekent hier menselijke stap twee. Geef één gehele index per punt. Maak forward_diffusion voor aansluitende matrices schone punten en ruis met twee coördinaten per rij; behoud de invoer en retourneer een eigen floatmatrix.
023. Bekende trainingsruis is geen generatierecept
Tijdens training kennen we zowel het schone punt als de toegevoegde ruis. Dat geeft een exact doel voor de ruisvoorspeller. Tijdens generatie ontbreekt die informatie: de sampler begint bij nieuwe normale ruis en moet met zijn geleerde voorspellingen terugwerken.
Zet de toegevoegde ruis op nul, dan zie je alleen het verzwakte signaal. Dit is een handcontrole van de formule, geen representatieve willekeurige ruistrekking. Een nulvector gebruiken bij alle training zou het netwerk een andere taak leren.
zero_noise = np.zeros_like(clean)
mean = forward_diffusion(
clean,zero_noise,[1],betas)
print(mean.round(3).tolist())
# [[1.697, -0.849]]
Verplaats de tijdstap. De vaste wolkjes worden zwakker en de bijdrage van normale ruis wordt groter.
- Cumulatieve α
- 0,738
- Signaalamplitude
- 0,859
- Ruisamplitude
- 0,512
Menselijke stap 16 gebruikt array-index 15. De gewichten zijn wortels; het zijn geen percentages van de puntcoördinaten.
Elk niveau gebruikt dezelfde getrokken ruis in de gesloten formule. De schuifregelaar voert geen opeenvolgende updates uit; het model leert hier nog niets.
In de volgende les krijgt een klein netwerk alleen het verstoorde punt en zijn tijdstap. Het schone punt en de bekende trainingsruis mogen niet als extra invoerkenmerken meegesmokkeld worden. Anders lijkt het verlies goed zonder dat er een bruikbare generator is geleerd.
Wat neem je mee?
- Het voorwaartse schema ligt vast en gebruikt een cumulatief product.
- Eén directe ruistrekking kan het resultaat op een gekozen stap nabootsen.
- Training kent de ruisdoelen; generatie moet op geleerde voorspellingen vertrouwen.