Към съдържанието
4/30Глава 4 от 30

Класификация, cross-entropy и как да не се заблудите сами

Постройте логистичен класификатор и вижте защо 98% accuracy може да означава модел, който не открива нищо.

На тази страница

Модел, който отговаря тази част е наред за всяка част, слизаща от лентата, е прав в 98.15 % от случаите. И същевременно е безполезен: от 74 дефектни части в тестовия набор не хваща нито една.

И двете изречения описват един и същ модел. Разстоянието между тях е тази глава.

Първата половина изгражда класификатора. Тя почти не изисква нищо ново: Глава 2 даде рецептата как да превърнем предположение за това как се произвеждат данните в loss функция, а Глава 3 даде механиката за слизане надолу по каквато и loss да ви даде тази рецепта. Приложете и двете към въпрос с отговор да/не и логистичната регресия се появява сама, плюс една нова идея — logit — за която ще се плаща отново в Глава 17.

Втората половина е по-трудната. Всичко след тази точка в курса се оценява по число, което някой е измерил, и ако не можете да различите реално подобрение от артефакт на измерването, всяка следваща глава е украса. Затова: confusion matrix, precision и recall, трите разделения, leakage и въпросът, на който почти никой не отговаря честно — колко тестови примера всъщност ми трябват?

Аритметиката тук минава през 20 000 реда, така че навсякъде е векторизирана — NumPy върши работата още от Глава 2 и оттук нататък вече не си струва да го отбелязваме.

Същата фабрика като в Глава 1, по-труден въпрос. Вместо приеми или отхвърли, въпросът е дефектна ли е тази част — а дефектите са редки, което прави измервателната половина на тази глава трудна, а моделиращата — измамно лесна.

belt.pyPYTHON
import numpy as np

rng = np.random.default_rng(4)
N = 20_000
width  = rng.normal(22.0, 0.9, N)      # millimetres
weight = rng.normal(57.0, 3.0, N)      # grams

z_true = -5.90 + 1.90 * (width - 22.0) + 0.42 * (weight - 57.0)
y = (rng.random(N) < 1 / (1 + np.exp(-z_true))).astype(float)

perm = rng.permutation(N)
train, val, test = perm[:12_000], perm[12_000:16_000], perm[16_000:]
TEXT
N = 20000  defects = 337  base rate = 0.0169
defects per split = 203 60 74

Три разделения, не две. Причината заслужава собствен раздел и получава такъв по-долу; засега обучавайте върху първото, настройвайте върху второто и не гледайте третото.

Features са стандартизирани — извадена е средната стойност, разделено е на стандартното отклонение — като се използват само статистиките от обучаващия набор, по причината, която Глава 1 показа с границата за сходимост на perceptron: нецентрираните данни правят геометрията враждебна. От кои редове имате право да изчислите тази средна стойност става жив въпрос по-късно в тази глава.

Perceptron връщаше знак. Знакът не може да различи отхвърли от отхвърли, но на косъм, а точно тази разлика е нужна на фабриката, за да реши кои части човек трябва да прегледа повторно първи.

Затова следвайте рецептата от Глава 2 буквално. Запишете какво твърдите за начина, по който се произвежда label, вземете likelihood, вземете log, сменете знака и имате loss. За изход да/не твърдението е Бернулиево разпределение: има вероятност pp частта да е дефектна, и

P(yp)=py(1p)1yP(y \mid p) = p^{\,y}\,(1-p)^{\,1-y}

което е просто компактен начин да се напише „pp ако y=1y = 1, и 1p1-p ако y=0y = 0“. Вземете log на това и сменете знака, и loss за един пример е

L=[ylogp+(1y)log(1p)]L = -\big[\,y \log p + (1 - y)\log(1 - p)\,\big]

Това е binary cross-entropy. Не е избрана, защото е удобна; тя е negative log-likelihood на единственото разпределение, което хвърляне на монета може да има. Нищо друго не беше налично.

Още липсва откъде идва pp. Моделът изчислява претеглена сума s=wx+bs = \mathbf{w}\cdot\mathbf{x} + b, която е реално число и покрива цялата права, а вероятността трябва да живее в (0,1)(0,1). Функцията, която пренася между тях, е logistic sigmoid:

σ(s)=11+es\sigma(s) = \frac{1}{1 + e^{-s}}
TEXT
logit -4.0  ->  p = 0.0180        loss when y=1 and p=0.9  : 0.1054
logit -1.0  ->  p = 0.2689        loss when y=1 and p=0.5  : 0.6931
logit  0.0  ->  p = 0.5000        loss when y=1 and p=0.01 : 4.6052
logit  4.0  ->  p = 0.9820

Четете дясната колона като ценоразпис. Да си прав с 90 % увереност струва 0.105. Да отказваш да се ангажираш струва 0.693 — което е log2\log 2, цената на свиването на рамене. Да си уверено грешен струва 4.6, четиридесет и четири пъти повече, и цената расте без граница, докато моделът става по-сигурен в грешката си. Cross-entropy не просто брои грешки: тя таксува арогантността.

Глава 3 каза: за да обучите каквото и да е, вземете производната на loss спрямо всеки параметър. Направете го за един пример. С s=wx+bs = \mathbf{w}\cdot\mathbf{x} + b и p=σ(s)p = \sigma(s):

Ls=py,Lw=(py)x,Lb=py\frac{\partial L}{\partial s} = p - y, \qquad \frac{\partial L}{\partial \mathbf{w}} = (p - y)\,\mathbf{x}, \qquad \frac{\partial L}{\partial b} = p - y
Покажи подробности

Двата реда, които карат бъркотията да се съкрати. Sigmoid има необичайно приятна производна, σ(s)=σ(s)(1σ(s))=p(1p)\sigma'(s) = \sigma(s)\,(1 - \sigma(s)) = p(1-p). А loss се диференцира до

Lp=yp+1y1p=pyp(1p)\frac{\partial L}{\partial p} = -\frac{y}{p} + \frac{1-y}{1-p} = \frac{p - y}{p\,(1-p)}

Умножете двете чрез chain rule и p(1p)p(1-p) се появява веднъж отгоре и веднъж отдолу. Съкращава се точно, и pyp - y е това, което остава. Това съкращаване не е съвпадение — случва се винаги, когато loss е negative log-likelihood на разпределение, а output функцията е тази, която разпределението естествено използва. Тази двойка има име — generalised linear model — и чистият gradient е нейният отпечатък.1

Така че update е prediction минус truth, по input. Нищо друго. Ето целия trainer, който е descent от Глава 3 с променен един ред:

logistic.pyPYTHON
def sigmoid(z):
    return np.where(z >= 0, 1.0 / (1.0 + np.exp(-z)),
                    np.exp(np.minimum(z, 0)) / (1.0 + np.exp(np.minimum(z, 0))))


def fit_logistic(X, y, lr=0.5, epochs=4000):
    w, b = np.zeros(X.shape[1]), 0.0
    for _ in range(epochs):
        p = sigmoid(X @ w + b)
        g = p - y                        
        w -= lr * (X.T @ g) / len(y)     
        b -= lr * g.sum() / len(y)       
    return w, b

np.where в sigmoid не е козметика. Директното изчисляване на 1/(1+es)1/(1+e^{-s}) прелива при големи отрицателни ss; разклонението избира алгебрично идентичната форма, която държи експонентата отрицателна. Това е кутията за floating-point от Глава 2, която събира първия си дълг, и ще събере по-голям след два раздела.

Защо не squared error, и защо отговорът е за gradient

Връзка към раздела: Защо не squared error, и защо отговорът е за gradient

Стандартното обяснение защо предпочитаме cross-entropy пред squared error е аргументът с likelihood по-горе: squared error получавате, когато допуснете Gaussian noise, labels не са Gaussian, следователно не го правете. Това е вярно и не убеждава никого, защото можете да напишете L=(py)2L = (p - y)^2 върху sigmoid и той ще се обучава.

Аргументът, който наистина стига до целта, е за gradient. Поставете squared error върху sigmoid и chain rule дава

Ls=2(py)p(1p)\frac{\partial L}{\partial s} = 2\,(p - y)\,p\,(1-p)

Този допълнителен p(1p)p(1-p) е онзи, който преди се съкрати. Сега не се съкращава и отива към нула винаги когато моделът е уверен — включително когато моделът е уверено грешен. Оценете и двете при няколко score-а за пример, чийто истински label е 1:

score ssppcross-entropy L/s\partial L/\partial ssquared error L/s\partial L/\partial sratio
8-80.0003350.999665-0.9996650.000670-0.0006701 491
4-40.0179860.982014-0.9820140.034690-0.03469028.3
2-20.1192030.880797-0.8807970.184956-0.1849564.8
000.5000000.500000-0.5000000.250000-0.2500002.0
+2+20.8807970.119203-0.1192030.025031-0.0250314.8

При s=8s = -8 моделът е възможно най-грешен, а squared error отговаря с gradient 1 491 пъти по-малък от този на cross-entropy. Колкото по-лоша е грешката, толкова по-малко моделът учи от нея. Gradient на cross-entropy междувременно се насища при 1-1: максимално грешно дава максимално голям сигнал, и не по-голям.

Пуснете състезанието. Две хиляди балансирани точки, идентични начални тегла, избрани да са уверено грешни (w=[6,6]\mathbf{w} = [-6, -6]), идентичен learning rate, различава се само loss. И двата run-а се оценяват с cross-entropy, за да са сравними колоните.

epochcross-entropy lossaccuracysquared-error lossaccuracy
15.48650.23005.94990.2290
101.55250.24605.90420.2290
500.46420.77805.69130.2320
1000.46390.77705.39550.2410
2000.46390.77704.63110.2745
5000.46390.77700.52910.7660
1 0000.46390.77700.46400.7765

Cross-entropy приключва до epoch 50. Squared error все още е на 24 % accuracy при epoch 100 — и не е мръднал от 23 % при epoch 10 — по-зле от налучкване, защото е започнал уверено грешно, а gradient, който би го спасил, е умножен по 0.0007. Измъква се около epoch 500 и стига до същото място. Така че честното обобщение е, че squared error върху sigmoid не е неправилен; той е бавен точно там, където скоростта има най-голямо значение. При модел с два параметъра губите 450 epochs. При мрежа със сто слоя, където някоя unit някъде винаги е уверено грешна, губите целия training run.

Три величини, нужни както трябва в Глава 8 за perplexity и в Глава 11 за penalty, който държи fine-tuned policy близо до reference. Те са по-лесни от репутацията си.2

Entropy е средният брой битове, които трябва да похарчите, за да комуникирате извадка от разпределение, ако използвате най-добрия възможен код за него:

H(p)=ipilog2piH(p) = -\sum_i p_i \log_2 p_i

Cross-entropy е това, което харчите, когато използвате код, построен за qq, върху данни, които всъщност идват от pp:

H(p,q)=ipilog2qiH(p, q) = -\sum_i p_i \log_2 q_i

KL divergence е излишъкът — загубата в битове, причинена от това да вярвате в qq, когато истината е pp:

DKL(pq)=H(p,q)H(p)D_{\mathrm{KL}}(p \parallel q) = H(p,q) - H(p)

Проверете и трите върху лентата:

TEXT
test defect rate                                = 0.0185
entropy of that coin                            = 0.1329 bits
cross-entropy of the constant predictor on test = 0.1330 bits
KL(test coin || fair coin)                      = 0.8671 bits
H + KL                                          = 1.0000 bits
cross-entropy of the p=0.5 predictor on test    = 1.0000 bits

Там се виждат две неща. Първо, модел, който просто докладва базовата честота в обучението, 1.69 %, постига cross-entropy от 0.1330 бита, почти точно entropy на тестовите labels — както и трябва, защото има правилното разпределение и никаква друга информация. Entropy е подът, който незнанието за конкретния индивид ви купува. Второ, модел, който свива рамене и казва 0.5, плаща точно 1 бит, а разликата между двете, 0.8671 бита, е точно KL divergence. H+DKL=H(p,q)H + D_{\mathrm{KL}} = H(p,q) не е тъждество за запаметяване; това е сметка, която можете да гледате как се натрупва.

И връзката обратно към обучението: когато label е един известен клас, „истинското“ разпределение е one-hot, неговата entropy е нула, и cross-entropy е равна на KL divergence. Минимизирането на cross-entropy и издърпването на разпределението на модела към истината са едно и също действие.

Повече от два отговора: softmax и изместването, което не струва нищо

Връзка към раздела: Повече от два отговора: softmax и изместването, което не струва нищо

Дефектното не е едно нещо. При формоване част може да излезе като short shot (недостатъчно материал), flash (твърде много, изстискано от матрицата) или burn. Четири изхода, значи четири logits, и те трябва да станат четири вероятности със сума едно. Това е softmax:

softmax(z)i=ezijezj\operatorname{softmax}(\mathbf{z})_i = \frac{e^{z_i}}{\sum_j e^{z_j}}

Той има свойство, което изглежда като случайност, а всъщност е цялата имплементация:

softmax(z+c)=softmax(z)\operatorname{softmax}(\mathbf{z} + c) = \operatorname{softmax}(\mathbf{z})

за всяка константа cc, защото ezi+c=ecezie^{z_i + c} = e^{c} e^{z_i} и ece^c се съкращават горе и долу. Само разликите между logits означават нещо. Абсолютното ниво не е информация.

За щастие, защото абсолютното ниво е това, което чупи компютъра:

TEXT
logits            = [800. 801. 799.]
naive softmax     = [nan nan nan]
shifted by -max   = [0.2447 0.6652 0.09  ]
same softmax after adding 1000 to every logit: True

e800e^{800} препълва 64-bit float, сумата става infinity, а infinity, разделено на infinity, е nan — не грешка, не crash, а тих отвор там, където преди имаше три вероятности. Изваждането на максималния logit не променя нищо математически и променя всичко числено, защото най-голямата експонента става точно e0=1e^0 = 1. Това е трикът logsumexp от Глава 2 в работни дрехи, и всяка сериозна имплементация го прави:

softmax.pyPYTHON
def softmax(Z):
    Z = Z - Z.max(axis=1, keepdims=True)   
    E = np.exp(Z)
    return E / E.sum(axis=1, keepdims=True)


def fit_softmax(X, Y, lr=1.0, epochs=6000):
    W, b = np.zeros((X.shape[1], Y.shape[1])), np.zeros(Y.shape[1])
    for _ in range(epochs):
        G = (softmax(X @ W + b) - Y) / len(X)   
        W -= lr * (X.T @ G)
        b -= lr * G.sum(0)
    return W, b

Gradient отново е prediction минус truth, сега с YY one-hot. Binary случаят през цялото време е бил специален случай.

Обучен върху 3 000 части и тестван върху 1 000, с по три измервания всяка (ширина, тегло, температура на стопилката), той достига 94.00 % accuracy. Ето какво крие това число:

truth ↓ / predicted →okshort shotflashburnrecall
ok8505900.984
short shot2221000.488
flash2003010.588
burn300390.929
precision0.9500.8080.7690.975

Моделът намира по-малко от половината short shots. Accuracy не може да види това, защото 86 % от частите са наред и правилното им разпознаване е достатъчно, за да носи средната стойност. Macro F1 — средната стойност на F1 по класове, която тежи рядък клас също толкова, колкото и чест — е 0.7983, срещу micro F1 от 0.9400, който по дефиниция е идентичен с accuracy. Когато някой докладва едно F1 число, попитайте кое.

Това е краят на моделирането. Останалата част от главата е за числата.

Вземете обучения binary модел и направете два варианта, като умножите всеки logit по константа: 0.35 за колеблива версия, 4 за свръхуверена. Умножението по положително число не може да промени нито един знак, така че и трите модела предсказват точно един и същ label за всички 4 000 тестови части. Accuracy не може да ги различи. Cross-entropy няма никакъв проблем:

modelaccuracycross-entropymean loss when rightmean loss when wrongworst single loss
hesitant (logits × 0.35)0.98300.15490.13691.19902.80
as trained0.98300.05640.01472.46897.82
overconfident (logits × 4)0.98300.15630.00099.142727.63

Колебливият модел плаща малък данък за всяка част, включително хилядите, които познава. Свръхувереният почти не плаща, когато е прав, и катастрофално много, когато греши — една част в този тестов набор му струва 27.63 nats сама по себе си. Двата стигат почти до една и съща обща стойност по противоположни маршрути, а обученият модел, чиито вероятности са калибрирани към данните, стои три пъти по-ниско от двата.

Това е най-острият начин да се формулира разликата между loss и metric. Loss е това, което оптимизирате: трябва да е диференцируема и вижда всичко, което моделът е казал, включително колко сигурен е бил. Metric е това, по което ви оценяват: може да е step function, бизнес правило, брой пропуснати дефекти. Те не са един и същ обект и не винаги са съгласни — затова дефинирате и двете, преди да започнете, и никога не оставяте loss да замести metric само защото случайно е на екрана.

Преди всеки модел, изискването: какъв score има най-мързеливият възможен отговор? На тази лента — винаги казвай, че е наред:

TEXT
always-say-fine baseline: accuracy = 0.9815
confusion (tn, fp, fn, tp) = (3926, 0, 74, 0)

98.15 %. Сега обученият логистичен модел при default threshold 0.5:

TEXT
logistic @0.5: accuracy=0.9830 precision=0.8000 recall=0.1081 F1=0.1905
confusion (tn, fp, fn, tp) = (3924, 2, 66, 8)

98.30 %. Той победи baseline с 0.15 процентни пункта, и всеки доклад, който спира при accuracy, ще го нарече победа. Confusion matrix казва какво всъщност се е случило:

predicted finepredicted defective
actually fine3 9242
actually defective668

Намерил е 8 дефектни части от 74 и е пуснал 66 да минат. Три числа назовават трите начина да прочетете тази таблица:

  • Precision =TP/(TP+FP)=8/10=0.800= \mathrm{TP}/(\mathrm{TP}+\mathrm{FP}) = 8/10 = 0.800. От частите, които е маркирал, колко наистина са били дефектни. Това е цената на излишните инспекции.
  • Recall =TP/(TP+FN)=8/74=0.108= \mathrm{TP}/(\mathrm{TP}+\mathrm{FN}) = 8/74 = 0.108. От дефектните части, колко е хванал. Това е цената да изпратите лоша част на клиент.
  • F1 =2PR/(P+R)=0.190= 2PR/(P+R) = 0.190, тяхната хармонична средна, която остава близо до по-малкото от двете и затова отказва да бъде ласкана само от едното.

Кое има значение зависи от фабриката, не от математиката: инспекцията струва няколко секунди, а изпратен дефект струва уведомление за изтегляне, така че тук recall доминира и 0.108 е провал.

Но моделът не е проблемът. Threshold е, а threshold не е част от модела — това е бизнес решение, приложено след това върху вероятност. Sweep-нете го:

thresholdTPFPFNaccuracyprecisionrecallF1
0.50082660.98300.8000.1080.190
0.2002728470.98120.4910.3650.419
0.10042118320.96250.2630.5680.359
0.05054236200.93600.1860.7300.297
0.0206757070.85580.1050.9050.188
0.005711 36030.65930.0500.9590.094

Четете колоната accuracy надолу. Тя пада през цялото време — от 98.30 % до 65.93 % — докато моделът минава от хващане на 8 дефекта към хващане на 71 от 74. Всяко полезно нещо, което този модел може да направи, влошава accuracy. Екип, който оптимизира заглавното число, би пуснал версията, която не намира нищо.

Покажи подробности

Class weighting не създава сигнал, а премества operating point. Обичайният първи рефлекс при imbalanced classes е да се претегли редкият клас в loss. Ако го направим с тегла 1, 10 и 60 върху positives:

weight on positivesaccuracyprecisionrecallF1AUC
10.98300.8000.1080.1900.9363
100.96050.2530.5810.3520.9361
600.82900.0910.9190.1660.9361

Precision и recall се местят много. AUC — вероятността моделът да rank-не случайна дефектна част над случайна добра, като напълно игнорира threshold — се мести с 0.0002, което е нищо. Reweighting плъзна същия модел по същата trade-off крива. Това често е желаното, и никога не е нова информация: ако ranking е лош, никаква схема за weighting няма да го спаси.

Три разделения и leak, който предстои да намерите

Връзка към раздела: Три разделения и leak, който предстои да намерите

Защо три разделения, а не две? Защото в момента, в който използвате набор от примери, за да изберете нещо — threshold, learning rate, кой от шест модела да пуснете — този набор е използван за fitting и score-ът му спира да бъде unbiased.3 Измерено на тази лента: sweep на threshold върху validation set избира 0.196, а моделът после получава F1 = 0.4122 върху недокоснатия test set. Ако sweep-ът беше пуснат директно върху test set, най-доброто постижимо там беше 0.4186 — число, което никой няма право да докладва.

Разликата тук е малка, 0.006, защото това е един hyperparameter, sweep-нат веднъж срещу 4 000 validation примера. Тя расте с всяко допълнително решение и с всяко свиване на validation set. Забележете също, че посоката не е гарантирана в един run: избраният threshold получи 0.3902 на validation и 0.4122 на test, така че validation този път го подцени. Bias е систематичен през много решения, не видим в едно.4

Сега упражнението. Log-ът на лентата пристига с трета колона, station_seconds: колко дълго всяка част е прекарала на инспекционната станция. Добавянето ѝ е промяна от един ред в preprocessing. Ето какво прави:

modelaccuracyprecisionrecallF1cross-entropyAUC
width + weight0.98300.8000.1080.1900.05640.9363
+ station_seconds0.99200.7920.7700.7810.02360.9970

Recall скача от 10.8 % на 77.0 %. F1 нараства над четири пъти. И забележете какво направи accuracy: 98.30 % → 99.20 %, печалба от девет десети от пункт, което е от онези числа, които в summary slide се закръглят до „около 99 % и в двата случая“. Accuracy не успя да види провала по-рано и сега не успява да види измамата.

Преди да четете нататък: моделът мами. Разберете как.

Как да ловите leak, в реда, който го намира най-бързо.

  1. Сравнете train и test. Overfitting се проявява като голяма разлика. Тук: честният модел 0.9838 train / 0.9830 test; leaky моделът 0.9936 train / 0.9920 test. И двете разлики са под 0.2 пункта. Leak не изглежда като overfitting — leaky feature е също толкова наличен в test time, така че моделът generalise-ва прекрасно към свят, който не съществува.

  2. Обучете по един модел на feature, самостоятелно. Всичко, което носи отговора, ще се обяви само:

    feature aloneaccuracyrecallF1AUC
    width0.98150.0140.0260.8691
    weight0.98150.0000.0000.7914
    station_seconds0.98500.4050.5000.9960

    Една колона сама по себе си rank-ва дефектите с AUC 0.9960. Две измервания, направени с шублер и кантар, успяват 0.87 и 0.79. Тази асиметрия е алармата.

  3. Попитайте кога е записано всяко число. Средно време на престой: 2.23 секунди за части, които са минали, 15.56 секунди за части, които са се провалили. Разбира се. Част престоява на станцията защото инспектор я е дръпнал от лентата — което се случва след, и само защото, някой е решил, че е дефектна. Колоната не е измерване на частта. Тя е измерване на присъдата.

the planted leakPYTHON
station = 1.8 + rng.exponential(0.35, N)                     # a part just passing through
audited = rng.random(N) < 0.006                              # random spot checks
station[audited] += rng.uniform(6.0, 26.0, audited.sum())
station[y == 1] = 9.0 + rng.exponential(7.0, (y == 1).sum())  

Подчертаният ред е leak: времето на престой на дефектна част се тегли от различно разпределение, защото човек я е свалил от лентата. Това е най-често срещаният сериозен bug в приложното machine learning и има име: target leakage — информация в training features, която не би била налична в момента, когато prediction трябва да бъде направена.5 Не хвърля exception. Произвежда по-добро число. Всеки стимул в проекта сочи към това да го запазите.

Защитата е един въпрос, задаван за всяка колона: в момента, в който ми трябва тази prediction, съществува ли вече тази стойност? На жива лента station_seconds е неизвестно, докато частта не бъде инспектирана — което е нещото, което моделът трябваше да замени.

Да предположим, че оценявате модел върху 20 примера и той познава 17. Докладвате 85 %.

TEXT
17 correct out of 20 -> accuracy 0.8500
  Wilson    95% CI : [0.6396, 0.9476]
  bootstrap 95% CI : [0.7000, 1.0000]
  P(a 65% model scores 17 or more out of 20) = 0.0444
  P(an 85% model scores 17 or more out of 20) = 0.6477

Честният прочит на 17/20 е някъде между 64 % и 95 %. Истински 65 % модел произвежда този резултат в 4.4 % от случаите — един run на двадесет и три — и ако сте пробвали шепа prompts и сте докладвали най-добрия, сами сте си произвели този run. Седемнадесет от двадесет не могат да различат 85 % модел от 65 % модел.

Два начина да поставите интервал върху rate, и двата трябва да са в инструментариума ви:

uncertainty.pyPYTHON
def wilson(k, n, z=1.959963985):
    """95% interval for k successes in n trials. Correct at small n; no simulation."""
    ph, d = k / n, 1 + z * z / n
    centre = (ph + z * z / (2 * n)) / d
    half = z * (ph * (1 - ph) / n + z * z / (4 * n * n)) ** 0.5 / d
    return centre - half, centre + half


def bootstrap_ci(correct, n_resamples=10_000, alpha=0.05, seed=0):
    """95% interval for the mean of any per-example score array. Works on F1 too."""
    rng = np.random.default_rng(seed)
    correct = np.asarray(correct, dtype=float)
    draws = correct[rng.integers(0, len(correct), size=(n_resamples, len(correct)))]
    lo, hi = np.quantile(draws.mean(axis=1), [alpha / 2, 1 - alpha / 2])
    return float(correct.mean()), float(lo), float(hi)

Използвайте Wilson6 за обикновен success rate; държи се добре при всяко nn и не изисква randomness. Забележете по-горе, че при n=20n = 20 горният край на bootstrap е 1.0000 — resampling на 20 точки лесно може да изтегли 20 правилни, така че не може да представи интервал, по-тесен от собствената си гранулярност. Използвайте bootstrap7, когато не съществува формула, което е повечето интересни случаи: F1, macro-averages, BLEU, pass@1, score на rubric-based judge. На тази лента F1 0.4122 на настроения модел носи bootstrap интервал [0.3009, 0.5156] — и това е числото, което трябва да се появи в доклада, защото point estimate сам по себе си кани сравнение, което не може да поддържа.

Още едно измерване, защото променя как трябва да сравнявате два модела. Два модела, оценени върху едни и същи 500 примера:

TEXT
model A: 0.8580  95% CI [0.8260, 0.8880]
model B: 0.8120  95% CI [0.7780, 0.8460]
the two intervals overlap: True
paired difference A-B: 0.0460  95% CI [0.0260, 0.0680]
they disagree on 31 of 500 examples (A right 27, B right 4)

Интервалите им се припокриват, и народното правило — припокриващи се error bars означават липса на значима разлика — би нарекло сравнението неубедително. Не е. Двата модела са работили върху едни и същи примери, така че правилната величина е разликата по пример, чийто интервал е [0.0260, 0.0680], удобно над нула. Те не са съгласни само за 31 от 500 items, и A печели 27 от тези несъгласия; споделените примери, лесни и трудни, се съкращават, вместо да добавят шум. Сравнявайте моделите paired и стигате до същия извод с частица от данните.

Вече имате модел, който извежда калибрирани вероятности, loss, изведена от твърдение за данните вместо избрана за удобство, gradient, който буквално е prediction минус truth, и — по-важното — механиката да разберете дали нещо от това работи. Десетредовият Wilson интервал по-горе се използва дословно отново: той носи prompt вариантите в Глава 15, retrieval таблиците в Глава 19 и golden set в Глава 29. Bootstrap е това, към което посягате, когато не съществува формула.

Но моделът все още е един слой. Той чертае линия, а Глава 1 доказа с четири реда XOR, че линия не е достатъчна. Поправката е да stack-нете: първи слой, който огъва пространството, втори, който чертае линията в огънатото пространство.

Точно там подреденият gradient от тази глава се изчерпва. Всичко по-горе работеше, защото L/s=py\partial L/\partial s = p - y можеше да бъде записано на ръка, веднъж, за модел с един слой между input и loss. Поставете втори слой по средата и въпросът сменя форма: каква е производната на loss спрямо тегло, което изобщо не докосва output — такова, чието влияние пристига само през друг слой, вероятно по няколко пътя едновременно?

Тази производна съществува. Изчисляването ѝ на ръка е безнадеждно за всичко по-голямо от играчка, а изчисляването ѝ параметър по параметър е безнадеждно в друг мащаб. Нужно е процедура, която получава всяка производна в мрежата от един backward pass върху същия graph, по който forward pass току-що е минал.

Това е Глава 5, и тя е двигателят, на който работи останалата част от този курс.


Струва си да четете заедно с тази глава още: Bishop, Pattern Recognition and Machine Learning §1.2, §1.5, §1.6 и §4.3, който покрива probability, decision theory, information theory и linear classification в реда, който тази глава следва; Murphy, Probabilistic Machine Learning: An Introduction, глави 6 и 10; Prince, Understanding Deep Learning §5.4–5.7; и Saito and Rehmsmeier, The Precision-Recall Plot Is More Informative than the ROC Plot When Evaluating Binary Classifiers on Imbalanced Datasets (PLOS ONE, 2015) — защо AUC, цитиран по-горе, не трябва да е единственото threshold-free число, което гледате, когато 1.7 % от частите са дефектни.

  1. Ma, T. and Ng, A. CS229 Lecture Notes, Stanford University, глави 2 и 3. Мястото, където съкращаването, което произвежда pyp - y, спира да изглежда като късмет: изберете разпределението от exponential family, което съответства на вашия output, използвайте неговия canonical link, и gradient винаги е prediction минус truth.

  2. Olah, C. Visual Information Theory (2015), colah.github.io/posts/2015-09-Visual-Information. Най-ясното налично обяснение на entropy, cross-entropy и KL divergence като разходи в битове, а не като формули.

  3. Abu-Mostafa, Y. S., Magdon-Ismail, M. and Lin, H.-T. Learning From Data (AMLBook, 2012), лекции 13 и 17 от курса на Caltech. Лекция 13 е validation; лекция 17, за трите принципа на learning, е мястото, където data snooping е назовано. Заедно те са източникът на дисциплината в тази глава: всеки поглед към dataset е fitting решение, независимо дали сте пуснали optimiser.

  4. James, G., Witten, D., Hastie, T. and Tibshirani, R. An Introduction to Statistical Learning, 2-ро издание (Springer, 2021), глави 2 и 5, за bias–variance decomposition и resampling. Придружаващият том е мястото, където капанът на selection е заявен директно: Hastie, Tibshirani and Friedman, The Elements of Statistical Learning, 2-ро издание, §7.10.2, The Wrong and Right Way to Do Cross-validation.

  5. Kaufman, S., Rosset, S., Perlich, C. and Stitelman, O. Leakage in Data Mining: Formulation, Detection, and Avoidance. ACM Transactions on Knowledge Discovery from Data 6(4), 2012. Формално разглеждане на провала, демонстриран по-горе, с case studies от състезания, спечелени от модел, който е научил артефакт от начина, по който данните са били сглобени.

  6. Wilson, E. B. Probable Inference, the Law of Succession, and Statistical Inference. Journal of the American Statistical Association 22(158), pp. 209–212 (1927). Score интервалът, използван в wilson() по-горе, все още правилният default за proportion. Учебникарският интервал p^±zp^(1p^)/n\hat{p} \pm z\sqrt{\hat{p}(1-\hat{p})/n} е този, който трябва да избягвате: дава безсмислици близо до 0 и 1 и undercovers тежко при малки nn.

  7. Efron, B. Bootstrap Methods: Another Look at the Jackknife. The Annals of Statistics 7(1), pp. 1–26 (1979). Идеята, която ви позволява да поставите интервал върху всяка статистика, която можете да изчислите, включително тези без sampling theory.


Създадено от

David Vicente Campos

Основател на NeuraLIA Labs и съосновател на MyRealFood

Компютърен инженер съм, завършил Университета в Леон. Съосновах MyRealFood, където като CTO създадох приложението, което милиони хора са използвали, за да се хранят по-здравословно, и основах NeuraLIA Labs, където изграждам AI продукти. Тук пиша за това, което трябваше да разбера по пътя, така, както ми се иска някой да ми го беше обяснил.

Още за автора

Публикувано от NeuraLIA Labs.

Получавайте нови публикации във входящата си поща

Новини за AI, ръководства и продуктови обновления — кратък имейл, когато публикуваме нещо, което си заслужава.

Индекс на курса

Abstract software decision engine with branching paths, probability nodes, and glowing gates.
jev12 мин четене

AI моделът Jev е създаден за решения, не за проза

Jev на TypeSafe AI привлича внимание, защото разглежда софтуерната интелигентност като проблем на вероятностите: изберете правилния клон, добавете увереност и не плащайте на LLM да пише текст, когато кодът има нужда от решение.

Abstract legal research workspace with documents, search nodes and governance controls.
openai11 мин четене

Astra for Law на OpenAI е правна AI система, не нов модел

Правният старт на OpenAI е не толкова за нов базов модел, колкото за системата около него: домейн извличане, надеждни инструменти, права, бенчмаркове и пътища за преглед.

Abstract agent runtime sorting documents, memory blocks and pointer nodes inside a bounded context frame.
context-engineering12 мин четене

Инженеринг на контекста за AI агенти с дълъг хоризонт

Дълго работещите агенти не се провалят само защото прозорецът е малък. Те се провалят, когато файлове, изходи от инструменти и остаряла история изтласкат задачата, която агентът е трябвало да завърши.

Готови ли сте LIA да избира вместо вас?

Създавайте с всички AI модели на едно място — започнете безплатно още днес.