Классификация, перекрёстная энтропия и как не обмануть себя
Соберите логистический классификатор и узнайте, почему 98 % accuracy может означать, что модель не находит ничего.
На этой странице
Модель, которая отвечает с этой деталью всё в порядке про каждую деталь, сходящую с конвейера, права в 98,15 % случаев. И при этом бесполезна: из 74 дефектных деталей в тестовой выборке она не ловит ни одной.
Оба предложения описывают одну и ту же модель. Расстояние между ними — эта глава.
Первая половина строит классификатор. Для этого почти ничего нового не нужно: глава 2 дала рецепт, как превращать предположение о том, как порождаются данные, в функцию потерь, а глава 3 дала механизм, чтобы спускаться вниз по любой потере, которую выдаст этот рецепт. Примените оба к вопросу да/нет — и получится логистическая регрессия плюс одна новая идея: logit, за который снова придётся платить в главе 17.
Вторая половина сложнее. Всё, что дальше будет в курсе, оценивается числом, которое кто-то измерил; и если вы не можете отличить реальное улучшение от артефакта измерения, все следующие главы — декорация. Поэтому: матрица ошибок, precision и recall, три разбиения, leakage и вопрос, на который почти никто не отвечает честно: сколько тестовых примеров мне на самом деле нужно?
Арифметика здесь идёт по 20 000 строк, поэтому всё векторизовано — NumPy делает работу с главы 2, и с этого момента уже не стоит каждый раз это отмечать.
Конвейер, но вопрос реже
Ссылка на раздел: Конвейер, но вопрос режеТа же фабрика, что и в главе 1, вопрос сложнее. Вместо принять или отклонить вопрос такой: эта деталь дефектная? — а дефекты редки, из-за чего измерительная половина этой главы становится трудной, а моделирующая — обманчиво простой.
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:]N = 20000 defects = 337 base rate = 0.0169
defects per split = 203 60 74Три разбиения, не два. Причина заслуживает собственного раздела и получит его ниже; пока обучайте на первом, настраивайте на втором и не смотрите на третье.
Признаки стандартизированы — из них вычтено среднее, затем они разделены на стандартное отклонение — с использованием только статистик обучающей выборки по той причине, которую глава 1 показала через границу сходимости перцептрона: нецентрированные данные делают геометрию враждебной. Из каких строк вам разрешено вычислять это среднее — позже в этой главе станет живым вопросом.
От вердикта к вероятности
Ссылка на раздел: От вердикта к вероятностиПерцептрон возвращал знак. Знак не может отличить отклонить от отклонить, но еле-еле, а именно эта разница нужна фабрике, чтобы решить, какие детали человек должен перепроверить первыми.
Поэтому буквально следуйте рецепту из главы 2. Запишите, что вы утверждаете о том, как порождается метка, возьмите likelihood, возьмите логарифм, смените знак — и у вас есть loss. Для исхода да/нет это утверждение — распределение Бернулли: есть вероятность , что деталь дефектная, и
что является просто компактной записью «, если , и , если ». Возьмите логарифм этого и смените знак — и loss для одного примера будет
Это бинарная перекрёстная энтропия. Её выбрали не потому, что она удобна; это отрицательный логарифм likelihood единственного распределения, которое может быть у броска монеты. Другого варианта не было.
Пока не хватает того, откуда берётся . Модель вычисляет взвешенную сумму , это вещественное число на всей числовой прямой, а вероятность должна жить в . Функция, которая переводит одно в другое, — логистическая сигмоида:
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 — это , цена пожатия плечами. Быть уверенно неправым стоит 4.6, в сорок четыре раза больше, и цена растёт без ограничения по мере того, как модель всё увереннее ошибается. Перекрёстная энтропия не просто считает ошибки: она выставляет счёт за самоуверенность.
Gradient — это предсказание минус истина
Ссылка на раздел: Gradient — это предсказание минус истинаГлава 3 сказала: чтобы что-либо обучать, получите производную loss по каждому параметру. Сделайте это для одного примера. При и :
Показать детали
Две строки, из-за которых весь беспорядок сокращается. У сигмоиды необычно приятная производная, . А loss дифференцируется в
Перемножьте их по правилу цепочки, и появится один раз сверху и один раз снизу. Он сокращается точно, и остаётся . Это сокращение не совпадение — так происходит всякий раз, когда loss является отрицательным логарифмом likelihood распределения, а выходная функция — та, которую это распределение естественно использует. У этой пары есть название — обобщённая линейная модель — и аккуратный gradient является её отпечатком.1
Значит, обновление — это предсказание минус истина, умноженное на вход. И всё. Вот весь trainer: это descent из главы 3 с изменённой одной строкой:
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, bnp.where в sigmoid — не косметика. Прямое вычисление переполняется при больших отрицательных ; ветка выбирает ту алгебраически идентичную форму, которая оставляет экспоненту отрицательной. Это коробка с плавающей точкой из главы 2 взыскивает первый долг, а через два раздела взыщет больший.
Почему не квадратичная ошибка, и почему ответ — про gradient
Ссылка на раздел: Почему не квадратичная ошибка, и почему ответ — про gradientСтандартное объяснение, почему перекрёстная энтропия предпочтительнее квадратичной ошибки, — аргумент likelihood выше: квадратичная ошибка получается из предположения о гауссовом шуме, метки не гауссовы, значит, не надо. Это верно и никого не убеждает, потому что можно написать поверх сигмоиды, и оно будет обучаться.
Работающий аргумент — про gradient. Положите квадратичную ошибку поверх сигмоиды, и правило цепочки даст
Этот дополнительный — тот самый, который раньше сократился. Теперь он не сокращается и стремится к нулю всякий раз, когда модель уверена, — в том числе когда модель уверенно неправа. Посчитайте оба варианта для нескольких счётов на примере, истинная метка которого равна 1:
| score | cross-entropy | squared error | ratio | |
|---|---|---|---|---|
| 0.000335 | 1,491 | |||
| 0.017986 | 28.3 | |||
| 0.119203 | 4.8 | |||
| 0.500000 | 2.0 | |||
| 0.880797 | 4.8 |
При модель ошибается настолько сильно, насколько вообще возможно, а квадратичная ошибка отвечает gradient, который в 1 491 раз меньше, чем у перекрёстной энтропии. Чем хуже ошибка, тем меньше модель на ней учится. Gradient перекрёстной энтропии тем временем насыщается на : максимально неверно даёт максимально большой сигнал, но не больше.
Запустим гонку. Две тысячи сбалансированных точек, одинаковые начальные веса, выбранные так, чтобы быть уверенно неверными (), одинаковая скорость обучения, отличается только loss. Оба запуска оцениваются cross-entropy, чтобы столбцы были сопоставимы.
| epoch | cross-entropy loss | accuracy | squared-error loss | accuracy |
|---|---|---|---|---|
| 1 | 5.4865 | 0.2300 | 5.9499 | 0.2290 |
| 10 | 1.5525 | 0.2460 | 5.9042 | 0.2290 |
| 50 | 0.4642 | 0.7780 | 5.6913 | 0.2320 |
| 100 | 0.4639 | 0.7770 | 5.3955 | 0.2410 |
| 200 | 0.4639 | 0.7770 | 4.6311 | 0.2745 |
| 500 | 0.4639 | 0.7770 | 0.5291 | 0.7660 |
| 1,000 | 0.4639 | 0.7770 | 0.4640 | 0.7765 |
Перекрёстная энтропия заканчивает к epoch 50. Квадратичная ошибка всё ещё на 24 % accuracy на epoch 100 — и не сдвинулась с 23 % на epoch 10 — хуже угадывания, потому что стартовала уверенно неверно, а gradient, который мог бы её спасти, был умножен на 0.0007. Она выбирается примерно к epoch 500 и приходит туда же. Поэтому честное резюме такое: квадратичная ошибка поверх сигмоиды не неправильна; она медленна ровно там, где скорость важнее всего. На модели с двумя параметрами вы теряете 450 epoch. В сети со ста слоями, где какой-нибудь unit где-нибудь всегда уверенно ошибается, вы теряете весь тренировочный запуск.
Entropy, cross-entropy и KL на одной странице
Ссылка на раздел: Entropy, cross-entropy и KL на одной страницеТри величины, которые как следует понадобятся в главе 8 для perplexity и в главе 11 для штрафа, удерживающего fine-tuned policy рядом с её reference. Они проще своей репутации.2
Entropy — это среднее число битов, которое нужно потратить, чтобы передать выборку из распределения, если использовать для него наилучший возможный код:
Cross-entropy — это то, что вы тратите, когда используете код, построенный для , на данных, которые на самом деле приходят из :
KL divergence — это излишек, потери в битах, вызванные верой в , когда правда — :
Проверьте все три на конвейере:
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 тестовых меток — как и должно быть, потому что у неё правильное распределение и никакой другой информации. Entropy — это пол, который покупает вам незнание об отдельном объекте. Во-вторых, модель, которая пожимает плечами и говорит 0.5, платит ровно 1 бит, а разрыв между ними, 0.8671 бита, — это точно KL divergence. — не тождество для заучивания; это счёт, сложение которого можно наблюдать.
И связь обратно с обучением: когда метка — один известный класс, «истинное» распределение one-hot, его entropy равна нулю, а cross-entropy равна KL divergence. Минимизировать cross-entropy и тянуть распределение модели к истине — одно и то же действие.
Больше двух ответов: softmax и сдвиг, который ничего не стоит
Ссылка на раздел: Больше двух ответов: softmax и сдвиг, который ничего не стоитДефект — это не что-то одно. При литье деталь может выйти как short shot (недостаточно материала), flash (слишком много, выдавлено из формы) или burn. Четыре исхода — значит, четыре logits, и они должны стать четырьмя вероятностями, которые суммируются в единицу. Это softmax:
У него есть свойство, которое выглядит случайностью, а на деле является всей реализацией:
для любой константы , потому что и сокращаются сверху и снизу. Значение имеют только разности между logits. Абсолютный уровень не несёт информации.
К счастью, потому что именно абсолютный уровень ломает компьютер:
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 переполняет 64-битное число с плавающей точкой, сумма становится бесконечностью, а бесконечность, делённая на бесконечность, — это nan: не ошибка, не сбой, а тихая дыра на месте трёх вероятностей. Вычитание максимального logit ничего не меняет математически и меняет всё численно, потому что самая большая экспонента становится ровно . Это трюк logsumexp из главы 2 в рабочей одежде, и каждая серьёзная реализация так делает:
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, bGradient снова равен предсказанию минус истина, теперь с one-hot . Бинарный случай всё это время был частным случаем.
Обученная на 3 000 деталях и протестированная на 1 000, с тремя измерениями для каждой (ширина, вес, температура расплава), модель достигает 94.00 % accuracy. Вот что это число скрывает:
| truth ↓ / predicted → | ok | short shot | flash | burn | recall |
|---|---|---|---|---|---|
| ok | 850 | 5 | 9 | 0 | 0.984 |
| short shot | 22 | 21 | 0 | 0 | 0.488 |
| flash | 20 | 0 | 30 | 1 | 0.588 |
| burn | 3 | 0 | 0 | 39 | 0.929 |
| precision | 0.950 | 0.808 | 0.769 | 0.975 |
Модель находит меньше половины short shots. Accuracy этого не видит, потому что 86 % деталей исправны, и правильных ответов по ним достаточно, чтобы вытянуть среднее. Macro F1 — среднее F1 по классам, которое взвешивает редкий класс так же, как частый, — равно 0.7983, против micro F1 0.9400, который по определению идентичен accuracy. Когда кто-то сообщает одно число F1, спросите, какое именно.
На этом моделирование заканчивается. Остаток главы — про числа.
Три модели, одна accuracy
Ссылка на раздел: Три модели, одна accuracyВозьмите обученную бинарную модель и сделайте два варианта, умножив каждый logit на константу: 0.35 для нерешительной версии и 4 для чрезмерно уверенной. Умножение на положительное число не может изменить ни один знак, поэтому все три модели предсказывают ровно одну и ту же метку для всех 4 000 тестовых деталей. Accuracy не может их различить. Cross-entropy — без труда:
| model | accuracy | cross-entropy | mean loss when right | mean loss when wrong | worst single loss |
|---|---|---|---|---|---|
| hesitant (logits × 0.35) | 0.9830 | 0.1549 | 0.1369 | 1.1990 | 2.80 |
| as trained | 0.9830 | 0.0564 | 0.0147 | 2.4689 | 7.82 |
| overconfident (logits × 4) | 0.9830 | 0.1563 | 0.0009 | 9.1427 | 27.63 |
Нерешительная модель платит небольшой налог за каждую деталь, включая тысячи правильных. Чрезмерно уверенная почти бесплатна, когда права, и катастрофична, когда ошибается: одна деталь в этом тестовом наборе сама по себе стоит ей 27.63 nats. Эти две приходят почти к одному и тому же итогу противоположными путями, а обученная модель, чьи вероятности откалиброваны по данным, находится в три раза ниже обеих.
Это самый резкий способ сформулировать разницу между loss и metric. Loss — то, что вы оптимизируете: он должен быть дифференцируемым и видит всё, что сказала модель, включая её уверенность. Metric — то, по чему вас оценивают: она может быть ступенчатой функцией, бизнес-правилом, счётом пропущенных дефектов. Это не один и тот же объект, и они не всегда согласны — поэтому вы определяете оба до начала и никогда не позволяете loss подменять metric только потому, что он оказался на экране.
Сначала глупый baseline
Ссылка на раздел: Сначала глупый baselineДо любой модели — требование: какой результат даёт самый ленивый возможный ответ? На этом конвейере: всегда говорить, что всё нормально:
always-say-fine baseline: accuracy = 0.9815
confusion (tn, fp, fn, tp) = (3926, 0, 74, 0)98,15 %. Теперь обученная логистическая модель при пороге по умолчанию 0.5:
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, назовёт это победой. Матрица ошибок говорит, что произошло на самом деле:
| predicted fine | predicted defective | |
|---|---|---|
| actually fine | 3,924 | 2 |
| actually defective | 66 | 8 |
Она нашла 8 дефектных деталей из 74 и пропустила 66. Три числа называют три способа читать эту таблицу:
- Precision . Среди деталей, которые она пометила, сколько действительно были дефектными. Это стоимость лишних инспекций.
- Recall . Среди дефектных деталей, сколько она поймала. Это стоимость отправки плохой детали клиенту.
- F1 , их гармоническое среднее, которое остаётся около меньшего из двух и поэтому не даёт одному числу льстить себе за счёт другого.
Что важно, зависит от фабрики, а не от математики: инспекция стоит несколько секунд, а отправленный дефект стоит уведомления об отзыве, поэтому здесь доминирует recall, и 0.108 — провал.
Но проблема не в модели. Проблема в пороге, а порог не является частью модели — это бизнес-решение, применяемое после вероятности. Просканируйте его:
| threshold | TP | FP | FN | accuracy | precision | recall | F1 |
|---|---|---|---|---|---|---|---|
| 0.500 | 8 | 2 | 66 | 0.9830 | 0.800 | 0.108 | 0.190 |
| 0.200 | 27 | 28 | 47 | 0.9812 | 0.491 | 0.365 | 0.419 |
| 0.100 | 42 | 118 | 32 | 0.9625 | 0.263 | 0.568 | 0.359 |
| 0.050 | 54 | 236 | 20 | 0.9360 | 0.186 | 0.730 | 0.297 |
| 0.020 | 67 | 570 | 7 | 0.8558 | 0.105 | 0.905 | 0.188 |
| 0.005 | 71 | 1,360 | 3 | 0.6593 | 0.050 | 0.959 | 0.094 |
Читайте столбец accuracy сверху вниз. Он всё время падает — с 98,30 % до 65,93 %, — пока модель переходит от 8 пойманных дефектов к 71 из 74. Каждое полезное действие этой модели ухудшает её accuracy. Команда, оптимизирующая заголовочное число, отправила бы в продакшен версию, которая ничего не находит.
Показать детали
Взвешивание классов не создаёт сигнал, оно сдвигает рабочую точку. Обычный первый рефлекс при несбалансированных классах — взвесить редкий класс в loss. Если сделать это с весами 1, 10 и 60 для положительных:
| weight on positives | accuracy | precision | recall | F1 | AUC |
|---|---|---|---|---|---|
| 1 | 0.9830 | 0.800 | 0.108 | 0.190 | 0.9363 |
| 10 | 0.9605 | 0.253 | 0.581 | 0.352 | 0.9361 |
| 60 | 0.8290 | 0.091 | 0.919 | 0.166 | 0.9361 |
Precision и recall сильно смещаются. AUC — вероятность того, что модель поставит случайную дефектную деталь выше случайной хорошей, полностью игнорируя порог, — меняется на 0.0002, то есть ни на что. Перевзвешивание сдвинуло ту же модель вдоль той же кривой компромисса. Часто именно это вам и нужно, и это никогда не новая информация: если ранжирование плохое, никакая схема взвешивания его не спасёт.
Три разбиения и утечка, которую вы сейчас найдёте
Ссылка на раздел: Три разбиения и утечка, которую вы сейчас найдётеПочему три разбиения, а не два? Потому что в тот момент, когда вы используете набор примеров, чтобы выбрать что угодно — порог, скорость обучения, какую из шести моделей отправить, — этот набор уже использован для подгонки, и его оценка перестаёт быть несмещённой.3 Измерено на этом конвейере: перебор порога на validation set выбирает 0.196, и затем модель получает F1 = 0.4122 на нетронутом test set. Если бы перебор запустили прямо на test set, лучший достижимый результат там был бы 0.4186 — число, которое никто не имеет права публиковать.
Зазор здесь мал, 0.006, потому что это один hyperparameter, один раз перебранный по 4 000 validation-примерам. Он растёт с каждым дополнительным решением и каждым уменьшением validation set. Заметьте также, что направление на одном запуске не гарантировано: выбранный порог дал 0.3902 на validation и 0.4122 на test, то есть validation занизил его в этот раз. Смещение систематично по множеству решений, а не видно в одном.4
Теперь упражнение. Журнал конвейера приходит с третьим столбцом, station_seconds: сколько времени каждая деталь провела на станции инспекции. Добавить его — изменение preprocessing в одну строку. Вот что оно делает:
| model | accuracy | precision | recall | F1 | cross-entropy | AUC |
|---|---|---|---|---|---|---|
| width + weight | 0.9830 | 0.800 | 0.108 | 0.190 | 0.0564 | 0.9363 |
| + station_seconds | 0.9920 | 0.792 | 0.770 | 0.781 | 0.0236 | 0.9970 |
Recall растёт с 10,8 % до 77,0 %. F1 увеличивается более чем в четыре раза. И обратите внимание, что сделала accuracy: 98,30 % → 99,20 %, прирост в девять десятых пункта — как раз такое число, которое на итоговом слайде округляют до «примерно 99 % в любом случае». Accuracy раньше не увидела провал, а теперь не видит мошенничество.
Прежде чем читать дальше: модель жульничает. Найдите как.
Как искать leakage, в порядке, который находит его быстрее всего.
-
Сравните train и test. Overfitting проявляется как большой разрыв. Здесь: честная модель 0.9838 train / 0.9830 test; модель с leak 0.9936 train / 0.9920 test. Оба разрыва меньше 0,2 пункта. Leakage не выглядит как overfitting — leaky-признак так же доступен на test time, поэтому модель прекрасно обобщает на мир, которого не существует.
-
Обучите по одной модели на каждый признак отдельно. Всё, что несёт ответ, объявит о себе:
feature alone accuracy recall F1 AUC width 0.9815 0.014 0.026 0.8691 weight 0.9815 0.000 0.000 0.7914 station_seconds0.9850 0.405 0.500 0.9960 Один столбец сам по себе ранжирует дефекты с AUC 0.9960. Два измерения, сделанные штангенциркулем и весами, дают 0.87 и 0.79. Эта асимметрия — сигнал тревоги.
-
Спросите, когда было записано каждое число. Среднее время пребывания: 2,23 секунды для деталей, которые прошли, 15,56 секунды для деталей, которые не прошли. Конечно. Деталь задерживается на станции потому что инспектор снял её с конвейера — а это происходит после и только потому, что кто-то решил, что она дефектная. Столбец — не измерение детали. Это измерение вердикта.
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()) Выделенная строка — leakage: время пребывания дефектной детали берётся из другого распределения, потому что человек снял её с конвейера. Это самая распространённая серьёзная ошибка в прикладном машинном обучении, и у неё есть название: target leakage — информация в обучающих признаках, которая не была бы доступна в момент, когда нужно сделать предсказание.5 Она не бросает исключение. Она даёт лучшее число. Каждый стимул в проекте толкает к тому, чтобы её оставить.
Защита — один вопрос, задаваемый каждому столбцу: в тот момент, когда мне нужно это предсказание, это значение уже существует? На живом конвейере station_seconds неизвестен до тех пор, пока деталь не была проинспектирована, — а именно это модель должна была заменить.
Сколько тестовых примеров мне нужно?
Ссылка на раздел: Сколько тестовых примеров мне нужно?Предположим, вы оцениваете модель на 20 примерах, и она отвечает правильно на 17. Вы сообщаете 85 %.
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 % случаев — один запуск из двадцати трёх, — а если вы попробовали несколько prompts и сообщили лучший, вы сами изготовили этот запуск. Семнадцать из двадцати не отличают модель 85 % от модели 65 %.
Два способа поставить интервал на долю, и оба должны быть в вашем toolkit:
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 для обычной доли успехов; он остаётся хорошо ведущим себя при любом и не требует randomness. Обратите внимание выше, что при верхний конец bootstrap равен 1.0000 — ресэмплирование 20 точек легко может вытянуть 20 правильных, поэтому оно не может представить интервал уже собственной гранулярности. Используйте bootstrap7 там, где нет формулы, а это большинство интересных случаев: F1, macro-averages, BLEU, pass@1, score rubric-based judge. На этом конвейере F1 настроенной модели 0.4122 несёт bootstrap-интервал [0.3009, 0.5156] — именно это число должно быть в отчёте, потому что одна точечная оценка приглашает к сравнению, которое она не может поддержать.
Ещё одно измерение, потому что оно меняет то, как нужно сравнивать две модели. Две модели оценены на одних и тех же 500 примерах:
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 элементов, и A выигрывает 27 из этих разногласий; общие примеры, лёгкие и трудные, сокращаются вместо того, чтобы добавлять шум. Сравнивайте модели попарно — и придёте к тому же выводу на доле данных.
Куда дальше
Ссылка на раздел: Куда дальшеТеперь у вас есть модель, которая выдаёт откалиброванные вероятности, loss, выведенный из утверждения о данных, а не выбранный из удобства, gradient, который буквально равен предсказанию минус истина, и — что важнее — механизм, чтобы выяснить, работает ли хоть что-то из этого. Десятистрочный интервал Wilson выше переиспользуется дословно: он несёт варианты prompt в главе 15, таблицы retrieval в главе 19 и golden set в главе 29. Bootstrap — то, за что вы берётесь, когда формулы нет.
Но модель всё ещё однослойная. Она рисует линию, а глава 1 доказала четырьмя строками XOR, что линии недостаточно. Исправление — складывать: первый слой изгибает пространство, второй рисует линию в изогнутом пространстве.
Именно здесь аккуратный gradient этой главы заканчивается. Всё выше работало, потому что можно было один раз выписать вручную для модели с одним слоем между входом и loss. Поставьте в середину второй слой, и вопрос меняет форму: какова производная loss по весу, который вообще не касается выхода, — по тому, чьё влияние приходит только через другой слой, возможно сразу по нескольким путям?
Такая производная существует. Вычислять её вручную безнадёжно для всего, что больше игрушки, а вычислять её по одному параметру за раз безнадёжно уже в другом масштабе. Нужна процедура, которая получает каждую производную в сети за один backward pass по тому же графу, по которому только что прошёл forward pass.
Это глава 5, и это двигатель, на котором работает остальной курс.
Источники и метод
Ссылка на раздел: Источники и методТакже стоит читать рядом с этой главой: Bishop, Pattern Recognition and Machine Learning §1.2, §1.5, §1.6 and §4.3, где probability, decision theory, information theory и linear classification изложены в порядке, которому следует эта глава; Murphy, Probabilistic Machine Learning: An Introduction, chapters 6 and 10; Prince, Understanding Deep Learning §5.4–5.7; and 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 % деталей дефектны.
Сноски
Ссылка на раздел: Сноски-
Ma, T. and Ng, A. CS229 Lecture Notes, Stanford University, chapters 2 and 3. Там сокращение, дающее , перестаёт выглядеть удачей: выберите распределение из экспоненциального семейства, соответствующее вашему выходу, используйте его каноническую link function, и gradient всегда будет «предсказание минус истина». ↩
-
Olah, C. Visual Information Theory (2015),
colah.github.io/posts/2015-09-Visual-Information. Самое ясное доступное объяснение entropy, cross-entropy и KL divergence как затрат в битах, а не как формул. ↩ -
Abu-Mostafa, Y. S., Magdon-Ismail, M. and Lin, H.-T. Learning From Data (AMLBook, 2012), lectures 13 and 17 of the Caltech course. Lecture 13 — validation; lecture 17, о трёх принципах обучения, — место, где названо data snooping. Вместе они являются источником дисциплины этой главы: каждый взгляд на набор данных — это решение о подгонке, независимо от того, запускали вы optimiser или нет. ↩
-
James, G., Witten, D., Hastie, T. and Tibshirani, R. An Introduction to Statistical Learning, 2nd edition (Springer, 2021), chapters 2 and 5, про bias–variance decomposition и resampling. Сопутствующий том — место, где ловушка выбора сформулирована прямо: Hastie, Tibshirani and Friedman, The Elements of Statistical Learning, 2nd edition, §7.10.2, The Wrong and Right Way to Do Cross-validation. ↩
-
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. Формальная трактовка сбоя, показанного выше, с кейсами из соревнований, выигранных моделью, которая выучила артефакт сборки данных. ↩
-
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 interval, использованный выше в
wilson(), всё ещё правильный дефолт для пропорции. Учебный интервал — тот, которого стоит избегать: он даёт бессмыслицу рядом с 0 и 1 и плохо покрывает при малых . ↩ -
Efron, B. Bootstrap Methods: Another Look at the Jackknife. The Annals of Statistics 7(1), pp. 1–26 (1979). Идея, которая позволяет поставить интервал на любую статистику, которую вы можете вычислить, включая те, для которых нет теории выборки. ↩