Уявіть: ви запускаєте новинний додаток із мільйоном щоденних публікацій. Без персоналізації користувач бачить однотипні пости, швидко втрачає інтерес і йде. Хронологічна стрічка втрачає до 60% залученості — це експериментально підтверджена цифра. Наш ранжувальник вирішує задачу сортування кандидатів на основі сотень ознак, не генеруючи контент, а розставляючи пріоритети. Типовий приклад: стрічка новин перестає бути релевантною після двох тижнів використання — користувачі скаржаться на одноманітність. Пайплайн враховує динаміку інтересів і запобігає filter bubble. Гібридний підхід: колаборативна фільтрація доповнюється семантичним аналізом контенту. Це дозволяє враховувати як явні, так і неявні вподобання. Результат — кожен користувач бачить стрічку, яка адаптується під його мінливі інтереси в реальному часі. Такий підхід підвищує утримання на 30% і збільшує час сесії в півтора рази. Переходимо до деталей реалізації.
Як ми будуємо пайплайн ранжування?
Персоналізація стрічки — двоступеневий пайплайн: retrieval і ranking. На першому етапі з мільйонів публікацій відбираємо кілька сотень кандидатів через approximate nearest neighbours (ANN) на основі підписок та інтересів. На другому — ранжуємо цих кандидатів важчою моделлю з сотнями ознак. Розділення етапів критичне: ranking-модель занадто повільна для всього каталогу, retrieval — недостатньо точна для фінального порядку.
Ознаки для ранжування
Хороша ранжувальна модель використовує три групи ознак:
- Контекст користувача: час доби, день тижня, тип сесії (холодна або продовження), активність за 24 години.
- Характеристики контенту: вік публікації, engagement rate (лайки/перегляди), швидкість набору переглядів за першу годину, авторський follower count та історичний CTR.
- Перетин користувача і контенту: семантична схожість з історією взаємодій, тематичне перекриття з топ-інтересами, знайомство з автором.
# Feature vector для одного кандидата
@dataclass
class RankingFeatures:
# Content features
post_age_hours: float
engagement_rate_24h: float
viral_velocity: float # views_per_hour in first 2 hours
# User-content interaction
topic_affinity: float # cosine sim між профілем юзера та ембеддингом поста
author_ctr_for_user: float # історичний CTR цього автора у цього юзера
# Context
hour_of_day: int
is_weekend: bool
session_depth: int # скільки постів вже переглянуто в сесії
Чому LightGBM, а не нейромережа?
Нейромережеві ранжувальники дають вищу якість, але LightGBM з LambdaRank objective — швидший в inference (2–5 мс на 200 кандидатів) і простіший в ітерації. Для середнього масштабу це оптимальний вибір. При аудиторії понад 10 млн оптимальним може стати дворівнева модель з нейромережевим ретривером і LightGBM для ранжування. Вартість розробки нейромережевого рішення вища, але часто економічно виправдана при високому навантаженні.
import lightgbm as lgb
model = lgb.LGBMRanker(
objective='lambdarank',
metric='ndcg',
ndcg_eval_at=[5, 10, 20],
n_estimators=500,
learning_rate=0.05,
num_leaves=63
)
model.fit(
X_train, y_train, # y — relevance labels: 0=ignored, 1=viewed, 2=liked, 3=shared
group=train_groups, # розмір груп запитів
eval_set=[(X_val, y_val)],
eval_group=[val_groups]
)
Як забезпечити безшовний скрол на мобільному?
Реалізуємо prefetch: коли користувач доскролив до 70% поточного батча, фоново підвантажуємо наступні 20 постів. На Android використовуємо Paging 3 з prefetchDistance = 5. Приклад:
// Android: Paging 3 з prefetch для персоналізованої стрічки
class FeedPagingSource(
private val feedApi: FeedApi,
private val userId: String
) : PagingSource<String, FeedPost>() {
override suspend fun load(params: LoadParams<String>): LoadResult<String, FeedPost> {
return try {
val response = feedApi.getPersonalizedFeed(
userId = userId,
cursor = params.key,
pageSize = params.loadSize
)
LoadResult.Page(
data = response.posts,
prevKey = null,
nextKey = response.nextCursor
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
}
// ViewModel
val feed = Pager(
config = PagingConfig(pageSize = 20, prefetchDistance = 5),
pagingSourceFactory = { FeedPagingSource(feedApi, userId) }
).flow.cachedIn(viewModelScope)
Склад робіт і терміни
| Етап | Тривалість | Результат |
|---|---|---|
| Аудит даних і сигналів | 1 тиждень | Документ за якістю логів і складом ознак |
| Feature pipeline + навчання моделі | 2–3 тижні | Baseline LightGBM ранжувальник з NDCG@10 > 0.45 |
| Розробка serving API та мобільного клієнта | 2 тижні | Інтегрований prefetch і A/B тест в продакшні |
| A/B тестування та ітерації | 2–4 тижні | Зафіксований приріст CTR@10 на 15–30% |
Ми гарантуємо здачу кожного етапу вчасно з повною документацією та передачею навченої моделі. Вартість робіт розраховується індивідуально, з урахуванням особливостей вашого стеку та обсягу даних.
Типові підводні камені
Перший — відсутність diversity. Без MPR (Maximum Marginal Relevance) стрічка стає однобокою. Рішення: не більше 2 постів одного автора в перших 10, плюс евристики за темами. Другий — неправильні лейбли. Використовувати лише перегляди — зміщення в клікбейт. Включайте лайки та шери. Третій — ігнорування cold start. Новий користувач без історії — показуємо популярне і швидко збираємо сигнали через bandit-алгоритми. Економія ресурсів досягається за рахунок використання готових компонентів і відкритих бібліотек.
Забезпечення різноманіття контенту
Ми використовуємо MPR-постобробку: після ранжування перераховуємо scores з урахуванням схожості кандидатів. Додатково застосовуємо глобальні евристики — наприклад, кожен наступний пост має відрізнятися за темою від попереднього, а кількість постів від одного автора обмежена.
Орієнтири за термінами
LightGBM ранжувальник з базовими ознаками + API — 2–3 тижні. Повна система з двоступеневим retrieval+ranking, diversity-постобробкою та A/B тестуванням — 6–10 тижнів.
| Варіант | Терміни | Входить |
|---|---|---|
| Базовий | 2–3 тижні | LightGBM ранжувальник, базові ознаки, API, без diversity |
| Стандарт | 4–6 тижнів | Базовий + аудит даних, MPR diversity, A/B тест |
| Повний | 6–10 тижнів | Стандарт + дворівневий пайплайн, deep learning retriever, cold start bandit |
Отримайте консультацію — ми зробимо швидку попередню оцінку за вашими даними. Зв'яжіться з нами, щоб обговорити деталі та оптимізувати бюджет.







