Ves al contingut
2/30Capítol 2 de 30

D’on surt una funció de pèrdua: versemblança, no convenció

Tres rectes traçades a ull sobre les mateixes vint mesures, i tres regles de puntuació que coronen tres guanyadores diferents.

En aquesta pàgina

La fulla que talla les peces es desgasta. Durant un torn de deu hores perd prou tall perquè les peces surtin de la cinta una fracció de mil·límetre més amples que al principi, i quan passen de 23,5 mil·límetres la inspecció les rebutja. Ningú de la planta sap quan passa això. El que tenen és un peu de rei, una llibreta i vint lectures del dimarts passat: les hores des que es va canviar la fulla i l’amplada de la peça mesurada en aquell moment.

Algú dibuixa una recta entre els punts. Algú altre en dibuixa una de lleugerament diferent. Una tercera persona en dibuixa una tercera. Totes tres semblen raonables sobre el paper, i discrepen en diverses hores sobre quan cal canviar la fulla: en aquesta planta, la diferència entre una setmana tranquil·la i un lot llençat.

Quina recta és millor?

Tal com està formulada, aquesta pregunta no té resposta. No una resposta difícil: cap resposta. «Millor» no és una propietat d’una recta com ho és el seu pendent; és una propietat d’una recta juntament amb una regla per puntuar rectes, i fins que algú no escrigui la regla no hi ha res a calcular. Aquest capítol es pren aquesta frase seriosament, i acaba amb el descobriment que la regla més comuna en machine learning no és una convenció sinó la conseqüència d’una afirmació sobre el món: una que pots posar a prova, i una que de vegades és falsa.

Una confessió abans de la primera línia de codi. Aquestes vint lectures no provenen d’una fàbrica real: les vaig generar a partir d’una recta que vaig triar, width=20.00+0.30h\text{width} = 20.00 + 0.30 \cdot h, més soroll aleatori amb una dispersió d’aproximadament una dècima de mil·límetre. Això importa, perquè tot el que segueix tracta de si un mètode recupera una veritat, i l’única manera de comprovar-ho és conèixer la veritat per endavant. Així doncs: 0,30 mil·límetres per hora és la resposta del final del llibre. No la pots fer servir, només comparar-t’hi.

Aquí tens les lectures i les tres rectes, puntuades de tres maneres: error quadràtic, que és allò a què tothom recorre; error absolut, que podria triar un estadístic; i pitjor error, que triaria el maquinista, perquè a l’inspector no li importa la teva mitjana: rebutja la peça individual que queda fora de tolerància.

NumPy arriba aquí, un capítol després del perceptró en Python pur, per una raó: al final d’aquest capítol avaluarem quatre-centes mil rectes candidates contra vint lectures cadascuna, i un bucle de Python és l’eina equivocada per a això. També és la notació en què estan escrites totes les fonts citades més avall.

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}")               

La quantitat de les línies ressaltades és el residu: el que deia la recta menys el que deia el peu de rei, un nombre per lectura. Tota regla de puntuació d’aquest capítol, i tota loss function dels vint-i-vuit capítols posteriors, és alguna manera d’aixafar una llista de residus fins a convertir-la en un únic nombre. Només difereixen en com l’aixafen.

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

Llegeix les columnes, no les files. L’error quadràtic diu B, l’error absolut diu A, el pitjor error diu C: tres regles, tres guanyadores, sobre els mateixos vint punts.

Vaig triar aquestes tres rectes perquè discrepassin, i ho he de dir clarament. La qüestió és com de fàcil va ser: uns minuts de cerca entre interceptes i pendents d’aspecte raonable produeixen centenars de triples així. El rànquing és una propietat de la regla que has triat, no un fet sobre les rectes, de manera que la regla no és un detall d’implementació: és la definició del problema. Això planteja la pregunta que aquest capítol existeix per respondre: amb quin criteri la tries?

Primer una qüestió més petita, perquè no hi ha tres rectes sinó infinites. Prenem per ara l’error quadràtic, ja que és el que tothom pren, i reduïm el problema a un sol nombre amb el truc que va estalviar al perceptró onze mil èpoques al capítol 1: restar la mitjana de totes dues columnes. Un cop el núvol de punts està centrat a l’origen, la millor recta sota error quadràtic passa exactament per l’origen; per tant, l’intercepte queda resolt i només resta triar el pendent.

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

Sis-cents un pendents candidats, un guanyador: 0,293 mil·límetres per hora contra una veritat de 0,300. Vint lectures sorolloses i un for-loop han arribat a menys d’una centèsima de mil·límetre per hora: un dos coma tres per cent.

La part interessant no és el guanyador, sinó la forma de la cerca. Imprimeix tota la corba, girada perquè la pèrdua vagi d’esquerra a dreta:

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

Això és una vall, vista de costat. Té un únic fons, les parets pugen suaument a tots dos costats i —aquesta és la part que l’escala del capítol 1 no podia oferir— en cada punt hi ha una direcció ben definida de «baixada». Recorda aquesta forma. El capítol 3 tracta íntegrament de caminar-hi avall sense visitar els sis-cents un punts, i de què canvia quan una vall té més d’un fons.

Tenim una vall perquè hem elevat al quadrat. L’error absolut li hauria donat un angle al fons; el pitjor error li hauria donat trams plans en què moure la recta no canviaria res. Elevar al quadrat és innegablement còmode, i la comoditat és aproximadament la raó que donen la majoria de cursos, disfressada de quatre maneres: fa positius els errors (el valor absolut també); penalitza més els errors grans (per què ho hauria de fer?); és diferenciable (la quarta potència també); és el que fa servir tothom (ho és, i això no és cap argument).

Aquesta és la posició honesta. L’error quadràtic ha seleccionat la recta B i l’error absolut ha seleccionat la recta A. Una d’aquestes és correcta per a aquesta fàbrica i l’altra és equivocada, i res del que s’ha dit fins ara no et pot dir quina. Per triar la regla has de saber alguna cosa sobre com les lectures han arribat a diferir de la recta, i això és una pregunta sobre el món, no sobre les matemàtiques. Respondre-la necessita una petita peça de maquinària.

Aquesta és l’afirmació que converteix «quina recta és millor» en una pregunta amb resposta.

Suposa que l’amplada d’una peça és la recta més un error aleatori, i suposa que aquest error s’extreu d’una gaussiana —la corba de campana— amb mitjana zero i desviació estàndard σ\sigma:

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

La densitat de la gaussiana és

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

Ara fes una cosa que el perceptró no podia fer. Per a un pendent candidat donat θ\theta, cada lectura té un residu, i la fórmula anterior converteix aquest residu en un nombre: com de plausible és un error exactament d’aquesta mida, si aquest pendent és la veritat? Una lectura sobre la recta obté un nombre gran; una lectura mig mil·límetre desviada, un de petit.

Les lectures són independents —el peu de rei no recorda l’última peça—, així que la regla del producte diu que la plausibilitat de la llibreta sencera és el producte de les densitats individuals. Aquest producte és la versemblança de θ\theta.1 Fixa’t en la direcció, perquè és la direcció de què tracta la regla de Bayes: les dades són fixes i conegudes, i el que varia és el paràmetre. Això no és «la probabilitat del pendent». És la probabilitat que el model assigna a les dades que realment has obtingut, llegida com una funció del pendent.

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

Un pendent de 0,293 fa aquesta llibreta quaranta-sis mil vegades més plausible que 0,25, i cent vint-i-set milions de vegades més plausible que 0,35. La màxima versemblança és el principi segons el qual tries el paràmetre que fa que allò que realment has observat sigui tan poc sorprenent com sigui possible. No és un teorema, sinó una proposta sobre què hauria de voler dir «millor»: una proposta amb contingut, perquè t’obliga a declarar la teva suposició sobre el soroll abans que se’t permeti puntuar res.

Executa les mateixes tres línies de codi sobre un mes de torns en lloc d’un de sol, i el mètode cau.

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

Dues mil multiplicacions i la resposta és inf. Canvia una constant —un peu de rei més barroer, de manera que les densitats surtin més petites que 1 en lloc de més grans— i el mateix codi retorna 0.0. Totes dues respostes són incorrectes, en direccions oposades, cap no llança una excepció que puguis capturar, i la segona ni tan sols imprimeix un avís.

No hi ha res malament en les matemàtiques. La versemblança amb aquests paràmetres és un nombre finit perfectament ben definit: el seu logaritme natural és 1400,91, de manera que el nombre mateix és aproximadament 1060810^{608}. El problema és que el teu ordinador no té aquest nombre, i val la pena entendre exactament quins nombres sí que té, perquè aquesta no serà l’última vegada que decidirà el resultat.

La solució per al producte que explota és l’habitual: prendre logaritmes. El logaritme converteix productes en sumes, és estrictament creixent i per tant no pot moure la ubicació del màxim, i una suma de dos mil nombres moderats és una cosa que float64 gestiona sense queixar-se. Per convenció prenem el logaritme negatiu de la versemblança, de manera que millor vol dir més petit. Ara substitueix la densitat gaussiana i mira què passa.

  1. Comença pel producte. La versemblança és L(θ)=i=1Np(yiθxi)\mathcal{L}(\theta) = \prod_{i=1}^{N} p(y_i - \theta x_i), amb pp la densitat gaussiana anterior.

  2. Pren-ne menys el logaritme. El producte es converteix en una suma, i l’exponencial de la densitat es cancel·la directament amb el logaritme:

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. Llença tot el que no contingui θ\theta. El primer terme és una constant. El 1/2σ21/2\sigma^2 davant de la suma és una constant positiva, i escalar una funció per una constant positiva no pot moure on és el seu mínim. El que queda és
i=1N(yiθxi)2\sum_{i=1}^{N}\left(y_i - \theta x_i\right)^2

que és la suma dels residus al quadrat: allò amb què havíem començat el capítol perquè era el primer que se li acut a qualsevol.

Aquest és el resultat pel qual existeix el capítol, i mereix ser enunciat sense matisos: l’error quadràtic no és una convenció. És el logaritme negatiu de la versemblança d’una gaussiana, amb les constants eliminades. Minimitzar l’error quadràtic és exactament el mateix acte que afirmar que els teus errors són gaussians i preguntar quin paràmetre fa les teves dades menys sorprenents. Feies aquesta afirmació des del principi; simplement no t’ho havien dit.

L’equivalència es pot comprovar, així que comprova-la: recorre els mateixos sis-cents un pendents amb el logaritme negatiu complet de la versemblança, constants incloses, i amb l’error quadràtic simple.

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

Nombres diferents a l’eix vertical, i un d’ells és negatiu, cosa que una suma de quadrats no és mai: un logaritme negatiu de la versemblança pot baixar de zero, perquè una densitat pot superar 1. El mateix fons de la mateixa vall, fins a l’últim punt de la graella.

Mostra la deducció completa

Quins descartaments són segurs, exactament? La mateixa maniobra apareix en cada capítol que deriva una pèrdua, i no sempre és innocent.

Descartar una constant additiva és segur sempre que no depengui del paràmetre que estàs optimitzant, i descartar una constant multiplicativa positiva és segur perquè argminθcf(θ)=argminθf(θ)\arg\min_\theta c\,f(\theta) = \arg\min_\theta f(\theta) per a qualsevol c>0c > 0. Totes dues coses fallen en el moment que σ\sigma també s’està ajustant: aleshores N2log(2πσ2)\frac{N}{2}\log(2\pi\sigma^2) no és cap constant, és el terme que impedeix al model afirmar σ=0\sigma = 0 i plausibilitat infinita. Això és exactament la secció següent.

Tornen a fallar d’una altra manera al capítol 3: una constant multiplicativa no mou el mínim, però sí que escala el gradient, i el gradient es multiplica per la taxa d’aprenentatge. Dividir per NN per obtenir l’error quadràtic mitjà en lloc de la suma és invisible per a la resposta i molt visible per a l’entrenament: amb la suma, doblar la mida del batch dobla cada pas que fas.

Hem fixat σ\sigma a 0,12 per decret, i ningú de la planta coneix la dispersió de l’error del seu peu de rei. Tracta-la com una segona incògnita i deixa que la màxima versemblança també la decideixi. Aquí el terme constant que acabem de descartar torna, perquè és l’única cosa que s’interposa entre el model i una afirmació de precisió perfecta.

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

Les dues coincideixen fins a quatre decimals, i no per accident: derivar aquesta expressió i igualar-la a zero dona σ^2=1Nri2\hat{\sigma}^2 = \frac{1}{N}\sum r_i^2 exactament. Així doncs, l’error quadràtic mitjà no és simplement com una variància. Sota aquest model és l’estimació de màxima versemblança de la variància del soroll: el nombre que has estat minimitzant tot aquest temps era una estimació de com de sorollós és el teu sensor.

Un matís, barat d’enunciar i car de redescobrir més endavant: aquesta estimació està esbiaixada a la baixa, perquè els residus s’han mesurat contra un ajust que ell mateix s’ha triat per fer-los petits. Simula-ho: dues-centes mil llibretes de vint lectures cadascuna, extretes d’una distribució la variància real de la qual és exactament 1, amb l’únic paràmetre de l’ajust estimat a partir de les lectures mateixes. Dividir la suma de quadrats per NN dona una mitjana de 0,9501; dividir-la per N1N-1 dona 1,0001; i (N1)/N(N-1)/N és 0,95 clavat. Cada paràmetre que ajustes costa un grau de llibertat, i aquest és el cas visible més petit d’un problema molt més gran: un model sempre sembla millor sobre les dades amb què s’ha ajustat. El capítol 4 converteix això en la disciplina de reservar dades, i el capítol 6 dona nom a l’efecte.

Una pèrdua és una afirmació sobre el soroll

Enllaç a la secció: Una pèrdua és una afirmació sobre el soroll

Si l’error quadràtic afirma que el soroll és gaussià, la següent pregunta és què passa quan l’afirmació és falsa. No lleugerament falsa: falsa de la manera com ho són les mesures reals.

Al taller, la majoria de lectures del peu de rei són bones fins a una dècima de mil·límetre, i una o dues vegades per torn un encenall queda sota la mordassa i la lectura s’equivoca de diversos mil·límetres. Errors així tenen cues pesades: petits la major part del temps, de tant en tant enormes, i enormes molt més sovint del que permet una corba de campana. La distribució de Cauchy és el model net estàndard d’aquest comportament, i la seva densitat és tan simple com la de la gaussiana:

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

La diferència és la cua: la gaussiana cau com eε2e^{-\varepsilon^2}, brutalment ràpid, i la Cauchy com 1/ε21/\varepsilon^2, gairebé gens. La conseqüència és més fàcil de veure que de dir:

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

La variància mostral de la gaussiana s’estabilitza en 0,0144, que és 0.1220.12^2, i s’hi queda. La de la Cauchy puja, i continua pujant mentre continuïs mostrejant, perquè no hi ha res cap a què convergeixi: la distribució de Cauchy no té variància, ni tampoc mitjana. L’error quadràtic, que es dedica a minimitzar una mitjana de quadrats, està demanant una quantitat que no existeix.

Així que aquí tens un torn en què el peu de rei ha estat enganyat. Les mateixes vint hores, la mateixa fulla, el mateix desplaçament de 0,30 mil·límetres per hora: només que ara el soroll és de Cauchy. Ajusta-ho dues vegades: una minimitzant residus al quadrat, una minimitzant el logaritme negatiu de la versemblança del soroll que realment ha generat les dades. El truc de centrar no ajuda aquí —només fixa l’intercepte per a l’error quadràtic—, així que tots dos ajustos es fan per força bruta sobre una graella d’interceptes i pendents, ja que encara no tenim cap manera de trobar el fons d’una vall llevat de visitar-lo.

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")

Les dues línies ressaltades són tota la diferència entre els ajustos. Pren el logaritme de la densitat de Cauchy, descarta les constants exactament com abans, i log(1+(r/s)2)\sum \log\left(1 + (r/s)^2\right) és el que sobreviu. Mateixa recepta, afirmació diferent sobre el soroll.

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

Els mínims quadrats informen d’un desplaçament de 0,108 mil·límetres per hora, aproximadament un terç de la taxa real, i conclouen que la fulla és bona fins a l’hora 19,6. La resposta real és l’hora 11,7. Actuant segons aquest ajust, la planta manté la premsa en marxa vuit hores extra fabricant peces fora de tolerància, sota l’autoritat de la loss function més estàndard del camp. L’ajust de Cauchy, amb les mateixes vint lectures, la mateixa graella i una sola línia de diferència en el codi, cau a l’hora 11,8.

Dues objeccions mereixen resposta, perquè totes dues són el primer que diu un bon enginyer.

El valor atípic és obvi: esborra’l. Pots fer-ho, i ajuda, però no n’hi ha prou. Esborrar l’única pitjor lectura mou el pendent de mínims quadrats de 0,108 a 0,239, cosa que encara situa el canvi de fulla a l’hora 13,1, una hora i mitja tard; esborrar la pitjor, tornar a ajustar i esborrar la que ara és pitjor et porta a 0,286, i fixa’t que això ja és un procediment, no una observació: esborra en canvi els dos residus més grans de l’ajust original i acabes a 0,223. Però ara has pres decisions de criteri que no pots escriure ni defensar, i automatitzar la regla no la rescata: «elimina el residu més gran i torna a ajustar», executat sobre mil torns simulats, té un error de pendent medià de 0,0177 contra el 0,0100 de l’ajust per versemblança, i s’equivoca en més de 0,05 en el 14,7% dels torns contra l’1,3%. L’eliminació és un pedaç sobre una suposició equivocada. La versemblança no necessita cap pedaç, perquè mai no ha suposat que el valor atípic fos impossible.

Has triat un conjunt de dades afortunat. Aquesta objecció és exactament correcta, i per això l’últim experiment simula mil torns independents i reajusta de totes dues maneres en cadascun.

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

Mediana, no mitjana, pel mateix motiu que tot en aquesta secció: els errors de mínims quadrats estan impulsats per una Cauchy, de manera que la seva mitjana no és una cosa estable per informar. Els mínims quadrats s’equivoquen greument en dos torns de cada cinc; l’ajust per versemblança s’equivoca greument en un torn de cada setanta-set, i el seu pitjor fracàs en mil torns és inferior a una cinquena part del pitjor dels mínims quadrats.

Res d’això fa dolent l’error quadràtic. El fa específic, i l’aritmètica diu exactament per què. Pren un residu de 0,1 mm i un de 7 mm. Elevat al quadrat, la lectura dolenta contribueix 4.900 vegades més al total que la bona, de manera que la recta és arrossegada sencera cap a ella; sota el logaritme negatiu de la versemblança de Cauchy, els mateixos dos residus contribueixen 0,527 i 8,133, una ràtio de 15,4. La lectura dolenta encara compta, simplement no decideix. Aquest és el començament de l’estadística robusta, on la pèrdua de Huber de 1964 parteix la diferència comportant-se quadràticament per als residus petits i linealment per als grans,7 i on Tukey ja havia mostrat com de poca contaminació cal perquè la variància mostral sigui una eina pitjor que la desviació absoluta mitjana.8

Una nota històrica, massa bona per deixar-la fora. Els mínims quadrats es van publicar primer, per Legendre el 1805, com un dispositiu algebraic convenient sense cap justificació més enllà que funcionava.9 Quatre anys després Gauss va recórrer l’argument a l’inrevés: va donar per fet que la mitjana aritmètica és la manera correcta de combinar mesures repetides, va preguntar quina distribució d’errors fa que la mitjana sigui el valor més probable, i va demostrar que essencialment només n’hi ha una: la que ara porta el seu nom.10 La derivació d’aquest capítol és la seva, té més de dos segles, i encara és la part que la majoria de cursos deixen fora.

Guanyat. Una loss function és una regla de puntuació, i el rànquing que produeix és una propietat de la regla, no dels candidats. Tota pèrdua d’aquest curs és el logaritme negatiu de la versemblança d’alguna suposició sobre el soroll, amb les constants llençades: la gaussiana dona error quadràtic aquí, Bernoulli dona entropia creuada al capítol 4, i una distribució categòrica sobre un vocabulari dona la pèrdua de next-token al capítol 8. La recepta no canvia mai: declara el soroll, escriu la versemblança, pren-ne menys el logaritme. I quan la suposició és equivocada el model no és només imprecís: és equivocat en una direcció que pots predir.

Encara falta. Hem trobat el fons de la vall visitant-hi cada punt. Això ha funcionat per a un paràmetre i sis-cents candidats, i ha sobreviscut a dos paràmetres amb 401.301 candidats en una cinquena part de segon. Tres paràmetres a la mateixa resolució són 201.051.801 candidats i ja no caben en un sol array; una xarxa petita del capítol 5 té milers de paràmetres, i els models als quals el capítol 10 posa preu en tenen milers de milions. La força bruta aquí no és lenta, és aritmèticament impossible, i res d’aquest capítol no suggereix cap alternativa.

Torna a mirar la vall, però. Dempeus a θ=0.20\theta = 0.20 amb una pèrdua de 0,0822, la direcció de «baixada» no és cap misteri: la pots veure a la pàgina, la corba baixa cap a la dreta. Si poguessis preguntar a la loss function cap a quin costat fa pendent en el punt on ets, sense avaluar-la enlloc més, podries fer un pas en aquella direcció, preguntar de nou i repetir fins que el terra fos pla.

Aquesta pregunta té un nom. El pendent d’una funció en un punt és la seva derivada, i per a una funció de molts paràmetres la col·lecció de pendents en totes les direccions alhora és el gradient. El capítol 1 no en podia fer servir cap, perquè l’error del perceptró era una escala sense pendent sobre el qual preguntar. Aquest capítol ha construït una cosa millor: una pèrdua suau a tot arreu i que prové d’una suposició declarada, no d’una preferència.

Així que la pregunta per al capítol 3 ja no és si existeix un pendent. És com calcular-lo, per què moure’s en contra seu baixa en lloc de pujar —un signe que gairebé tots els cursos et demanen que acceptis per fe—, i quina distància cal recórrer abans de tornar a preguntar, que resulta ser l’únic nombre que decideix si un entrenament convergeix, oscil·la al voltant de la resposta per sempre o fuig cap a l’infinit.


També val la pena llegir, juntament amb aquest capítol: Prince, Understanding Deep Learning §5.1–5.2 i apèndix C, que construeix totes les pèrdues del llibre a partir de la màxima versemblança en l’ordre emprat aquí; Goodfellow, Bengio i Courville, Deep Learning §3.1–3.11 i §5.5, la secció de màxima versemblança dels quals també deriva la divergència KL que necessita el capítol 4; Murphy, Probabilistic Machine Learning: An Introduction capítol 2 i §4.2, sobre què garanteix i què no garanteix la màxima versemblança; Deisenroth, Faisal i Ong, Mathematics for Machine Learning §6.1–6.4 per a la regla de la suma, la regla del producte i la regla de Bayes fetes com cal; la nota breu de Tom Mitchell per a la CMU Estimating Probabilities: MLE and MAP (2016); i §22.7 de Dive into Deep Learning, que arriba al mateix resultat en codi executable.

  1. Fisher, R. A. On the mathematical foundations of theoretical statistics. Philosophical Transactions of the Royal Society A 222, pp. 309–368 (1922). On la versemblança s’estableix com a mètode general, juntament amb «parameter», «statistic», suficiència i eficiència. El nom mateix, i la separació respecte de la probabilitat, és d’un any abans: 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.

  2. IEEE Standard for Floating-Point Arithmetic, IEEE 754-2019. Defineix binary32 i binary16, i les regles d’arrodoniment que fan que l’experiment de suma surti com surt.

  3. Kalamkar, D. et al. A Study of BFLOAT16 for Deep Learning Training. arXiv:1905.12322 (2019). Els paràmetres del format, i el cas a favor d’intercanviar bits de mantissa per bits d’exponent.

  4. Micikevicius, P. et al. Mixed Precision Training. ICLR 2018, arXiv:1710.03740. L’escalat de la pèrdua, i les magnituds de gradient mesurades que el fan necessari en float16.

  5. Goldberg, D. What Every Computer Scientist Should Know About Floating-Point Arithmetic. ACM Computing Surveys 23(1), pp. 5–48 (1991). Encara és la millor explicació única de per què els dos ordres de suma discrepen.

  6. Kahan, W. Pracniques: further remarks on reducing truncation errors. Communications of the ACM 8(1), p. 40 (1965). Suma compensada en mitja pàgina.

  7. Huber, P. J. Robust estimation of a location parameter. The Annals of Mathematical Statistics 35(1), pp. 73–101 (1964). La pèrdua que és quadràtica prop de zero i lineal a les cues, derivada en lloc de pedaçada.

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

  9. Legendre, A. M. Nouvelles méthodes pour la détermination des orbites des comètes (París, 1805), apèndix Sur la méthode des moindres quarrés. La primera publicació dels mínims quadrats, com a dispositiu computacional.

  10. Gauss, C. F. Theoria Motus Corporum Coelestium (Hamburg, 1809), llibre II, §§175–179. L’argument de la mitjana aritmètica a la llei d’errors normal, i d’aquí als mínims quadrats.

A punt per deixar que triï LIA?

Crea amb tots els models d'IA en un sol lloc — comença gratis avui mateix.