Передбачення CTR та CVR у рекламних системах
Ви запускаєте рекламну кампанію на мільйон показів на день. Передбачення CTR занижене на 15% — ви програєте топові місця в аукціоні. Завищене на 20% — зливаєте бюджет на нерелевантні кліки. Ми вирішуємо це завдання за допомогою кастомних ML-моделей під ключ. Наша AI-система передбачення CTR/CVR аналізує аудиторію, контекст та історичні дані, щоб оптимізувати ставки в реальному часі. Помилка в CTR-передбаченні на 20% може коштувати до 15-25% рекламного бюджету — це гроші, які йдуть на неефективні покази.
CTR та CVR — фундаментальні сигнали для ціноутворення в programmatic рекламі. Помилка в CTR-передбаченні на 20% безпосередньо транслюється в переплату або недовиграш аукціонів. У масштабі сотень мільйонів показів на день навіть покращення AUC з 0.76 до 0.78 означає мільйони зекономленого або заробленого бюджету. За нашими даними, LightGBM з калібруванням перевершує логістичну регресію за AUC на 0.03-0.05 — це пряме порівняння ефективності.
Як ми будуємо CTR-моделі під ключ
CTR-передбачення — бінарна класифікація з трьома ключовими складностями: крайній дисбаланс класів (CTR 0.1-2%), величезний об'єм (мільярди прикладів на день), приховані конверсії (CVR спостерігається тільки для тих, хто клікнув, що створює selection bias). Ми використовуємо LightGBM з налаштуванням scale_pos_weight та подальшим калібруванням. Калібрований LightGBM дає Log Loss у 1.3 рази нижче, ніж сирі ймовірності.
import numpy as np
import pandas as pd
import lightgbm as lgb
from sklearn.calibration import CalibratedClassifierCV
from sklearn.metrics import roc_auc_score, log_loss
class CTRFeatureEngineer:
"""Ознаки для CTR-моделі в display advertising"""
def build_features(self, bid_logs: pd.DataFrame) -> pd.DataFrame:
"""
bid_logs: історичні логи показів з прапорами clicked/converted
"""
df = bid_logs.copy()
# === Статистичні ознаки користувача ===
user_stats = df.groupby('user_id').agg(
user_historical_ctr=('clicked', 'mean'),
user_impression_count=('clicked', 'count'),
user_conversion_rate=('converted', 'mean'),
).reset_index()
# === Статистичні ознаки площадки ===
site_stats = df.groupby('site_domain').agg(
site_ctr=('clicked', 'mean'),
site_conversion_rate=('converted', 'mean'),
site_volume=('clicked', 'count'),
).reset_index()
# === Ознаки перетину (user × ad) ===
df['hour'] = pd.to_datetime(df['timestamp']).dt.hour
df['is_weekend'] = pd.to_datetime(df['timestamp']).dt.dayofweek >= 5
df['is_prime_time'] = df['hour'].between(18, 22)
# Cross-ознаки: важливіші за одиночні
df['ad_position_encoded'] = df['ad_position'].map({'atf': 1, 'btf': 0}).fillna(0.5)
df = df.merge(user_stats, on='user_id', how='left')
df = df.merge(site_stats, on='site_domain', how='left')
# Smoothed CTR для боротьби з розрідженістю (Wilson smoothing)
alpha = 100 # Prior strength
global_ctr = df['clicked'].mean()
df['user_smooth_ctr'] = (
df['user_historical_ctr'].fillna(global_ctr) * df['user_impression_count'].fillna(0) +
global_ctr * alpha
) / (df['user_impression_count'].fillna(0) + alpha)
feature_cols = [
'user_smooth_ctr', 'user_impression_count',
'site_ctr', 'site_volume',
'hour', 'is_weekend', 'is_prime_time',
'ad_position_encoded', 'banner_width', 'banner_height',
'floor_price',
]
return df[feature_cols].fillna(0)
class CTRModel:
"""LightGBM для CTR з правильним калібруванням"""
def __init__(self):
self.model = lgb.LGBMClassifier(
n_estimators=1000,
learning_rate=0.03,
num_leaves=255,
min_child_samples=200,
subsample=0.8,
colsample_bytree=0.7,
scale_pos_weight=50, # Корекція дисбалансу: 1 клік на 50 показів
random_state=42,
n_jobs=-1,
)
self.calibrator = None
self._is_calibrated = False
def train(self, X_train: np.ndarray, y_train: np.ndarray,
X_val: np.ndarray, y_val: np.ndarray):
"""Навчання з ранньою зупинкою"""
self.model.fit(
X_train, y_train,
eval_set=[(X_val, y_val)],
eval_metric='auc',
callbacks=[
lgb.early_stopping(100, verbose=False),
lgb.log_evaluation(200)
]
)
# Калібрування — ОБОВ'ЯЗКОВЕ для використання в bid price calculation
# Сирий LightGBM дає хороший ranking, але погані ймовірності
self.calibrator = CalibratedClassifierCV(self.model, cv='prefit', method='isotonic')
self.calibrator.fit(X_val, y_val)
self._is_calibrated = True
def predict_ctr(self, X: np.ndarray) -> np.ndarray:
"""Калібровані ймовірності кліків"""
if self._is_calibrated:
return self.calibrator.predict_proba(X)[:, 1]
return self.model.predict_proba(X)[:, 1]
def evaluate(self, X_test: np.ndarray, y_test: np.ndarray) -> dict:
raw_probs = self.model.predict_proba(X_test)[:, 1]
cal_probs = self.predict_ctr(X_test) if self._is_calibrated else raw_probs
return {
'auc_raw': round(roc_auc_score(y_test, raw_probs), 4),
'auc_calibrated': round(roc_auc_score(y_test, cal_probs), 4),
'logloss_raw': round(log_loss(y_test, raw_probs), 4),
'logloss_calibrated': round(log_loss(y_test, cal_probs), 4),
'mean_predicted_ctr': round(float(cal_probs.mean()), 5),
'actual_ctr': round(float(y_test.mean()), 5),
}
class DelayedConversionCorrector:
"""
Корекція delayed conversions у CVR-моделі.
Конверсії можуть відбуватися через години/дні після кліку.
Обрізка навчальної вибірки за часом створює зміщення.
"""
def adjust_for_delayed_conversions(self, clicks: pd.DataFrame,
observation_window_hours: int = 24) -> pd.DataFrame:
"""
Відкидаємо нещодавні кліки, у яких ще не минув window конверсії.
Інакше CVR буде занижений для останніх прикладів.
"""
cutoff = pd.Timestamp.now() - pd.Timedelta(hours=observation_window_hours)
return clicks[clicks['click_time'] < cutoff]
def estimate_conversion_delay_distribution(self,
conversions: pd.DataFrame) -> dict:
"""Розподіл затримок конверсій"""
delays = (conversions['conversion_time'] - conversions['click_time']).dt.total_seconds() / 3600
return {
'p50_hours': round(float(delays.quantile(0.50)), 1),
'p90_hours': round(float(delays.quantile(0.90)), 1),
'p99_hours': round(float(delays.quantile(0.99)), 1),
'recommended_window': f"{int(delays.quantile(0.95))} hours",
}
Як оцінити якість моделі CTR?
Основні метрики: AUC-ROC (>0.75 хороше значення), Log Loss (<0.10) та Calibration Error (<0.005). У продакшені додатково дивляться на ΔAUC від нових фіч — приріст >0.001 виправдовує інжиніринг. Калібрований LightGBM дає Log Loss у 1.3 рази нижче, ніж сирі ймовірності.
Чому калібрування ймовірностей критичне?
Некалібрована модель може систематично переплачувати на аукціонах. Сирий LightGBM дає відмінні ранжування, але погані ймовірності. Ми використовуємо ізотонічну регресію, яка знижує Calibration Error до <0.005. Це критично для bid price calculation: якщо модель передбачає CTR 0.5%, а реальний — 0.8%, ви переплачуєте за кожен клік. Точне калібрування безпосередньо економить бюджет — економія від впровадження каліброваної LightGBM моделі становить до 30% витрат на рекламу.
Щотижневе оновлення моделі
Рекламний ландшафт змінюється швидко: нові креативи, сезонність, зміна поведінки аудиторії. Ми рекомендуємо щотижневе донавчання. Для цього налаштовуємо пайплайн автоматичного ретрейнінгу з моніторингом дрейфу ознак. У періоди високої волатильності (наприклад, чорна п'ятниця) може знадобитися daily-оновлення, але зазвичай це надмірно та вносить шум.
Delayed conversions: проблема та рішення
У CVR-моделях конверсії можуть відбуватися через години або дні після кліку. Якщо просто обрізати навчальну вибірку за часом, останні спостереження матимуть занижений CVR. Ми використовуємо корекцію, відкидаючи кліки з не минулим вікном конверсії (зазвичай 24-72 години залежно від розподілу затримок). Це покращує калібрування CVR на 10-20%.
Типовий розподіл затримок конверсій для e-commerce
| Перцентиль | Затримка (години) |
|---|---|
| p50 | 2.5 |
| p90 | 18.0 |
| p99 | 72.0 |
| Рекомендоване вікно | 95 годин |
Метрики якості CTR/CVR моделей
| Метрика | Хороше значення | Призначення |
|---|---|---|
| AUC-ROC | > 0.75 для CTR | Ранжувальна здатність |
| Log Loss | < 0.10 | Якість ймовірностей |
| Calibration Error | < 0.005 | Точність CTR-оцінок |
| NDCG@1000 | > 0.85 | Топ-аукціони |
| Delta AUC від нової фічі | > 0.001 | Окупність інжинірингу |
Для CTR-моделі AUC 0.76 vs 0.74 — значуща різниця в масштабі. Калібрування обов'язкове: некалібрована модель може систематично переплачувати.
Процес впровадження AI-передбачення CTR/CVR
Ми працюємо по етапах:
- Аудит даних — збираємо логи показів, перевіряємо якість та повноту. Оцінюємо приріст від ML.
- Feature Engineering — будуємо ознаки: smoothed CTR, site stats, часові патерни. Використовуємо Wilson smoothing для боротьби з розрідженістю.
- Навчання та калібрування — LightGBM + isotonic calibration. Перевіряємо на відкладеній тестовій вибірці.
- A/B тестування — у продакшені порівнюємо з baseline за revenue.
- Документація та деплой — експортуємо модель в ONNX або Triton, налаштовуємо моніторинг дрейфу.
Що входить у розробку моделі
Деліверабли:
- Звіт за аудитом даних та потенційним приростом
- Python-пакет з inference-пайплайном
- Документація API для bidder
- Навчання команди замовника
- Підтримка 3 місяці після деплою
Типові помилки в MVP CTR/CVR
- Ігнорування delayed conversions — CVR занижена на 10-20%.
- Відсутність валідації: одна крос-валідація, а не три часових відкладених.
- Занадто часті оновлення (daily) — шум від випадкових флуктуацій.
- Використання лише лінійних моделей — пропускають нелінійні перетини.
Замовте аудит ваших рекламних даних — ми визначимо потенційний приріст CTR. Отримайте консультацію: оцінимо ваші дані та скажемо, який приріст CTR можливий. Наш досвід: понад 20 проєктів для programmatic-платформ, 5+ років у ML-оптимізації реклами. Звертайтеся за безкоштовною консультацією щодо ваших даних.







