Детектор ИИ-текста для русского языка
russian-ai-text-detector-modernbert оценивает вероятность, что русский текст написала нейросеть, а не
человек. Обучен на текстах ChatGPT (GPT-3.5, GPT-4, GPT-4o, o1, o3), GigaChat, YandexGPT, Qwen, Llama, Gemma, DeepSeek, Mistral, Command R и других языковых моделей из корпуса
LLMTrace. Помогает проверить текст на ИИ: статью, новость, отзыв, пост, ответ
на вопрос.
Это дообученный энкодер deepvk/RuModernBERT-small на
\(34.5\) млн параметров, переведённый в ONNX с весами fp32.
Файл model.onnx занимает \(140\) МБ и работает на CPU через onnxruntime, без
torch и GPU. Рядом лежит model_int8.onnx на \(36.3\) МБ с весами int8, aiw-ru его не скачивает: int8 меняет ответ у \(1.0%\) текстов valid. Веса PyTorch для дообучения и своего экспорта — model.safetensors на \(138\) МБ. На отложенной части корпуса ROC AUC \(0.993\), accuracy
\(0.963\). У LightGBM на признаках aiw-ru ROC AUC \(0.943\).
Модель необязательная: детектор и скиллы aiw-ru работают без неё. Ставит её aiw-ru models install без имени, вместе с LightGBM, или aiw-ru models install modernbert, и только если пользователь попросит.
Другие модели aiw-ru: russian-ai-text-detector-bert (transformer), russian-ai-text-detector-mini-frida (mini-frida), LightGBM на признаках aiw-ru (lightgbm).
In English
A Russian AI-generated text detector. It estimates the probability that a
Russian text was written by a person or by an LLM such as ChatGPT (GPT-3.5, GPT-4, GPT-4o, o1, o3), GigaChat, YandexGPT, Qwen, Llama, Gemma, DeepSeek, Mistral, Command R
and others, which makes it usable as an AI text detector, a ChatGPT detector or an
"AI slop" filter for Russian. The model is deepvk/RuModernBERT-small fine-tuned, trained on the
Russian part of the LLMTrace corpus and exported to ONNX with fp32 weights:
model.onnx takes \(140\) MB and runs on CPU with onnxruntime, no torch
or GPU needed. An int8 export, model_int8.onnx, is an optional extra file of \(36.3\) MB; aiw-ru uses fp32 because int8 flips the label on \(1.0%\) of validation texts. PyTorch weights for fine-tuning or your own export are in model.safetensors, \(138\) MB.
Test ROC AUC \(0.993\), accuracy \(0.963\). Install it with
aiw-ru models install modernbert. The rest of the card is in Russian; usage is below.
Какую модель выбрать
Это точный вариант среди трансформеров aiw-ru, за точность он платит скоростью и размером.
| Модель | Людей принято за ИИ | ROC AUC | Accuracy | Файл, МБ | M1, мс | x86, мс |
|---|---|---|---|---|---|---|
modernbert, эта модель | \(3.4%\) | \(0.9930\) | \(0.963\) | \(140\) | \(207.2\) | — |
transformer | \(7.2%\) | \(0.9869\) | \(0.945\) | \(29.7\) | \(39.8\) | \(101.1\) |
mini-frida | \(9.5%\) | \(0.9909\) | \(0.948\) | \(130\) | \(137.2\) | \(216.2\) |
lightgbm | \(16.5%\) | \(0.9431\) | \(0.868\) | \(10.0\) | — | — |
modernbert(эта модель) — самая точная: выбор по умолчанию, но тяжелее трансформера и в несколько раз медленнее.transformer— почти так же точна, лёгкая и в несколько раз быстрее ModernBERT: для слабой машины.mini-frida— ROC AUC выше, чем у трансформера, но при пороге 50 % чаще принимает людей за ИИ; в 4 раза тяжелее и в 3 раза медленнее.lightgbm— самая лёгкая, работает и без onnxruntime (Mac на Intel), но заметно менее точна.
Если установлено несколько моделей, aiw-ru берёт вероятность у первой из них в порядке таблицы. Порядок задаёт доля людей, принятых за ИИ, а не ROC AUC: ошибиться в человеке дороже, чем пропустить ИИ-текст.
Задержка — один текст на \(512\) токенов в один поток на M1 и на двух ядрах Xeon виртуальной машины Colab (x86). Размер у LightGBM — файл модели, признаки для неё считает сам детектор aiw-ru.
Данные
Русская часть LLMTrace classification
(статья), ревизия 252e21e3dca7. Модель обучена на \(237,929\) текстах из train, лучший шаг и ранняя остановка — по ROC AUC на \(49,747\) текстах из valid.
Итоговые цифры посчитаны по test, это \(52,521\) текст: эту часть
корпуса модель при обучении не видела. Отчёт об обучении с выбором базы и командами для
воспроизведения лежит на GitHub.
Результаты на test
Текст считается написанным ИИ, если вероятность не меньше \(0.5\). Текст длиннее \(8192\) токенов модель читает только до этой границы: среднее по окнам на valid не точнее. aiw-ru classify вдобавок проверяет длинный текст фрагментами по \(512\) токенов: на окне целиком ИИ-вставка тонет в человеческом тексте.
| Класс | Precision | Recall | F1 | Текстов |
|---|---|---|---|---|
| люди | \(0.945\) | \(0.966\) | \(0.956\) | \(21,417\) |
| ИИ | \(0.976\) | \(0.962\) | \(0.969\) | \(31,104\) |
| accuracy | \(0.963\) | \(52,521\) | ||
| среднее по классам | \(0.961\) | \(0.964\) | \(0.962\) | \(52,521\) |
Сравнение с LightGBM на признаках aiw-ru и с оценкой правил детектора с порогом \(40\) на том же test:
| На test | Эта модель | mini-frida | transformer | LightGBM | Правила aiw-ru |
|---|---|---|---|---|---|
| Accuracy | \(0.963\) | \(0.948\) | \(0.945\) | \(0.868\) | \(0.503\) |
| ROC AUC | \(0.993\) | \(0.991\) | \(0.987\) | \(0.943\) | \(0.615\) |
| ROC AUC, люди против текстов с нуля | \(0.994\) | \(0.992\) | \(0.989\) | \(0.946\) | \(0.597\) |
| F1, среднее по классам | \(0.962\) | \(0.945\) | \(0.943\) | \(0.863\) | \(0.466\) |
| Людей принято за ИИ | \(3.4%\) | \(9.5%\) | \(7.2%\) | \(16.5%\) | \(5.9%\) |
На valid accuracy \(0.964\), ROC AUC \(0.993\).
По жанрам
| Жанр | Людей | ИИ | Accuracy | ROC AUC | ROC AUC LightGBM | ROC AUC правил |
|---|---|---|---|---|---|---|
article | \(5109\) | \(5343\) | \(0.966\) | \(0.994\) | \(0.963\) | \(0.709\) |
factual | \(1383\) | \(2209\) | \(0.943\) | \(0.986\) | \(0.896\) | \(0.673\) |
news | \(3326\) | \(3488\) | \(0.955\) | \(0.993\) | \(0.890\) | \(0.566\) |
poetry | \(3561\) | \(3383\) | \(0.960\) | \(0.989\) | \(0.932\) | \(0.488\) |
question | \(1932\) | \(5894\) | \(0.969\) | \(0.994\) | \(0.951\) | \(0.729\) |
review | \(2678\) | \(4019\) | \(0.979\) | \(0.995\) | \(0.960\) | \(0.638\) |
short_form | \(450\) | \(3000\) | \(0.941\) | \(0.989\) | \(0.942\) | \(0.556\) |
story | \(2978\) | \(3768\) | \(0.973\) | \(0.995\) | \(0.967\) | \(0.570\) |
По длине текста
Слова считает детектор aiw-ru, как и для LightGBM.
| Слов в тексте | Текстов | Accuracy | ROC AUC | Accuracy LightGBM | ROC AUC LightGBM |
|---|---|---|---|---|---|
| \(0\text{–}49\) | \(14,742\) | \(0.940\) | \(0.985\) | \(0.819\) | \(0.902\) |
| \(50\text{–}149\) | \(17,766\) | \(0.963\) | \(0.994\) | \(0.849\) | \(0.924\) |
| \(150\text{–}399\) | \(16,327\) | \(0.981\) | \(0.997\) | \(0.913\) | \(0.968\) |
| \(400\) и больше | \(3686\) | \(0.982\) | \(0.988\) | \(0.952\) | \(0.970\) |
По типу задания генератору
create — текст с нуля; update, delete, expand — модель правила,
сокращала или дописывала человеческий текст.
| Задание генератору | ИИ-текстов | Recall | Recall LightGBM |
|---|---|---|---|
create | \(14,064\) | \(0.964\) | \(0.899\) |
delete | \(5966\) | \(0.934\) | \(0.812\) |
expand | \(4850\) | \(0.979\) | \(0.945\) |
update | \(6224\) | \(0.970\) | \(0.903\) |
По моделям-генераторам
Самые частые генераторы в test.
| Модель-генератор | Текстов в test | Recall | Recall LightGBM |
|---|---|---|---|
gpt-3.5 | \(4772\) | \(0.977\) | \(0.929\) |
gpt-4o | \(3884\) | \(0.976\) | \(0.908\) |
databricks/dbrx-instruct | \(1513\) | \(0.941\) | \(0.859\) |
google/gemma-2-27b-it | \(1498\) | \(0.978\) | \(0.888\) |
01-ai/Yi-1.5-34B-Chat | \(1472\) | \(0.963\) | \(0.898\) |
yandex/YandexGPT-5-Lite-8B-instruct | \(1469\) | \(0.936\) | \(0.853\) |
CohereForAI/c4ai-command-r-08-2024 | \(1461\) | \(0.992\) | \(0.951\) |
unsloth/Llama-3.3-70B-Instruct | \(1452\) | \(0.963\) | \(0.915\) |
Qwen/Qwen2.5-72B-Instruct | \(1449\) | \(0.970\) | \(0.887\) |
mistralai/Ministral-8B-Instruct-2410 | \(1396\) | \(0.920\) | \(0.829\) |
Qwen/QwQ-32B | \(1386\) | \(0.980\) | \(0.885\) |
GigaChat-Max | \(1266\) | \(0.974\) | \(0.931\) |
gigachat | \(1181\) | \(0.929\) | \(0.831\) |
deepseek-ai/DeepSeek-R1-Distill-Qwen-32B | \(942\) | \(0.966\) | \(0.869\) |
WizardLM-2-7B | \(891\) | \(0.989\) | \(0.926\) |
Длинные тексты
Тексты длиннее \(8192\) токенов, в test их \(63\). Сравниваются вероятность по началу текста и среднее по окнам:
| Правило | ROC AUC valid | Accuracy valid | ROC AUC test | Accuracy test |
|---|---|---|---|---|
| начало текста | \(1.000\) | \(1.000\) | \(0.988\) | \(0.968\) |
| среднее по окнам | \(1.000\) | \(0.958\) | \(0.960\) | \(0.984\) |
Нормализация
В LLMTrace оформление говорит о том, откуда взят человеческий текст: переводы
строк, markdown, вид тире и кавычек, ё. Поэтому до токенизации текст проходит
нормализацию: разметка и типографика сводятся к простому виду, правила лежат в
inference.json. В пилоте база cointegrated/rubert-tiny2 без нормализации дала ROC AUC \(0.960\) на valid, с нормализацией \(0.957\). Нормализация всё равно остаётся: модель, выучившая оформление корпуса, ошибётся на человеческом тексте, набранном в другом редакторе или скопированном из чата.
Скорость и размер
Задержка на один текст в миллисекундах, медиана \(30\) прогонов, у вызовов дольше секунды \(5\), Apple M1, 8 ядер:
| Среда | 128 токенов, потоков: 1 | 128 токенов, потоков: 8 | 512 токенов, потоков: 1 | 512 токенов, потоков: 8 | 2048 токенов, потоков: 1 | 2048 токенов, потоков: 8 | 8192 токена, потоков: 1 | 8192 токена, потоков: 8 |
|---|---|---|---|---|---|---|---|---|
| onnxruntime fp32 | \(57.6\) | \(43.5\) | \(207.2\) | \(99.9\) | \(1081.6\) | \(427.0\) | \(7851.4\) | \(2966.6\) |
| onnxruntime int8 | \(27.9\) | \(23.1\) | \(107.8\) | \(64.3\) | \(641.1\) | \(320.5\) | \(6061.6\) | \(2611.3\) |
| torch fp32 | \(16.8\) | \(18.0\) | \(70.9\) | \(48.3\) | \(632.7\) | \(284.6\) | \(8664.1\) | \(3106.6\) |
Размер зависимостей вывода после установки через uv для Python 3.12:
| Зависимости вывода | Платформа | МБ на диске |
|---|---|---|
| onnxruntime + tokenizers | aarch64-apple-darwin | \(127\) |
| onnxruntime + tokenizers | x86_64-manylinux_2_28 | \(156\) |
| onnxruntime + tokenizers | x86_64-pc-windows-msvc | \(112\) |
| torch + transformers | aarch64-apple-darwin | \(669\) |
| torch + transformers | x86_64-manylinux_2_28 | \(5649\) |
| torch (CPU) + transformers | x86_64-manylinux_2_28 | \(908\) |
| torch + transformers | x86_64-pc-windows-msvc | \(642\) |
Размер считается вместе со всеми зависимостями, в том числе numpy и
huggingface-hub, которые aiw-ru с extra ml и так ставит для LightGBM.
На этой машине torch считает быстрее onnxruntime: \(70.9\) мс против \(207.2\) мс у ONNX fp32 на \(512\) токенах в один поток. Но torch с transformers занимает на диске в \(5.3\text{–}5.8\) раза больше, а на Linux со сборкой torch под CUDA по умолчанию — \(5649\) МБ. aiw-ru ставят и на слабые ноутбуки, поэтому в поставке ONNX: установка лёгкая, а ROC AUC на test совпадает с PyTorch до четвёртого знака.
Как пользоваться
Из командной строки:
uv tool install "aiw-ru[ml]"
aiw-ru models install modernbert
aiw-ru classify текст.md
Из Python без aiw-ru нужны только onnxruntime, tokenizers и numpy:
import json
import os
import re
# Без этой строки onnxruntime 1.30 при импорте включает телеметрию Microsoft,
# и процесс изредка падает на выходе (recursive_mutex lock failed, код 134).
os.environ.setdefault("ORT_DISABLE_TELEMETRY", "1")
import numpy as np
import onnxruntime as ort
from huggingface_hub import snapshot_download
from tokenizers import Tokenizer
files = ["inference.json", "model.onnx*", "tokenizer.json"]
path = snapshot_download("toiletsandpaper/russian-ai-text-detector-modernbert", allow_patterns=files)
spec = json.load(open(f"{path}/inference.json", encoding="utf-8"))
session = ort.InferenceSession(f"{path}/{spec['file']}", providers=["CPUExecutionProvider"])
tokenizer = Tokenizer.from_file(f"{path}/{spec['tokenizer']}")
tokenizer.no_truncation()
tokenizer.no_padding()
def probability(text: str) -> float:
"""Вероятность, что текст написала языковая модель."""
text = text[: spec["max_chars"]]
for rule in spec["normalize"]:
text = re.sub(rule["pattern"], rule["replacement"], text, flags=re.MULTILINE)
ids = tokenizer.encode(text.strip(), add_special_tokens=False).ids
prefix, suffix = [spec["cls_id"]], [spec["sep_id"]]
width = spec.get("read_length", spec["max_length"]) - len(prefix) - len(suffix)
windows = [ids[i : i + width] for i in range(0, max(len(ids), 1), width)][: spec["max_windows"]]
windows = [[*prefix, *w, *suffix] for w in windows]
batch = np.full((len(windows), max(map(len, windows))), spec["pad_id"], dtype=np.int64)
mask = np.zeros_like(batch)
for k, w in enumerate(windows):
batch[k, : len(w)] = w
mask[k, : len(w)] = 1
logits = session.run([spec["output"]], {"input_ids": batch, "attention_mask": mask})[0]
p = np.exp(logits - logits.max(axis=1, keepdims=True))
return float((p[:, spec["labels"].index("ai")] / p.sum(axis=1)).mean())
print(probability(open("текст.md", encoding="utf-8").read()))
inference.json описывает вывод целиком: файл ONNX, токенизатор,
max_length, токены вокруг окна, правила нормализации из обучения, порог
\(0.5\) и число окон для длинных текстов.
Для дообучения и своего экспорта в репозитории есть веса PyTorch, model.safetensors.
aiw-ru и пример выше их не скачивают. Их читает transformers:
from transformers import AutoModelForSequenceClassification, AutoTokenizer
model = AutoModelForSequenceClassification.from_pretrained("toiletsandpaper/russian-ai-text-detector-modernbert")
tokenizer = AutoTokenizer.from_pretrained("toiletsandpaper/russian-ai-text-detector-modernbert")
Текст перед токенизатором нормализуй по правилам normalize из inference.json, как в
примере выше: на нём модель и обучалась.
Обучение
| Параметр | Значение |
|---|---|
| База | deepvk/RuModernBERT-small, ревизия 06d09cc59b2c |
| Параметров | \(34.5\) млн |
| Устройство | NVIDIA GeForce RTX 4090, cuda |
| Точность | bf16, GPU умеет bf16: диапазон как у fp32, масштабировать потери не нужно |
| Эпох | не больше \(3\), лучшая проверка на эпохе \(2.00\) |
| Ранняя остановка | после \(3\) проверок без роста ROC AUC на valid, сработала |
| Проверок на valid за эпоху | \(2\) |
| Оптимизатор | AdamW, скорость 0.0001, разогрев \(6.0%\) шагов, линейный спад |
| Затухание весов, клиппинг градиента | 0.01, 1 |
| Пачка | \(32\) текста близкой длины, лимит пачки — \(16,384\) токена |
max_length | \(8192\) токена |
| Локальное окно внимания | ±\(64\) токена |
seed | \(1\) |
| Время обучения | \(16\) мин, \(742\) текста в секунду |
| Файл модели | model.onnx, веса fp32, \(140\) МБ |
| Обучено | 2026-09-26, коммит 5cd5f2d |
Ограничения
- Корпус один. На научных статьях, дипломах и диссертациях модель не проверялась, а жанры за пределами таблицы по жанрам она не видела.
- Вероятность — не доказательство авторства. Не используйте модель для решений о людях: на test она принимает за ИИ \(3.4%\) человеческих текстов.
- Правку человеческого текста моделью распознать труднее, чем текст с нуля, см. таблицу по типу задания.
- Нормализация убирает оформление, но не длину: короткие тексты модель различает хуже, см. таблицу по длине.
- В корпусе генераторы до 2025 года; тексты новых моделей могут распознаваться хуже.
Лицензия и данные
Модель распространяется по лицензии Apache 2.0, как и
deepvk/RuModernBERT-small. Корпус LLMTrace — Apache 2.0, человеческие тексты в
нём собраны из сторонних источников со своими лицензиями.
@misc{tolstykh2025llmtrace,
title = {LLMTrace: A Corpus for Classification and Fine-Grained Localization of AI-Written Text},
author = {Tolstykh, Irina and Tsybina, Aleksandra and Yakubson, Sergey and Kuprashevich, Maksim},
year = {2025},
eprint = {2509.21269},
archivePrefix = {arXiv},
}
RuModernBERT построен по схеме ModernBERT.
@misc{deepvk2025rumodernbert,
title = {RuModernBERT: Modernized BERT for Russian},
author = {Spirin, Egor and Malashenko, Boris and Sokolov, Andrey},
url = {https://huggingface.co/deepvk/rumodernbert-base},
publisher = {Hugging Face},
year = {2025},
}