Mistä häviöfunktio syntyy: likelihood, ei käytäntö
Kolme viivaa samoille 20 mittaukselle ja kolme pisteytyssääntöä, jotka valitsevat eri voittajat. Squared error on valinta.
Tällä sivulla
Osia leikkaava terä kuluu. Kymmenen tunnin vuoron aikana se menettää terävyyttään sen verran, että hihnalta tulevat osat ovat lopulta millimetrin murto-osan leveämpiä kuin alussa, ja kun ne ylittävät 23,5 millimetriä, tarkastus hylkää ne. Kukaan tehtaalla ei tiedä, milloin se tapahtuu. Heillä on työntömitta, muistivihko ja kaksikymmentä mittausta viime tiistailta: tunnit terän vaihdosta ja osan leveys sillä hetkellä.
Joku piirtää pisteiden läpi viivan. Joku toinen piirtää hieman erilaisen. Kolmas piirtää kolmannen. Kaikki kolme näyttävät paperilla järkeviltä, ja ne ovat eri mieltä terän vaihtoajasta useiden tuntien verran — tässä tehtaassa se on ero rauhallisen viikon ja romutetun erän välillä.
Mikä viiva on parempi?
Näin esitettynä kysymykseen ei ole vastausta. Ei vaikeaa vastausta — ei vastausta lainkaan. ”Parempi” ei ole viivan ominaisuus samalla tavalla kuin sen kulmakerroin on; se on viivan ominaisuus yhdessä viivojen pisteytyssäännön kanssa, ja ennen kuin joku kirjoittaa säännön ylös, ei ole mitään laskettavaa. Tämä luku ottaa tuon lauseen vakavasti ja päättyy havaintoon, että koneoppimisen yleisin sääntö ei ole käytäntö vaan seuraus maailmaa koskevasta väitteestä — väitteestä, jonka voi testata ja joka on joskus väärä.
Yksi tunnustus ennen ensimmäistä koodiriviä. Nämä kaksikymmentä mittausta eivät ole oikeasta tehtaasta: generoin ne valitsemastani suorasta, , sekä satunnaisesta kohinasta, jonka hajonta on noin millimetrin kymmenesosa. Sillä on väliä, koska kaikki alla käsittelee sitä, palauttaako menetelmä totuuden, ja sen tarkistamiseen totuus on tiedettävä etukäteen. Siispä: 0,30 millimetriä tunnissa on kirjan takaosan vastaus. Et saa käyttää sitä, vain tarkistaa sitä vasten.
Kolme sääntöä, kolme voittajaa
Linkki osioon: Kolme sääntöä, kolme voittajaaTässä ovat mittaukset ja kolme viivaa, pisteytettynä kolmella tavalla: squared error, johon kaikki tarttuvat; absolute error, johon tilastotieteilijä saattaisi tarttua; ja worst error, johon koneistaja tarttuisi, koska tarkastajaa ei kiinnosta keskiarvosi — hän hylkää yksittäisen toleranssin ylittävän osan.
NumPy tulee mukaan tässä, yhden luvun puhdas-Python-perceptronin jälkeen, yhdestä syystä: tämän luvun lopussa arvioimme neljäsataatuhatta ehdokasviivaa jokaista kahtakymmentä mittausta vasten, ja Python-silmukka on siihen väärä työkalu. Se on myös notaatio, jolla jokainen alla viitattu lähde on kirjoitettu.
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}") Korostettujen rivien suure on residuaali: se, mitä viiva sanoi, miinus se, mitä työntömitta sanoi, yksi luku kutakin mittausta kohti. Jokainen tämän luvun pisteytyssääntö ja jokainen häviöfunktio sitä seuraavissa kahdessakymmenessäkahdeksassa luvussa on jokin tapa puristaa residuaalien lista yhdeksi luvuksi. Ne eroavat vain siinä, miten ne puristavat.
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.320Lue sarakkeita, älä rivejä. Squared error sanoo B, absolute error sanoo A, worst error sanoo C: kolme sääntöä, kolme voittajaa samoilla kahdellakymmenellä pisteellä.
Valitsin nämä kolme viivaa niin, että ne olisivat eri mieltä, ja se on syytä sanoa suoraan. Olennaista on, kuinka helppoa se oli — muutaman minuutin haku järkevän näköisten vakiotermien ja kulmakertoimien joukosta tuottaa satoja tällaisia kolmikkoja. Järjestys on valitsemasi säännön ominaisuus, ei viivoja koskeva tosiasia, joten sääntö ei ole toteutuksen yksityiskohta: se on ongelman määritelmä. Tämä nostaa esiin kysymyksen, jota varten tämä luku on olemassa: millä perusteella valitset sen?
Yksi parametri ja laakso
Linkki osioon: Yksi parametri ja laaksoEnsin pienempi asia, koska viivoja ei ole kolme vaan äärettömän monta. Otetaan toistaiseksi squared error, koska kaikki ottavat sen, ja kutistetaan ongelma yhdeksi luvuksi samalla tempulla, joka säästi perceptronilta yksitoistatuhatta epookkia luvussa 1: vähennetään keskiarvo molemmista sarakkeista. Kun pistepilvi on keskitetty origoon, paras viiva squared error -säännön alla kulkee täsmälleen origon kautta — joten vakiotermi on ratkaistu ja jäljelle jää vain kulmakerroin.
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.010115Kuusisataayksi ehdokaskulmakerrointa, yksi voittaja: 0,293 millimetriä tunnissa totuutta 0,300 vastaan. Kaksikymmentä kohinaista mittausta ja for-silmukka pääsivät sadanneksen millimetrin tuntivauhdin päähän — kaksi ja kolmasosa prosenttia.
Kiinnostava osa ei ole voittaja vaan haun muoto. Tulosta koko käyrä käännettynä niin, että loss kulkee vasemmalta oikealle:
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.7928Se on laakso sivulta nähtynä. Sillä on yksi pohja, seinämät nousevat pehmeästi molemmin puolin, ja — tämä on se osa, jota luvun 1 portaikko ei voinut tarjota — jokaisessa sen pisteessä on hyvin määritelty ”alamäen” suunta. Muista tuo muoto. Luku 3 käsittelee kokonaan sitä, miten sitä kävellään alas käymättä kaikissa kuudessasadaassayhdessä pisteessä, ja sitä, mikä muuttuu, kun laaksolla on useampi kuin yksi pohja.
Miksi siis neliö?
Linkki osioon: Miksi siis neliö?Meillä on laakso, koska korotimme neliöön. Absolute error olisi tehnyt sen pohjaan kulman; worst error olisi tuottanut tasaisia osuuksia, joilla viivan liikuttaminen ei muuta mitään. Neliöiminen on kiistatta kätevää — ja kätevyys on suunnilleen se syy, jonka useimmat kurssit antavat, neljällä tavalla puettuna: se tekee virheistä positiivisia (niin tekee itseisarvokin); se rankaisee suuria virheitä enemmän (miksi pitäisi?); se on derivoituva (niin on neljäs potenssikin); sitä kaikki käyttävät (niin käyttävät, eikä se ole argumentti).
Rehellinen kanta on tämä. Squared error valitsi viivan B ja absolute error valitsi viivan A. Toinen niistä on oikea tälle tehtaalle ja toinen väärä, eikä mikään tähän mennessä sanottu kerro kumpi. Säännön valitsemiseksi sinun on tiedettävä jotain siitä, miten mittaukset päätyivät poikkeamaan viivasta, ja se on kysymys maailmasta, ei matematiikasta. Siihen vastaaminen tarvitsee yhden pienen koneiston osan.
Viivan likelihood
Linkki osioon: Viivan likelihoodTässä on väite, joka muuttaa kysymyksen ”mikä viiva on parempi” kysymykseksi, johon on vastaus.
Oletetaan, että osan leveys on viiva plus satunnainen virhe, ja oletetaan, että virhe on peräisin Gaussisesta jakaumasta — kellokäyrästä — jonka keskiarvo on nolla ja keskihajonta :
Gaussisen jakauman tiheys on
Tee nyt jotain, mihin perceptron ei pystynyt. Annetulle ehdokaskulmakertoimelle jokaisella mittauksella on residuaali, ja yllä oleva kaava muuttaa tuon residuaalin luvuksi: kuinka uskottava täsmälleen tämän kokoinen virhe on, jos tämä kulmakerroin on totuus? Viivalla oleva mittaus saa suuren luvun, puoli millimetriä sivussa oleva pienen.
Mittaukset ovat riippumattomia — työntömitta ei muista edellistä osaa — joten tulosääntö sanoo, että koko vihkon uskottavuus on yksittäisten tiheyksien tulo. Tuo tulo on parametrin likelihood.1 Huomaa suunta, koska juuri siitä Bayesin säännössä on kyse: data on kiinteä ja tunnettu, ja parametri vaihtelee. Tämä ei ole ”kulmakertoimen todennäköisyys”. Se on todennäköisyys, jonka malli antaa saamallesi datalle, luettuna kulmakertoimen funktiona.
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.190312Kulmakerroin 0,293 tekee tästä vihkosta 46 000 kertaa uskottavamman kuin 0,25 ja 127 miljoonaa kertaa uskottavamman kuin 0,35. Maximum likelihood on periaate, jonka mukaan valitset parametrin, joka tekee todella havaitsemastasi mahdollisimman vähän yllättävää. Se ei ole teoreema vaan ehdotus siitä, mitä ”paras” pitäisi tarkoittaa — ehdotus, jolla on sisältöä, koska se pakottaa sinut sanomaan oletuksesi kohinasta ennen kuin saat pisteyttää mitään.
Tulo hajoaa
Linkki osioon: Tulo hajoaaAja samat kolme koodiriviä yhden vuoron sijasta kuukauden vuoroille, ja menetelmä kaatuu.
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+308Kaksituhatta kertolaskua ja vastaus on inf. Muuta yhtä vakiota — epätarkempi työntömitta, jolloin tiheydet ovat pienempiä kuin 1 eivätkä suurempia — ja sama koodi palauttaa 0.0. Molemmat vastaukset ovat väärin, vastakkaisiin suuntiin, kumpikaan ei nosta poikkeusta, jonka voisit napata, eikä jälkimmäinen edes tulosta varoitusta.
Matematiikassa ei ole mitään vikaa. Likelihood noilla asetuksilla on täysin hyvin määritelty äärellinen luku: sen luonnollinen logaritmi on 1400,91, joten itse luku on noin . Ongelma on, ettei tietokoneellasi ole tuota lukua, ja kannattaa ymmärtää tarkasti, mitkä luvut sillä on, koska tämä ei ole viimeinen kerta, kun se päättää lopputuloksen.
Mistä neliö tulee
Linkki osioon: Mistä neliö tuleeRäjähtävän tulon korjaus on tavallinen: otetaan logaritmit. Logaritmi muuttaa tulot summiksi, se on aidosti kasvava, joten se ei voi siirtää maksimin sijaintia, ja kahdentuhannen maltillisen luvun summa on jotain, minkä float64 käsittelee valittamatta. Käytännön mukaan otamme negatiivisen log-likelihoodin, jotta parempi tarkoittaa pienempää. Sijoita nyt Gaussinen tiheys ja katso, mitä tapahtuu.
-
Aloita tulosta. Likelihood on , missä on yllä oleva Gaussinen tiheys.
-
Ota miinus logaritmi. Tulosta tulee summa, ja tiheyden eksponenttifunktio kumoutuu logaritmin kanssa suoraan:
- Heitä pois kaikki, mikä ei sisällä termiä . Ensimmäinen termi on vakio. Summan edessä oleva on positiivinen vakio, eikä funktion skaalaaminen positiivisella vakiolla voi siirtää sen minimin paikkaa. Jäljelle jää
mikä on neliöityjen residuaalien summa — se asia, jolla aloitimme luvun, koska se on ensimmäinen asia, joka kenelle tahansa tulee mieleen.
Tämä on tulos, jota varten luku on olemassa, ja se ansaitsee tulla sanotuksi ilman varauksia: squared error ei ole käytäntö. Se on Gaussisen jakauman negatiivinen log-likelihood, kun vakiot on poistettu. Squared error -arvon minimointi on täsmälleen sama teko kuin väittää, että virheesi ovat Gaussisia, ja kysyä, mikä parametri tekee datastasi vähiten yllättävää. Teit tuon väitteen koko ajan; sinulle ei vain kerrottu sitä.
Ekvivalenssi on tarkistettavissa, joten tarkista se: käy läpi samat kuusisataayksi kulmakerrointa täydellä negatiivisella log-likelihoodilla, vakioineen kaikkineen, ja tavallisella squared error -arvolla.
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: TrueEri luvut pystyakselilla, ja toinen niistä on negatiivinen, mitä neliösumma ei koskaan ole: negatiivinen log-likelihood voi mennä alle nollan, koska tiheys voi ylittää arvon 1. Sama laakson pohja, samaan viimeiseen ruudukon pisteeseen asti.
Näytä koko derivointi
Mitkä poisheitot ovat täsmälleen turvallisia? Sama liike esiintyy jokaisessa luvussa, joka johtaa lossin, eikä se ole aina viaton.
Additiivisen vakion pudottaminen on turvallista aina, kun se ei riipu optimoimastasi parametrista, ja positiivisen kertovan vakion pudottaminen on turvallista, koska mille tahansa . Molemmat epäonnistuvat heti, kun myös sovitetaan: silloin ei ole lainkaan vakio, vaan termi, joka estää mallia väittämästä ja ääretöntä uskottavuutta. Se on täsmälleen seuraava osio.
Ne epäonnistuvat taas eri tavalla luvussa 3: kertova vakio ei siirrä minimiä, mutta se skaalaa gradientin, ja gradient kerrotaan learning rate -arvolla. Jakaminen luvulla , jotta saadaan mean squared error eikä sum, on vastaukselle näkymätöntä ja koulutusajolle erittäin näkyvää — summalla batch-koon kaksinkertaistaminen kaksinkertaistaa jokaisen ottamasi askeleen.
Sigmakaan ei ole ilmainen
Linkki osioon: Sigmakaan ei ole ilmainenKiinnitimme -arvon 0,12:een mielivaltaisesti, eikä kukaan tehtaalla tiedä työntömittansa virheen hajontaa. Käsittele sitä toisena tuntemattomana ja anna maximum likelihood -periaatteen päättää sekin. Tässä äsken pois heitetty vakiotermi palaa takaisin, koska se on ainoa asia mallin ja täydellisen tarkkuusväitteen välissä.
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.1006Nämä kaksi ovat samaa mieltä neljän desimaalin tarkkuudella, eivätkä sattumalta: derivoimalla tuon lausekkeen ja asettamalla sen nollaksi saadaan täsmälleen . Siis mean squared error ei ole vain varianssin kaltainen. Tässä mallissa se on kohinan varianssin maximum-likelihood-estimaatti — luku, jota olet koko ajan minimoinut, oli arvio siitä, kuinka kohinainen anturisi on.
Yksi mutka, helppo sanoa ja kallis löytää myöhemmin uudelleen: tuo estimaatti on alaspäin harhainen, koska residuaalit mitattiin sovitetta vasten, joka oli itse valittu tekemään niistä pieniä. Simuloi se — kaksisataatuhatta vihkoa, joissa kussakin on kaksikymmentä mittausta, vedettynä jakaumasta, jonka todellinen varianssi on täsmälleen 1, ja jossa sovitteen yksi parametri estimoidaan itse mittauksista. Neliösumman jakaminen arvolla antaa keskiarvoksi 0,9501; jakaminen arvolla antaa 1,0001; ja on tasan 0,95. Jokainen sovittamasi parametri maksaa yhden vapausasteen, ja tämä on pienin näkyvä esimerkki paljon suuremmasta ongelmasta: malli näyttää aina paremmalta sillä datalla, johon se sovitettiin. Luku 4 muuttaa tämän datan sivuun pitämisen kurinalaisuudeksi, ja luku 6 antaa ilmiölle nimen.
Loss on väite kohinasta
Linkki osioon: Loss on väite kohinastaJos squared error väittää, että kohina on Gaussista, seuraava kysymys on, mitä tapahtuu, kun väite on väärä. Ei hieman väärä — vaan väärä sillä tavalla kuin oikeat mittaukset ovat vääriä.
Tehtaan lattialla useimmat työntömittalukemat ovat oikein millimetrin kymmenesosan tarkkuudella, ja kerran tai pari vuorossa lastun pala jää leuan alle ja lukema heittää useita millimetrejä. Tällaiset virheet ovat paksuhäntäisiä: suurimman osan ajasta pieniä, toisinaan valtavia, ja valtavia paljon useammin kuin kellokäyrä sallii. Cauchyn jakauma on tuon käyttäytymisen standardi puhdas malli, ja sen tiheys on yhtä yksinkertainen kuin Gaussisen:
Ero on hännässä: Gaussinen pienenee muodossa , raa'an nopeasti, ja Cauchy muodossa , tuskin lainkaan. Seuraus on helpompi nähdä kuin sanoa:
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.10Gaussisen otosvarianssi asettuu arvoon 0,0144, joka on , ja pysyy siinä. Cauchyn nousee ja jatkaa nousua niin kauan kuin otostat, koska sillä ei ole mitään, mihin konvergoida: Cauchyn jakaumalla ei ole varianssia eikä edes keskiarvoa. Squared error, jonka koko tehtävä on minimoida neliöiden keskiarvoa, pyytää suuretta, jota ei ole olemassa.
Tässä siis yksi vuoro, jossa työntömittaa huijattiin. Samat kaksikymmentä tuntia, sama terä, sama 0,30 millimetriä tunnissa oleva drift — vain kohina on nyt Cauchy. Sovita kahdesti: kerran minimoimalla neliöidyt residuaalit, kerran minimoimalla datan oikeasti tuottaneen kohinan negatiivinen log-likelihood. Keskittämistemppu ei auta tässä — se kiinnittää vakiotermin vain squared error -tapauksessa — joten molemmat sovitteet tehdään raa'alla voimalla vakiotermien ja kulmakertoimien ruudukon yli, koska meillä ei vieläkään ole keinoa löytää laakson pohjaa muuten kuin käymällä siellä.
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")Kaksi korostettua riviä ovat koko ero sovitteiden välillä. Ota Cauchyn tiheyden logaritmi, pudota vakiot täsmälleen kuten ennen, ja on se, mikä jää. Sama resepti, eri väite kohinasta.
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 evaluatedPienimmät neliöt ilmoittaa driftiksi 0,108 millimetriä tunnissa, suunnilleen kolmasosan todellisesta vauhdista, ja päättelee, että terä kelpaa tuntiin 19,6 asti. Todellinen vastaus on tunti 11,7. Tuon sovitteen perusteella tehdas ajaa prässiä kahdeksan ylimääräistä tuntia ja valmistaa toleranssin ylittäviä osia alan standardeimman häviöfunktion arvovallalla. Cauchy-sovite, joka käyttää samoja kahtakymmentä mittausta, samaa ruudukkoa ja yhden rivin eroa koodissa, osuu tuntiin 11,8.
Kaksi vastalausetta ansaitsee vastaukset, koska molemmat ovat ensimmäinen asia, jonka hyvä insinööri sanoo.
Poikkeava havainto on ilmeinen — poista se. Voit tehdä niin, ja se auttaa, eikä se riitä. Yhden huonoimman mittauksen poistaminen siirtää pienimpien neliöiden kulmakertoimen arvosta 0,108 arvoon 0,239, mikä silti asettaa terän vaihdon tuntiin 13,1, puolitoista tuntia myöhään; huonoimman poistaminen, uudelleensovitus ja sen poistaminen, mikä on nyt huonoin, vie arvoon 0,286 — ja huomaa, että tämä on jo menettely, ei havainto: poista alkuperäisen sovitteen kaksi suurinta residuaalia sen sijaan, ja päädyt arvoon 0,223. Olet nyt kuitenkin tehnyt harkintapäätöksiä, joita et voi kirjoittaa ylös tai puolustaa, eikä säännön automatisointi pelasta sitä: pudota-suurin-residuaali-ja-sovita-uudelleen, ajettuna tuhannella simuloidulla vuorolla, saa mediaanikulmakerroinvirheeksi 0,0177 likelihood-sovitteen arvoa 0,0100 vastaan, ja on pielessä yli 0,05:llä 14,7 prosentissa vuoroista likelihoodin 1,3 prosenttia vastaan. Poistaminen on paikka väärän oletuksen päällä. Likelihood ei tarvitse paikkaa, koska se ei koskaan olettanut poikkeamaa mahdottomaksi.
Valitsit onnekkaan datasarjan. Tuo vastalause on täsmälleen oikea, minkä vuoksi viimeinen koe simuloi tuhat riippumatonta vuoroa ja sovittaa molemmilla tavoilla jokaisen.
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 shiftsMediaani, ei keskiarvo, samasta syystä kuin kaikki muukin tässä osiossa: pienimpien neliöiden virheitä ajaa Cauchy, joten niiden keskiarvo ei ole vakaa raportoitava asia. Pienimmät neliöt on pahasti väärässä kahdessa vuorossa viidestä; likelihood-sovite on pahasti väärässä yhdessä vuorossa seitsemästäkymmenestäseitsemästä, ja sen pahin epäonnistuminen tuhannessa vuorossa on alle viidesosa pienimpien neliöiden pahimmasta.
Mikään tästä ei tee squared error -säännöstä huonoa. Se tekee siitä tarkan, ja aritmetiikka kertoo täsmälleen miksi. Ota residuaali 0,1 mm ja toinen 7 mm. Neliöitynä huono lukema vaikuttaa kokonaisuuteen 4 900 kertaa niin paljon kuin hyvä, joten viiva raahautuu kokonaisena sitä kohti; Cauchyn log-likelihoodin alla samat kaksi residuaalia tuottavat 0,527 ja 8,133, suhteen 15,4. Huono lukema lasketaan edelleen, se ei vain saa päättää. Tämä on robustin tilastotieteen alku, jossa Huberin vuoden 1964 loss jakaa eron käyttäytymällä kvadraattisesti pienillä residuaaleilla ja lineaarisesti suurilla,7 ja jossa Tukey oli jo osoittanut, kuinka vähän kontaminaatiota tarvitaan tekemään otosvarianssista huonompi työkalu kuin keskimääräinen absoluuttinen poikkeama.8
Yksi historiallinen huomio, liian hyvä jätettäväksi pois. Pienimmät neliöt julkaisi ensin Legendre vuonna 1805 kätevänä algebrallisena välineenä ilman muuta perustelua kuin että se toimi.9 Neljä vuotta myöhemmin Gauss ajoi argumentin takaperin: hän otti annettuna, että aritmeettinen keskiarvo on oikea tapa yhdistää toistomittauksia, kysyi, mikä virhejakauma tekee keskiarvosta todennäköisimmän arvon, ja näytti, että olennaisesti vain yksi tekee niin — se, joka on nyt nimetty hänen mukaansa.10 Tämän luvun johtaminen on hänen, se on yli kaksi vuosisataa vanha, ja se on yhä se osa, jonka useimmat kurssit jättävät pois.
Mitä voit nyt sanoa ja mitä et vieläkään osaa tehdä
Linkki osioon: Mitä voit nyt sanoa ja mitä et vieläkään osaa tehdäAnsaittu. Häviöfunktio on pisteytyssääntö, ja sen tuottama järjestys on säännön, ei ehdokkaiden, ominaisuus. Jokainen tämän kurssin loss on jonkin kohinaa koskevan oletuksen negatiivinen log-likelihood, vakiot pois heitettyinä — Gaussinen antaa tässä squared error -säännön, Bernoulli antaa cross-entropyn luvussa 4, ja kategorinen jakauma sanaston yli antaa next-token loss -arvon luvussa 8. Resepti ei koskaan muutu: sano kohina, kirjoita likelihood, ota miinus logaritmi. Ja kun oletus on väärä, malli ei ole vain epätarkka, vaan väärässä suuntaan, jonka voit ennustaa.
Vielä puuttuu. Löysimme laakson pohjan käymällä sen jokaisessa pisteessä. Se toimi yhdelle parametrille ja kuudellesadalle ehdokkaalle, ja selvisi kahdesta parametrista 401 301 ehdokkaalla viidesosasekunnissa. Kolme parametria samalla resoluutiolla on 201 051 801 ehdokasta eikä enää mahdu yhteen taulukkoon; pienessä verkossa luvussa 5 on tuhansia parametreja, ja malleissa, joille luku 10 laskee hinnan, on miljardeja. Raaka voima ei ole tässä hidasta, se on aritmeettisesti mahdotonta, eikä mikään tässä luvussa ehdota vaihtoehtoa.
Katso silti takaisin laaksoon. Kun seisot kohdassa loss-arvolla 0,0822, ”alamäen” suunta ei ole mysteeri — näet sen sivulla, käyrä laskee oikealle. Jos voisit kysyä häviöfunktiolta, mihin suuntaan se kallistuu siinä pisteessä, jossa seisot, arvioimatta sitä missään muualla, voisit ottaa askeleen siihen suuntaan, kysyä uudelleen ja toistaa, kunnes maa on tasainen.
Tuolla kysymyksellä on nimi. Funktion kulmakerroin pisteessä on sen derivaatta, ja monen parametrin funktiolle kaikkien suuntien kulmakertoimien kokoelma kerralla on gradient. Luku 1 ei voinut käyttää sellaista, koska perceptronin virhe oli portaikko, jolla ei ollut kysyttävää kulmakerrointa. Tämä luku on rakentanut jotain parempaa: loss-arvon, joka on sileä kaikkialla ja joka tuli julkilausutusta oletuksesta eikä mieltymyksestä.
Siksi luvun 3 kysymys ei ole enää, onko kulmakerrointa olemassa. Se on, miten se lasketaan, miksi liikkuminen sitä vastaan vie alamäkeen eikä ylämäkeen — merkki, jonka melkein jokainen kurssi pyytää ottamaan uskolla — ja kuinka pitkälle pitää astua ennen kuin kysyy uudelleen, mikä osoittautuu yhdeksi luvuksi, joka päättää konvergoiko koulutusajo, värähteleekö se vastauksen ympärillä ikuisesti vai juokseeko se äärettömyyteen.
Lähteet ja menetelmä
Linkki osioon: Lähteet ja menetelmäTämän luvun rinnalla kannattaa lukea myös: Prince, Understanding Deep Learning §5.1–5.2 ja liite C, joka rakentaa kirjan jokaisen loss-arvon maximum likelihood -periaatteesta tässä käytetyssä järjestyksessä; Goodfellow, Bengio ja Courville, Deep Learning §3.1–3.11 ja §5.5, jonka maximum-likelihood-osio johtaa myös luvun 4 tarvitseman KL-divergenssin; Murphy, Probabilistic Machine Learning: An Introduction luku 2 ja §4.2 siitä, mitä maximum likelihood takaa ja mitä ei; Deisenroth, Faisal ja Ong, Mathematics for Machine Learning §6.1–6.4 summasäännön, tulosäännön ja Bayesin säännön kunnolliseen käsittelyyn; Tom Mitchellin lyhyt CMU-muistio Estimating Probabilities: MLE and MAP (2016); sekä Dive into Deep Learning -teoksen §22.7, joka päätyy samaan tulokseen ajettavalla koodilla.
Viitteet
Linkki osioon: Viitteet-
Fisher, R. A. On the mathematical foundations of theoretical statistics. Philosophical Transactions of the Royal Society A 222, s. 309–368 (1922). Teos, jossa likelihood esitetään yleisenä menetelmänä yhdessä käsitteiden ”parameter”, ”statistic”, riittävyys ja tehokkuus kanssa. Itse nimeäminen ja erottaminen todennäköisyydestä on vuotta aiempi: 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. ↩
-
IEEE Standard for Floating-Point Arithmetic, IEEE 754-2019. Määrittelee binary32- ja binary16-muodot sekä pyöristyssäännöt, joiden vuoksi summauskoe päätyy tulokseensa. ↩
-
Kalamkar, D. et al. A Study of BFLOAT16 for Deep Learning Training. arXiv:1905.12322 (2019). Muodon parametrit ja perustelu mantissabittien vaihtamiselle eksponenttibitteihin. ↩
-
Micikevicius, P. et al. Mixed Precision Training. ICLR 2018, arXiv:1710.03740. Loss scaling ja mitatut gradienttien suuruudet, jotka tekevät siitä välttämättömän float16:ssa. ↩
-
Goldberg, D. What Every Computer Scientist Should Know About Floating-Point Arithmetic. ACM Computing Surveys 23(1), s. 5–48 (1991). Yhä paras yksittäinen selitys sille, miksi kaksi summausjärjestystä ovat eri mieltä. ↩
-
Kahan, W. Pracniques: further remarks on reducing truncation errors. Communications of the ACM 8(1), s. 40 (1965). Kompensoitu summaus puolessa sivussa. ↩
-
Huber, P. J. Robust estimation of a location parameter. The Annals of Mathematical Statistics 35(1), s. 73–101 (1964). Loss, joka on kvadraattinen lähellä nollaa ja lineaarinen hännissä, johdettuna eikä paikattuna kasaan. ↩
-
Tukey, J. W. A survey of sampling from contaminated distributions, teoksessa Contributions to Probability and Statistics (Stanford University Press, 1960), s. 448–485. ↩
-
Legendre, A. M. Nouvelles méthodes pour la détermination des orbites des comètes (Pariisi, 1805), liite Sur la méthode des moindres quarrés. Ensimmäinen pienimpien neliöiden julkaisu laskennallisena välineenä. ↩
-
Gauss, C. F. Theoria Motus Corporum Coelestium (Hampuri, 1809), kirja II, §§175–179. Argumentti aritmeettisesta keskiarvosta normaalin virhelakiin ja siitä pienimpiin neliöihin. ↩