Spring til indhold
2/30Kapitel 2 af 30

Hvor en tabsfunktion kommer fra: likelihood, ikke konvention

Tre linjer over de samme 20 målinger og tre scoringsregler, der kårer hver sin vinder. Kvadreret fejl er et valg.

På denne side

Klingen, der skærer delene, slides ned. Over et ti-timers skift mister den nok skarphed til, at delene kommer af båndet en brøkdel af en millimeter bredere, end de startede, og når de passerer 23,5 millimeter, afviser kontrollen dem. Ingen på fabrikken ved, hvornår det sker. Det, de har, er en skydelære, en notesbog og tyve aflæsninger fra sidste tirsdag: timerne siden klingen blev skiftet, og bredden på delen målt i det øjeblik.

Nogen tegner en linje gennem punkterne. En anden tegner en lidt anden. En tredje person tegner en tredje. Alle tre ser rimelige ud på papiret, og de er uenige med flere timer om, hvornår klingen skal skiftes — på denne fabrik forskellen mellem en rolig uge og et kasseret batch.

Hvilken linje er bedre?

Som spørgsmålet står, har det intet svar. Ikke et svært svar — slet intet svar. »Bedre« er ikke en egenskab ved en linje på samme måde som dens hældning er; det er en egenskab ved en linje sammen med en regel for at score linjer, og indtil nogen skriver reglen ned, er der intet at beregne. Dette kapitel tager den sætning alvorligt og ender med opdagelsen af, at den mest almindelige regel i machine learning ikke er en konvention, men konsekvensen af en påstand om verden — en, du kan teste, og en, der nogle gange er falsk.

En indrømmelse før den første kodelinje. Disse tyve aflæsninger er ikke fra en virkelig fabrik: Jeg genererede dem fra en linje, jeg valgte, width=20.00+0.30h\text{width} = 20.00 + 0.30 \cdot h, plus tilfældig støj med en spredning på omkring en tiendedel millimeter. Det betyder noget, fordi alt nedenfor handler om, hvorvidt en metode genfinder en sandhed, og den eneste måde at kontrollere det på er at kende sandheden på forhånd. Så: 0,30 millimeter i timen er svaret bag i bogen. Du må ikke bruge det, kun kontrollere mod det.

Her er aflæsningerne og de tre linjer, scoret på tre måder: kvadreret fejl, som alle rækker ud efter; absolut fejl, som en statistiker måske ville; og værste fejl, som maskinarbejderen ville, fordi inspektøren er ligeglad med dit gennemsnit — han afviser den ene del, der er uden for tolerance.

NumPy kommer ind her, ét kapitel efter den rene Python-perceptron, af én grund: Ved slutningen af dette kapitel evaluerer vi fire hundrede tusind kandidatlinjer mod tyve aflæsninger hver, og et Python-loop er det forkerte værktøj til det. Det er også den notation, alle kilderne citeret nedenfor er skrevet i.

loss.pyPYTHON
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}")               

Størrelsen i de fremhævede linjer er residualet: det, linjen sagde, minus det, skydelæren sagde, ét tal per aflæsning. Hver scoringsregel i dette kapitel, og hver tabsfunktion i de otteogtyve kapitler efter det, er en måde at mase en liste af residualer ned til ét enkelt tal. De adskiller sig kun i, hvordan de maser.

TEXT
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.320

Læs kolonnerne, ikke rækkerne. Kvadreret fejl siger B, absolut fejl siger A, værste fejl siger C: tre regler, tre vindere, på de samme tyve punkter.

Jeg valgte disse tre linjer, så de ville være uenige, og det bør jeg sige ligeud. Pointen er, hvor let det var — nogle få minutters søgning over intercepts og hældninger, der ser fornuftige ud, finder hundredvis af sådanne tripler. Rangeringen er en egenskab ved den regel, du valgte, ikke et faktum om linjerne, så reglen er ikke en implementeringsdetalje: Den er definitionen af problemet. Hvilket rejser det spørgsmål, dette kapitel findes for at besvare: På hvilket grundlag vælger du den?

Først en mindre sag, for der er ikke tre linjer, men uendeligt mange. Tag kvadreret fejl indtil videre, eftersom det er det, alle tager, og krymp problemet til et enkelt tal ved hjælp af tricket, der sparede perceptronen elleve tusind epoker i kapitel 1: Træk gennemsnittet fra begge kolonner. Når punktskyen er centreret om origo, går den bedste linje under kvadreret fejl præcis gennem origo — så interceptet er afgjort, og kun hældningen er tilbage at vælge.

loss.py (continued)PYTHON
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}")
TEXT
601 candidates -> theta=0.293 mse=0.010115

Seks hundrede og én kandidathældninger, én vinder: 0,293 millimeter i timen mod en sandhed på 0,300. Tyve støjende aflæsninger og et for-loop kom inden for en hundrededel millimeter i timen — to og en tredjedel procent.

Det interessante er ikke vinderen, men formen på søgningen. Print hele kurven, roteret så tabet løber fra venstre mod højre:

loss.py (continued)PYTHON
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}")
TEXT
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.7928

Det er en dal, set fra siden. Den har én bund, væggene stiger jævnt på begge sider, og — dette er den del, trappen i kapitel 1 ikke kunne tilbyde — på hvert eneste punkt på den findes der en veldefineret retning »nedad«. Husk den form. Kapitel 3 handler udelukkende om at gå ned ad den uden at besøge alle seks hundrede og ét punkter, og om hvad der ændrer sig, når en dal har mere end én bund.

Vi har en dal, fordi vi kvadrerede. Absolut fejl ville have givet den et knæk i bunden; værste fejl ville have givet den flade stræk, hvor det slet ikke ændrer noget at flytte linjen. Kvadrering er unægteligt bekvemt — og bekvemmelighed er omtrent den grund, de fleste kurser giver, pyntet på fire måder: Det gør fejl positive (det gør absolutværdi også); det straffer store fejl mere (hvorfor skulle det?); det er differentiabelt (det er fjerde potens også); det er det, alle bruger (det er det, og det er ikke et argument).

Her er den ærlige position. Kvadreret fejl valgte linje B, og absolut fejl valgte linje A. En af dem er rigtig for denne fabrik, og den anden er forkert, og intet af det, der hidtil er sagt, kan fortælle dig hvilken. For at vælge reglen skal du vide noget om hvordan aflæsningerne kom til at afvige fra linjen, og det er et spørgsmål om verden, ikke om matematik. At besvare det kræver et lille stykke maskineri.

Her er påstanden, der forvandler »hvilken linje er bedre« til et spørgsmål med et svar.

Antag, at bredden på en del er linjen plus en tilfældig fejl, og antag, at fejlen trækkes fra en Gaussisk fordeling — klokkekurven — med middelværdi nul og standardafvigelse σ\sigma:

yi=θxi+εi,εiN(0,σ2)y_i = \theta x_i + \varepsilon_i, \qquad \varepsilon_i \sim \mathcal{N}(0, \sigma^2)

Den Gaussiske tæthed er

p(ε)=1σ2πexp ⁣(ε22σ2)p(\varepsilon) = \frac{1}{\sigma\sqrt{2\pi}} \exp\!\left(-\frac{\varepsilon^2}{2\sigma^2}\right)

Gør nu noget, perceptronen ikke kunne. For en given kandidathældning θ\theta har hver aflæsning et residual, og formlen ovenfor forvandler det residual til et tal: Hvor plausibel er en fejl af præcis den størrelse, hvis denne hældning er sandheden? En aflæsning på linjen får et stort tal, en aflæsning en halv millimeter væk et lille.

Aflæsningerne er uafhængige — skydelæren husker ikke den sidste del — så produktreglen siger, at plausibiliteten af hele notesbogen er produktet af de individuelle tætheder. Det produkt er likelihood for θ\theta.1 Bemærk retningen, for det er den retning, Bayes' regel handler om: Dataene er faste og kendte, og det er parameteren, der varierer. Dette er ikke »sandsynligheden for hældningen«. Det er den sandsynlighed, modellen tildeler de data, du faktisk fik, læst som en funktion af hældningen.

likelihood.pyPYTHON
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}")
TEXT
theta=0.25   likelihood = 521.952
theta=0.293   likelihood = 2.42028e+07
theta=0.35   likelihood = 0.190312

En hældning på 0,293 gør denne notesbog seksogfyrre tusind gange mere plausibel end 0,25 og hundrede og syvogtyve millioner gange mere plausibel end 0,35. Maximum likelihood er princippet om, at du vælger den parameter, der gør det, du faktisk observerede, så lidt overraskende som muligt. Det er ikke et teorem, men et forslag til, hvad »bedst« bør betyde — et forslag med indhold, fordi det tvinger dig til at angive din antagelse om støjen, før du får lov til at score noget.

Kør de samme tre kodelinjer på en måneds skift i stedet for ét, og metoden falder sammen.

likelihood.py (continued)PYTHON
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)
TEXT
RuntimeWarning: overflow encountered in reduce
2000 readings, sigma = 0.12 mm : inf
2000 readings, sigma = 2.00 mm : 0.0
largest float64 : 1.7976931348623157e+308

To tusind multiplikationer, og svaret er inf. Skift én konstant — en mere sjusket skydelære, så tæthederne bliver mindre end 1 i stedet for større — og den samme kode returnerer 0.0. Begge svar er forkerte, i hver sin retning, ingen af dem rejser en exception, du kan fange, og den anden printer ikke engang en advarsel.

Der er intet galt med matematikken. Likelihood ved disse indstillinger er et perfekt veldefineret endeligt tal: Dets naturlige logaritme er 1400,91, så selve tallet er omkring 1060810^{608}. Problemet er, at din computer ikke har det tal, og det er værd at forstå præcis, hvilke tal den har, fordi det ikke er sidste gang, den afgør udfaldet.

Løsningen på det eksploderende produkt er den sædvanlige: Tag logaritmer. Logaritmen forvandler produkter til summer, den er strengt voksende, så den kan ikke flytte maksimums placering, og en sum af to tusind moderate tal er noget, float64 håndterer uden klage. Efter konvention tager vi den negative log-likelihood, så bedre betyder mindre. Indsæt nu den Gaussiske tæthed, og se hvad der sker.

  1. Start med produktet. Likelihood er L(θ)=i=1Np(yiθxi)\mathcal{L}(\theta) = \prod_{i=1}^{N} p(y_i - \theta x_i), med pp som den Gaussiske tæthed ovenfor.

  2. Tag minus logaritmen. Produktet bliver en sum, og eksponentialleddet i tætheden ophæves direkte af logaritmen:

logL(θ)=N2log ⁣(2πσ2)+12σ2i=1N(yiθxi)2-\log \mathcal{L}(\theta) = \frac{N}{2}\log\!\left(2\pi\sigma^2\right) + \frac{1}{2\sigma^2}\sum_{i=1}^{N}\left(y_i - \theta x_i\right)^2
  1. Smid alt væk, der ikke indeholder θ\theta. Det første led er en konstant. 1/2σ21/2\sigma^2 foran summen er en positiv konstant, og at skalere en funktion med en positiv konstant kan ikke flytte, hvor dens minimum er. Tilbage står
i=1N(yiθxi)2\sum_{i=1}^{N}\left(y_i - \theta x_i\right)^2

som er summen af kvadrerede residualer — det, vi startede kapitlet med, fordi det var det første, nogen tænker på.

Det er resultatet, kapitlet findes for, og det fortjener at blive sagt uden forbehold: Kvadreret fejl er ikke en konvention. Det er den negative log-likelihood for en Gaussisk fordeling, med konstanterne fjernet. At minimere kvadreret fejl er præcis den samme handling som at påstå, at dine fejl er Gaussiske, og spørge, hvilken parameter der gør dine data mindst overraskende. Du fremsatte den påstand hele tiden; du fik det bare ikke at vide.

Ækvivalensen kan kontrolleres, så kontroller den: Scan de samme seks hundrede og én hældninger med den fulde negative log-likelihood, konstanter og det hele, og med almindelig kvadreret fejl.

likelihood.py (continued)PYTHON
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())
TEXT
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: True

Forskellige tal på den lodrette akse, og et af dem er negativt, hvilket en sum af kvadrater aldrig er: En negativ log-likelihood kan gå under nul, fordi en tæthed kan overstige 1. Samme bund i samme dal, til sidste gitterpunkt.

Vis hele udledningen

Hvilke frasorteringer er helt præcist sikre? Den samme manøvre optræder i hvert kapitel, der udleder et tab, og den er ikke altid uskyldig.

At droppe en additiv konstant er sikkert, når den ikke afhænger af den parameter, du optimerer, og at droppe en positiv multiplikativ konstant er sikkert, fordi argminθcf(θ)=argminθf(θ)\arg\min_\theta c\,f(\theta) = \arg\min_\theta f(\theta) for enhver c>0c > 0. Begge dele fejler i det øjeblik, σ\sigma også bliver fitted: Så er N2log(2πσ2)\frac{N}{2}\log(2\pi\sigma^2) slet ikke en konstant, men det led, der forhindrer modellen i at påstå σ=0\sigma = 0 og uendelig plausibilitet. Det er præcis næste afsnit.

De fejler anderledes igen i kapitel 3: En multiplikativ konstant flytter ikke minimum, men den skalerer gradienten, og gradienten bliver multipliceret med learning rate. At dividere med NN for at få den gennemsnitlige kvadrerede fejl snarere end summen er usynligt for svaret og meget synligt for træningskørslen — med summen fordobler en fordobling af din batchstørrelse hvert skridt, du tager.

Vi fastsatte σ\sigma til 0,12 ved dekret, og ingen på fabrikken kender spredningen i skydelærens fejl. Behandl den som en anden ukendt, og lad maximum likelihood afgøre den også. Her kommer konstantleddet, vi lige smed væk, tilbage, fordi det er det eneste, der står mellem modellen og en påstand om perfekt præcision.

likelihood.py (continued)PYTHON
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))
TEXT
best sigma on the grid       : 0.1006
sqrt(mean squared residual)  : 0.1006

De to stemmer overens til fire decimaler, og ikke ved et tilfælde: At differentiere det udtryk og sætte det lig nul giver σ^2=1Nri2\hat{\sigma}^2 = \frac{1}{N}\sum r_i^2 præcis. Så den gennemsnitlige kvadrerede fejl er ikke blot som en varians. Under denne model er den maximum-likelihood-estimatet for støjens varians — det tal, du hele tiden har minimeret, var et estimat af, hvor støjende din sensor er.

Én krølle, billig at formulere og dyr at genopdage senere: Estimatet er biased lavt, fordi residualerne blev målt mod et fit, der selv var valgt for at gøre dem små. Simuler det — to hundrede tusind notesbøger med tyve aflæsninger hver, trukket fra en fordeling, hvis sande varians er præcis 1, med den ene parameter i fittet estimeret fra selve aflæsningerne. At dividere summen af kvadrater med NN giver et gennemsnit på 0,9501; at dividere med N1N-1 giver 1,0001; og (N1)/N(N-1)/N er 0,95 på prikken. Hver parameter, du fitter, koster én frihedsgrad, og dette er det mindste synlige eksempel på et meget større problem: En model ser altid bedre ud på de data, den blev fitted til. Kapitel 4 gør det til disciplinen at holde data tilbage, og kapitel 6 giver effekten sit navn.

Hvis kvadreret fejl påstår, at støjen er Gaussisk, er det næste spørgsmål, hvad der sker, når påstanden er falsk. Ikke lidt falsk — falsk på den måde, virkelige målinger er falske.

På fabriksgulvet er de fleste skydelæreaflæsninger gode inden for en tiendedel millimeter, og en eller to gange per skift kommer et spån under kæben, og aflæsningen er flere millimeter forkert. Den slags fejl er heavy-tailed: små det meste af tiden, lejlighedsvis enorme, og enorme langt oftere end en klokkekurve tillader. Cauchy-fordelingen er den klassiske rene model for den adfærd, og dens tæthed er lige så enkel som den Gaussiskes:

p(ε)=1πs(1+(ε/s)2)p(\varepsilon) = \frac{1}{\pi s \left(1 + (\varepsilon/s)^2\right)}

Forskellen er halen: Den Gaussiske falder som eε2e^{-\varepsilon^2}, brutalt hurtigt, og Cauchy som 1/ε21/\varepsilon^2, næsten slet ikke. Konsekvensen er lettere at se end at sige:

PYTHON
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}")
TEXT
      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.10

Den Gaussiskes sample-varians falder til ro på 0,0144, som er 0.1220.12^2, og bliver der. Cauchys stiger og bliver ved med at stige, så længe du sampler, fordi der ikke er noget for den at konvergere mod: Cauchy-fordelingen har ingen varians og heller intet gennemsnit. Kvadreret fejl, hvis hele ærinde er at minimere et gennemsnit af kvadrater, bliver bedt om en størrelse, der ikke findes.

Så her er ét skift, hvor skydelæren blev narret. Samme tyve timer, samme klinge, samme drift på 0,30 millimeter i timen — kun støjen er nu Cauchy. Fit det to gange: én gang ved at minimere kvadrerede residualer, én gang ved at minimere den negative log-likelihood for den støj, der faktisk genererede dataene. Centreringstricket hjælper ikke her — det fastlåser kun interceptet for kvadreret fejl — så begge fit foretages med brute force over et gitter af intercepts og hældninger, eftersom vi stadig ikke har nogen måde at finde bunden af en dal på ud over at besøge den.

swarf.pyPYTHON
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 to fremhævede linjer er hele forskellen mellem fittene. Tag logaritmen af Cauchy-tætheden, drop konstanterne præcis som før, og log(1+(r/s)2)\sum \log\left(1 + (r/s)^2\right) er det, der overlever. Samme opskrift, anden påstand om støjen.

TEXT
     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 evaluated

Mindste kvadraters metode rapporterer en drift på 0,108 millimeter i timen, omtrent en tredjedel af den reelle rate, og konkluderer, at klingen er god indtil time 19,6. Det sande svar er time 11,7. Handler fabrikken på det fit, kører den pressen i otte ekstra timer og laver dele, der er uden for tolerance, med den mest standardiserede tabsfunktion i feltet som autoritet. Cauchy-fittet, med de samme tyve aflæsninger, det samme gitter og én linjes forskel i koden, lander på time 11,8.

To indvendinger fortjener svar, fordi begge er det første, en god ingeniør siger.

Outlieren er åbenlys — slet den bare. Det kan du, og det hjælper, og det er ikke nok. At slette den enkeltstående værste aflæsning flytter mindste-kvadraters-hældningen fra 0,108 til 0,239, hvilket stadig placerer klingeskiftet ved time 13,1, halvanden time for sent; at slette den værste, fitte igen og slette det, der er værst nu, bringer dig til 0,286 — og bemærk, at dette allerede er en procedure, ikke en observation: Slet de to største residualer fra det oprindelige fit i stedet, og du lander på 0,223. Men du har nu truffet skønsmæssige valg, du ikke kan skrive ned eller forsvare, og at automatisere reglen redder den ikke: drop-det-største-residual-og-fit-igen, kørt over tusind simulerede skift, har en median hældningsfejl på 0,0177 mod likelihood-fittets 0,0100 og er mere end 0,05 forkert i 14,7% af skiftene mod 1,3%. Sletning er en lap oven på en forkert antagelse. Likelihood behøver ingen lap, fordi den aldrig antog, at outlieren var umulig.

Du valgte et heldigt datasæt. Den indvending er helt rigtig, og derfor simulerer det sidste eksperiment tusind uafhængige skift og fitter på begge måder på hvert.

swarf.py (continued)PYTHON
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")
TEXT
     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 shifts

Median, ikke gennemsnit, af samme grund som alt andet i dette afsnit: Mindste-kvadraters-fejlene drives af en Cauchy, så deres gennemsnit er ikke en stabil størrelse at rapportere. Mindste kvadraters metode er alvorligt forkert to skift ud af fem; likelihood-fittet er alvorligt forkert ét skift ud af syvoghalvfjerds, og dets værste fejl på tværs af tusind skift er mindre end en femtedel af mindste kvadraters værste.

Intet af dette gør kvadreret fejl dårlig. Det gør den specifik, og aritmetikken siger præcis hvorfor. Tag et residual på 0,1 mm og et på 7 mm. Kvadreret bidrager den dårlige aflæsning 4.900 gange så meget til totalen som den gode, så linjen bliver trukket kropsligt hen imod den; under Cauchy log-likelihood bidrager de samme to residualer med 0,527 og 8,133, et forhold på 15,4. Den dårlige aflæsning tæller stadig, den får bare ikke lov at bestemme. Dette er begyndelsen på robust statistik, hvor Hubers tab fra 1964 deler forskellen ved at opføre sig kvadratisk for små residualer og lineært for store,7 og hvor Tukey allerede havde vist, hvor lidt kontaminering der skal til for at gøre sample-variansen til et dårligere værktøj end den gennemsnitlige absolutte afvigelse.8

En historisk note, for god til at udelade. Mindste kvadraters metode blev først publiceret af Legendre i 1805 som et bekvemt algebraisk redskab uden anden begrundelse end, at det virkede.9 Fire år senere vendte Gauss argumentet om: Han tog det som givet, at det aritmetiske gennemsnit er den rigtige måde at kombinere gentagne målinger på, spurgte hvilken fejlfordeling der gør gennemsnittet til den mest sandsynlige værdi, og viste, at i det væsentlige kun én gør — den, der nu er opkaldt efter ham.10 Udledningen i dette kapitel er hans, den er mere end to århundreder gammel, og den er stadig den del, de fleste kurser udelader.

Hvad du nu kan sige, og hvad du stadig ikke kan gøre

Link til afsnittet: Hvad du nu kan sige, og hvad du stadig ikke kan gøre

Optjent. En tabsfunktion er en scoringsregel, og den rangering, den producerer, er en egenskab ved reglen, ikke ved kandidaterne. Hvert tab i dette kursus er den negative log-likelihood for en eller anden antagelse om støjen, med konstanterne smidt væk — Gaussisk giver kvadreret fejl her, Bernoulli giver cross-entropy i kapitel 4, og en kategorisk fordeling over et vokabular giver next-token loss i kapitel 8. Opskriften ændrer sig aldrig: Angiv støjen, skriv likelihood, tag minus logaritmen. Og når antagelsen er forkert, er modellen ikke blot upræcis, den er forkert i en retning, du kan forudsige.

Mangler stadig. Vi fandt bunden af dalen ved at besøge hvert punkt i den. Det virkede for én parameter og seks hundrede kandidater og overlevede to parametre ved 401.301 kandidater på en femtedel af et sekund. Tre parametre med samme opløsning er 201.051.801 kandidater og passer ikke længere i ét array; et lille netværk i kapitel 5 har tusindvis af parametre, og de modeller, kapitel 10 sætter pris på, har milliarder. Brute force her er ikke langsomt, det er aritmetisk umuligt, og intet i dette kapitel antyder et alternativ.

Se dog tilbage på dalen. Når du står ved θ=0.20\theta = 0.20 med et tab på 0,0822, er retningen »nedad« ikke nogen gåde — du kan se den på siden, kurven hælder ned mod højre. Hvis du kunne spørge tabsfunktionen, hvilken vej den hælder på det punkt, du står på, uden at evaluere den andre steder, kunne du tage et skridt den vej, spørge igen og gentage, indtil jorden er flad.

Det spørgsmål har et navn. Hældningen af en funktion i et punkt er dens afledte, og for en funktion af mange parametre er samlingen af hældninger i alle retninger på én gang gradienten. Kapitel 1 kunne ikke bruge en, fordi perceptronens fejl var en trappe uden hældning at spørge om. Dette kapitel har bygget noget bedre: et tab, der er glat overalt, og som kom fra en angivet antagelse snarere end en præference.

Så spørgsmålet for kapitel 3 er ikke længere, om en hældning findes. Det er, hvordan man beregner den, hvorfor det at bevæge sig mod den går nedad snarere end opad — et fortegn, næsten alle kurser beder dig tage på tillid — og hvor langt man skal træde, før man spørger igen, hvilket viser sig at være det ene tal, der afgør, om en træningskørsel konvergerer, oscillerer omkring svaret for evigt eller løber mod uendelig.


Også værd at læse sammen med dette kapitel: Prince, Understanding Deep Learning §5.1–5.2 og appendiks C, som bygger hvert tab i bogen fra maximum likelihood i den rækkefølge, der bruges her; Goodfellow, Bengio og Courville, Deep Learning §3.1–3.11 og §5.5, hvis maximum-likelihood-afsnit også udleder KL-divergensen, som kapitel 4 kræver; Murphy, Probabilistic Machine Learning: An Introduction kapitel 2 og §4.2, om hvad maximum likelihood gør og ikke garanterer; Deisenroth, Faisal og Ong, Mathematics for Machine Learning §6.1–6.4 for sumreglen, produktreglen og Bayes' regel gjort ordentligt; Tom Mitchells korte CMU-note Estimating Probabilities: MLE and MAP (2016); og §22.7 af Dive into Deep Learning, som når samme resultat i kørbar kode.

  1. Fisher, R. A. On the mathematical foundations of theoretical statistics. Philosophical Transactions of the Royal Society A 222, s. 309–368 (1922). Hvor likelihood opstilles som en generel metode, sammen med »parameter«, »statistic«, sufficiency og efficiency. Selve navngivningen og adskillelsen fra probability er et år tidligere: Fisher, R. A., On the "probable error" of a coefficient of correlation deduced from a small sample, Metron 1, s. 3–32 (1921), s. 24–25.

  2. IEEE Standard for Floating-Point Arithmetic, IEEE 754-2019. Definerer binary32 og binary16 samt afrundingsreglerne, der får summeringseksperimentet til at falde ud, som det gør.

  3. Kalamkar, D. et al. A Study of BFLOAT16 for Deep Learning Training. arXiv:1905.12322 (2019). Formatets parametre og argumentet for at bytte mantisse-bits for eksponent-bits.

  4. Micikevicius, P. et al. Mixed Precision Training. ICLR 2018, arXiv:1710.03740. Loss scaling og de målte gradientstørrelser, der gør det nødvendigt i float16.

  5. Goldberg, D. What Every Computer Scientist Should Know About Floating-Point Arithmetic. ACM Computing Surveys 23(1), s. 5–48 (1991). Stadig den bedste enkeltstående forklaring på, hvorfor de to summeringsrækkefølger er uenige.

  6. Kahan, W. Pracniques: further remarks on reducing truncation errors. Communications of the ACM 8(1), s. 40 (1965). Kompenseret summering på en halv side.

  7. Huber, P. J. Robust estimation of a location parameter. The Annals of Mathematical Statistics 35(1), s. 73–101 (1964). Tabet, der er kvadratisk nær nul og lineært i halerne, udledt snarere end lappet sammen.

  8. Tukey, J. W. A survey of sampling from contaminated distributions, i Contributions to Probability and Statistics (Stanford University Press, 1960), s. 448–485.

  9. Legendre, A. M. Nouvelles méthodes pour la détermination des orbites des comètes (Paris, 1805), appendiks Sur la méthode des moindres quarrés. Den første publicering af mindste kvadraters metode som et beregningsredskab.

  10. Gauss, C. F. Theoria Motus Corporum Coelestium (Hamburg, 1809), bog II, §§175–179. Argumentet fra det aritmetiske gennemsnit til den normale fejllov og derfra til mindste kvadraters metode.


Skabt af

David Vicente Campos

Grundlægger af NeuraLIA Labs og medstifter af MyRealFood

Jeg er dataingeniør fra Universitetet i León. Jeg var med til at stifte MyRealFood, hvor jeg som CTO byggede den app, som millioner af mennesker har brugt til at spise bedre, og jeg grundlagde NeuraLIA Labs, hvor jeg bygger AI-produkter. Her skriver jeg om det, jeg har måttet forstå undervejs, sådan som jeg ville ønske, nogen havde forklaret det for mig.

Mere om forfatteren

Udgivet af NeuraLIA Labs.

Få nye indlæg i din indbakke

AI-nyheder, guides og produktopdateringer — en kort mail, når vi udgiver noget, der er værd at bruge tid på.

Vil du hellere have beskeder? De samme indlæg, her:WhatsApp-fællesskab (åbnes i en ny fane)Telegram-kanal (åbnes i en ny fane)

Kursusindeks

Abstract software decision engine with branching paths, probability nodes, and glowing gates.
jev11 min læsning

Jev AI-modellen er bygget til beslutninger, ikke prosa

TypeSafe AI’s Jev får opmærksomhed, fordi den behandler softwareintelligens som et sandsynlighedsproblem: vælg den rigtige gren, tilføj tillid, og undgå at betale en LLM for at skrive tekst, når kode har brug for en beslutning.

Abstract agent runtime sorting documents, memory blocks and pointer nodes inside a bounded context frame.
context-engineering11 min læsning

Kontekstteknik til langsigtede AI-agenter

Langvarige agenter fejler ikke kun, fordi vinduet er lille. De fejler, når filer, tool-outputs og forældet historik fortrænger den opgave, agenten skulle færdiggøre.

Klar til at lade LIA vælge for dig?

Byg med alle AI-modeller ét sted — kom gratis i gang i dag.