Waar een verliesfunctie vandaan komt: likelihood, geen conventie
Drie lijnen op dezelfde twintig metingen en drie scoreregels met drie winnaars. Kwadratische fout is een keuze.
Op deze pagina
Het mes dat de onderdelen snijdt, slijt. Tijdens een dienst van tien uur verliest het genoeg scherpte dat de onderdelen aan het einde van de band een fractie van een millimeter breder zijn dan aan het begin, en zodra ze boven 23,5 millimeter komen, keurt de inspectie ze af. Niemand in de fabriek weet wanneer dat gebeurt. Wat ze hebben is een schuifmaat, een notitieboek en twintig metingen van afgelopen dinsdag: de uren sinds het mes is vervangen, en de breedte van het onderdeel op dat moment.
Iemand trekt een lijn door de punten. Iemand anders tekent er een die net iets anders loopt. Een derde tekent een derde. Alle drie zien er op papier redelijk uit, en ze verschillen meerdere uren in hun antwoord op de vraag wanneer het mes moet worden vervangen — in deze fabriek het verschil tussen een rustige week en een afgekeurde batch.
Welke lijn is beter?
Zo gesteld heeft die vraag geen antwoord. Geen moeilijk antwoord — helemaal geen antwoord. "Beter" is geen eigenschap van een lijn zoals haar helling dat is; het is een eigenschap van een lijn samen met een regel om lijnen te scoren, en totdat iemand die regel opschrijft, valt er niets te berekenen. Dit hoofdstuk neemt die zin serieus, en eindigt met de ontdekking dat de meest gebruikte regel in machine learning geen conventie is, maar het gevolg van een claim over de wereld — een claim die je kunt testen, en die soms onwaar is.
Eén bekentenis vóór de eerste regel code. Deze twintig metingen komen niet uit een echte fabriek: ik heb ze gegenereerd uit een lijn die ik koos, , plus willekeurige ruis met een spreiding van ongeveer een tiende millimeter. Dat doet ertoe, omdat alles hieronder gaat over de vraag of een methode een waarheid terugvindt, en de enige manier om dat te controleren is de waarheid vooraf kennen. Dus: 0,30 millimeter per uur is het antwoord achter in het boek. Je mag het niet gebruiken, alleen ertegen controleren.
Drie regels, drie winnaars
Link naar de sectie: Drie regels, drie winnaarsHier zijn de metingen en de drie lijnen, op drie manieren gescoord: kwadratische fout, waar iedereen naar grijpt; absolute fout, waar een statisticus misschien voor kiest; en grootste fout, waar de machinist voor zou kiezen, omdat de inspecteur niets geeft om je gemiddelde — hij keurt dat ene onderdeel af dat buiten tolerantie valt.
NumPy verschijnt hier, één hoofdstuk na de pure-Python perceptron, om één reden: aan het einde van dit hoofdstuk evalueren we vierhonderdduizend kandidaat-lijnen tegen elk twintig metingen, en een Python-loop is daarvoor het verkeerde gereedschap. Het is ook de notatie waarin elke hieronder geciteerde bron is geschreven.
import numpy as np
# Hours since the blade was changed, and the width of the part measured then.
SHIFT = np.array([
(0.5, 20.17), (1.0, 20.28), (1.5, 20.53), (2.0, 20.61), (2.5, 20.69),
(3.0, 20.94), (3.5, 21.21), (4.0, 21.31), (4.5, 21.27), (5.0, 21.35),
(5.5, 21.58), (6.0, 21.80), (6.5, 21.67), (7.0, 22.07), (7.5, 22.10),
(8.0, 22.31), (8.5, 22.48), (9.0, 22.66), (9.5, 22.90), (10.0, 23.13),
])
h, y = SHIFT[:, 0], SHIFT[:, 1]
LINES = {"A": (20.10, 0.26), "B": (20.20, 0.28), "C": (20.30, 0.26)}
for name, (a, b) in LINES.items():
r = y - (a + b * h)
print(f"{name} mean square {np.mean(r**2):.5f}"
f" mean absolute {np.mean(np.abs(r)):.5f}"
f" worst {np.max(np.abs(r)):.3f}") De grootheid in de gemarkeerde regels is het residu: wat de lijn zei min wat de schuifmaat zei, één getal per meting. Elke scoreregel in dit hoofdstuk, en elke verliesfunctie in de achtentwintig hoofdstukken erna, is een manier om een lijst residuen samen te persen tot één enkel getal. Ze verschillen alleen in hoe ze persen.
A mean square 0.02699 mean absolute 0.12600 worst 0.430
B mean square 0.02524 mean absolute 0.13700 worst 0.350
C mean square 0.03179 mean absolute 0.15000 worst 0.320Lees de kolommen, niet de rijen. Kwadratische fout zegt B, absolute fout zegt A, grootste fout zegt C: drie regels, drie winnaars, op dezelfde twintig punten.
Ik heb deze drie lijnen zo gekozen dat ze het oneens zouden zijn, en dat moet ik gewoon zeggen. Het punt is hoe makkelijk dat was — een paar minuten zoeken over redelijk ogende intercepts en hellingen levert honderden van zulke drietallen op. De rangorde is een eigenschap van de regel die je kiest, geen feit over de lijnen, dus de regel is geen implementatiedetail: hij is de definitie van het probleem. Dat roept de vraag op waarvoor dit hoofdstuk bestaat: op welke gronden kies je hem?
Eén parameter, en een vallei
Link naar de sectie: Eén parameter, en een valleiEerst iets kleiners, want er zijn niet drie lijnen maar oneindig veel. Neem voorlopig kwadratische fout, omdat iedereen dat doet, en verklein het probleem tot één getal met de truc die de perceptron elfduizend epochs bespaarde in Hoofdstuk 1: trek van beide kolommen het gemiddelde af. Zodra de puntenwolk rond de oorsprong gecentreerd is, loopt de beste lijn onder kwadratische fout precies door de oorsprong — dus de intercept ligt vast en alleen de helling moet nog worden gekozen.
u, v = h - h.mean(), y - y.mean() # 5.25 hours, 21.553 mm
def mse(theta):
return np.mean((v - theta * u) ** 2)
grid = np.arange(0.0, 0.6001, 0.001)
curve = np.array([mse(t) for t in grid])
print(grid.size, "candidates ->", f"theta={grid[curve.argmin()]:.3f}", f"mse={curve.min():.6f}")601 candidates -> theta=0.293 mse=0.010115Zeshonderdeen kandidaat-hellingen, één winnaar: 0,293 millimeter per uur tegenover een waarheid van 0,300. Twintig rumoerige metingen en een for-loop kwamen binnen een honderdste millimeter per uur — tweeënhalve procent.
Het interessante is niet de winnaar, maar de vorm van de zoekruimte. Print de hele curve, gedraaid zodat de loss van links naar rechts loopt:
ts = np.arange(0.0, 0.6001, 0.04)
ls = np.array([mse(t) for t in ts])
for t, l in zip(ts, ls):
col = round(l / ls.max() * 50)
print(f"theta={t:.2f} |{' ' * col}*{' ' * (50 - col)}| mse={l:7.4f}")theta=0.00 | * | mse= 0.7244
theta=0.04 | * | mse= 0.5428
theta=0.08 | * | mse= 0.3878
theta=0.12 | * | mse= 0.2593
theta=0.16 | * | mse= 0.1575
theta=0.20 | * | mse= 0.0822
theta=0.24 | * | mse= 0.0336
theta=0.28 | * | mse= 0.0116
theta=0.32 | * | mse= 0.0161
theta=0.36 | * | mse= 0.0473
theta=0.40 | * | mse= 0.1050
theta=0.44 | * | mse= 0.1894
theta=0.48 | * | mse= 0.3004
theta=0.52 | * | mse= 0.4379
theta=0.56 | * | mse= 0.6021
theta=0.60 | *| mse= 0.7928Dat is een vallei, van opzij gezien. Ze heeft één bodem, de wanden lopen aan beide kanten soepel omhoog, en — dit is wat de trap van Hoofdstuk 1 niet kon bieden — op elk punt erop bestaat er een goed gedefinieerde richting van "omlaag". Onthoud die vorm. Hoofdstuk 3 gaat volledig over eraf lopen zonder alle zeshonderdeen punten te bezoeken, en over wat er verandert wanneer een vallei meer dan één bodem heeft.
Waarom dan kwadrateren?
Link naar de sectie: Waarom dan kwadrateren?We hebben een vallei omdat we kwadrateerden. Absolute fout zou onderaan een knik hebben gegeven; grootste fout zou vlakke stukken hebben gegeven waar het verschuiven van de lijn helemaal niets verandert. Kwadrateren is onmiskenbaar handig — en handigheid is grofweg de reden die de meeste cursussen geven, in vier vermommingen: het maakt fouten positief (absolute waarde doet dat ook); het bestraft grote fouten zwaarder (waarom zou dat moeten?); het is differentieerbaar (de vierde macht is dat ook); het is wat iedereen gebruikt (dat klopt, en dat is geen argument).
Dit is de eerlijke positie. Kwadratische fout selecteerde lijn B en absolute fout selecteerde lijn A. Eén daarvan is juist voor deze fabriek en de andere is fout, en niets van wat tot nu toe is gezegd kan je vertellen welke. Om de regel te kiezen moet je iets weten over hoe de metingen van de lijn zijn gaan verschillen, en dat is een vraag over de wereld, niet over wiskunde. Om die te beantwoorden heb je één klein stuk gereedschap nodig.
De likelihood van een lijn
Link naar de sectie: De likelihood van een lijnDit is de claim die "welke lijn is beter" verandert in een vraag met een antwoord.
Neem aan dat de breedte van een onderdeel de lijn plus een willekeurige fout is, en neem aan dat die fout uit een Gaussische verdeling komt — de klokkromme — met gemiddelde nul en standaardafwijking :
De dichtheid van de Gaussische verdeling is
Doe nu iets wat de perceptron niet kon. Voor een gegeven kandidaat-helling heeft elke meting een residu, en de formule hierboven zet dat residu om in een getal: hoe plausibel is een fout van precies die grootte, als deze helling de waarheid is? Een meting op de lijn krijgt een groot getal, een meting een halve millimeter ernaast een klein getal.
De metingen zijn onafhankelijk — de schuifmaat onthoudt het vorige onderdeel niet — dus de productregel zegt dat de plausibiliteit van het hele notitieboek het product is van de afzonderlijke dichtheden. Dat product is de likelihood van .1 Let op de richting, want dat is waar de regel van Bayes over gaat: de data staat vast en is bekend, en de parameter varieert. Dit is niet "de kans op de helling". Het is de kans die het model toekent aan de data die je daadwerkelijk kreeg, gelezen als functie van de helling.
SIGMA = 0.12
def gaussian(r, sigma):
return np.exp(-r ** 2 / (2 * sigma ** 2)) / (sigma * np.sqrt(2 * np.pi))
def likelihood(theta):
return np.prod(gaussian(v - theta * u, SIGMA))
for t in (0.25, 0.293, 0.35):
print(f"theta={t} likelihood = {likelihood(t):.6g}")theta=0.25 likelihood = 521.952
theta=0.293 likelihood = 2.42028e+07
theta=0.35 likelihood = 0.190312Een helling van 0,293 maakt dit notitieboek zesenveertigduizend keer plausibeler dan 0,25, en honderzevenentwintig miljoen keer plausibeler dan 0,35. Maximum likelihood is het principe dat je de parameter kiest die wat je daadwerkelijk hebt waargenomen zo weinig verrassend mogelijk maakt. Het is geen stelling maar een voorstel over wat "best" zou moeten betekenen — een voorstel met inhoud, omdat het je dwingt je aanname over de ruis te formuleren voordat je iets mag scoren.
Het product breekt
Link naar de sectie: Het product breektDraai dezelfde drie regels code op een maand aan diensten in plaats van één, en de methode valt om.
rng = np.random.default_rng(7)
u_big = rng.uniform(-5.25, 5.25, 2000) # 2000 readings, not 20
v_big = 0.30 * u_big + 0.12 * rng.standard_normal(2000)
print("2000 readings, sigma = 0.12 mm :", np.prod(gaussian(v_big - 0.30 * u_big, 0.12)))
noisy = 0.30 * u_big + 2.0 * rng.standard_normal(2000)
print("2000 readings, sigma = 2.00 mm :", np.prod(gaussian(noisy - 0.30 * u_big, 2.0)))
print("largest float64 :", np.finfo(np.float64).max)RuntimeWarning: overflow encountered in reduce
2000 readings, sigma = 0.12 mm : inf
2000 readings, sigma = 2.00 mm : 0.0
largest float64 : 1.7976931348623157e+308Tweeduizend vermenigvuldigingen en het antwoord is inf. Verander één constante — een slordigere schuifmaat, zodat de dichtheden kleiner dan 1 uitvallen in plaats van groter — en dezelfde code geeft 0.0 terug. Beide antwoorden zijn fout, in tegengestelde richtingen, geen van beide gooit een exception die je kunt vangen, en de tweede print niet eens een waarschuwing.
Er is niets mis met de wiskunde. De likelihood bij die instellingen is een perfect goed gedefinieerd eindig getal: de natuurlijke logaritme ervan is 1400,91, dus het getal zelf is ongeveer . Het probleem is dat je computer dat getal niet heeft, en het is de moeite waard om precies te begrijpen welke getallen hij wél heeft, want dit is niet de laatste keer dat hij de uitkomst bepaalt.
Waar het kwadraat vandaan komt
Link naar de sectie: Waar het kwadraat vandaan komtDe oplossing voor het exploderende product is de gebruikelijke: neem logaritmen. De logaritme zet producten om in sommen, is strikt stijgend en kan dus de plek van het maximum niet verplaatsen, en een som van tweeduizend gematigde getallen is iets wat float64 zonder klagen verwerkt. Volgens conventie nemen we de negatieve log-likelihood, zodat beter kleiner betekent. Vervang nu de Gaussische dichtheid en kijk wat er gebeurt.
-
Begin met het product. De likelihood is , met de Gaussische dichtheid hierboven.
-
Neem min de log. Het product wordt een som, en de exponentiële term in de dichtheid valt rechtstreeks weg tegen de logaritme:
- Gooi alles weg dat niet bevat. De eerste term is een constante. De vóór de som is een positieve constante, en het schalen van een functie met een positieve constante kan niet verplaatsen waar haar minimum ligt. Wat overblijft is
en dat is de som van gekwadrateerde residuen — het ding waarmee we het hoofdstuk begonnen omdat het het eerste is waar iedereen aan denkt.
Dat is het resultaat waarvoor dit hoofdstuk bestaat, en het verdient het zonder voorbehoud te worden uitgesproken: kwadratische fout is geen conventie. Het is de negatieve log-likelihood van een Gaussische verdeling, met de constanten verwijderd. Kwadratische fout minimaliseren is precies dezelfde handeling als beweren dat je fouten Gaussisch zijn en vragen welke parameter je data het minst verrassend maakt. Je deed die bewering al die tijd al; alleen vertelde niemand het je.
De equivalentie is controleerbaar, dus controleer haar: scan dezelfde zeshonderdeen hellingen met de volledige negatieve log-likelihood, constanten en al, en met gewone kwadratische fout.
N = v.size
def nll(theta):
r = v - theta * u
return N * np.log(SIGMA * np.sqrt(2 * np.pi)) + np.sum(r ** 2) / (2 * SIGMA ** 2)
nlls = np.array([nll(t) for t in grid])
mses = np.array([mse(t) for t in grid])
print(f"argmin of the negative log-likelihood : theta={grid[nlls.argmin()]:.3f} nll={nlls.min():.6f}")
print(f"argmin of the mean squared error : theta={grid[mses.argmin()]:.3f} mse={mses.min():.6f}")
print("same index:", nlls.argmin() == mses.argmin())argmin of the negative log-likelihood : theta=0.293 nll=-17.001977
argmin of the mean squared error : theta=0.293 mse=0.010115
same index: TrueAndere getallen op de verticale as, en één ervan is negatief, wat een som van kwadraten nooit is: een negatieve log-likelihood kan onder nul komen, omdat een dichtheid groter dan 1 kan zijn. Dezelfde bodem van dezelfde vallei, tot op het laatste roosterpunt.
Volledige afleiding tonen
Welke weggooi-acties zijn precies veilig? Dezelfde manoeuvre verschijnt in elk hoofdstuk dat een loss afleidt, en ze is niet altijd onschuldig.
Een additieve constante laten vallen is veilig wanneer die niet afhangt van de parameter die je optimaliseert, en een positieve multiplicatieve constante laten vallen is veilig omdat voor elke . Beide falen zodra ook wordt gefit: dan is helemaal geen constante, maar de term die voorkomt dat het model en oneindige plausibiliteit claimt. Dat is precies de volgende sectie.
Ze falen opnieuw op een andere manier in Hoofdstuk 3: een multiplicatieve constante verplaatst het minimum niet, maar schaalt wel de gradient, en de gradient wordt vermenigvuldigd met de learning rate. Delen door om de gemiddelde kwadratische fout te krijgen in plaats van de som is onzichtbaar voor het antwoord en heel zichtbaar voor de training run — met de som verdubbelt het verdubbelen van je batchgrootte elke stap die je zet.
Sigma is ook niet gratis
Link naar de sectie: Sigma is ook niet gratisWe hebben per fiat op 0,12 gezet, en niemand in de fabriek kent de spreiding van de fout van hun schuifmaat. Behandel die als een tweede onbekende en laat maximum likelihood er ook over beslissen. Hier komt de constante term die we net weggooiden terug, omdat die het enige is tussen het model en een claim van perfecte precisie.
r = v - 0.293 * u
sigmas = np.arange(0.01, 1.0001, 0.0001)
nll_sigma = N * np.log(sigmas * np.sqrt(2 * np.pi)) + np.sum(r ** 2) / (2 * sigmas ** 2)
print("best sigma on the grid :", round(float(sigmas[nll_sigma.argmin()]), 4))
print("sqrt(mean squared residual) :", round(float(np.sqrt(np.mean(r ** 2))), 4))best sigma on the grid : 0.1006
sqrt(mean squared residual) : 0.1006De twee komen tot vier decimalen overeen, en niet per ongeluk: differentieer die uitdrukking en stel haar gelijk aan nul, en je krijgt exact . Dus de gemiddelde kwadratische fout is niet alleen als een variantie. Onder dit model is hij de maximum-likelihoodschatting van de variantie van de ruis — het getal dat je al die tijd hebt geminimaliseerd was een schatting van hoe rumoerig je sensor is.
Eén nuance, goedkoop om te noemen en duur om later opnieuw te ontdekken: die schatting is naar beneden vertekend, omdat de residuen zijn gemeten tegen een fit die zelf is gekozen om ze klein te maken. Simuleer het — tweehonderdduizend notitieboeken met elk twintig metingen, getrokken uit een verdeling waarvan de ware variantie exact 1 is, waarbij de ene parameter van de fit uit de metingen zelf wordt geschat. De som van kwadraten delen door geeft een gemiddelde van 0,9501; delen door geeft 1,0001; en is precies 0,95. Elke parameter die je fit kost één vrijheidsgraad, en dit is de kleinste zichtbare instantie van een veel groter probleem: een model ziet er altijd beter uit op de data waarop het is gefit. Hoofdstuk 4 maakt daar de discipline van het achterhouden van data van, en Hoofdstuk 6 geeft het effect zijn naam.
Een loss is een claim over de ruis
Link naar de sectie: Een loss is een claim over de ruisAls kwadratische fout stelt dat de ruis Gaussisch is, is de volgende vraag wat er gebeurt wanneer die stelling onwaar is. Niet een beetje onwaar — onwaar zoals echte metingen onwaar zijn.
Op de werkvloer zijn de meeste schuifmaatmetingen goed tot op een tiende millimeter, en één of twee keer per dienst komt er een spaantje onder de bek en zit de meting er meerdere millimeters naast. Zulke fouten zijn heavy-tailed: meestal klein, af en toe enorm, en enorm veel vaker dan een klokkromme toelaat. De Cauchy-verdeling is het standaard nette model voor dat gedrag, en haar dichtheid is even eenvoudig als die van de Gaussische verdeling:
Het verschil zit in de staart: de Gaussische verdeling valt af als , bruut snel, en de Cauchy als , nauwelijks. Het gevolg is makkelijker te zien dan te zeggen:
rng = np.random.default_rng(3)
g = 0.12 * rng.standard_normal(10 ** 6) # Gaussian noise
c = 0.12 * rng.standard_cauchy(10 ** 6) # Cauchy noise, same scale
for k in (10 ** 2, 10 ** 3, 10 ** 4, 10 ** 5, 10 ** 6):
print(f"{k:>9,} samples gaussian var {g[:k].var():.4f} cauchy var {c[:k].var():10.2f}") 100 samples gaussian var 0.0164 cauchy var 0.26
1,000 samples gaussian var 0.0146 cauchy var 59.88
10,000 samples gaussian var 0.0145 cauchy var 358.17
100,000 samples gaussian var 0.0144 cauchy var 3097.98
1,000,000 samples gaussian var 0.0144 cauchy var 32886.10De steekproefvariantie van de Gaussische verdeling stabiliseert op 0,0144, wat is, en blijft daar. Die van de Cauchy klimt, en blijft klimmen zolang je samplet, omdat er niets is waar hij naartoe kan convergeren: de Cauchy-verdeling heeft geen variantie, en ook geen gemiddelde. Kwadratische fout, waarvan het hele werk bestaat uit het minimaliseren van een gemiddelde van kwadraten, wordt gevraagd om een grootheid die niet bestaat.
Dus hier is één dienst waarin de schuifmaat werd misleid. Dezelfde twintig uur, hetzelfde mes, dezelfde drift van 0,30 millimeter per uur — alleen is de ruis nu Cauchy. Fit twee keer: één keer door gekwadrateerde residuen te minimaliseren, één keer door de negatieve log-likelihood te minimaliseren van de ruis die de data daadwerkelijk heeft gegenereerd. De centreertruc helpt hier niet — die pint de intercept alleen vast voor kwadratische fout — dus beide fits gebeuren brute force over een rooster van intercepts en hellingen, omdat we nog steeds geen manier hebben om de bodem van een vallei te vinden behalve door haar te bezoeken.
SWARF = np.array([
(0.5, 20.08), (1.0, 21.95), (1.5, 20.86), (2.0, 27.51), (2.5, 20.64),
(3.0, 20.75), (3.5, 21.01), (4.0, 21.03), (4.5, 21.37), (5.0, 20.60),
(5.5, 22.03), (6.0, 21.95), (6.5, 21.98), (7.0, 22.01), (7.5, 21.73),
(8.0, 22.97), (8.5, 22.60), (9.0, 22.66), (9.5, 22.44), (10.0, 22.78),
])
hs, ys = SWARF[:, 0], SWARF[:, 1]
A = np.arange(18.0, 22.001, 0.005) # 801 intercepts
B = np.arange(-0.20, 0.8001, 0.002) # 501 slopes
R = ys - (A[:, None, None] + B[None, :, None] * hs) # every line against every point
SCALE = 0.12
square = np.sum(R ** 2, axis=2) # least squares
cauchy = np.sum(np.log(1 + (R / SCALE) ** 2), axis=2) # Cauchy likelihood
for name, surface in (("least squares", square), ("Cauchy likelihood", cauchy)):
i, j = np.unravel_index(surface.argmin(), surface.shape)
print(f"{name:>18}: width = {A[i]:.3f} + {B[j]:.4f} * hours"
f" -> 23.5 mm at hour {(23.5 - A[i]) / B[j]:.2f}")
print(f"{'the truth':>18}: width = 20.000 + 0.3000 * hours"
f" -> 23.5 mm at hour {(23.5 - 20.0) / 0.30:.2f}")
print(f"{A.size * B.size:,} candidate lines evaluated")De twee gemarkeerde regels zijn het hele verschil tussen de fits. Neem de log van de Cauchy-dichtheid, laat de constanten precies zoals eerder vallen, en is wat overblijft. Zelfde recept, andere claim over de ruis.
least squares: width = 21.380 + 0.1080 * hours -> 23.5 mm at hour 19.63
Cauchy likelihood: width = 19.935 + 0.3020 * hours -> 23.5 mm at hour 11.80
the truth: width = 20.000 + 0.3000 * hours -> 23.5 mm at hour 11.67
401,301 candidate lines evaluatedLeast squares rapporteert een drift van 0,108 millimeter per uur, ongeveer een derde van de echte snelheid, en concludeert dat het mes goed is tot uur 19,6. Het ware antwoord is uur 11,7. Als de fabriek naar die fit handelt, laat ze de pers acht extra uren onderdelen maken die buiten tolerantie vallen, op gezag van de meest standaard verliesfunctie in het vak. De Cauchy-fit, met dezelfde twintig metingen, hetzelfde rooster en één regel verschil in de code, komt uit op uur 11,8.
Twee bezwaren verdienen antwoord, omdat beide het eerste zijn wat een goede engineer zegt.
De outlier is duidelijk — verwijder hem gewoon. Dat kan, en het helpt, en het is niet genoeg. De ene slechtste meting verwijderen verschuift de least-squareshelling van 0,108 naar 0,239, wat de meswissel nog steeds op uur 13,1 zet, anderhalf uur te laat; de slechtste verwijderen, opnieuw fitten en verwijderen wat nu het slechtst is brengt je op 0,286 — en merk op dat dit al een procedure is, geen observatie: verwijder in plaats daarvan de twee grootste residuen van de oorspronkelijke fit en je komt uit op 0,223. Maar je hebt nu oordelen geveld die je niet kunt opschrijven of verdedigen, en de regel automatiseren redt het niet: drop-the-largest-residual-then-refit, gedraaid over duizend gesimuleerde diensten, heeft een mediane hellingsfout van 0,0177 tegenover 0,0100 voor de likelihood-fit, en zit in 14,7% van de diensten meer dan 0,05 fout tegenover 1,3%. Verwijderen is een pleister op een verkeerde aanname. De likelihood heeft geen pleister nodig, omdat die nooit aannam dat de outlier onmogelijk was.
Je hebt een gelukkige dataset gekozen. Dat bezwaar is precies juist, en daarom simuleert het laatste experiment duizend onafhankelijke diensten en fit het beide manieren op elk ervan opnieuw.
A = np.arange(18.0, 22.001, 0.02) # a coarser grid: a thousand fits to do
B = np.arange(-0.20, 0.8001, 0.005)
lines = A[:, None, None] + B[None, :, None] * hs
rng = np.random.default_rng(2026)
err_sq, err_ca = [], []
for _ in range(1000): # 1000 independent shifts
ys = 20.00 + 0.30 * hs + SCALE * rng.standard_cauchy(hs.size)
R = ys - lines
_, j = np.unravel_index(np.sum(R ** 2, axis=2).argmin(), (A.size, B.size))
_, q = np.unravel_index(np.sum(np.log1p((R / SCALE) ** 2), axis=2).argmin(), (A.size, B.size))
err_sq.append(abs(B[j] - 0.30))
err_ca.append(abs(B[q] - 0.30))
err_sq, err_ca = np.array(err_sq), np.array(err_ca)
for name, e in (("least squares", err_sq), ("Cauchy likelihood", err_ca)):
print(f"{name:>18}: median slope error {np.median(e):.4f} mm/h"
f" off by more than 0.05 in {100 * np.mean(e > 0.05):4.1f}% of shifts"
f" worst {e.max():.3f}")
print(f"the likelihood fit is the closer of the two in {100 * np.mean(err_ca < err_sq):.1f}% of shifts") least squares: median slope error 0.0350 mm/h off by more than 0.05 in 40.4% of shifts worst 0.500
Cauchy likelihood: median slope error 0.0100 mm/h off by more than 0.05 in 1.3% of shifts worst 0.090
the likelihood fit is the closer of the two in 75.6% of shiftsMediaan, niet gemiddelde, om dezelfde reden als al het andere in deze sectie: de least-squaresfouten worden door een Cauchy gedreven, dus hun gemiddelde is geen stabiel ding om te rapporteren. Least squares zit in twee van de vijf diensten ernstig fout; de likelihood-fit in één van de zevenenzeventig, en zijn ergste misser over duizend diensten is minder dan een vijfde van de ergste misser van least squares.
Niets hiervan maakt kwadratische fout slecht. Het maakt haar specifiek, en de rekenkunde zegt precies waarom. Neem een residu van 0,1 mm en een van 7 mm. Gekwadrateerd draagt de slechte meting 4.900 keer zoveel bij aan het totaal als de goede, dus de lijn wordt er met haar hele gewicht naartoe getrokken; onder de Cauchy-log-likelihood dragen dezelfde twee residuen 0,527 en 8,133 bij, een verhouding van 15,4. De slechte meting telt nog steeds mee, ze mag alleen niet beslissen. Dit is het begin van robuuste statistiek, waar Hubers loss uit 1964 het verschil splitst door zich kwadratisch te gedragen voor kleine residuen en lineair voor grote,7 en waar Tukey al had laten zien hoe weinig contaminatie nodig is om de steekproefvariantie een slechter gereedschap te maken dan de gemiddelde absolute afwijking.8
Nog één historische noot, te goed om weg te laten. Least squares werd als eerste gepubliceerd, door Legendre in 1805, als een handig algebraïsch hulpmiddel zonder andere rechtvaardiging dan dat het werkte.9 Vier jaar later draaide Gauss het argument om: hij nam als gegeven dat het rekenkundig gemiddelde de juiste manier is om herhaalde metingen te combineren, vroeg welke foutverdeling het gemiddelde tot de meest waarschijnlijke waarde maakt, en liet zien dat er in essentie maar één is — degene die nu naar hem is genoemd.10 De afleiding in dit hoofdstuk is de zijne, ze is meer dan twee eeuwen oud, en ze is nog steeds het deel dat de meeste cursussen weglaten.
Wat je nu kunt zeggen, en wat je nog niet kunt doen
Link naar de sectie: Wat je nu kunt zeggen, en wat je nog niet kunt doenVerdiend. Een verliesfunctie is een scoreregel, en de rangorde die zij produceert is een eigenschap van de regel, niet van de kandidaten. Elke loss in deze cursus is de negatieve log-likelihood van een aanname over de ruis, met de constanten weggegooid — Gaussisch geeft hier kwadratische fout, Bernoulli geeft cross-entropy in Hoofdstuk 4, en een categorische verdeling over een vocabulaire geeft de next-token loss in Hoofdstuk 8. Het recept verandert nooit: formuleer de ruis, schrijf de likelihood, neem min de log. En wanneer de aanname fout is, is het model niet alleen onnauwkeurig, maar fout in een richting die je kunt voorspellen.
Nog ontbrekend. We vonden de bodem van de vallei door elk punt erin te bezoeken. Dat werkte voor één parameter en zeshonderd kandidaten, en overleefde twee parameters met 401.301 kandidaten in een vijfde seconde. Drie parameters op dezelfde resolutie zijn 201.051.801 kandidaten en passen niet meer in één array; een klein netwerk in Hoofdstuk 5 heeft duizenden parameters, en de modellen waar Hoofdstuk 10 een prijs op zet hebben miljarden. Brute force is hier niet traag, maar rekenkundig onmogelijk, en niets in dit hoofdstuk suggereert een alternatief.
Kijk echter terug naar de vallei. Staand op met een loss van 0,0822 is de richting van "omlaag" geen mysterie — je kunt het op de pagina zien, de curve helt naar rechts omlaag. Als je de verliesfunctie zou kunnen vragen welke kant ze helt op het punt waar je staat, zonder haar ergens anders te evalueren, zou je een stap die kant op kunnen zetten, opnieuw vragen, en herhalen tot de grond vlak is.
Die vraag heeft een naam. De helling van een functie in een punt is haar afgeleide, en voor een functie van veel parameters is de verzameling hellingen in alle richtingen tegelijk de gradient. Hoofdstuk 1 kon er geen gebruiken, omdat de fout van de perceptron een trap was zonder helling om naar te vragen. Dit hoofdstuk heeft iets beters gebouwd: een loss die overal glad is en die voortkwam uit een expliciete aanname in plaats van een voorkeur.
Dus de vraag voor Hoofdstuk 3 is niet langer of er een helling bestaat. Het is hoe je die berekent, waarom bewegen tegen die helling in omlaag gaat in plaats van omhoog — een teken dat bijna elke cursus je op geloof vraagt te accepteren — en hoe ver je moet stappen voordat je opnieuw vraagt, wat uiteindelijk het ene getal blijkt dat bepaalt of een training run convergeert, eindeloos rond het antwoord oscilleert, of naar oneindig wegloopt.
Bronnen en methode
Link naar de sectie: Bronnen en methodeOok de moeite waard om naast dit hoofdstuk te lezen: Prince, Understanding Deep Learning §5.1–5.2 en Appendix C, dat elke loss in het boek opbouwt vanuit maximum likelihood in de volgorde die hier wordt gebruikt; Goodfellow, Bengio en Courville, Deep Learning §3.1–3.11 en §5.5, waarvan de maximum-likelihoodsectie ook de KL-divergentie afleidt die Hoofdstuk 4 nodig heeft; Murphy, Probabilistic Machine Learning: An Introduction hoofdstuk 2 en §4.2, over wat maximum likelihood wel en niet garandeert; Deisenroth, Faisal en Ong, Mathematics for Machine Learning §6.1–6.4 voor de somregel, productregel en de regel van Bayes netjes uitgewerkt; Tom Mitchells korte CMU-notitie Estimating Probabilities: MLE and MAP (2016); en §22.7 van Dive into Deep Learning, dat hetzelfde resultaat bereikt in uitvoerbare code.
Referenties
Link naar de sectie: Referenties-
Fisher, R. A. On the mathematical foundations of theoretical statistics. Philosophical Transactions of the Royal Society A 222, pp. 309–368 (1922). Waar likelihood als algemene methode wordt uiteengezet, samen met "parameter", "statistic", sufficiency en efficiency. De naamgeving zelf, en de scheiding van probability, is een jaar ouder: Fisher, R. A., On the "probable error" of a coefficient of correlation deduced from a small sample, Metron 1, pp. 3–32 (1921), pp. 24–25. ↩
-
IEEE Standard for Floating-Point Arithmetic, IEEE 754-2019. Definieert binary32 en binary16, en de afrondingsregels waardoor het sommatie-experiment uitkomt zoals het uitkomt. ↩
-
Kalamkar, D. et al. A Study of BFLOAT16 for Deep Learning Training. arXiv:1905.12322 (2019). De parameters van het formaat, en het pleidooi om mantissebits in te ruilen voor exponentbits. ↩
-
Micikevicius, P. et al. Mixed Precision Training. ICLR 2018, arXiv:1710.03740. Loss scaling, en de gemeten gradientgroottes die het in float16 noodzakelijk maken. ↩
-
Goldberg, D. What Every Computer Scientist Should Know About Floating-Point Arithmetic. ACM Computing Surveys 23(1), pp. 5–48 (1991). Nog steeds de beste afzonderlijke uitleg van waarom de twee optelvolgordes verschillen. ↩
-
Kahan, W. Pracniques: further remarks on reducing truncation errors. Communications of the ACM 8(1), p. 40 (1965). Gecompenseerde sommatie in een halve pagina. ↩
-
Huber, P. J. Robust estimation of a location parameter. The Annals of Mathematical Statistics 35(1), pp. 73–101 (1964). De loss die kwadratisch is rond nul en lineair in de staarten, afgeleid in plaats van aan elkaar geplakt. ↩
-
Tukey, J. W. A survey of sampling from contaminated distributions, in Contributions to Probability and Statistics (Stanford University Press, 1960), pp. 448–485. ↩
-
Legendre, A. M. Nouvelles méthodes pour la détermination des orbites des comètes (Paris, 1805), appendix Sur la méthode des moindres quarrés. De eerste publicatie van least squares, als rekenhulpmiddel. ↩
-
Gauss, C. F. Theoria Motus Corporum Coelestium (Hamburg, 1809), Book II, §§175–179. Het argument van het rekenkundig gemiddelde naar de normale foutwet, en vandaar naar least squares. ↩