De perceptron vanaf nul: wat een neuron berekent
Bouw een perceptron in pure Python, zie hem falen op XOR en ontdek waarom de convergentiestelling succes belooft zonder te zeggen wanneer.
Op deze pagina
Er staat een lopende band in een fabriek. Onderdelen komen voorbij, en iemand moet beslissen welke worden verzonden en welke teruggaan. Voor elk onderdeel worden twee getallen gemeten: de breedte in millimeter en het gewicht in gram. Meer informatie is er niet.
De voor de hand liggende manier om dit te automatiseren is de regel opschrijven. Accepteer als de breedte onder de 22 millimeter is. Dat werkt totdat de leverancier de legering verandert en de gewichten verschuiven. Dus voeg je een clausule toe. Dan wordt de tolerantie opnieuw onderhandeld en voeg je er nog een toe. Zes maanden later is de functie veertig regels lang, weet niemand meer waarom regel 19 erin staat, en is degene die hem schreef vertrokken.
De andere manier is het onderwerp van deze cursus. Je schrijft de regel niet. Je schrijft de vorm van de regel — een template met gaten erin — en je laat de voorbeelden bepalen wat er in die gaten komt. Die omkering is de hele kern van machine learning, en in dit hoofdstuk is de template zo klein als een template maar kan zijn: twee getallen en een drempel.
Aan het einde heb je in ongeveer twintig regels Python een perceptron geschreven, hem zien slagen, hem zien falen, en beide begrepen. Het bestand dat je hier schrijft is geen speelgoed dat in het volgende hoofdstuk wordt weggegooid: het is de eerste commit in een repository die, negenentwintig hoofdstukken later, eindigt als een agent met een tool-loop en een permissiemodel.
Het model: een gewogen som en een lijn
Link naar de sectie: Het model: een gewogen som en een lijnEen perceptron neemt de metingen, vermenigvuldigt elke meting met een getal dat hij zelf beheert, telt ze op, telt er nog één getal bij op en kijkt naar het teken.
Schrijf de metingen van één onderdeel als een vector — breedte en gewicht. De perceptron houdt een gewichtsvector en een bias bij. Zijn score is
en zijn antwoord is het teken van die score: accepteren als , anders afwijzen.
Dat is het hele model. Alles wat de perceptron ooit over de fabriek zal weten, zit in drie getallen.
De geometrie is het waard om even bij stil te staan, omdat dit het beeld is dat de volgende negenentwintig hoofdstukken blijft werken, zelfs wanneer de vergelijkingen niet meer op één regel passen. De verzameling punten waarvoor — waar de perceptron precies onbeslist is — is een rechte lijn in het vlak. Aan de ene kant is de score positief en wordt alles geaccepteerd; aan de andere kant is hij negatief en wordt alles afgewezen. Leren betekent voor een perceptron: die lijn verplaatsen.
Twee feiten over die lijn volgen rechtstreeks uit de algebra, en allebei doen er later toe:
- staat er loodrecht op. De gewichtsvector ligt niet langs de grens, maar wijst er dwars overheen, naar de geaccepteerde kant.
- schuift hem zonder hem te draaien. Zonder bias zou de lijn gedwongen door de oorsprong gaan, wat voor een fabriek die millimeters en grammen meet een absurde beperking zou zijn — het zou betekenen dat een onderdeel met nul breedte en nul gewicht precies op de grens zit.
De leerregel, en waarom die geen calculus nodig heeft
Link naar de sectie: De leerregel, en waarom die geen calculus nodig heeftDe perceptron begint zonder iets te weten: en . Elke score is nul, dus hij accepteert alles.
Laat hem nu één voorbeeld tegelijk zien. Label de geaccepteerde onderdelen als en de afgewezen onderdelen als . Stel voor elk voorbeeld één vraag: kwam het teken goed uit? De compacte manier om die vraag op te schrijven is controleren of positief is — als het label en de score hetzelfde teken hebben, is hun product positief, en als ze van teken verschillen is het negatief.
Als het antwoord ja is, verander je niets. Als het antwoord nee is, geef je een duwtje:
Dat is het hele algoritme, en het is de moeite waard om te begrijpen waarom dit het juiste duwtje is in plaats van het uit je hoofd te leren. Stel dat een onderdeel geaccepteerd had moeten worden () en de score negatief uitviel. optellen bij verandert de score op datzelfde onderdeel met
wat een positief getal is. De score van het onderdeel dat net fout ging, gaat omhoog, precies de richting die nodig was. De regel is geen heuristiek die iemand heeft gegokt; het is de kleinste verandering die aantoonbaar het geval vóór zich verbetert. Natuurlijk kan hij een ander geval kapotmaken, en daarom ga je nog een ronde.
Let op wat ontbreekt. Er staat nergens een afgeleide. Dat is geen vergissing, en het is het eerste echt belangrijke idee in de cursus.
Wat je zou willen differentiëren is de fout — het aantal verkeerd geclassificeerde onderdelen. Maar dat aantal is een trap: het blijft vlak op 4 terwijl je de lijn een beetje verschuift, en zakt dan naar 3 zodra de lijn een punt kruist. De afgeleide is bijna overal nul en bij de treden ongedefinieerd. Calculus krijgt er geen grip op. De perceptronregel werkt daarom om dat probleem heen door helemaal niet om een helling te vragen: hij vraagt alleen “goed of fout?”, en beweegt in een richting die hij geometrisch kan rechtvaardigen.
Dat is een echte oplossing, en ook een doodlopende weg. In hoofdstuk 2 willen we een loss die ergens vandaan komt in plaats van gekozen wordt, in hoofdstuk 4 een model dat rapporteert hoe zeker het is, en in hoofdstuk 5 iets met meer dan één laag — en geen van die dingen is bereikbaar vanuit een regel die alleen “fout” kent. Een bruikbare helling terugkrijgen is wat de volgende twee hoofdstukken afdwingt. Maar de perceptron mag iets doen wat geen van zijn opvolgers kan: leren zonder enige calculus.
Hem schrijven
Link naar de sectie: Hem schrijvenPure Python, geen NumPy. Lijsten en een loop. NumPy komt in het volgende hoofdstuk, wanneer de rekenkunde niet meer past in een loop die je zou willen lezen; het nu introduceren zou de rekenkunde achter een library verbergen, precies op het moment dat je die wilt zien.
def score(w, b, x):
return w[0] * x[0] + w[1] * x[1] + b
def predict(w, b, x):
return 1 if score(w, b, x) >= 0 else -1
def train(data, epochs=200):
"""Returns (w, b, epoch_it_converged) — or None for the epoch if it never did."""
w, b = [0.0, 0.0], 0.0
for epoch in range(epochs):
mistakes = 0
for x, y in data:
if y * score(w, b, x) <= 0:
w[0] += y * x[0]
w[1] += y * x[1]
b += y
mistakes += 1
if mistakes == 0:
return w, b, epoch + 1
return w, b, NoneDe vier gemarkeerde regels zijn het algoritme. Al het andere is boekhouding.
En de band, met acht onderdelen die ervan zijn gemeten — vier die zijn verzonden en vier die terugkwamen:
BELT = [
((18.0, 47.0), +1), ((19.5, 52.0), +1), ((20.2, 49.0), +1), ((21.0, 55.0), +1),
((24.0, 61.0), -1), ((25.5, 66.0), -1), ((23.0, 70.0), -1), ((26.0, 58.0), -1),
]
w, b, epoch = train(BELT, epochs=200)
print(epoch, w, b)Deze acht onderdelen zijn scheidbaar door een rechte lijn — elk geaccepteerd onderdeel is smaller dan 22 mm en elk afgewezen onderdeel is 23 mm of meer. Een verticale grens bij 22 millimeter doet het werk. Dus de perceptron zou die moeten vinden.
Voer hem uit:
None [-142.1, -13.0] 54.0Tweehonderd epochs, 454 correcties, en hij is niet geconvergeerd. De gewichten zijn groot en hebben het verkeerde teken. Er is iets mis — behalve dat er niets mis is, en de reden is het nuttigste in dit hoofdstuk.
De convergentiestelling, en het getal dat die je echt geeft
Link naar de sectie: De convergentiestelling, en het getal dat die je echt geeftDe perceptron heeft een garantie, bewezen door Novikoff in 1962.1 Als de data überhaupt door een lijn gescheiden kan worden, maakt het algoritme hoogstens
correcties voordat het er geen meer maakt — waarbij de straal van de data is, de lengte van de langste voorbeeldvector, en de margin is: de afstand van het scheidende hypervlak tot het dichtstbijzijnde punt in de uitgebreide ruimte waarin de bias een derde coördinaat is. Daarom verandert het centreren van de data die waarde, terwijl de afstand in millimeters dat niet doet.
De garantie is onvoorwaardelijk en noemt geen epochs, learning rates of geluk. Ze noemt ook geen tijd, en juist die weglating is het punt.
Vul onze getallen in. Rechtstreeks gemeten aan de acht onderdelen, met de bias opgenomen als een constante feature:
| straal | margin | grens | werkelijk gemaakte correcties | |
|---|---|---|---|---|
| ruwe millimeters en grammen | 73,69 | 0,045 | 2.633.550 | 29.870 |
| na aftrekken van het gemiddelde | 12,82 | 0,989 | 168 | 1 |
De stelling is nooit geschonden. Voer de ruwe versie lang genoeg uit en hij convergeert inderdaad — bij epoch 11.976, na 29.870 correcties — ruimschoots binnen zijn grens van 2.633.550, en die kloof is op zichzelf het punt: de stelling begrenst het slechtste geval, niet het typische geval. Hij had simpelweg zestig keer meer epochs nodig dan iemand zou uitzitten.
De tweede rij is dezelfde acht onderdelen, dezelfde twintig regels code, met drie regels toegevoegd om de gemiddelde breedte en het gemiddelde gewicht van elke meting af te trekken. Dat is alles. Dat is de hele verandering. Het verplaatst de puntenwolk zodat die de oorsprong kruist in plaats van rond (22, 57) te zweven, en het effect op de grens is een factor vijftienduizend, omdat beide termen tegelijk verbeteren: daalt van 74 naar 13 omdat de punten niet langer vanaf een ver weg gelegen oorsprong worden gemeten, en stijgt van 0,045 naar 0,989 omdat de margin wordt gemeten tegen een gewichtsvector die geen enorme bias meer hoeft mee te dragen om de data te bereiken.
mean_w = sum(x[0] for x, _ in BELT) / len(BELT) # 22.15
mean_g = sum(x[1] for x, _ in BELT) / len(BELT) # 57.25
CENTRED = [(((x[0] - mean_w), (x[1] - mean_g)), y) for x, y in BELT]
w, b, epoch = train(CENTRED, epochs=200)
print(epoch, w, b)2 [-4.15, -10.25] 1.0Geconvergeerd in twee epochs, met precies één correctie.
Hier zit een echte les in, en die is niet “vergeet niet je inputs te normaliseren”, al moet je dat wel doen. De les is dat een garantie dat een algoritme eindigt je niets vertelt over of jij erbij bent wanneer dat gebeurt, en dat de kloof tussen die twee meestal geometrie is. Dit is de eerste verschijning van een patroon dat je opnieuw tegenkomt in hoofdstuk 6 bij initialisatie, in hoofdstuk 10 bij learning-rate schedules, en in hoofdstuk 13 bij kwantisatie: de wiskunde zegt dat iets mogelijk is, en de engineering bepaalt of het praktisch is. Een cursus die je alleen de stelling leert, geeft je een model dat drie dagen traint en jou de schuld geeft.
Vier punten, één lijn, geen oplossing
Link naar de sectie: Vier punten, één lijn, geen oplossingNu de mislukking die het eerste tijdperk van neural networks beëindigde, en hij past in vier rijen.
Vergeet de fabriek. Neem twee inputs die elk 0 of 1 zijn, en vraag dat het antwoord is wanneer precies één ervan 1 is:
| 0 | 0 | |
| 0 | 1 | |
| 1 | 0 | |
| 1 | 1 |
Dit is XOR — exclusive or. Teken de vier punten op papier voordat je verder leest: drie hoeken van een eenheidsvierkant en de vierde. Markeer de twee diagonale hoeken en als accepteren, en en als afwijzen. Teken nu één rechte lijn met de twee geaccepteerde punten aan de ene kant en de twee afgewezen punten aan de andere.
Dat kan niet. Niet omdat het moeilijk is, of omdat je een slimmer algoritme nodig hebt; de lijn bestaat niet. Drie regels algebra laten zien waarom. Als een perceptron alle vier goed had, dan leveren de vier rijen in volgorde op:
Tel de middelste twee ongelijkheden op: , dus . De laatste zegt . Samen: , wat vereist, wat vereist. En de eerste ongelijkheid zegt . Zo’n bestaat niet, dus zulke gewichten bestaan niet. Geen enkele perceptron, met wat voor getallen dan ook, classificeert XOR.
Voer hem toch uit, want een algoritme zien falen is meer waard dan te horen krijgen dat het zal falen:
100 epochs -> converged=None w=[0.0, 0.0] b=0.0 correct=2/4
1,000 epochs -> converged=None w=[0.0, 0.0] b=0.0 correct=2/4
100,000 epochs -> converged=None w=[0.0, 0.0] b=0.0 correct=2/4Hij divergeert niet, en hij spartelt ook niet rond in de buurt van een redelijk antwoord. Hij cyclet: hij loopt een korte lus door de gewichtsruimte en komt precies terug waar hij begon, voor altijd, met twee van de vier goed — wat je ook zou halen door te gokken. Honderdduizend epochs en honderd zijn niet van elkaar te onderscheiden, omdat het algoritme geen vooruitgang boekt die een langere run zou kunnen afmaken. Vergelijk dat met de band, die na 200 epochs vast leek te zitten en in werkelijkheid naar een echt antwoord toe aan het malen was. Van buitenaf lijken de twee de eerste paar seconden op elkaar. Ze uit elkaar houden, zonder de stelling, is onmogelijk — nog een argument om de stelling te kennen.
Wat Minsky en Papert werkelijk zeiden
Link naar de sectie: Wat Minsky en Papert werkelijk zeidenIn 1969 publiceerden Marvin Minsky en Seymour Papert Perceptrons, een boeklange wiskundige studie naar precies wat dit model wel en niet kan representeren.2 XOR is het meest geciteerde resultaat, en het citaat wordt meestal ingezet als beschuldiging: dat het boek uit rivaliteit of rancune vijftien jaar neural network-onderzoek heeft gedood.
De wiskunde in het boek klopt, en is interessanter dan het XOR-voorbeeld. Minsky en Papert waren niet primair geïnteresseerd in de vraag of één perceptron XOR kon doen; ze waren geïnteresseerd in wat er gebeurt wanneer perceptrons beperkte receptieve velden krijgen — waarbij elke unit slechts een deel van de input ziet — en ze bewezen dat bepaalde globale eigenschappen van een afbeelding, zoals of een figuur verbonden is, op die manier niet kunnen worden berekend, ongeacht hoeveel units je gebruikt. Dat is een echt diep resultaat over localiteit, en het heeft niets te maken met het populaire verhaal.
Het populaire verhaal klopt historisch ook niet. Minsky en Papert bespreken multi-layer perceptrons expliciet en zeggen dat de vraag naar hun kracht open is — ze vermoedden dat uitbreiding van de theorie “steriel” zou zijn, wat een voorspelling is, geen bewijs, en die voorspelling was fout. Wat in 1969 ontbrak was niet het idee om lagen te stapelen; het was een manier om een stack te trainen. De perceptronregel kan dat niet: hij moet weten hoe fout elke unit is, en voor een unit die ergens in het midden begraven zit, is er geen label om mee te vergelijken. Die kloof bleef open totdat backpropagation in 1986 populair werd gemaakt,3 en hem dichten is wat hoofdstuk 5 doet.
De eerlijke samenvatting is dus dit. Het boek bewees een echte beperking van een echt model. De financieringsinstorting van het veld in de jaren zeventig had veel oorzaken, waarvan één was dat de beloften die begin jaren zestig voor perceptrons waren gedaan buitensporig waren geweest. En het technische obstakel was oplosbaar, maar niemand had de tool nog.
Wat bleef bestaan
Link naar de sectie: Wat bleef bestaanDe perceptron is achtenzestig jaar oud en je hebt er net een geschreven. Het is de moeite waard om precies te zijn over welke delen ervan nog in de machine zitten waarmee je deze cursus zult eindigen, want het antwoord is: meer dan je zou denken.
Nog steeds hier. De vorm — vermenigvuldigen met gewichten, optellen, een bias toevoegen, een niet-lineaire functie op het resultaat toepassen — is precies de vorm van één unit in elk neural network in deze cursus, inclusief die binnen een transformer-blok in hoofdstuk 9. De update-bij-fout-regel is stochastic gradient descent in vermomming: het is precies wat je krijgt door de methode van hoofdstuk 3 toe te passen op een specifieke loss function. Incrementeel trainen — een handvol voorbeelden tegelijk in plaats van de hele dataset in één keer — blijft hoe modellen vandaag op elke schaal worden getraind. Hoofdstuk 3 meet waar die afweging werkelijk zit.
Verdwenen. De drempel zelf: in hoofdstuk 4 vervangen door een functie die een waarschijnlijkheid uitvoert in plaats van een oordeel, omdat “afwijzen” en “afwijzen, maar het scheelde weinig” verschillende stukken informatie zijn en het teken dat verschil weggooit. De enkele laag, vervangen in hoofdstuk 5. En handgekozen features: iemand koos breedte en gewicht voor deze band, en die keuze deed meer werk dan het algoritme. Hoofdstuk 8 is waar het model zijn eigen features begint te kiezen.
Waar dit hierna naartoe gaat
Link naar de sectie: Waar dit hierna naartoe gaatDe perceptron liep op twee dingen tegelijk vast, en die blijken hetzelfde te zijn.
Hij kan XOR niet representeren, omdat één lijn niet genoeg is. Dat oplossen betekent lagen stapelen — een eerste laag die de ruimte buigt, een tweede die de lijn in de gebogen ruimte tekent. Dat is hoofdstuk 5.
Maar je kunt een stack niet trainen met de perceptronregel, omdat die alleen “fout” kent, en een unit midden in een netwerk geen eigen label heeft om fout over te zijn. Om een stack te trainen moet je weten hoe fout, en in welke richting, voor elk gewicht — je hebt een helling nodig. En de foutfunctie van de perceptron, de trap, heeft er geen.
Dus vóór de stack moet er een loss function zijn met een bruikbare afgeleide. Ook niet eentje die gekozen is omdat hij handig te differentiëren is: eentje die ergens vandaan komt, die iets waars zegt over de data, en waarvan de gradient uit die betekenis volgt in plaats van achteraf te zijn geconstrueerd om er netjes uit te zien.
Dat is hoofdstuk 2, en het begint met een vraag die de perceptron nooit hoefde te beantwoorden: niet “is dit onderdeel goed?”, maar “hoe waarschijnlijk zijn deze metingen, als dit de waarheid is?”
Bronnen en methode
Link naar de sectie: Bronnen en methodeOok de moeite waard om naast dit hoofdstuk te lezen: Rosenblatts oorspronkelijke paper, The Perceptron: A Probabilistic Model for Information Storage and Organization in the Brain (Psychological Review 65(6), 1958), die leesbaarder is dan zijn reputatie doet vermoeden; McCulloch en Pitts, A Logical Calculus of the Ideas Immanent in Nervous Activity (Bulletin of Mathematical Biophysics 5, 1943), de paper die voor het eerst een neuron modelleerde als een drempel op een gewogen som; de perceptronsectie van Hal Daumé III’s A Course in Machine Learning, die dezelfde update met een andere nadruk afleidt; en hoofdstukken 2 en 3 van Deisenroth, Faisal en Ongs Mathematics for Machine Learning voor de lineaire algebra, als het kader hierboven je naar meer liet verlangen dan het gaf.
Referenties
Link naar de sectie: Referenties-
Novikoff, A. B. J. On convergence proofs for perceptrons. Proceedings of the Symposium on the Mathematical Theory of Automata, vol. 12, pp. 615–622 (Polytechnic Institute of Brooklyn, 1962). De oorspronkelijke formulering en het bewijs van de hierboven gebruikte grens op fouten. ↩
-
Minsky, M. and Papert, S. Perceptrons: An Introduction to Computational Geometry (MIT Press, 1969; expanded edition 1988). Het XOR-resultaat is elementair; de substantiële resultaten gaan over order-limited predicates en verbondenheid. ↩
-
Rumelhart, D. E., Hinton, G. E. and Williams, R. J. Learning representations by back-propagating errors. Nature 323, pp. 533–536 (1986). ↩