Werkplaats / De werkplaats
In jouw eigen tempo
← Neurale netwerken
MODULE 04 · LES 12

Leer met een handvol voorbeelden tegelijk

Maak herhaalbare minibatches, bereken een gemiddelde gradiënt en voer een echte SGD-epoch uit.

75 min3 checkpoints
DIT GA JE ONTDEKKEN
  • Je verwerkt iedere trainingsrij eenmaal per epoch, inclusief de laatste kleine batch.
  • Je berekent minibatch-gradiënten en houdt parameters tijdens één update consistent.

Je netwerk heeft veel trainingsvoorbeelden. Wachten op alle rijen vóór iedere update kan duur zijn; één rij geeft juist een grillige richting. Een minibatch laat een klein groepje voorbeelden samen de volgende stap bepalen.

Eén doorgang langs alle trainingsrijen heet een epoch. Deze manier van leren heet stochastic gradient descent (SGD); we gebruiken lineaire regressie om het batchgedrag te volgen. Een groter netwerk gebruikt hetzelfde principe: bereken één gemiddelde batchfout, stuur die terug en werk daarna de parameters bij.

1. Schud rijindices en neem de staart mee

Stel dat je vijf rijen hebt en batches van twee maakt. Dat geeft twee volle batches en een laatste batch met één rij: 2+2+1. Ook die laatste rij hoort bij de epoch; haar weggooien zou betekenen dat een deel van de data niet leert.

Schud de indices, zodat X en y met precies dezelfde rijselectie gekoppeld blijven. Gebruik voor deze oefening random.Random(seed).shuffle met een lokale generator. Dezelfde seed herhaalt de volgorde zonder de globale Python-toevalsreeks te veranderen.

import random
def batchindices(n, batch_size, seed=7):
    if n < 0 or batch_size <= 0:
        raise ValueError('Ongeldige batch')
    indices = list(range(n))
    random.Random(seed).shuffle(indices)
    return [indices[i:i+batch_size]
            for i in range(0,n,batch_size)]

print(batchindices(5, 2, 7))
# [[4, 0], [3, 1], [2]]

De slice neemt een stuk van de lijst; de laatste slice mag korter zijn. Maak batchindices met gehele n en batch_size, weiger negatieve n en niet-positieve batch_size met ValueError, en geef bij n=0 een lege lijst. Iedere geldige rijindex komt eenmaal voor.

Willekeurig schudden veronderstelt hier uitwisselbare trainingsrijen. Bij afhankelijke tijddata of een betekenisvolle volgorde kan dat de taak veranderen. Een nieuwe epoch kan een andere seed krijgen; houd die keuze reproduceerbaar.

01
CHECKPOINTSchud indices, behoud alle rijen

2. Middel over de huidige batch

Het model voorspelt Xw+b. X bevat één voorbeeld per rij, w één gewicht per feature, b is de bias en y de doelvector. Noem het verschil tussen voorspelling en doel r; de MSE-gradiënt gebruikt het aantal rijen van deze batch, niet het totale aantal trainingsrijen.

r=Xw+b−ydw=2nXTrdb=2n∑iri

Neem de eerste batch met featurewaarden [4,0], doelen [9,1] en beginwaarden w=0.5, b=1. De voorspellingen zijn [3,1], dus de fouten [−6,0]. Dan is dw gelijk aan −24 en db aan −6.

import numpy as np
def batchgradient(x, y, w, b):
    x = np.asarray(x, dtype=float)
    y = np.asarray(y, dtype=float)
    w = np.asarray(w, dtype=float)
    r = x@w + b - y
    return 2*x.T@r/len(x),float(2*r.mean())

dw, db = batchgradient([[4],[0]], [9,1],
                       [.5], 1.)
print(dw.tolist(), db)  # [-24.0] -6.0

Maak deze functie voor geldige, niet-lege batches met één of meer features. Geef (dw,db) terug, met dw als NumPy-vector met dezelfde vorm als w en db als getal; verander X, y of w niet. Verdubbel je exact dezelfde batchrijen, dan blijven de gemiddelde gradiënten gelijk; een som zonder deling zou verdubbelen.

02
CHECKPOINTMiddel over de huidige batch

3. De volgende batch ziet de nieuwe parameters

Met leersnelheid 0.1 wordt de eerste update w=0.5−0.1×(−24)=2.9 en b=1−0.1×(−6)=1.6. De volgende batch [3,1] gebruikt deze waarden en levert dw=11.4, db=4.8. Daarna zijn w=1.76 en b=1.12; de laatste rij brengt ze op 1.904 en 1.192.

Rijindicesw na updateb na update
[4,0]2.91.6
[3,1]1.761.12
[2]1.9041.192
x = np.arange(5.).reshape(-1,1)
y = 2*x[:,0] + 1
w, b = np.array([.5]), 1.
for rows in batchindices(5, 2, 7):
    dw, db = batchgradient(
        x[rows],y[rows],w,b)
    w, b = w-.1*dw, b-.1*db
    print(round(w[0],3), round(b,3))
# 2.9 1.6
# 1.76 1.12
# 1.904 1.192

Bereken dw en db allebei vóór je iets bijwerkt. Herbereken je db met de nieuwe w, dan hoort één update bij twee verschillende modellen. Middel de laatste gradiënt over haar eigen ene rij; delen door batch_size=2 zou die bijdrage ten onrechte halveren.

Maak sgd_epoch(x,y,w,b,eta,batch_size,seed=7) met dezelfde loop. Begin met een kopie van w en geef (new_w,new_b) terug, met een nieuwe NumPy-vector met dezelfde vorm als w en een biasgetal. Zo blijft de oorspronkelijke beginarray beschikbaar voor een eerlijke vergelijking.

def sgd_epoch(x,y,w,b,eta,batch_size,
              seed=7):
    x = np.asarray(x, dtype=float)
    y = np.asarray(y, dtype=float)
    w = np.array(w, dtype=float)
    batches = batchindices(len(x),
                           batch_size,seed)
    for rows in batches:
        dw, db = batchgradient(
            x[rows], y[rows], w, b)
        w, b = w-eta*dw, b-eta*db
    return w, b

start = np.array([.5])
eind, bias = sgd_epoch(x,y,start,1.,.1,2)
print(start.tolist())  # [0.5]
print(np.round(eind,3), round(bias,3))
# [1.904] 1.192
03
CHECKPOINTVoer de updates in de juiste volgorde uit

Wat neem je mee?

  • Iedere epoch verwerkt alle rijen, inclusief een kleinere laatste batch.
  • Middel over de actuele batch en bereken alle gradiënten met dezelfde oude parameters.
  • Volgende batches zien nieuwe waarden; volgorde, batchgrootte en leersnelheid beïnvloeden samen het traject.

Hierna voeg je geheugen aan die opeenvolgende zoekrichtingen toe met momentum.

Verder lezen & bronnen

Inhoud gecontroleerd op 2026-10-28.
DE VOLGENDE STAPGeef de zoekrichting een geheugen
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
Schud indices, behoud alle rijen

Schrijf batchindices(n,batch_size,seed=7) als lijst indexlijsten. n mag nul zijn, batch_size moet positief zijn.

Een hint

Gebruik random.Random(seed).shuffle op list(range(n)) en slices met stap batch_size.

02
Middel over de huidige batch

Schrijf batchgradient(x,y,w,b) als (dw,db), voor een niet-lege batch met één regressiedoel per rij. dw is een NumPy-vector met dezelfde vorm als w; db is een getal.

Een hint

Zet arrays om naar NumPy, bereken de foutvector en gebruik de werkelijke len(x).

03
Voer de updates in de juiste volgorde uit

Schrijf sgd_epoch(x,y,w,b,eta,batch_size,seed=7) als (new_w,new_b), met new_w als nieuwe NumPy-vector met dezelfde vorm als w en new_b als getal.

Een hint

Kopieer w, indexeer x en y met iedere indexlijst, en bereken dw/db voordat je bijwerkt.

Vastgelopen? Bekijk de voorbeeldoplossing
import numpy as np
import random

def batchindices(n,batch_size,seed=7):
    if n<0 or batch_size<=0:
        raise ValueError("Ongeldige batchgrootte")
    indices=list(range(n))
    random.Random(seed).shuffle(indices)
    return [indices[i:i+batch_size] for i in range(0,n,batch_size)]

def batchgradient(x,y,w,b):
    x,y,w=[np.asarray(v,dtype=float) for v in (x,y,w)]
    error=x@w+b-y
    return 2*x.T@error/len(x),float(2*error.mean())

def sgd_epoch(x,y,w,b,eta,batch_size,seed=7):
    x,y=np.asarray(x,dtype=float),np.asarray(y,dtype=float)
    w=np.asarray(w,dtype=float).copy()
    for indices in batchindices(len(x),batch_size,seed):
        dw,db=batchgradient(x[indices],y[indices],w,b)
        w=w-eta*dw
        b=b-eta*db
    return w,b

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