- Je onderscheidt vaste feature-extractie van fine-tuning.
- Je traint een nieuwe classifier en bewijst dat bevroren parameters niet veranderen.
- Je behandelt trainingstoestand en dataverschuiving apart van de updatekeuze.
Je hebt een encoder die beeldpatronen herkent, maar voor een nieuwe taak weinig voorbeelden. Alles opnieuw leren kan bruikbare kennis weggooien. Bij transfer learning hergebruik je een representatie en leer je bijvoorbeeld alleen een nieuwe classifier erbovenop.
Zie de encoder als een meetinstrument en de classifierhead als een beslisregel op die metingen. Je kunt het instrument vastzetten of voorzichtig mee aanpassen, fine-tuning genoemd. Onze kleine arrays zijn gecontroleerde oefenwaarden, geen voorgetraind model; in een echt project kun je de feature-extractor uit je CNN hergebruiken.
1. Bereken features met een vaste encoder
De encoder krijgt invoermatrix X, gewichten E en bias c en levert features H. Elke rij blijft één voorbeeld. Voor deze oefening gebruiken we tanh als activatie; bevriezen betekent dat de encodergewichten niet worden bijgewerkt.
Neem X=[[0],[1]], E=[[atanh(0.5)]] en c=[0]. Atanh is hier de inverse van tanh: E≈0.549306, zodat de features precies [[0],[0.5]] worden. Je verandert de oorspronkelijke invoer en encoder niet om die nieuwe waarden te maken; de twee rijen zijn twee voorbeelden die straks ieder een label krijgen.
import numpy as np
x = np.array([[0.],[1.]])
e = np.array([[np.arctanh(.5)]])
c = np.array([0.])
features = np.tanh(x@e+c)
print(np.round(features,6).tolist())
# [[0.],[.5]]
Maak frozen_features(x,encoder) met encoder-sleutels w en b. Zet de rekenarrays vóór het matrixproduct om naar floating point, zodat gehele getallen niet ongemerkt overlopen; retourneer een nieuwe array en behoud x en encoder. Vaste features kun je hergebruiken als encoder, preprocessing en evaluatiemodus gelijk blijven; noteer daarom modelversie en voorbereiding bij een echte featurecache.
012. Leer een nieuwe binaire head
De head vormt een logit z=Hw+b en een sigmoidkans p. Het label y is nul of één. Met gemiddelde binaire cross-entropy wordt het foutsignaal per voorbeeld (p−y)/n, waarbij n de batchgrootte is; zo blijft de stap niet afhangen van alleen het aantal kopieën van dezelfde data.
Gebruik nu dezelfde features [[0],[0.5]], met labels [0,1] en nulgewichten voor de nieuwe head. Beide kansen zijn 0.5; de gemiddelde fouten zijn [0.25,−0.25], dus dw=0×0.25+0.5×(−0.25)=−0.125 en db=0. Bij leersnelheid 0.2 wordt w=0.025 en b=0; het verlies vóór die stap is log(2)≈0.693147.
h = features
y = np.array([0.,1.])
w = np.zeros(1); b = 0.
z = h@w+b
p = np.exp(-np.logaddexp(0,-z))
error = (p-y)/len(y)
dw = h.T@error
db = error.sum()
w = w-.2*dw; b = b-.2*db
out = np.round(w,6).tolist()
print(out,round(b,6))
# [0.025] 0.0
Bereken het verlies stabiel als het gemiddelde van logaddexp(0,z)−yz. Rechtstreeks log(sigmoid(z)) nemen kan bij extreme logits log(0) opleveren; een correcte hoge zekerheid mag geen rekenfout veroorzaken. Het volgende voorbeeld toont het verlies op dezelfde twee voorbeelden na de update, lager dan vóór de update; de encoder is nog steeds gelijk.
z = h@w+b
loss = np.mean(
np.logaddexp(0,z)-y*z)
print(round(float(loss),6))
# 0.690032
Maak train_head(features,labels,epochs=300,eta=.2), start w en b op nul en retourneer w,b,history. Bewaar één werkelijk verlies vóór iedere update en behoud features en labels; bij ongebalanceerde labels leert ook de bias mee.
Met nul epochs voer je geen updates uit: w en b blijven nul en history is leeg. De oefenfixture controleert het leermechanisme; kies instellingen in een echte taak met validatie en evalueer daarna pas op aparte testdata.
023. Kies updates per parametergroep
Bevriezen is een expliciete updatekeuze. Neem encodergewichten [1,2], headgewicht [3] en gradiënten [0.5,−0.5] en [2]. Met alleen een headstap van 0.1 wordt de head 2.8 en blijft de encoder [1,2]; met een encoderstap van 0.01 erbij wordt die [0.995,2.005].
encoder = np.array([1.,2.])
head = np.array([3.])
ge = np.array([.5,-.5])
gh = np.array([2.])
print((head-.1*gh).tolist())
print((encoder-.01*ge).tolist())
# [2.8]
# [.995,2.005]
Maak selective_step(params,grads,rates). Alleen namen in rates krijgen hun eigen stap; geef een nieuwe dictionary met nieuwe arrays, ook voor bevroren groepen. Weiger onbekende namen en negatieve leersnelheden met ValueError; een nulrate is een geldige keuze zonder verandering.
Eén echt berekende sigmoid-classifier: invoer 1 → tanh-encoder → head. De beginwaarden zijn een leerfixture, geen voorgetraind model. Iedere stap gebruikt de gradiënt van de huidige binaire cross-entropy.
0 stappen uitgevoerd. Bij een bevroren encoder blijft de feature gelijk terwijl de head leert. Dit experiment heeft één trainingspunt en meet geen generalisatie.
Zet een stap met de encoder bevroren en vergelijk de feature vóór en na de stap. Ontgrendel daarna de encoder: nu verandert ook het meetinstrument. Een kleine encoderstap kan bestaande kennis minder abrupt wijzigen, maar een representatie kan informatie missen die de nieuwe taak nodig heeft; vergelijk daarom op validatie met een passende baseline.
Let ook op toestand buiten gewichten. BatchNorm kan in trainmodus lopende statistieken aanpassen en dropout blijft willekeurig; vaste feature-extractie vraagt daarom een passende evaluatiemodus. Onze NumPy-encoder heeft zulke verborgen toestand niet: gewichten bevriezen en evaluatiemodus zijn in echte frameworks verschillende keuzes.
03Wat neem je mee?
- Vaste features hergebruiken vereist dezelfde encoder en preprocessing.
- De nieuwe head leert uit gemiddelde fouten; history bevat echte verliezen.
- Fine-tuning kiest per groep een update, terwijl je ook andere modeltoestand bewaakt.
Hierna onderzoek je hoe netwerken informatie door een reeks vasthouden.