Почему одной метки «опасно / безопасно» недостаточно

Один и тот же запрос может одновременно относиться к нескольким категориям — например, содержать признаки и финансового мошенничества, и киберпреступления. Поэтому задача поставлена как multi-label: модель принимает 15 независимых бинарных решений на каждый текст, а не выбирает одну категорию из списка.

Это нужно для управляемой модерации: Child_exploitation требуется эскалировать на ручную проверку, а Swear_words достаточно залогировать. Бинарный вердикт такого различия не даёт.

Сами 15 выходов неоднородны, и мы разделяем их явно:

  • 11 категорий вреда — эксплуатация детей, насильственные действия, вооружение, наркотики, причинение вреда себе, дискриминация и язык ненависти, нацизм, ненасильственные и финансовые преступления, киберпреступления, порнографические материалы.
  • 4 тематических флага — мат, политика, религиозные преступления, ЛГБТ. По умолчанию выключены и включаются под конкретное внедрение.

Правильное прочтение срабатываний — повод показать текст человеку, а не вывод о нарушении. Сама модель ничего не блокирует: она отдаёт вероятности, а действие выбирает вызывающая система.

Как устроена модель

Один forward-проход из четырёх стадий:

  • Многоязычный энкодер mmBERT-base (307M параметров) превращает текст в скрытые состояния. Лимит контекста — 1024 токена.
  • 15 категориальных экспертов читают состояния параллельно, каждый со своими обучаемыми query-токенами. Эксперты независимы, поэтому редкая категория не делит решение с частой. Единый батчевый forward вместо цикла по категориям даёт ускорение примерно втрое.
  • Модуль взаимодействия категорий — двухслойный трансформер, читающий все 15 представлений вместе. Он кодирует то, что метки сообщают друг о друге: контент про оружие обычно и про насилие, нацизм обычно и про дискриминацию.
  • Суммирование и сигмоида дают 15 независимых вероятностей, каждая со своим порогом.

При обучении используется асимметричная функция потерь (ASL), намеренно штрафующая пропуски сильнее ложных срабатываний, дифференцированный learning rate (backbone 3e-5, эксперты 5e-4) и EMA-усреднение весов. Пороги подбираются для каждой категории отдельно через grid search, лежат в диапазоне 0.49–0.62 и хранятся отдельно от весов — чувствительность корректируется в production без переобучения.

Лимит в 1024 токена обоснован реальным распределением длин обучающего корпуса:

Перцентиль P50 P75 P90 P95 P99 P99.5
Токены ~43 ~87 ~184 ~279 ~555 ~714

Половина текстов короче 43 токенов, но хвост растёт нелинейно. При max_length=1024 покрывается 99.78% датасета из 1 836 335 примеров; оставшиеся 0.22% длиннее лимита и все безопасные, поэтому они исключаются из обучения целиком, а не усекаются.

Сравнение с 9 открытыми решениями

Мы прогнали единый бенчмарк двух наших моделей против 9 открытых guardrail- и toxicity-моделей сторонних разработчиков. Выборка — 7 480 примеров внешнего набора, собранного специально для оценки: он не пересекается со сплитами обучающего корпуса, каждый пример размечен тремя аннотаторами (Fleiss' κ = 0.82), баланс 50/50.

Модель Binary F1 F1-micro (14) F1-weighted (14) F1-macro (14)
HiveTrace Multilabel (наша) 0.9588 0.7403 0.7987 0.7026
YuFengXGuard_0.6B 0.9544 0.7197 0.6625 0.4581
Qwen3Guard_8B 0.9337 0.5700 0.6683 0.3952
Qwen3Guard_4B 0.9327 0.5676 0.6678 0.3923
Qwen3Guard_0.6B 0.9235 0.5514 0.6470 0.3746
gliner_guard_omni (наша) 0.9076 0.7111 0.7006 0.5489
opir_multitask_multilang 0.8718 0.2824 0.2564 0.2735
gliguard_300m 0.8025 0.5563 0.5268 0.2395
apanc_russian_sensitive_topics 0.7295 0.4792 0.4997 0.4149
rubert_tiny_toxicity 0.5455 0.5254 0.4114 0.1270
unitary_multilingual_toxic 0.1304 0.5183 0.3584 0.0927

Главный вывод: бинарной метрики недостаточно для выбора модели. У Qwen3Guard_8B binary F1 = 0.9337, но multilabel F1-macro всего 0.3952 — модель хорошо ловит «опасно / безопасно» и плохо определяет, чем именно опасно.

По binary F1 наша модель идёт вровень с ближайшим конкурентом: 0.9588 против 0.9544. Разрыв 0.0044 при n = 7 480 укладывается в выборочную погрешность, поэтому честная формулировка — «на уровне», а не «обходим». А разрыв по F1-macro (0.7026 против 0.4581) — другого порядка и распределён по всем категориям.

Отличается и характер ошибок. Семейство Qwen3Guard работает почти исключительно на precision (0.9997 при recall 0.876) — оно не поднимает ложных тревог и платит за это пропусками. Наша модель держит precision 0.9725 при recall 0.9455 — тот баланс, ради которого настраивалась асимметричная функция потерь.

Экономика инференса

Замеры сделаны на одной NVIDIA RTX 3090 для трёх бэкендов при фиксированных размерах батча.

Batch PyTorch P95 TensorRT P95 TensorRT, текстов/с PyTorch, $/1M TensorRT, $/1M
1 17.4 мс 5.9 мс 173.4 1.3528 0.4806
8 18.2 мс 7.9 мс 1045.1 0.1788 0.0797
16 18.8 мс 12.5 мс 1345.1 0.0944 0.0620
64 72.4 мс 69.1 мс 954.6 0.0909 0.0873

Оптимум — TensorRT при batch=16: лучшая задержка среди практичных размеров батча, наивысшая пропускная способность и наименьшая стоимость одновременно, компромисса между ними здесь нет. Это в 2.9 раза дешевле наивного фиксированного batch=8 на PyTorch и в 21.8 раза дешевле работы вообще без батчинга. При batch ≥ 32 бэкенды сходятся и преимущество TensorRT практически исчезает. Веса модели ни одна из этих оптимизаций не затрагивает.

Для сравнения: внешний LLM-as-judge через API — это 300–2000 мс задержки и $63–400 за миллион запросов. Собственная модель выигрывает у него на 1–2 порядка по латентности и на 3–4 порядка по стоимости, а данные при этом не покидают инфраструктуру компании.

Где система ошибается

Агрегаты скрывают неровный профиль, поэтому называем слабые места прямо. Шесть из 13 risk-категорий бенчмарка попадают в диапазон 0.73–0.96 (Drugs 0.90, LGBT 0.89, Swear_words 0.86, Self_harm 0.85, Sexual_content 0.82, Child_exploitation 0.73), остальные ниже: Weapons 0.68, Religion 0.62, Politics 0.58, Cybercrime 0.56, Hate_discrimination 0.56, Violence 0.48, Non_violent_crime 0.34.

F1 по 13 каноническим risk-категориям и производной метке Safe

Hate_discrimination с F1 0.56 — критичная категория, которой нужен отдельный дашборд recall, а не вера в агрегат. Non_violent_crime с F1 0.34 — объединённая категория бенчмарка, вобравшая финансовые преступления, поэтому часть слабости объясняется свёрткой. Если убрать производную метку Safe, macro-F1 только по 13 risk-категориям — 0.682 против 0.7026 по 14 меткам.

Есть и системные ограничения:

  • принципиально новые техники обфускации потребуют дообучения — модель устойчива к трансформациям из обучающей выборки;
  • окна длинных запросов скорятся без контекста остального документа;
  • совместное срабатывание 4+ категорий в обучающих данных встречалось редко, и поведение модели здесь менее предсказуемо.

Подстраховка одна и та же во всех случаях: пороги правятся в production мгновенно, а для критичных для вас категорий, рекомендуется human-in-the-loop эскалация в «серой зоне».

Guardrail-классификатор закрывает регуляторный и репутационный риск на объёмах, где ручная модерация уже не масштабируется, и оставляет данные внутри контура компании.

Полная методология бенчмарка, детали архитектуры и метрики доступны в техническом отчёте.