Статистика сравнения¶
Эта страница — вывод того, что считают ranklens.stats, ranklens.aggregate
и ranklens compare: какая гипотеза проверяется, откуда интервал, p-value и q-value
и почему им можно верить.
Единица наблюдения — запрос¶
Для метрики \(M\) (например, NDCG@10) и запроса \(q\), который есть в обоих прогонах, разница
Всё дальнейшее — о среднем \(\bar\delta = \frac1n \sum_q \delta_q\) по \(n\) парным запросам. Запросы из одного прогона отбрасываются: парному тесту нужны пары.
Ресемплируются запросы, а не строки выдачи. Документы одной выдачи зависимы: если ресемплировать строки, получится больше «независимых» наблюдений, чем есть, и интервал станет уже правды. Вывод — о совокупности запросов, из которой взята выборка, а не о документах или пользователях.
Интервал: парный bootstrap¶
Из \(n\) разниц \(B\) раз берётся выборка того же размера с возвращением, для каждой считается среднее \(\bar\delta^{*}_b\). Интервал уровня \(1-\alpha\) — перцентили:
Он отвечает на вопрос «насколько точно измерено среднее», но гипотезу не проверяет. Покрытие проверено симуляцией: на 1000 выборок 95%-й интервал содержит истинное среднее в 95.3% случаев. На малых выборках со скошенными разницами перцентильный интервал бывает уже, чем нужно, — поэтому p-value считается отдельно.
p-value: перестановочный тест¶
Гипотеза. \(H_0\): распределение \(\delta_q\) симметрично относительно нуля — для каждого запроса всё равно, какой прогон назвать A, а какой B.
При \(H_0\) знак каждой разницы случаен. Тест \(m\) раз переворачивает знаки независимо с вероятностью ½ и считает, сколько раз \(b\) среднее по модулю не меньше наблюдаемого:
Единицы в числителе и знаменателе — наблюдаемая расстановка знаков: она тоже принадлежит множеству перестановок. Поэтому \(p > 0\) всегда, а \(P_{H_0}(p \le \alpha) \le \alpha\) для любого \(m\) — тест точный, а не асимптотический. При \(m = 10\,000\) минимальное значение ≈ \(10^{-4}\).
p-value дискретен с шагом \(1/(m+1)\), поэтому равномерность под \(H_0\) проверяется долей отвержений на уровнях 0.05, 0.1, 0.2, а не тестом Колмогорова — Смирнова.
Много сравнений: Бенджамини — Хохберг¶
При 40 сегментах и \(\alpha = 0.05\) даже одинаковые модели дают в среднем \(40 \cdot 0.05 = 2\) «значимых» сегмента. Поправка Бонферрони (порог \(\alpha/m\)) контролирует вероятность хотя бы одной ошибки (FWER) и на десятках сегментов почти ничего не пропускает. BH контролирует другое — FDR, ожидаемую долю ложных среди объявленных находками:
где \(R\) — число находок, \(V\) — число ложных среди них.
Процедура. Отсортируем p-values: \(p_{(1)} \le \dots \le p_{(m)}\). Находки — все гипотезы с рангом не больше \(k = \max\{ i : p_{(i)} \le \tfrac{i}{m}\alpha \}\). Эквивалентно — через q-value, которое печатается в отчёте:
и находка — это \(q < \alpha\). Минимум по \(j \ge i\) нужен, чтобы q не убывал при росте p: иначе более сильное свидетельство получило бы больший q.
from ranklens.stats import benjamini_hochberg
# 0.01 * 3/1 = 0.03; 0.04 * 3/2 = 0.06; 0.5 * 3/3 = 0.5
print(benjamini_hochberg([0.01, 0.04, 0.5]))
(0.03, 0.06, 0.5)
p = 0.04 по отдельности проходит порог 0.05, а среди трёх сравнений — уже нет.
Когда BH корректен. Для независимых p-values (и для положительно зависимых, PRDS).
Сегменты не пересекаются по запросам, значит их сравнения независимы — ровно
условие теоремы. Чтобы этой независимости не мешала общая случайность, каждый
сегмент получает свой поток случайных чисел, порождённый из seed: при общем потоке
сегменты одного размера получали бы одинаковые ресемплы.
Что считается семьёй. С --segments — сегменты одной метрики; без них — метрики
между собой.
Интервалы не поправляются. Интервал сегмента может не содержать ноль при \(q \ge \alpha\) — решает q-value.
Сколько нужно запросов: MDE¶
Минимальный обнаружимый эффект — наименьшая истинная разница, которую двусторонний тест уровня \(\alpha\) обнаружит с вероятностью (мощностью) \(1-\beta\). По ЦПТ \(\bar\delta \approx \mathcal N(\Delta, \sigma^2/n)\), где \(\sigma\) — стандартное отклонение \(\delta_q\). Отвержение — когда \(|\bar\delta| > z_{1-\alpha/2}\,\sigma/\sqrt n\). Требуем, чтобы при истинной разнице \(\Delta\) это происходило с вероятностью \(1-\beta\) (вклад противоположного хвоста пренебрежимо мал):
При \(\alpha = 0.05\) и мощности 0.8 множитель ≈ 1.96 + 0.84 = 2.80:
from ranklens.stats import mde, required_queries
print(f"MDE на 200 запросах при sd = 0.1: {mde(sd=0.1, n_queries=200):.4f}")
for delta in (0.02, 0.01, 0.005):
print(f"чтобы увидеть +{delta}: {required_queries(delta, sd=0.1)} запросов")
MDE на 200 запросах при sd = 0.1: 0.0198
чтобы увидеть +0.02: 197 запросов
чтобы увидеть +0.01: 785 запросов
чтобы увидеть +0.005: 3140 запросов
Незначимый результат на малой выборке не означает «эффекта нет»: он значит, что эффект ниже разрешающей способности этой выборки.
Неточная разметка: PPI++¶
Всё выше предполагает, что метрика по запросу измерена верно. Если разметка систематически ошибается — LLM завышает оценки длинным документам, один асессор строже консилиума, — дельта \(\bar\delta\) смещена, и интервал останется узким вокруг неверного числа: ресемплинг запросов этого не видит.
Две разметки. Дешёвая (proxy: LLM, один асессор) есть на всех запросах; эталонная (gold: консилиум, эксперты) — на случайной подвыборке \(G\) из \(n\) запросов. \(U\) — остальные \(N\) запросов, только с proxy. Для каждого запроса — дельта «B минус A» по proxy-разметке \(\delta^{p}_q\) и, на \(G\), по gold-разметке \(\delta^{g}_q\).
Оценка (prediction-powered inference):
Второе слагаемое — поправка: на тех же запросах видно, насколько proxy ошибается именно в этой разнице. Несмещённость для любого \(\lambda\): \(G\) — случайная выборка, поэтому \(\mathbb E\,\overline{\delta^p}_U = \mathbb E\,\overline{\delta^p}_G\), и слагаемые с \(\lambda\) сокращаются в среднем: \(\mathbb E\,\hat\Delta_\lambda = \mathbb E\,\delta^g\). Если люди размечали «интересные» запросы, а не случайные, это равенство неверно, и оценка смещена.
Дисперсия. \(U\) и \(G\) не пересекаются, слагаемые независимы:
PPI++: \(\lambda\) по данным. Производная по \(\lambda\) равна нулю при
\(c\) оценивается на \(G\), \(\sigma_p^2\) — по всем proxy-дельтам. \(\lambda = 0\) — только gold, \(\lambda = 1\) — классический PPI; \(\lambda^*\) не хуже ни того, ни другого: хороший proxy получает \(\lambda\) около 1, бесполезный — около 0. Интервал и p-value — нормальное приближение.
Симуляция: 2000 повторов, \(N = 2000\), \(n = 200\), истинная дельта 0.010:
| proxy | разброс PPI++ | PPI (\(\lambda=1\)) | только gold | \(\hat\lambda\) |
|---|---|---|---|---|
| хороший, смещён на +0.006 | 0.0029 | 0.0032 | 0.0071 | 0.83 |
| плохой | 0.0067 | 0.0091 | 0.0069 | 0.17 |
| без связи с gold | 0.0070 | 0.0102 | 0.0070 | 0.00 |
Все три несмещены; proxy без поправки дал бы 0.016. Покрытие 95% интервала PPI++ — 0.945–0.956. При плохом proxy классический PPI хуже, чем «только gold», — поэтому \(\lambda\) оценивается, а не фиксируется.
Чего эти числа не говорят¶
- Не «отличаются ли ранжирования». Одинаковый NDCG возможен при совершенно
разном порядке документов — для этого
ranklens.metrics.rbo. - Не «заметят ли пользователи». Это онлайн-эксперимент.
- Не «верна ли разметка» — если не использовать gold-подвыборку и PPI++ (выше).