Werkplaats / De werkplaats
In jouw eigen tempo
← Multimodaal & generatief
MODULE 02 · LES 06

Leer welke ruis bij een verstoord punt hoort

Voeg tijdkenmerken toe en bereken ruisvoorspellingen, verlies en eigen netwerkgradiënten.

85 min3 checkpoints
DIT GA JE ONTDEKKEN
  • Je maakt begrensde tijdkenmerken en voorspelt twee ruiscoördinaten met een MLP.
  • Je berekent het gemiddelde kwadratische ruisverlies en alle vier parametergradiënten.

Hetzelfde verstoorde punt kan op verschillende tijdstappen een andere betekenis hebben. Vroeg in het proces lijkt het nog sterk op een schoon voorbeeld; laat in het proces bevat het vooral ruis. Een ruisvoorspeller moet daarom weten naar welke stap hij kijkt.

Denk aan een restaurateur die zowel de beschadigde tekening als de ernst van de beschadiging ziet. Ons kleine netwerk krijgt twee puntcoördinaten en twee tijdkenmerken. Het schone punt en de werkelijk toegevoegde ruis blijven buiten zijn invoer; die ruis is alleen het trainingsdoel.

We hergebruiken de MLP en backprop uit de eerdere track. Het netwerk heeft één tanh-laag en voorspelt twee getallen: de ruis in beide coördinaten. Dit kleine model is bedoeld om de leerregel te begrijpen, niet om de beeldkwaliteit van grote diffusiemodellen na te bootsen.

1. Maak de tijd zichtbaar

Onze arrays gebruiken index j van nul tot en met T−1, voor T ruistappen. Zet die om naar hoek φ met de volgende formule en voeg sin(φ) en cos(φ) toe aan het punt. Zo blijven de tijdwaarden begrensd en heeft iedere toegestane stap een eigen positie op een halve cirkel.

φ=π(j+1)T
features = diffusion_features(
    [[2.,-1.]],[0],2)
print(features.round(3).tolist())
# [[2.0, -1.0, 1.0, 0.0]]

Bij T=2 hoort index nul bij hoek π/2: sinus één en cosinus nul. De laatste stap ligt bij hoek π: sinus nul en cosinus min één. Het zijn gekozen tijdkenmerken, geen geleerde informatie over het schone punt.

Maak diffusion_features met één floatrij per punt: eerst beide coördinaten, daarna sinus en cosinus. Gebruik de gegeven validators voor stappen, gehele indices en matrices. Een verkeerde index moet een fout opleveren; stilzwijgend afronden of de laatste stap afknippen verbergt een programmeerfout.

01
CHECKPOINTVoeg begrensde tijdkenmerken toe

2. Voorspel ruis met gewone netwerkgewichten

Noem de featurematrix F, de verborgen activaties H en de voorspelde ruis E. De eerste matrix W₁ heeft vier invoerrijen; de tweede matrix W₂ heeft twee uitvoerkolommen. Biasvectoren b₁ en b₂ worden per voorbeeld opgeteld.

H=tanh(FW1+b1)
E=HW2+b2

Voor een handvoorbeeld kiezen we één verborgen eenheid met nulgewichten in W₁ en bias nul. Dan is H overal nul. Met W₂=[1,2] en uitvoerbias nul voorspelt het netwerk dus [0,0], ongeacht het punt.

parameters = {
    'w1':np.zeros((4,1)),
    'b1':np.zeros(1),
    'w2':np.array([[1.,2.]]),
    'b2':np.zeros(2)}
prediction = predict_noise(
    parameters,[[2.,-1.]],[0],2)
print(prediction.tolist())
# [[0.0, 0.0]]

Maak predict_noise met deze twee lineaire stappen en tanh ertussen. Een nulvoorspelling betekent niet dat er geen ruis in het voorbeeld zit: het betekent alleen dat deze gewichten nul voorspellen. Het latere bouwproject gebruikt willekeurige initialisatie voor echte training; dit speciaal gekozen handvoorbeeld dient alleen om na te rekenen.

VERKEN HET ZELFDe tijd helpt het netwerk de ruis te schatten

Vergelijk dezelfde onafhankelijke validatiepunten vóór en na training. Het netwerk krijgt het verstoorde punt en twee tijdkenmerken.

Vier invoerkenmerken voor het gekozen punt
xysin φcos φ
1,0131,1640,7070,707
-3-3033Coördinaat 1Coördinaat 2
  • Werkelijke doelruis ε
  • Voorspelde ruis E
Uitvoer vergeleken met het trainingsdoel
VectorCoord. 1Coord. 2
Doel ε-1,241-1,088
Model E-0,2620,106
MSE over alle 8 punten
0,858
Menselijke stap / index
16 / 15

Verlies over acht voorbeelden kan per stap schommelen. Een betere gemiddelde score garandeert geen perfecte voorspelling voor ieder afzonderlijk punt.

Voorberekende uitkomsten van 2000 echte updates met één vaste seed. Het doel ε staat alleen naast de voorspelling ter vergelijking; het is geen netwerkinvoer. In het bouwproject train je zelf.

02
CHECKPOINTVoorspel de twee ruiscoördinaten

3. Reken het verschil terug naar de parameters

Vergelijk E met de werkelijk toegevoegde ruis ε. Bij B punten en twee coördinaten is het verlies L het gemiddelde van 2B gekwadrateerde verschillen. E_i en ε_i zijn de twee ruiscoördinaten van punt i; de gekwadrateerde L2-norm telt beide kwadraten op.

L=12B∑i=1B‖Ei−εi‖22

De afgeleide G per uitvoerwaarde is twee maal het verschil gedeeld door 2B. Dat vereenvoudigt tot het verschil gedeeld door B. Vergeet de feature-as niet bij het middelen.

G=E−εB

Neem voor hetzelfde voorbeeld doelruis [1,−1]. De voorspelling [0,0] heeft verschillen [−1,1], dus gemiddeld kwadraatverlies één. De gradiënt van b₂ is eveneens [−1,1].

loss,gradients = noise_loss_and_grad(
    parameters,[[2.,-1.]],[0],
    [[1.,-1.]],2)
print(loss)
print(gradients['b2'].tolist())
# 1.0
# [-1.0, 1.0]

Voor de verborgen eenheid is de terugkomende gradiënt −1×1+1×2=1. Omdat tanh bij nul afgeleide één heeft, is de W₁-gradiënt gelijk aan de featurevector als kolom. Dit controleert tegelijk de rol van de tijdkenmerken.

print(gradients['w1'].round(3).tolist())
# [[2.0], [-1.0], [1.0], [0.0]]

Maak noise_loss_and_grad met uitvoer (verlies, gradiëntdictionary) voor alle vier parameterarrays. Behoud de invoer; deze functie berekent de leerinformatie maar voert nog geen update uit. Het bouwproject past later Adam toe en bewaart het werkelijk gemeten minibatchverlies.

03
CHECKPOINTBereken MSE en alle gradiënten

Wat neem je mee?

  • De voorspeller krijgt alleen het verstoorde punt en zijn tijdkenmerken.
  • Een gewone MLP kan de ruisvector als regressiedoel leren.
  • Het verlies middelt over punten én coördinaten; backprop levert vier parametergradiënten.

Verder lezen & bronnen

Inhoud gecontroleerd op 2026-10-29.
DE VOLGENDE STAPGenereer stap voor stap vanuit nieuwe ruis
JOUW EXPERIMENT

Probeer het zelf

Python
main.py

Python start zodra jij code uitvoert.

UITVOER

Je uitvoer verschijnt hier.
Alles rekent in je eigen browser.

Jouw checkpoints0 / 3
01
Voeg begrensde tijdkenmerken toe

Schrijf diffusion_features. Geef per punt [x,y,sin(pi*(index+1)/steps),cos(pi*(index+1)/steps)] als zelfstandige floatmatrix. Maximaal 1024 punten, steps is Python-int 2–128 en iedere nulgebaseerde index is geheel en geldig. Gebruik de gegeven validators; ongeldige vormen/types/waarden geven ValueError. Behoud alle invoer en geef zelfstandige uitvoer terug.

Een hint

Werk de handberekening uit en controleer de vormen. Gebruik de zichtbare gegeven validators en hulpfuncties.

02
Voorspel de twee ruiscoördinaten

Schrijf predict_noise. Bereken features, H=tanh(features@w1+b1) en E=H@w2+b2 als zelfstandige floatmatrix. Parameters voldoen aan de gegeven _dd_parameters-validator: verborgen breedte h van 1–64, w1(4,h),b1(h),w2(h,2),b2(2). Gebruik de gegeven helpers; geen verborgen activatiearray boven 65536 cellen. Ongeldige invoer geeft ValueError. Behoud alle invoer en geef zelfstandige uitvoer terug.

Een hint

Werk de handberekening uit en controleer de vormen. Gebruik de zichtbare gegeven validators en hulpfuncties.

03
Bereken MSE en alle gradiënten

Schrijf noise_loss_and_grad. Geef (loss,gradients): Python-floatloss is het gemiddelde van alle 2B gekwadrateerde uitvoerfouten. Reken dout=2*(E-target)/E.size terug door W2 en tanh naar alle vier parameterarrays met exact hun vormen. Geen update uitvoeren. Gebruik de gegeven parameter-/featurevalidators; target_noise heeft dezelfde (B,2)-vorm en invoergrenzen. Ongeldige invoer geeft ValueError. Behoud alle invoer en geef zelfstandige uitvoer terug.

Een hint

Werk de handberekening uit en controleer de vormen. Gebruik de zichtbare gegeven validators en hulpfuncties.

Vastgelopen? Bekijk de voorbeeldoplossing
import numpy as np

# Jouw functies

def diffusion_features(noisy,indices,steps):
    noisy=_dd_array(noisy,2);steps=_dd_int(steps,2,128)
    if noisy.shape[1]!=2 or len(noisy)>1024:raise ValueError('Maximaal1024tweedimensionale punten vereist')
    indices=_dd_indices(indices,len(noisy),steps);phase=np.pi*(indices+1)/steps
    return np.column_stack([noisy,np.sin(phase),np.cos(phase)])

def predict_noise(parameters,noisy,indices,steps):
    parameters=_dd_parameters(parameters);features=diffusion_features(noisy,indices,steps)
    if len(features)*len(parameters['b1'])>65536:raise ValueError('Verborgen laag te groot')
    hidden=np.tanh(features@parameters['w1']+parameters['b1'])
    return hidden@parameters['w2']+parameters['b2']

def noise_loss_and_grad(parameters,noisy,indices,target_noise,steps):
    parameters=_dd_parameters(parameters);features=diffusion_features(noisy,indices,steps);target=_dd_array(target_noise,2)
    if target.shape!=(len(features),2) or len(features)*len(parameters['b1'])>65536:raise ValueError('Passende ruisdoelen en begrensde activaties vereist')
    hidden=np.tanh(features@parameters['w1']+parameters['b1']);prediction=hidden@parameters['w2']+parameters['b2']
    residual=prediction-target;loss=float(np.mean(residual**2));dout=2*residual/residual.size
    dhidden=(dout@parameters['w2'].T)*(1-hidden**2)
    gradients={'w1':features.T@dhidden,'b1':dhidden.sum(axis=0),'w2':hidden.T@dout,'b2':dout.sum(axis=0)}
    return loss,gradients

# Gegeven helpers — gecontroleerde kopieën en grenzen

def _dd_array(value,ndim):
    raw=np.asarray(value)
    if raw.ndim!=ndim or raw.dtype.kind not in 'iuf' or not raw.size or raw.size>65536:
        raise ValueError('Niet-lege reële array met de juiste vorm tot65536cellen vereist')
    result=raw.astype(float,copy=True)
    if not np.isfinite(result).all() or np.max(np.abs(result))>1e6:raise ValueError('Eindige begrensde waarden vereist')
    return result

def _dd_int(value,minimum,maximum):
    if type(value) is not int or not minimum<=value<=maximum:raise ValueError('Geheel getal buiten oefengrens')
    return value

def _dd_indices(indices,count,steps):
    indices=np.asarray(indices)
    if indices.dtype.kind not in 'iu' or indices.shape!=(count,) or np.any(indices<0) or np.any(indices>=steps):
        raise ValueError('Eén gehele nulgebaseerde stapindex per punt vereist')
    return indices.astype(int,copy=True)

def _dd_parameters(parameters):
    if type(parameters) is not dict or set(parameters)!={'w1','b1','w2','b2'}:raise ValueError('Vier expliciete parameterarrays vereist')
    result={name:_dd_array(parameters[name],2 if name.startswith('w') else 1) for name in parameters}
    hidden=len(result['b1'])
    if not 1<=hidden<=64 or result['w1'].shape!=(4,hidden) or result['w2'].shape!=(hidden,2) or result['b2'].shape!=(2,):
        raise ValueError('Aansluitende kleine netwerkvormen vereist')
    return result

Voer de code uit en verklaar voor jezelf waarom elke stap werkt.