ONNX 600 МБ → YAKE! в Tauri: статистика быстрее нейросети | AiManual
AiManual Logo Ai / Manual.
24 Апр 2026 Гайд

Как заменить нейросеть на статистику: замена ONNX-модели (600 МБ) на YAKE! в локальном приложении

Пошаговое руководство по замене тяжелой ONNX-модели (600 МБ) на легкий статистический алгоритм YAKE! в local-first приложении на Tauri, Rust и SQLite FTS5. Сокр

Танцы с ONNX: 600 МБ ради пары ключевых слов

Представьте: вы пишете десктопное приложение на Tauri. Local-first, приватное, всё на Rust. Пользователь импортирует десяток документов, а система должна сама вытащить из них ключевые слова и построить поиск. Логичный шаг — взять готовую ONNX-модель для keyword extraction. BERT, DistilBERT, что-то из HuggingFace. Экспорт в ONNX, прогон через ONNX Runtime. 600 мегабайт веса. 200–400 мс на один документ. И это в приложении, которое задумывалось как молниеносный блокнот.

Я не шучу. Полгода назад мы зашили so-называемую tiny-модель (6 слоёв, 768 hidden) в бинарник. Размер вырос с 15 МБ до 615 МБ. Первый запуск на машине пользователя — подгрузка ONNX Runtime, создание сессии, аллокация тензоров. Потом ещё и предобработка токенизатора. Всё это ради того, чтобы вытащить из заголовка «Купить молоко и хлеб» слова «молоко» и «хлеб».

Бесит? Ещё как. Но главный вопрос: а обязательно ли для этой задачи вообще использовать нейросеть? Ключевые слова — это не семантика предложения, это частотность, дисперсия и небольшой процент редких терминов. Задача, с которой отлично справляются статистические алгоритмы. YAKE! — один из них.

YAKE! — бензопила для ключевых слов

YAKE! (Yet Another Keyword Extractor) — это алгоритм без машинного обучения. Он анализирует текст на основе пяти статистических признаков: позиция слова, частота, связь с другими словами, длина, дисперсия. Никаких моделей, никаких весов. Чистая математика. И он встраивается в любое приложение буквально парой файлов.

На Rust есть крейт yake-rust (форк от yake-rs, обновлён под последний stable). Он весит около 200 KB с зависимостями. Время экстракции — 5–15 мс на документ. Да, точность чуть ниже BERT на датасетах типа Inspec. Но в реальном продакшене пользователи не замечают разницы, если вы не строите семантический поиск с нуля.

💡
YAKE! отлично работает для русского, английского, немецкого и ещё десятка языков. Главное — передать правильный стоп-лист. Библиотека включает стоп-слова по умолчанию, но для русского они базовые. Лучше подгрузить свой (можно взять из NLTK).

Сравнение: ONNX (600 MB) vs YAKE! (0.2 MB)

Параметр ONNX-модель YAKE!
Размер дистрибутива ~615 MB ~0.2 MB
Скорость (1 документ) 200–400 мс (включая токенизацию) 5–15 мс
Потребление памяти ~900 MB (ONNX Runtime + сессия) <10 MB
Точность (F1 на Inspec) 0.52–0.58 0.47–0.53
Зависимости onnxruntime, tokenizers, ndarray только std + lazy_static

Разница в точности — 5–10 пунктов. Но для задачи автоматического тегирования заметок в локальном приложении это некритично. Пользователь всё равно хранит ключевые слова только для поиска, а не для NLP-пайплайна.

Пошаговый план: вырезаем ONNX, ставим YAKE!

1 Чистим зависимости проекта Tauri

В Cargo.toml удаляем всё, что связано с ONNX Runtime и tokenizers. Если использовали ort (crate for ONNX Runtime), удаляем и его. Заодно проверяем src-tauri/Cargo.toml:

# Было
ort = "2.0"
tokenizers = "0.15"
# Стало – ничего, только yake

2 Добавляем крейт yake-rust

В Cargo.toml:

[dependencies]
yake = { package = "yake-rust", version = "0.3" }
serde = { version = "1", features = ["derive"] }

На момент апреля 2026 стабильная версия — 0.3.1. Она использует Rayon для параллельной обработки, что идеально для батчей.

3 Пишем функцию извлечения ключевых слов в Rust

Пример кода, который вызывается из Tauri command:

use yake::{Yake, YakeParams};

#[tauri::command]
pub fn extract_keywords(text: &str) -> Vec {
    let params = YakeParams::new()
        .dedup_algo(yake::Dedup::Seq)
        .window_size(2)
        .top_n(10); // берём 10 лучших
    
    let yake = Yake::with_params(params);
    let result = yake.extract_keywords(text);
    result.into_iter().map(|k| k.word).collect()
}

Ошибка, которую я видел у коллег: передача текста без удаления стоп-слов. YAKE! сам их учитывает, но если в стоп-листе нет русского «и», «в», «на» — он может выдать эти слова как ключевые. Обязательно проверьте стоп-слова для вашего языка.

4 Подключаем результат к SQLite FTS5

Мы храним извлечённые ключевые слова в отдельном столбце таблицы документов и используем SQLite FTS5 для полнотекстового поиска. Такой гибридный подход (ключевые слова + полнотекст) даёт хороший recall. В Tauri для SQLite отлично подходит крейт rusqlite с фичей bundled. Пример индекса:

CREATE VIRTUAL TABLE docs_fts USING fts5(title, body, keywords, content='docs');

При добавлении документа сохраняем его в docs, потом вставляем в FTS с теми же ID. Поле keywords заполняем результат работы YAKE! (через пробел).

5 Обновляем фронтенд

В TypeScript-части Tauri убираем вызов ONNX Runtime (если он был). Теперь команда — invoke('extract_keywords', { text }). Всё. Никаких громоздких бинарников.

Нюансы и грабли, на которые я наступил

YAKE! не понимает контекст

Если в документе слово «сеть» встречается одинаково часто и в значении IT-инфраструктура, и в значении рыболовная, YAKE! выдаст его как одно ключевое слово. Для нашего приложения это нормально. Но если вы строите медицинскую или юридическую систему — могут быть казусы. Здесь лучше комбинировать YAKE! с семантическими эмбеддингами, например, через fastembed. Но это уже утяжеление. Если хотите олдовый, но лёгкий путь — YAKE! + FTS5 даёт 90% потребностей.

Русский язык: морфология

YAKE! не стеммит и не лемматизирует. Слова «дом», «дома», «дому» считаются разными. Для ключевых слов это грустно: recall падает, если пользователь ищет «дом», а в тексте «дома». Решение — стемминг перед YAKE! Поднимаем крейт rust-stemmers и прогоняем термы. Сделайте это на Rust перед вызовом экстракции:

use rust_stemmers::{Algorithm, Stemmer};

let stemmer = Stemmer::create(Algorithm::Russian);
let stemmed_text: String = text
    .split_whitespace()
    .map(|w| stemmer.stem(w).to_string())
    .collect::>()
    .join(" ");

Размер дистрибутива — не единственный бонус

После удаления ONNX Runtime мы перестали зависеть от его версий. Больше никаких «проклятий» с dll/so/dylib. Сборка Tauri стала занимать 40 секунд, а не 4 минуты. И да, пользователи на старых Linux-дистрибутивах перестали жаловаться на segfault при запуске ONNX сессии.

Где ONNX всё-таки нужен?

Не советую использовать YAKE! для:

  • Семантического поиска (тут нужны эмбеддинги, как в этом гайде по Qwen3-0.6B INT8).
  • Классификации текстов (ONNX + DistilBERT справится точнее).
  • Извлечения именованных сущностей (NER).

Но для простого keyword extraction — YAKE! убивает ONNX всухую.

Частые вопросы

Сколько памяти реально освободилось?

После удаления ONNX Runtime и модели: ~800 MB (раньше ~1.2 GB на старте). YAKE! занимает около 5 MB.

Можно ли вообще не использовать YAKE!, а просто брать частые слова?

Можно, но YAKE! даёт лучшую дисперсию и убирает слишком общие слова. Простая частотность выдаёт «и», «в», «это». YAKE! с дедупликацией и весами даёт информативные слова.

Что по скорости на 1000 документов?

С YAKE! — около 2–3 секунд. С ONNX — больше минуты. При этом процессор не греется.

Совет под конец (неочевидный)

Если вы всё же хотите семантику, но не хотите тащить тяжёлую ONNX, посмотрите на гибридный поиск в Newelle: YAKE! для быстрых ключевых слов + легковесные эмбеддинги от fastembed (модель all-MiniLM-L6-v2 в ONNX весит 80 MB). Так вы получаете лучшее от двух миров без 600 MB.

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

Подписаться на канал