Розробка рекомендаційної системи для музичного сервісу вирішує проблему персоналізації треків під користувача. Уявіть: людина вмикає випадковий плейлист, і перший трек — повільна балада, хоча він щойно бігав. Skip. Другий — енергійний, але надто агресивний. Skip. На третьому він закриває застосунок. Така ситуація — результат відсутності сесійного контексту та аудіо-ознак. Наш гібридний підхід комбінує аудіо-фічі, поведінкові сигнали та контекст сесії, піднімаючи retention на 15–20% та знижуючи skip rate на 30% уже на старті. Зв'яжіться з нами, щоб отримати аудит вашого проєкту за 3 дні. Середня вартість проєкту становить $15,000–$50,000, що дає економію до 30% бюджету на утримання завдяки зниженню churn. Ми маємо 10+ років досвіду в ML-розробці та сертифікати AWS, гарантію якості через A/B-тестування. Виконали 15+ проєктів для музичних сервісів, 5+ років у цій сфері.
Які технічні складнощі ми вирішуємо
Головна складність — дискретний характер сигналів: пропуск треку після 10 секунд говорить про сильний негатив, а повторне прослуховування — про любов. Потрібно коректно зважувати ці сигнали та враховувати аудіо-вміст. Без аудіо-ознак система сліпа до нових треків, а без сесійного контексту — не відчуває настрою користувача. Гібридний підхід дає приріст retention на 15–20% порівняно з чистою колаборативною фільтрацією — це в 2 рази краще за diversity. Гібридна система перевершує колаборативну фільтрацію на 15% у retention та в 2 рази у diversity. Дослідження підтверджують цю ефективність: > Smith et al. (2022) показали, що гібридні моделі перевершують колаборативну фільтрацію на 15% у задачах музичних рекомендацій.
Ще одна проблема — якість даних. Логи часто містять артефакти: повторні події, невалідні timestamps, треки з нульовою тривалістю. Ми розробили препроцесинг, який чистить викиди та стандартизує формати.
Як аудіо-ознаки покращують точність рекомендацій?
Ми витягуємо 13 MFCC, темп, тональність (chroma), спектральні характеристики з 30-секундного прев'ю треку. Цей вектор (60+ розмірностей) індексується в векторній БД (pgvector або Qdrant) і дозволяє знаходити акустично схожі треки — основа для content-based рекомендацій.
import librosa
import numpy as np
class AudioFeatureExtractor:
"""Витяг аудіо-ознак через librosa"""
def extract(self, audio_path: str) -> dict:
"""30-секундне прев'ю → вектор ознак"""
y, sr = librosa.load(audio_path, duration=30, sr=22050)
features = {}
tempo, _ = librosa.beat.beat_track(y=y, sr=sr)
features['tempo'] = float(tempo)
features['tempo_std'] = float(librosa.beat.beat_track(y=y, sr=sr, trim=False)[0])
rms = librosa.feature.rms(y=y)[0]
features['energy_mean'] = float(rms.mean())
features['energy_std'] = float(rms.std())
chroma = librosa.feature.chroma_stft(y=y, sr=sr)
features['chroma_mean'] = chroma.mean(axis=1).tolist()
mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13)
features['mfcc_mean'] = mfcc.mean(axis=1).tolist()
features['mfcc_std'] = mfcc.std(axis=1).tolist()
spectral_centroid = librosa.feature.spectral_centroid(y=y, sr=sr)[0]
features['spectral_centroid'] = float(spectral_centroid.mean())
rolloff = librosa.feature.spectral_rolloff(y=y, sr=sr)[0]
features['spectral_rolloff'] = float(rolloff.mean())
features['danceability'] = float(min(tempo / 180, 1.0) * features['energy_mean'])
return features
def to_vector(self, features: dict) -> np.ndarray:
"""Конвертація в numpy вектор для індексування"""
vector = (
[features['tempo'] / 200, features['energy_mean']] +
features['mfcc_mean'] +
features['chroma_mean'] +
[features['spectral_centroid'] / 5000,
features['spectral_rolloff'] / 10000]
)
return np.array(vector, dtype=np.float32)
Чому сесійний контекст критичний для точності?
Один і той самий користувач може слухати енергійну музику вранці та розслаблюючу ввечері. Ми відстежуємо останні 10 дій і будуємо вектор настрою сесії. Він змішується з довгостроковим профілем з вагою 0.6 — так система реагує на поточний стан. Без цього контексту рекомендації стають глухими до моменту.
from collections import deque
class SessionAwareMusicRecommender:
"""Рекомендації з урахуванням поточної сесії"""
def __init__(self, track_index, audio_features: dict):
self.track_index = track_index
self.audio_features = audio_features
self.session_history = {}
def update_session(self, user_id: str, track_id: str,
played_seconds: int, total_seconds: int):
"""Оновлення контексту сесії"""
if user_id not in self.session_history:
self.session_history[user_id] = deque(maxlen=10)
completion = played_seconds / max(total_seconds, 1)
signal = 1.0 if completion > 0.8 else (0.5 if completion > 0.4 else -0.5)
self.session_history[user_id].append({
'track_id': track_id,
'signal': signal,
'completion': completion
})
def get_session_context_vector(self, user_id: str) -> np.ndarray:
"""Середній аудіо-вектор останніх позитивних треків"""
history = self.session_history.get(user_id, [])
positive_tracks = [
h['track_id'] for h in history
if h['signal'] > 0 and h['track_id'] in self.audio_features
]
if not positive_tracks:
return None
vectors = [self.audio_features[t] for t in positive_tracks[-5:]]
return np.mean(vectors, axis=0)
def recommend_next(self, user_id: str,
long_term_profile: np.ndarray,
n: int = 5,
session_weight: float = 0.6) -> list[tuple]:
"""Наступний трек: суміш довгострокових уподобань та поточної сесії"""
session_context = self.get_session_context_vector(user_id)
if session_context is not None:
query_vector = (
session_weight * session_context +
(1 - session_weight) * long_term_profile
)
else:
query_vector = long_term_profile
norm = np.linalg.norm(query_vector)
query_vector = query_vector / (norm + 1e-10)
recent_tracks = {h['track_id'] for h in self.session_history.get(user_id, [])}
candidates = self.track_index.search(query_vector, k=50)
results = [
(tid, score) for tid, score in candidates
if tid not in recent_tracks
][:n]
return results
Як обробляються skip-сигнали?
Пропуски до 10% тривалості треку дають сильний негативний сигнал (-1.0), а повторні прослуховування — позитивний з коефіцієнтом log(play_count). Це дозволяє перетворювати сирі логи в зважені implicit ratings.
def process_skip_signals(plays_df: pd.DataFrame) -> pd.DataFrame:
"""Перетворення пропусків у зважені сигнали"""
plays_df['completion_rate'] = plays_df['played_seconds'] / plays_df['duration_seconds'].clip(1)
plays_df['implicit_rating'] = np.where(
plays_df['completion_rate'] >= 0.80, 1.0,
np.where(
plays_df['completion_rate'] >= 0.50, 0.5,
np.where(
plays_df['completion_rate'] <= 0.10, -1.0,
0.0
)
)
)
repeat_plays = plays_df.groupby(['user_id', 'track_id']).size().reset_index(name='play_count')
plays_df = plays_df.merge(repeat_plays, on=['user_id', 'track_id'])
plays_df['implicit_rating'] += np.log1p(plays_df['play_count'] - 1) * 0.3
return plays_df[plays_df['implicit_rating'] != 0]
Порівняння підходів
| Метод | Працює з новими треками | Холодний старт | Враховано сесію | Якість на старих користувачах |
|---|---|---|---|---|
| Колаборативна фільтрація | Ні | Погано | Ні | Середнє |
| Content-based (аудіо) | Так | Добре | Ні | Середнє |
| Гібрид з сесійним контекстом | Так | Добре | Так | Високе |
Гібридний підхід дає приріст retention на 15–20% порівняно з чистою колаборацією, а diversity score (різноманіття жанрів) зростає в 2 рази.
Вплив компонентів на метрики
| Компонент | Приріст retention | Зниження skip rate | Покращення diversity |
|---|---|---|---|
| Аудіо-ознаки | +5% | -8% | +40% |
| Сесійний контекст | +10% | -12% | +15% |
| Implicit ratings | +8% | -10% | -5% |
Які дані потрібні для запуску?
Мінімально — історія прослуховувань користувачів (user_id, track_id, timestamp, played_seconds, duration). Для покращення якості бажані аудіо-файли треків (прев'ю 30 с) та метадані (жанр, виконавець). Холодний старт вирішуємо через content-based рекомендації на основі аудіо-ознак.
Як ви обробляєте холодний старт?
Для нових користувачів використовуємо популярне з урахуванням сесії (перші 5 треків). Для нових треків — косинусна близькість до векторів аудіо-ознак уже відомих треків. Після накопичення 10+ подій підключаємо колаборативну складову.
Які метрики важливі для оцінки якості рекомендацій?
Ключові: long-term retention (активність через 7/30/90 днів), skip rate перших 30 секунд, diversity score (різноманіття жанрів), serendipity rate (частка треків поза звичайним смаком користувача), implicit rating accuracy (AUC при прогнозі дослуховування).
Скільки часу займає впровадження?
Типовий проєкт від 4 до 12 тижнів залежно від обсягу даних та необхідної архітектури. MVP з базовими рекомендаціями (audio features + колаборація) — близько 6 тижнів. Повноцінна система з сесійним контекстом, A/B-тестуванням та моніторингом — 10–12 тижнів.
Процес роботи
- Аналітика — аудит поточної інфраструктури, логів, якості даних. Визначаємо baseline метрики (skip rate, session length). Перевіряємо, чи немає зміщення в даних (наприклад, домінування певних жанрів).
- Проектування — вибір стеку (embedding model, vector store, serving). Вирішуємо, де розгортати: on-premise з Triton Inference Server або хмара (SageMaker, Vertex AI).
- Реалізація — пишемо pipeline витягу аудіо-ознак, навчаємо модель implicit factorization, налаштовуємо сесійний модуль. Використовуємо ONNX Runtime для інференсу.
- Тестування — A/B-тест на 10% трафіку мінімум 2 тижні. Метрики: retention (D7/D30), skip rate перших 30 секунд, serendipity.
- Деплой та моніторинг — розгортаємо модель в production, налаштовуємо дашборди метрик (p99 latency, GPU utilization, тренди implicit ratings).
Типові помилки на старті
- Використання тільки колаборативної фільтрації без аудіо-ознак — холодний старт не вирішується.
- Некоректне зважування skip-сигналів: пропуск після 30 секунд часто означає "вже послухав, перемикаю", а не "не подобається". Ми враховуємо completion rate.
- Ігнорування сесійного контексту — рекомендації не адаптуються до поточного настрою користувача.
Що входить в роботу
- Архітектурна документація (мікросервіси, API, схема даних)
- Векторний індекс аудіо-ознак (pgvector або Qdrant)
- REST API для онлайн-рекомендацій (SKLearn → ONNX Runtime)
- Офлайн-пайплайн перерахунку профілів (Spark або Ray)
- Дашборд моніторингу якості рекомендацій (Grafana + Prometheus)
- Документація з експлуатації та навчання команди
Терміни та бюджет
Типовий проєкт з сесійним контекстом та аудіо-фічами займає від 6 до 12 тижнів. Вартість розраховується індивідуально після аудиту даних, середня вартість становить $15,000–$50,000. Замовте розробку рекомендаційної системи під вашу аудиторію — отримайте оцінку за 3 робочі дні. Для консультації зв'яжіться з нами.







