Werkplaats / De werkplaats
In jouw eigen tempo
← Klassieke machine learning
MODULE 01 · LES 03

Laat de lijn stap voor stap leren

Leid de MSE-gradiënten af en bouw een trainingslus die beide parameters samen bijwerkt.

55 min3 checkpoints
DIT GA JE ONTDEKKEN
  • Je berekent en controleert de gradiënten van een lineaire regressie.
  • Je traint met gradient descent en vergelijkt de eindparameters met een bekende oplossing.

Je kunt de beste rechte lijn direct uitrekenen. Maar bij een groter model bestaat vaak geen bruikbare gesloten formule. Daarom trainen we dezelfde lijn nu met kleine verbeterstappen: een aanpak die je later ook voor een neuraal netwerk gebruikt.

Je kent de gradiënt als richting van sterkste lokale stijging. Hier zijn de twee instellingen helling w en intercept b; de hoogte van het landschap is de MSE. Beide knoppen beïnvloeden dezelfde voorspellingen, dus hun gevoeligheden moeten bij dezelfde oude instellingen horen.

1. Van een voorspelfout naar twee gevoeligheden

Neem twee trainingsparen: x = [−1, 1] en y = [−1, 3]. Ze liggen op de lijn met w = 2 en b = 1. Start echter bij w = b = 0: beide voorspellingen zijn nul en de MSE is (1 + 9)/2 = 5.

Voor de afgeleide schrijven we de fout nu als voorspelling min doel, het omgekeerde teken van het residu. Het kwadraat verandert daardoor niet. Noem deze fout e en het gemiddelde verlies L:

ei=wxi+b−yiL=1n∑ei2

i wijst een meetpaar aan en n is het aantal paren. De afgeleide van een kwadraat geeft 2e. De kettingregel vermenigvuldigt dat voor w met x, en voor b met 1; de factor 1/n blijft staan.

∂L∂w=2n∑xiei∂L∂b=2n∑ei

Het teken ∂ benoemt een partiële afgeleide: verander één instelling terwijl de andere vast blijft. Bij ons startpunt is e = [1, −3]. De producten x maal e zijn [−1, −3], dus dw = −4 en db = −2.

def lijn_gradient(xs, ys, w, b):
    if not xs or len(xs) != len(ys):
        raise ValueError("Ongeldige paren")
    sw = 0
    sb = 0
    for i in range(len(xs)):
        e = w * xs[i] + b - ys[i]
        sw = sw + xs[i] * e
        sb = sb + e
    return (2 * sw / len(xs),
            2 * sb / len(xs))

xs = [-1, 1]
ys = [-1, 3]
print(lijn_gradient(xs, ys, 0, 0))
# (-4.0, -2.0)

Maak deze functie voor kleine eindige waarden; lege of ongelijk lange lijsten geven ValueError. Gebruik dezelfde foutconventie in beide componenten. De controle vergelijkt je gradiënt ook met centrale verschillen, zoals in de eerdere gradiëntenles.

01
CHECKPOINTBereken beide gevoeligheden op dezelfde plek

2. Eén gezamenlijk vertrekpunt

Trek een klein deel van iedere afgeleide af. Met leersnelheid eta = 0.1 wordt w gelijk aan 0 − 0.1 × (−4) = 0.4, en b aan 0 − 0.1 × (−2) = 0.2. De negatieve gevoeligheden betekenen hier dat verhogen het verlies lokaal verlaagt.

wnieuw=w−ηdwbnieuw=b−ηdb

η is de Griekse letter eta; dw en db zijn de twee zojuist berekende gevoeligheden. Veelgemaakte fout: eerst w wijzigen en daarna db opnieuw berekenen. Dan gebruik je twee verschillende vertrekpunten en voer je een ander algoritme uit.

def lijn_stap(xs, ys, w, b, eta):
    dw, db = lijn_gradient(xs, ys, w, b)
    return (w - eta * dw, b - eta * db)

w, b = lijn_stap(xs, ys, 0, 0, 0.1)
print(w, b)  # 0.4 0.2

De toewijzing dw, db = ... pakt de twee waarden uit het teruggegeven paar. Maak lijn_stap met één gradiëntberekening. Bewaar de invoerlijsten; alleen het nieuwe parameterpaar wordt teruggegeven.

02
CHECKPOINTWerk samen bij, niet achter elkaar opnieuw berekenen

3. Controleer verbetering met het echte verlies

Met de nieuwe lijn zijn de voorspellingen −0.2 en 0.6, dus e = [0.8, −2.4]. De MSE is (0.64 + 5.76)/2 = 3.2. Bereken het verlies zelf, zodat je niet alleen naar bewegende parameters kijkt.

def lijn_loss(xs, ys, w, b):
    totaal = 0
    for i in range(len(xs)):
        e = w * xs[i] + b - ys[i]
        totaal = totaal + e ** 2
    return totaal / len(xs)

print(lijn_loss(xs, ys, w, b))  # 3.2
wg, bg = lijn_stap(xs, ys, 0, 0, 1.1)
print(lijn_loss(xs, ys, wg, bg))  # 7.2

Een stap met eta = 1.1 verhoogt het verlies naar 7.2: de juiste richting garandeert geen goede stapgrootte. Verander eta tijdens onderzoek, maar vergelijk steeds dezelfde begininstellingen en trainingsdata.

Maak train_lijn(xs,ys,eta,stappen). Start bij w = b = 0 en geef (w,b,history) terug; history bevat eerst het beginverlies en daarna één waarde per update. Weiger lege/ongelijke lijsten en een negatief aantal stappen met ValueError; stappen is een geheel getal.

w = 0.0
b = 0.0
history = [lijn_loss(xs, ys, w, b)]
for i in range(2):
    w, b = lijn_stap(xs, ys, w, b, 0.1)
    history.append(lijn_loss(xs, ys, w, b))
print(w, b)    # ongeveer 0.72, 0.36
print(history)  # ongeveer [5, 3.2, 2.048]

Bij nul stappen blijft history één lang. Meer kleine stappen brengen deze lijn richting (2, 1), maar een dalende trainingsloss bewijst nog geen goede voorspellingen op onbekende data.

03
CHECKPOINTBewaar het verloop om problemen te zien

Wat neem je mee?

  • De kettingregel geeft één gevoeligheid voor helling en één voor intercept.
  • Beide updates gebruiken hetzelfde oude parameterpaar.
  • Bewaar het werkelijk berekende verlies, inclusief het beginpunt, en controleer de stapgrootte.

Hierna vergelijk je je eigen regressie met een bibliotheekmodel op apart gehouden testdata.

Verder lezen & bronnen

Inhoud gecontroleerd op 2026-10-28.
DE VOLGENDE STAPJe eigen regressie naast scikit-learn
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
Bereken beide gevoeligheden op dezelfde plek

Schrijf lijn_gradient(xs,ys,w,b) als (dw,db).

Een hint

dw gebruikt fout*x, db alleen fout. Deel beide sommen door n en vermenigvuldig met twee.

02
Werk samen bij, niet achter elkaar opnieuw berekenen

Schrijf lijn_stap(xs,ys,w,b,eta) als de twee nieuwe parameters.

Een hint

Roep lijn_gradient één keer aan en trek beide updates van de oude waarden af.

03
Bewaar het verloop om problemen te zien

Schrijf train_lijn(xs,ys,eta,stappen) als (w,b,history).

Een hint

Begin met één losswaarde. Voeg na iedere lijn_stap de nieuwe loss toe.

Vastgelopen? Bekijk de voorbeeldoplossing
def lijn_gradient(xs, ys, w, b):
    if not xs or len(xs) != len(ys):
        raise ValueError("Ongeldige paren")
    sw = 0
    sb = 0
    for i in range(len(xs)):
        e = w * xs[i] + b - ys[i]
        sw = sw + xs[i] * e
        sb = sb + e
    return (2 * sw / len(xs), 2 * sb / len(xs))


def lijn_stap(xs, ys, w, b, eta):
    dw, db = lijn_gradient(xs, ys, w, b)
    return w - eta * dw, b - eta * db


def lijn_loss(xs, ys, w, b):
    totaal = 0
    for i in range(len(xs)):
        e = w * xs[i] + b - ys[i]
        totaal = totaal + e ** 2
    return totaal / len(xs)


def train_lijn(xs, ys, eta, stappen):
    if not xs or len(xs) != len(ys) or stappen < 0:
        raise ValueError("Ongeldige training")
    w = 0.0
    b = 0.0
    history = [lijn_loss(xs, ys, w, b)]
    for i in range(stappen):
        w, b = lijn_stap(xs, ys, w, b, eta)
        history.append(lijn_loss(xs, ys, w, b))
    return w, b, history

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