Срезы данных в машинном обучении: как рост accuracy на 2,5% скрывает провал recall вдвое
Модель точнее на 2,5%, но пропускает вдвое больше нарушений: как срезы данных спасают от ложных выводов перед релизом

Общая метрика accuracy выросла с 93% до 95,5%, очередь на модерацию сократилась, но на карточках нового партнёрского фида модель стала пропускать 12 нарушений из 20 вместо прежних четырёх, и без проверки по срезам этот провал остался бы невидимым.
Одна агрегированная цифра качества ML-модели (модели машинного обучения) способна замаскировать деградацию на конкретном сегменте данных. Если вы выкатываете новую версию классификатора без разбивки по источникам, вы рискуете ухудшить продукт там, где это критично, и узнать об этом уже после релиза.
Иннокентий Бодров, Product Leader с опытом более 18 лет в технологических продуктах, разобрал эту ловушку на воспроизводимом учебном сценарии. Суть проблемы знакома всем, кто работает с классификаторами: новый источник данных меняет баланс выборки, и общие метрики растут за счёт большой «старой» группы, скрывая провал на малой «новой». Ниже пошаговая процедура, которую можно применить к любому размеченному набору данных, не только к модерации объявлений.
Что понадобится?
- Python 3.8+ со стандартной библиотекой (внешние пакеты не нужны, но pandas ускорит работу с большими наборами).
- Размеченный контрольный набор с двумя столбцами предсказаний: от старой и от новой модели, плюс столбец-метка реального класса.
- Поле-источник в каждой строке, по нему будете нарезать срезы (фид, канал, регион, тип контента).
- Около 30 минут, чтобы пройти весь цикл от кода до решения о релизе.
Пошаговая инструкция
1. Подготовьте данные в едином формате.
Каждая строка контрольного набора должна содержать четыре поля: источник, подтверждённый класс (нарушение или нет), предсказание модели A (старой) и предсказание модели B (новой). Добавьте идентификатор карточки и время создания, чтобы убедиться, что обе версии получили одинаковый вход.
2. Задайте срезы.
Срез (slice) в машинном обучении означает подмножество данных, выделенное по признаку: источник, тип контента, длина текста, регион. В нашем случае срезы два: основной поток (900 карточек) и партнёрский фид (100 карточек).
3. Посчитайте базовые метрики для каждого среза отдельно.
Вот воспроизводимый код на стандартной библиотеке Python, который делает именно это:
from collections import defaultdict
def segment(name, positives, negatives, a_tp, a_fp, b_tp, b_fp):
rows = []
for i in range(positives):
rows.append((name, 1, int(i < a_tp), int(i < b_tp)))
for i in range(negatives):
rows.append((name, 0, int(i < a_fp), int(i < b_fp)))
return rows
rows = (
segment("main", 90, 810, a_tp=72, a_fp=40, b_tp=81, b_fp=20)
+ segment("partner_api", 20, 80, a_tp=16, a_fp=8, b_tp=8, b_fp=4)
)
def evaluate(data, prediction_column):
counts = defaultdict(int)
for _, actual, pred_a, pred_b in data:
predicted = (pred_a, pred_b)[prediction_column]
counts[(actual, predicted)] += 1
tp = counts[(1, 1)]
fp = counts[(0, 1)]
fn = counts[(1, 0)]
tn = counts[(0, 0)]
n = tp + fp + fn + tn
return {
"n": n, "tp": tp, "fp": fp, "fn": fn, "tn": tn,
"accuracy": (tp + tn) / n if n else None,
"recall": tp / (tp + fn) if tp + fn else None,
"precision": tp / (tp + fp) if tp + fp else None,
}
for name in ("all", "main", "partner_api"):
part = rows if name == "all" else [r for r in rows if r[0] == name]
for model, column in (("A", 0), ("B", 1)):
m = evaluate(part, column)
print(name, model,
f"n={m['n']} TP={m['tp']} FP={m['fp']}",
f"FN={m['fn']} TN={m['tn']}",
f"accuracy={m['accuracy']:.1%}",
f"recall={m['recall']:.1%}",
f"precision={m['precision']:.1%}")
4. Сравните recall по срезам, а не только accuracy.
Accuracy (точность на всём наборе) выросла с 93% до 95,5%. Recall (полнота, доля найденных нарушений) на всём наборе тоже подросла: с 80% до 80,9%. Но recall на партнёрском фиде упала с 80% до 40%.
Recall (полнота) показывает, какую долю реальных нарушений модель обнаружила. FN (false negative, ложноотрицательный результат) означает нарушение, мимо которого модель прошла. FP (false positive, ложноположительный результат) означает чистую карточку, которую модель зря отправила модератору.
5. Определите цену каждого исхода для продукта.
Один пропущенный FN в модерации означает нарушение, которое увидит пользователь. Один лишний FP означает задержку для добросовестного продавца и лишнюю работу для модератора. Выбор метрики зависит от того, что дороже для конкретного продукта.
6. Примите решение о релизе на основе среза, а не агрегата.
Если recall на критичном срезе упала вдвое, общий рост accuracy не является основанием для выкатки. Варианты: доработать модель на данных нового фида, применить отдельный порог для партнёрского источника или временно направлять весь партнёрский фид на ручную проверку.
Запускаем код из шага 3. На выходе три блока строк.
Строка all B показывает: accuracy=95,5%, recall=80,9%. Всё отлично.
Строка partner_api B показывает: accuracy=80,0%, recall=40,0%. Модель B пропустила 12 нарушений из 20 на партнёрском фиде вместо 4 у модели A.
Без разбивки по срезам вы бы увидели только первую строку и приняли бы решение о релизе. После разбивки видно, что новая модель разгружает модераторов за счёт основного потока, но теряет контроль там, где появился новый источник данных.
Публичный кейс подтверждает тот же принцип
Исследователи Jigsaw проверили две версии классификатора токсичности Perspective API на корпусе из 1,8 млн комментариев. На коротких комментариях с упоминанием геев или лесбиянок метрика BPSN AUC (она сравнивает оценки нетоксичных комментариев среза с токсичными вне среза) у первой версии составила 0,57, а после коррекции обучающих данных (training data) выросла до 0,82.
При этом авторы обнаружили, что коррекция лучше помогла коротким комментариям и не сняла проблему полностью на всём корпусе. Вывод тот же: после исправления нужно повторно проверить не только целевой срез, но и соседние группы по длине и содержанию.
Это результаты офлайн-оценки моделей в исследовании, а не измерение ошибок модерации в действующем сервисе.
Что делать с этим прямо сейчас, по ролям?
Авторам Дзена и копирайтерам. Если вы используете ML-классификаторы для автоматической модерации комментариев или фильтрации спама, перед обновлением модели проверьте метрики отдельно на комментариях от разных источников трафика. Разница между «стало лучше в среднем» и «стало лучше везде» может стоить вам потери аудитории.
Маркетологам. Любой A/B-тест с ML-компонентом (рекомендации, скоринг лидов, автоматическая категоризация) нуждается в разбивке результатов по сегментам. Агрегированная конверсия может расти за счёт крупного сегмента, скрывая провал на малом, но стратегически важном.
Предпринимателям и продакт-менеджерам. Код из статьи работает без внешних зависимостей и не требует GPU. Для тех, кто глубже изучает основы машинного обучения, полезен учебник Николенко и соавторов (часто ищут как «машинное обучение основы николенко pdf»), где подробно разобраны метрики классификации и их ограничения.
Доверять одной метрике. Агрегированные precision, recall или F1 тоже скрывают ухудшение отдельной группы. Accuracy здесь не виновата, виноват отказ от разбивки.
Забыть про FN. В задачах модерации пропущенное нарушение (FN) обычно дороже ложного срабатывания (FP). Если вы оптимизируете accuracy, вы неявно приравниваете цену этих ошибок.
Проверить только целевой срез. Кейс Jigsaw показывает: после исправления одного среза соседние группы могут не улучшиться или ухудшиться. Проверяйте весь набор.
Не зафиксировать вход. Если модели A и B получили разные карточки или разную разметку, сравнение бессмысленно. Убедитесь, что обе версии оценены на одних и тех же строках.
Процедура, которую описал Бодров, на первый взгляд элементарна: разбей данные по источнику, посчитай метрики отдельно. Но я регулярно вижу, как даже опытные команды пропускают этот шаг, когда «общая цифра выросла». Код занимает 30 строк, не требует ни pandas, ни sklearn, и это делает порог входа практически нулевым.
Честная оговорка: все числа в статье учебные, а не данные работающего сервиса. В реальном проекте разметка бывает неполной, срезы пересекаются, а «правильного» порога для recall не существует без бизнес-контекста. Но сама привычка вскрывать агрегат перед релизом экономит больше, чем любой рост accuracy на пару процентов.
Привычка вскрывать агрегированную метрику по срезам перед каждым релизом не требует ни бюджета, ни сложных инструментов: 30 строк на Python и 30 минут, а цена пропущенного среза может обойтись в откат модели и репутационный ущерб, который не покроет никакой рост accuracy.

Основатель dzen.guru. Эксперт по монетизации и продвижению на Дзен. Автор курса «Старт на Дзен 2026».
Читайте также

Вайбкодинг в Эвоторе: продакт без навыков кода собрала микросервис за дни вместо месяцев
Компания Эвотор показала, как продакт-менеджер без навыков программирования за один квартал собрала несколько рабочих микросервисов, включая бесплатный…

Что такое ИИ-агент без кода: LANSOFT показала «вайб-конфигурирование» вместо вайб-кодинга
Компания LANSOFT через подразделение LDM впервые описала рабочую схему, в которой ИИ-агент собирает приложения не написанием кода, а конфигурированием готовых…

Парадокс рынка труда: безработица 2,2%, а IT-вакансий стало на 56% меньше
Я вижу, что оригинал обрывается на полуслове. Работаю строго по тому, что есть в источнике, без додумывания обрезанных фрагментов. Российский рынок труда в…
Комментарии