English translation is not available yet. Showing Russian content.

Реализовать cost-aware routing на основе классификатора сложности (BERT)

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Реализовать cost-aware routing на основе классификатора сложности (BERT)

1. Цель задачи

Разработать систему routing|cost-aware routing для LLM-приложения, которая перенаправляет «простые» запросы на дешёвую модель (например, GPT-3.5-turbo), а «сложные» — на дорогую (например, GPT-4). Для классификации сложности запроса обучить BERT-классификатор на исторических данных. Добиться снижения затрат на LLM-вызовы на 60% при падении качества (accuracy) не более 5% по сравнению с использованием только дорогой модели.

Ключевой результат Рабочий пайплайн: классификатор → роутер → две модели, метрики cost и accuracy на тестовом наборе, автоматический отчёт.


2. Исходные данные

Что нужноОткуда взять
Исторические логи запросов к LLM (минимум 5000 примеров)Реальные данные production / синтезированные через LLM (см. ниже)
Ответы (качество) от дешёвой и дорогой модели для каждого запросаПрогнать запросы через обе модели или взять из existing logs
Разметка «сложности» запроса (бинарно: simple / complex)Разметить руками / полуавтоматически (через LLM-as-judge)
Две LLM: дешёвая и дорогаяOpenAI API / локальные модели (например, Qwen2.5-0.5B vs Qwen2.5-7B)
Python-средаЛокальная или Colab (GPU для BERT)
Бюджет на API (опционально)~$5–10 для тестового набора

Если нет реальных данных — симулируем:

  1. Сгенерировать 6000 синтетических запросов через GPT-4-turbo, промпт:
    "Сгенерируй 1000 вопросов на русском языке для тестирования LLM, половина — простые фактологические (например, 'Сколько планет в Солнечной системе?'), половина — сложные (требующие многошагового рассуждения, например, 'Если 2 поезда выехали навстречу друг другу...')".
  2. Прогнать все запросы через дешёвую и дорогую модель, сравнить качество ответов (например, через GPT-4-as-judge или BERTScore).
  3. Разметить «сложность» как simple, если ответы обеих моделей совпали по качеству (или разница <5% метрики), иначе complex.

3. Технологический стек

КомпонентИнструментыНазначение
КлассификаторBERT (bert-base-multilingual-cased)Обучение классификатора сложности
LLM (дешёвая)GPT-3.5-turbo / Qwen2.5-1.5BГенерация ответов
LLM (дорогая)GPT-4 / Qwen2.5-7BГенерация ответов (baseline)
Бекенд роутераPython FastAPI (опционально)Промышленный вариант
Оценка качестваBERTScore, GPT-4-as-judgeМетрики accuracy ответов
МониторингPrometheus + Grafana (опционально)Сбор метрик cost и accuracy в real-time
ЭкспериментыMLflow / Weights & BiasesЛогирование обучения и результатов
СредаPython 3.10+, PyTorch, transformers, scikit-learnОбучение и тестирование

4. Этапы выполнения

Этап 1: Подготовка данных и разметка сложности (оценка 1.5 часа)

Действия

  1. Сбор / генерация запросов

    • Если нет исторических данных — сгенерировать 6000 запросов по схеме из раздела 2.
    • Разделить на train (70%), validation (15%), test (15%).
  2. Получение ответов от обеих моделей

    import openai
    def get_response(model, prompt):
        response = openai.ChatCompletion.create(model=model, messages=[{"role": "user", "content": prompt}])
        return response.choices[0].message.content
    
    • Для каждого запроса получить ответ от дешёвой модели (A) и дорогой (B).
    • Сохранить в JSON: {"query": "...", "response_cheap": "...", "response_expensive": "..."}.
  3. Оценка качества и разметка

    • Использовать GPT-4-as-judge для оценки каждого ответа по шкале 1-5 (или сравнить ответы между собой).
    • Разметить label=0 (simple), если разница в оценке <= 1 балл, и label=1 (complex), если разница >= 2 балла.
    • Добавить колонку label в датасет.
  4. Препроцессинг для BERT

    • Токенизация: tokenizer = BertTokenizer.from_pretrained('bert-base-multilingual-cased').
    • Макс. длина 128 токенов.
    • Создать torch Dataset.

Ожидаемый результат этапа

  • Файл dataset.parquet с полями: query, label, response_cheap, response_expensive.
  • Токенизированные input_ids, attention_mask в train_ds.pt.

Этап 2: Обучение BERT-классификатора (оценка 1.5 часа)

Действия

  1. Определение модели

    from transformers import BertForSequenceClassification, Trainer, TrainingArguments
    model = BertForSequenceClassification.from_pretrained('bert-base-multilingual-cased', num_labels=2)
    
  2. Конфигурация обучения

  3. Запуск Trainer

    trainer = Trainer(model=model, args=training_args, train_dataset=train_dataset, eval_dataset=val_dataset)
    trainer.train()
    
  4. Сохранение лучшей модели

    • model.save_pretrained('./cost_router_bert')
    • tokenizer.save_pretrained('./cost_router_bert').
  5. Оценка на validation

Ожидаемый результат этапа

  • Модель классификатора в папке ./cost_router_bert.
  • Метрики валидации в логах.

Этап 3: Интеграция роутера и цикл оценки (оценка 1.5 часа)

Действия

  1. Реализация роутера

    def route_query(query, threshold=0.5):
        inputs = tokenizer(query, return_tensors='pt', truncation=True, max_length=128)
        outputs = model(**inputs)
        prob = torch.softmax(outputs.logits, dim=1)[0][1].item()  # вероятность complex
        if prob > threshold:
            return 'expensive'
        else:
            return 'cheap'
    
  2. Тестирование на тестовом наборе

    • Для каждого запроса из test split:
      • Выбрать модель по route_query.
      • Взять соответствующий ответ из датасета (если он был получен ранее).
      • Сравнить качество ответа с эталоном (ответ дорогой модели).
    • Вычислить cost (сумма cost всех вызовов) и accuracy (доля ответов, не уступающих эталону больше чем на 1 балл).
  3. Симуляция cost

    • Цена за токен: cheap = $0.0015/1K токенов ввода, $0.002/1K вывода; expensive = $0.03 / $0.06 (примерные цены OpenAI).
    • Рассчитать среднюю стоимость и фактическую стоимость роутера.
  4. Сравнение с baseline

    • Baseline: только дорогая модель → cost = max, accuracy = 1.0.
    • Дешёвая модель → cost = min, accuracy = некая base_acc.
    • Роутер → ожидаем cost ~ 40% от дорогой (снижение 60%), accuracy >= 0.95.

Ожидаемый результат этапа

  • Таблица с метриками: baseline дорогой, baseline дешёвый, роутер.
  • Предварительное значение cost saving и accuracy drop.

Этап 4: Оптимизация порога и финальное тестирование (оценка 1 час)

Действия

  1. Подбор порога

    • Перебрать threshold от 0.1 до 0.9 с шагом 0.1.
    • Для каждого порога вычислить cost и accuracy на validation.
    • Построить график (cost vs accuracy) — найти порог, где cost -60% и accuracy -5%.
  2. Финальная проверка на test

    • Взять лучший порог (например, 0.6).
    • Прогнать тестовый набор: финальные метрики.
  3. A/B тест (опционально)

    • Симулировать random split трафика: 50% роутер, 50% дорогая модель.
    • Сравнить средний cost и accuracy за 1000 запросов.

Ожидаемый результат этапа

  • Оптимальный порог и финальные метрики.
  • График (или таблица) в отчёте.

Этап 5: Написание отчёта и подготовка презентации (оценка 30 минут)

Действия

  1. Создание отчёта

    • Markdown-документ с разделами: постановка, данные, обучение, результаты, выводы.
    • Включить таблицу метрик и графика.
  2. Фиксация артефактов

    • Модель cost_router_bert.
    • Код пайплайна (Jupyter notebook или Python-скрипты).
    • Файл с результатами final_metrics.json.

Ожидаемый результат этапа

  • Отчёт в корне проекта.
  • Все артефакты в репозитории.

5. Критерии приемки (Definition of Done)

  • Обученный BERT-классификатор сохранён в ./cost_router_bert/ и загружается без ошибок.
  • Реализован модуль роутера (router.py), который принимает строку запроса и возвращает имя модели ('cheap' или 'expensive').
  • На тестовом наборе (минимум 500 запросов) средняя стоимость вызовов снижена на 60% и более по сравнению с baseline (дорогая модель).
  • Падение accuracy (по метрике GPT-4-as-judge) не превышает 5% от baseline дорогой модели.
  • Воспроизводимый пайплайн: python train.py && python evaluate.py — выдаёт те же метрики.
  • В корне проекта лежит report.md с описанием эксперимента, таблицей метрик и графиком порога.
  • Код покрыт комментариями и docstring, понятен для ревью.
  • (Доп.) Модель можно запустить в реальном API (FastAPI эндпоинт /v1/chat/completions с routing).

6. Ожидаемый результат

Артефакты

ФайлСодержание
cost_router_bert/Папка с весами BERT-классификатора и токенизатором
router.pyФункция route_query(query, threshold=0.6)
final_metrics.jsonJSON: {"baseline_expensive_cost": 12.34, "baseline_expensive_accuracy": 1.0, "router_cost": 4.94, "router_accuracy": 0.96, "cost_saving_pct": 60.0, "accuracy_drop_pct": 4.0}
report.mdДокумент с описанием шагов, метриками и графиком
notebook.ipynb (опц.)Jupyter notebook с полным экспериментом

Дополнительные результаты (опционально):

  • FastAPI-сервис с эндпоинтом /classify и /v1/chat/completions с routing.

7. Возможные сложности и их решение

СложностьРешение
Неравномерное распределение классов (слишком много simple)Взвешенная loss (class_weight), oversampling, или порог по квантили
Качество разметки (шум)Использовать majority vote между двумя judge-моделями; исключить неопределённые примеры (разница оценки 1 балл)
Дорогие API-вызовы для тестового набораИспользовать локальные модели (например, Qwen2.5-1.5B vs 7B) — cost равен только электроэнергии
BERT не хватает контекста (запросы > 512 токенов)Использовать Longformer или обрезать запрос до первых 128 токенов; при необходимости дообучить с поддержкой длинных последовательностей
Cost saving не достигает 60%Уменьшить порог (направить больше запросов на cheap); проверить, не упала ли accuracy сильно; возможно, нужно подобрать другую дешёвую модель (например, Claude Haiku)
Падение accuracy > 5%Увеличить порог; собрать больше сложных примеров; дообудить BERT на большем датасете

8. Бюджет времени (оценка)

ЭтапВремя (часы)
1. Подготовка данных и разметка1.5
2. Обучение BERT-классификатора1.5
3. Интеграция роутера и оценка1.5
4. Оптимизация порога и финал1.0
5. Отчёт и артефакты0.5
Итого6.0

Примечание для первого раза: Время может увеличиться до 8–10 часов из-за настройки окружения, API-лимитов и отладки. Рекомендуется выделить два рабочих дня.


9. Связанные вопросы из базы знаний

ВопросТема
15Выбор модели LLM под задачу
42Оценка стоимости вызовов LLM
78Создание классификатора для routing
121Fine-tuning BERT под кастомную задачу
189Метрики качества ответов (BERTScore, GPT-judge)
210A/B тестирование в production
305Оптимизация latency и cost
420Методы разметки данных (human + LLM)
555Подбор порога классификации
713Мониторинг cost и качества в real-time

10. Чек-лист самопроверки

  • Я проверил, что классификатор загружается и выдаёт предсказания для любого нового запроса.
  • Я запустил evaluate.py на тестовом наборе и получил cost saving >= 60% и accuracy drop <= 5%.
  • Я проверил, что роутер корректно переключается между cheap и expensive моделями (не использует одну постоянно).
  • Я построил график «cost vs accuracy» для разных порогов и выбрал оптимальный.
  • Я написал отчёт report.md, который объясняет каждое решение и содержит таблицу с метриками.
  • (Доп.) Я протестировал сервис через FastAPI и убедился, что он отвечает без ошибок.