Содержание
Коротко
Автор строит домашний чат-бот с RAG по бытовым вопросам — от бассейна до записи к врачу. Перед векторным поиском вопрос прогоняется через классификатор на Qwen 3 0.6B (600M параметров). Промпт без дообучения дал ~10% точности; после дообучения через Unsloth и смены формата ответа на двухбуквенные коды — ~92%.
Что произошло
Идея простая: индекс в векторной базе размечен метаданными (категории вроде pool, hvac, cooking). Запрос «Когда меняли насос в бассейне?» сначала должен попасть в категорию pool, и только потом искать среди релевантных записей — так сужается пространство поиска и растёт качество RAG.
Для ответов на общие вопросы автор использует Qwen 3 4B, а для классификации — сверхлёгкую Qwen 3 0.6B локально через Ollama. Гипотеза: хватит ли 600M параметров, если показать модели ~850 размеченных пар «вопрос → категория» и прогнать QLoRA в Unsloth.
Базовая линия — только промпт с жёстким списком из 19 категорий и требованием вернуть ровно одно имя. На батарее из 131 интеграционного теста правильных ответов было 13 (~10%). Типичные сбои: модель злоупотребляет широкими метками вроде electric, выдумывает категории (apartments) или возвращает фрагменты вместо полного имени.
Первое дообучение подняло точность до ~79%, но оставались обрезанные ответы (ac вместо hvac) и путаница между семантически близкими темами (вода: pool, water heater, fountain). Второй эксперимент — вместо имён категорий модель учат выдавать фиксированные двухбуквенные коды (KK = hvac, OO = pool и т.д.) без пересекающегося смысла в тексте ответа. Точность выросла до ~92% (120 из 131). Оставшиеся 11 ошибок в основном «водные» категории: water heater ↔ pool, gutters ↔ mosquito.
Почему это важно
Классификация перед RAG — дешёвый способ улучшить полноту поиска без раздувания эмбеддинг-индекса. Маленькая модель на CPU/GPU потребляет меньше, чем гонять каждый запрос через 4B+, и её можно переобучать на новых категориях дома или офиса.
Статья наглядно показывает, что формат выхода важнее «ещё одного абзаца в промпте»: короткие непересекающиеся коды стабилизируют генерацию у крошечных LLM лучше, чем свободный текст с 19 длинными метками. Это переносимо на маршрутизацию запросов, первичную сортировку обращений в поддержке, фильтрацию логов — везде, где нужен дешёвый предварительный классификатор.
Автор также отмечает цикл обратной связи: пользовательские исправления можно добавлять в датасет для следующего цикла дообучения, не переписывая весь пайплайн.
На практике
- Зафиксируйте базовую линию — прогоните «сырую» модель на отложенном тесте до любого дообучения; без цифр не видно, помогло ли дообучение.
- Датасет — ~850 примеров с разбиением 70/15/15 (обучение/валидация/тест); следите за балансом редких категорий.
- Unsloth + QLoRA — стартовые гиперпараметры из документации часто достаточны; важнее качество разметки, чем тонкая настройка на первом проходе.
- Формат ответа — попробуйте неизменяемые коды фиксированной длины вместо имён классов, если модель «обрезает» или путает похожие слова.
- Постобработка — маппинг код → категория на стороне приложения; при необходимости нормализуйте синонимы (
ac→hvac). - Альтернатива — автор добавил отдельную статью с логистической регрессией на эмбеддингах; для некоторых задач классический ML проще, чем LLM-классификатор.
| Этап | Точность (131 тест) |
|---|---|
| Промпт без дообучения | ~10% |
| Fine-tuning, имена категорий | ~79% |
| Fine-tuning, двухбуквенные коды | ~92% |
Итог
Домашний эксперимент — хороший кейс «маленькая LLM как узкий инструмент»: не заменяет большую модель для ответов, но надёжно режет поток запросов по метаданным. Если строите локальный RAG, имеет смысл заложить отдельный лёгкий классификатор и экспериментировать с форматом меток до раздувания промпта.