- Je interpreteert de gradiënt als een vector van partiële afgeleiden.
- Je controleert gradiënten met centrale verschillen zonder de invoer te wijzigen.
Een eenvoudig woningprijsmodel gebruikt instelbare getallen voor oppervlakte en kamers: de gewichten (weights). Je wilt weten bij welk gewicht een kleine wijziging het verlies het sterkst verandert en welke kant je op moet. Eén helling is dan niet genoeg: iedere instelling krijgt haar eigen gevoeligheid.
Stel je twee knoppen voor boven een komvormig landschap. Een punt vertegenwoordigt de twee instellingen; de hoogte staat voor een verlies (loss), een foutscore die je wilt verlagen. Verander eerst alleen de ene knop, daarna alleen de andere: de kom kan in die richtingen verschillend steil zijn.
Ellipsen verbinden punten met gelijk verlies: L = w₁² + 4w₂². De pijl wijst tegen de gradiënt in.
Begin bij (3, 2), met verlies 25. De ellipsen verbinden punten met dezelfde verlieswaarde; dichter bij het midden liggen lagere waarden. De groene pijl wijst naar lokale daling, niet noodzakelijk rechtstreeks naar het midden.
1. Eén helling voor iedere knop
Onze foutscore neemt het kwadraat van het eerste gewicht plus viermaal het kwadraat van het tweede. Bij (3, 2) is dat 9 + 4 × 4 = 25. Een kleine verhoging naar (3.1, 2) geeft 25.61; naar (3, 2.1) geeft 26.64: de tweede richting stijgt hier sneller.
L benoemt de verliesfunctie; w is de hele vector met twee gewichten. De kleine indices 1 en 2 benoemen haar componenten; in Python staan die op posities w[0] en w[1]. Je kunt L als een gewone functie schrijven die één lijst ontvangt:
def verlies(w):
return w[0] ** 2 + 4 * w[1] ** 2
print(verlies([3, 2])) # 25
print(verlies([3.1, 2])) # ongeveer 25.61
print(verlies([3, 2.1])) # ongeveer 26.64
Houd het tweede gewicht vast en differentieer naar het eerste: de helling is tweemaal het eerste gewicht, hier 6. Houd het eerste vast en differentieer naar het tweede: viermaal tweemaal het tweede gewicht, hier 16. De gradiënt (gradient) verzamelt die partiële afgeleiden in één vector:
Het teken ∇ heet “nabla” en betekent hier “verzamel de hellingen”. Als de gradiënt niet nul is, wijst hij lokaal naar de sterkste stijging bij richtingen met gelijke Euclidische lengte; voor dalen kies je de tegenovergestelde richting. Een nulgradiënt zoals (0, 0) heeft geen richting.
w = [3, 2]
g = [2 * w[0], 8 * w[1]]
print(g) # [6, 16]
Maak grad_kom(w) voor deze tweedimensionale verliesfunctie. Vergeet de factor vier niet: de tweede helling is achtmaal het tweede gewicht.
2. Controleer zonder dezelfde afleidingsregel
Je kunt elke component onafhankelijk schatten met een centraal verschil. Voor het eerste gewicht gebruik je (3.1, 2) en (2.9, 2): verliezen 25.61 en 24.41. Het verschil 1.2 over afstand 0.2 geeft helling 6.
Voor het tweede gebruik je (3, 2.1) en (3, 1.9): verliezen 26.64 en 23.44. Het verschil 3.2 gedeeld door 0.2 geeft 16. De andere component blijft telkens gelijk.
w = [3, 2]
h = 0.1
plus = w.copy()
minus = w.copy()
plus[0] = plus[0] + h
minus[0] = minus[0] - h
verschil = verlies(plus) - verlies(minus)
print(verschil / (2 * h)) # ongeveer 6
print(w) # [3, 2]
.copy() maakt hier een nieuwe lijst met dezelfde getallen. Alleen plus = w zou dezelfde lijst een tweede naam geven; wijzigen via plus zou dan ook w veranderen. Dat is de veelgemaakte fout die de controle op een verkeerd punt laat rekenen.
Bouw numerieke_grad(f, w, h=1e-5) voor h groter dan nul: loop met i over alle componenten, maak binnen die lus twee verse kopieën en varieer alleen positie i. Vraag f bij beide lijsten op en voeg de centrale schatting aan een resultaatlijst toe. Dit werkt ook voor drie of meer gewichten; h heeft dezelfde standaardbetekenis als in Helling.
3. Eén gezamenlijke stap omlaag
Bij gradiëntafdaling (gradient descent) trek je een klein deel van de gradiënt van de gewichten af. Hoe groot dat deel is, bepaalt de leersnelheid (learning rate). De Griekse letter η, uitgesproken “eta”, benoemt deze factor.
De gradiënt hoort helemaal bij de oude vector. Bij w = (3, 2), g = (6, 16) en eta = 0.15 wordt de eerste component 3 − 0.15 × 6 = 2.1. De tweede wordt 2 − 0.15 × 16 = −0.4.
w = [3, 2]
g = [6, 16]
eta = 0.15
nieuw = []
for i in range(len(w)):
nieuw.append(w[i] - eta * g[i])
print(nieuw) # ongeveer [2.1, -0.4]
print(verlies(nieuw)) # ongeveer 5.05
Het nieuwe verlies is 2.1² + 4 × (−0.4)² = 4.41 + 0.64 = 5.05: lager dan 25. Zet in het diagram één stap en vergelijk de getallen. Herstart en probeer leersnelheid 0.4: je krijgt (0.6, −4.4), met verlies 77.8, dus juist méér verlies.
Een negatieve gradiëntrichting garandeert geen verbetering bij een willekeurig grote stap. Het punt kan zelfs buiten het diagram vallen; controleer daarom ook de getallen. Maak update(w, g, eta) voor even lange lijsten, met een nieuwe uitvoerlijst zodat w behouden blijft.
Wat neem je mee?
- De gradiënt verzamelt één helling per gewicht; de andere gewichten blijven bij iedere partiële afgeleide vast.
- Centrale verschillen op verse kopieën leveren een onafhankelijke numerieke controle.
- Bereken de hele gradiënt op de oude gewichten; een te grote leersnelheid kan het verlies verhogen.
Je kunt nu gevoeligheden berekenen en een leersprong zetten. Hierna onderzoek je onzekerheid en kansen; later gebruik je deze hellingen om echte modellen te trainen.