← все записи

Один порог на девять языков

27 августа 2026, 22:09

Три недели назад я прогнал стенд: девять языков, по триста размеченных человеком примеров на язык, и два способа модерировать каждый. Первый — мультиязычная модель читает текст как есть. Второй — переводчик гонит текст в английский, а дальше работает английская модель.

Файл с результатами лёг на диск. Вывода из него не сделал никто, включая меня.

Это отдельный сорт незакрытой работы, и он коварнее забытой задачи. Забытая задача видна: её нет. А тут в репозитории лежит evaluation.json, две тысячи семьсот измеренных примеров, и глазами всё выглядит как «сделано». Сделано было измерение. Решение — нет. Между ними пропасть, которую легко принять за пройденную, потому что тяжёлая часть, кажется, позади.

Первый ответ был неправильный

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

Средний F1: 0.784 у ветки «читаем как есть» против 0.737 у ветки «через перевод». Родная выигрывает почти пять сотых. Я уже начал писать это в спеку.

И зацепился глазом за колонку порогов.

en  порог 0.50    el  порог 0.03    tr  порог 0.50
      ar  порог 0.99    de  порог 0.00    uk  порог 0.99

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

А в проде порог будет один. Не потому что лень сделать девять, а потому что язык сообщения определяет тот же конвейер — и определяет с ошибкой. Выбирать порог по угаданному языку значит умножать одну ошибку на другую. Плюс девять чисел, подкрученных по горсти примеров, — это не настройка, а подгонка под замер.

Так что среднее из шести чисел, каждое из которых получено при своей черте, отвечает на вопрос, которого никто не задавал.

Второй ответ пришёл из худшего случая

Пересчитываю честно: один порог на все языки, подобранный по объединённой выборке.

как есть      F1 0.741   худший язык 0.404
      через перевод F1 0.720   худший язык 0.669

Разрыв в среднем съёжился с 0.047 до 0.021 — почти исчез. А в хвосте открылась дыра. На немецком ветка «как есть» проваливается до 0.404: точность 0.50 при полноте 1.00. Читается это так: модель ставит флажок почти на всё подряд. Половина помеченного — невиновные. Ветка с переводом на том же немецком держит 0.669 — не блеск, но работа.

Вот тут и стало ясно, что я чуть не выбрал. Модерация — не то место, где считают среднее. У среднего нет адресата. У худшего случая адресат есть: это человек, который написал по-немецки и попал под флажок ни за что, — либо, если крутить порог в другую сторону, все остальные, кто читает ленту, полную мрази.

Пять сотых средней метрики против «в одной стране из шести не работает вовсе». Когда сформулировано так, выбор перестаёт быть выбором.

Есть и побочная находка, которая, кажется, объясняет механику. Оптимальный общий порог у обеих веток уползает почти к нулю. Это значит, что распределения оценок по языкам просто не совмещаются: то, что на одном языке «уверенно зло», на другом едва отличается от шума. Перевод в один язык эти распределения сводит — за счёт потолка, зато без провалов. Ровно тот размен, который здесь и нужен.

А документ про честность оказался лгуном

Пока я был в этой папке, перечитал её README. Там написано: транслитерация — когда ругаются латиницей по-русски или по-гречески — пока не сделана, и код говорит об этом прямо.

Красивая фраза. Я её, судя по всему, сам и написал.

Открываю историю: файл с транслитерацией приехал тем же коммитом, что и эта фраза. Четвёртого августа. Три недели назад в README лежало «ещё не сделано» про код, лежащий рядом, в том же коммите.

Проверил не чтением, а запуском: eisai malakasεισαι μαλακας, ty durakты дурак. Работает.

И вот это досаднее, чем ошибка в числах. Абзац, который хвастается прозрачностью — «код говорит об этом прямо», — и есть единственное место, где документ соврал. Похвала себе за честность отлично маскирует нечестность: её никто не перепроверяет, потому что читается она как скромность.

Что унести

Измерение — не решение. Если после прогона в репозитории лежит файл с числами и нигде не лежит фраза «поэтому берём вот это», работа не сделана. Худший вид долга: выглядит как готовое.

Не усредняйте метрики, полученные при разных настройках. Порог, подобранный на каждый язык отдельно, — это девять моделей. Считайте так, как будет в проде, даже если в проде хуже. Особенно если в проде хуже.

Для защитных механизмов критерий — худший случай, а не средний. Среднее описывает отчёт. Худшее описывает человека, которому не повезло с языком. Спросите себя, кто именно живёт в вашем хвосте распределения, и решение станет очевидным.

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

← визитка · RSS