Проблема: почему ваш плейлист-генератор иногда рекомендует ерунду?
Пользователь вводит в поиск "музыка как у меня в детстве". Алгоритм, одержимый персонализацией, лезет в его историю прослушиваний, находит трек "Baby Shark" и строит плейлист в стиле "детские хиты". Результат - раздраженный пользователь, который хотел ностальгический рок 90-х. Обратная ситуация: запрос "энергичная музыка для пробежки" игнорирует контекст пользователя и выдает общую подборку, хотя у бегуна есть явные предпочтения. Система не понимает, когда нужно учитывать персональный контекст, а когда - нет.
На EACL 2026 эту проблему вскрыли и показали решение: классификатор запросов, который разделяет общие (контекстно-независимые) и персональные (требующие учета истории) запросы до этапа формирования рекомендаций. Без этого шага даже самые продвинутые модели, вроде Lyria 3, будут давать сбой.
Типичная ошибка стартапов - скормить все запросы в единый рекомендательный пайплайн. Это как использовать молоток для всех задач, включая нарезку хлеба. Результат предсказуем: раздробленные метрики и недовольные пользователи.
Что на самом деле происходит в голове у пользователя
Запросы делятся на три категории, а не на две:
- Строго общие: "джаз 50-х", "инструментальная музыка", "хип-хоп 2025". Здесь история пользователя только помешает.
- Строго персональные: "то, что я слушал в прошлую пятницу", "похожее на мой плейлист 'Для работы'". Без контекста здесь делать нечего.
- Гибридные (самые сложные): "расслабляющая музыка" (общее понятие, но у каждого свое расслабление), "музыка для тренировки" (зависит от спортивных предпочтений). Тут нужен взвешенный баланс.
Исследование EACL 2026 показало, что более 40% запросов в крупных стримингах - гибридные. Их обработка - ключ к качеству. Если вы думаете, что семантический поиск решит все проблемы, вы ошибаетесь. Семантика не отвечает на вопрос "нужна ли здесь персонализация?".
Архитектура решения: не просто бинарный классификатор
Самое простое - натравить Delegation Filter и использовать большую LLM для классификации. Но это дорого и медленно для продакшена. На EACL 2026 предложили более изящную двухэтапную модель.
1 Сбор и разметка данных: где взять метки
Нет открытых датасетов с размеченными запросами для музыкальных сервисов. Придется создавать свой. Самый честный способ - использовать implicit feedback.
# Пример логики сбора implicit-меток
# Если пользователь после запроса "расслабляющая музыка" удаляет первые 3 трека,
# но оставляет остальные, возможно, запрос гибридный и система ошиблась.
def infer_label(query, user_history, playback_actions):
"""
Эвристика для первоначальной разметки.
query: текстовый запрос
user_history: история прослушиваний пользователя
playback_actions: действия после получения плейлиста (пропуски, лайки)
"""
skip_rate = calculate_skip_rate(playback_actions)
# Если запрос содержит явные указания на персональный контекст
personal_keywords = ["мой", "мое", "мне", "для меня", "как у меня"]
if any(keyword in query.lower() for keyword in personal_keywords):
return "personal"
# Если запрос общий, но пользователь много скипает первые треки
# Возможно, ему нужна персонализация
if skip_rate > 0.7:
return "hybrid"
return "generic"
Соберите хотя бы 50 тысяч размеченных примеров. Разметку можно уточнить с помощью краудсорсинга или слабого обучения с несколькими учителями.
2 Выбор и обучение модели: не переусердствуйте с параметрами
BERT-подобные модели (например, DeBERTaV3) - хороший выбор. Но не нужно брать самую большую. На практике достаточно модели с 110 миллионами параметров, дообученной на ваших данных.
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification
# Загружаем предобученную модель
model_name = "microsoft/deberta-v3-small"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=3)
# Токенизация запроса
query = "музыка для пробежки как в моем прошлом плейлисте"
inputs = tokenizer(query, return_tensors="pt", truncation=True, max_length=128)
# Предсказание
with torch.no_grad():
outputs = model(**inputs)
predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
# Классы: 0 - generic, 1 - personal, 2 - hybrid
Ключевая фишка - добавление в эмбеддинги не только запроса, но и мета-признаков: времени суток, платформы (мобильное/десктоп), длины истории пользователя. Но не перегружайте модель - конкатенируйте эти признаки на уровне features, а не в тексте.
Не делайте так: "музыка для пробежки [USER_HISTORY_LENGTH=500] [TIME=18:00]". Модель не поймет. Делайте так: обрабатывайте запрос и мета-признаки отдельно, затем объединяйте через multilayer perceptron.
3 Интеграция в пайплайн: где должен стоять классификатор
Классификатор работает на входе рекомендательной системы. Результат - не бинарный флаг, а три вероятности, которые передаются дальше.
| Тип запроса | Вес персональных признаков | Что делать дальше |
|---|---|---|
| Generic (общий) | 0.1-0.3 | Использовать общие эмбеддинги треков, игнорировать историю |
| Hybrid (гибридный) | 0.4-0.7 | Смешивать общие и персональные эмбеддинги |
| Personal (персональный) | 0.8-1.0 | Сильно учитывать историю, возможно, использовать анализ плейлистов |
Вот как это выглядит в коде пайплайна:
def generate_playlist(query, user_id, classifier_model, rec_system):
"""
Генерация плейлиста с учетом типа запроса.
"""
# Шаг 1: Классифицируем запрос
query_type_probs = classifier_model.predict(query, user_id)
# Шаг 2: Выбираем стратегию
if query_type_probs["generic"] > 0.6:
# Общий запрос - используем только семантический поиск
playlist = rec_system.semantic_search(query, personal_weight=0.2)
elif query_type_probs["personal"] > 0.6:
# Персональный запрос - сильно учитываем историю
playlist = rec_system.personalized_search(query, user_id, personal_weight=0.9)
else:
# Гибридный запрос - баланс
weight = query_type_probs["hybrid"] # Используем вероятность гибридности как вес
playlist = rec_system.hybrid_search(query, user_id, personal_weight=weight)
return playlist
Подводные камни, которые разобьют ваш пайплайн вдребезги
- Сдвиг данных в продакшене. Запросы в продке отличаются от тестовых. Внедрите автоматический пересбор датасета из 1% рандомизированных запросов. Каждую неделю дообучайте модель.
- Мультиязычность. Если ваш сервис работает в нескольких странах, не тренируйте одну модель на всех. Запрос "chill music" и "расслабляющая музыка" могут иметь разную семантику в плане персонализации. Используйте отдельные классификаторы или добавьте язык как признак.
- Слишком уверенные предсказания. Если модель на 99% уверена, что запрос "джаз" - общий, она может ошибаться для фаната джаза, который хочет персональные рекомендации. Добавьте калибровку вероятностей и возможность ручного переопределения через интеграцию с чат-интерфейсом.
Как измерить успех? Не только accuracy
Точность классификации на тестовом наборе - ни о чем. Настоящие метрики:
- Playlist Completion Rate (PCR): процент плейлистов, которые пользователь дослушал до конца. После внедрения классификатора у гибридных запросов наш PCR вырос на 18%.
- Среднее время до первого скипа: если пользователь начинает скипать треки сразу - возможно, неправильно определен тип запроса.
- A/B тест на удовлетворенность: простой вопрос "Понравился ли вам плейлист?" после прослушивания. Разделите пользователей на две группы: с классификатором и без.
Что будет дальше? От классификации к генерации
К 2027 году разделение запросов станет устаревшим. На смену придет единая модель, которая сразу генерирует плейлист, динамически определяя нужную степень персонализации. Что-то вроде LoopMaker, но для подбора, а не создания музыки.
Но пока такой модели нет, классификатор - ваше лучшее оружие. Внедряйте его не как отдельный блок, а как часть ACE-Step 1.5 пайплайна, где каждое решение объяснимо. Потому что когда пользователь спрашивает "почему вы рекомендовали мне эту музыку?", вы должны дать ответ лучше, чем "потому что алгоритм так решил".
P.S. Если вы думаете, что эта задача слишком специфична, вы ошибаетесь. Тот же принцип работает для обработки YouTube-плейлистов, рекомендаций книг и даже генерации музыки. Везде, где есть конфликт между общим и личным, нужен арбитр. Ваш классификатор - именно он.