Multi-touch атрибуционные модели теряют надёжность в post-cookie эпохе, а marketing mix modeling возвращается на передний план. Open-source MMM-инструменты Google и Meta (LightweightMMM, Robyn) дают маркетологу возможность измерять эффективность каналов на агрегированном уровне. В начале 2025 года Meta выпустила Robyn 3.11 с Bayesian-оптимизацией и параллельным поиском гиперпараметров — инструмент стал production-ready. В этой статье разбираем настройку Robyn через три ключевые концепции: кривая насыщения (diminishing returns), адсток-декей (отложенный эффект) и holdout-валидация (надёжность модели).

Что такое Robyn и почему он важен сейчас

Robyn — R-пакет, выпущенный Meta в 2021 году как open-source решение. Модель на основе ridge-регрессии принимает данные о расходах по каналам и конверсиях на недельной/дневной агрегации, затем вычисляет incremental-вклад каждого канала в общие конверсии. В крупном обновлении 2024 года модель интегрировала компоненты временных рядов Prophet и добавила поддержку JSON-экспорта — это позволило подключать Robyn к Python workflow'ам.

Три характеристики выделяют Robyn среди других MMM-подходов: во-первых, моделирование связи расход-конверсия не линейным способом, а через Hill-Adstock-трансформацию (реалистичное насыщение); во-вторых, оптимизация гиперпараметров через генетический алгоритм и gradient-free Nevergrad-оптимизатор (ручная настройка не нужна); в-третьих, автоматическое формирование метрик качества модели (NRMSE, DECOMP.RSSD, MAPE). В production, для проверки надёжности модели критична встроенная функция holdout-валидации — покажем её ниже.

Преимущество marketing mix modeling перед attribution в том, что работа с агрегированными данными не подвержена ограничениям GDPR/CCPA и избегает сложности cross-device journey. Недостаток — модель остаётся на уровне недельной гранулярности: она подходит для квартального распределения бюджета, но не для intraday-оптимизации кампаний. В архитектуре first-party данных компании Roibase MMM работает рядом с результатами incrementality-тестов: высокий ROAS в MMM недостаточен — требуется валидация через geo-split-тест или synthetic control.

Подготовка данных: расходы по каналам + макро-переменные

Robyn принимает на вход временной ряд с минимальным набором колонок в недельной гранулярности:

# Пример структуры данных (104 недели = ~2 года)
data <- data.frame(
  date = seq(as.Date("2024-01-01"), by = "week", length.out = 104),
  revenue = rnorm(104, 50000, 8000),
  facebook_spend = rnorm(104, 5000, 1000),
  google_search_spend = rnorm(104, 7000, 1500),
  display_spend = rnorm(104, 3000, 800),
  competitor_index = rnorm(104, 100, 15),  # макро-переменная
  holiday_flag = sample(0:1, 104, replace = TRUE)
)

Количество каналов: Рекомендуется 2–15 каналов. Свыше 20 повышается риск переобучения (overfitting), снижается стабильность коэффициентов. Если есть long-tail каналы (affiliate, influencer, podcast), их лучше объединить в одну колонку типа other_digital.

Макро-переменные: Сезонность, праздники, индекс конкурентов, макроэкономические показатели должны быть включены как контрольные переменные — иначе модель припишет весь рост конверсий медиа-каналам. Встроенная интеграция Robyn с Prophet автоматически захватывает тренд и праздники, но если в секторе произойдёт специфический шок (Black Friday, Ramadan), нужен флаг holiday_flag.

Проверки качества данных:

  • Ни одна колонка не должна иметь нулевую вариацию (постоянные расходы = бесполезна)
  • Пропуски (missing values) — допуск ~5%; Robyn автоматической импутации не делает
  • Недельная гранулярность предпочтительна — дневные данные добавляют шум, месячные означают недостаток наблюдений

Если расходные данные приходят из разных источников (Google Ads API, Meta Marketing API, внутренняя бухгалтерия), в ETL-pipeline следует построить процесс данных-анализа. В нашем production workflow таблица marketing_spend_weekly в BigQuery обновляется каждый понедельник; dbt-модель синхронизирует агрегацию, R-скрипт читает из неё и запускает Robyn.

Насыщение и адсток: Hill-Adstock трансформация

Robyn пропускает расходы по каждому каналу через двухэтапное преобразование: сначала адсток (отложенный эффект), затем насыщение (убывающая отдача).

Адсток-декей (geometric или Weibull)

Эффект TV-рекламы не прекращается мгновенно — память зрителя удерживает впечатление несколько недель. Адсток моделирует это явление. Robyn поддерживает два типа: geometric (простая экспоненциальная убыль) и weibull (гибкая, S-образная кривая).

Geometric adstock:

adstocked_spend[t] = spend[t] + θ × adstocked_spend[t-1]

Здесь θ (theta) — коэффициент декея. Значение 0.5 означает, что эффект предыдущей недели наполовину переходит в текущую. Robyn автоматически ищет оптимальный θ в диапазоне 0–0.9.

Weibull adstock: Более сложный — имеет параметры shape и scale. Для awareness-каналов (TV, outdoor, influencer) Weibull часто даёт лучшую подгонку, так как эффект может медленно начинаться, достигать пика и затем быстро спадать.

Практический совет: На первой итерации используйте geometric — сходимость быстрее. Если качество модели низко (NRMSE > 0.15) и микс содержит awareness-каналы, попробуйте Weibull.

Насыщение: Hill-функция

Удвоение расходов не даёт удвоения конверсий — есть убывающая отдача. Robyn моделирует это через Hill-уравнение:

effect = spend^α / (K^α + spend^α)
  • α (alpha): крутизна кривой — маленькое значение означает медленное насыщение, большое — быстрое
  • K: точка полусатурации — при расходе на этом уровне достигается половина максимального эффекта

Robyn находит оба параметра для каждого канала во время поиска гиперпараметров. Результат — response curve для каждого канала: например, Facebook Ads показывает plateau после 10K€ расходов, а Google Search продолжает линейный рост до 20K€.

Практическое применение кривых насыщения: В сценариях перераспределения бюджета. Если канал уже находится в плоской зоне (низкий slope), перевод бюджета оттуда в канал с крутым slope повысит общий ROAS.

Запуск модели и поиск гиперпараметров

Установка Robyn занимает две строки:

install.packages("Robyn")
library(Robyn)

В функции InputCollect определяется структура данных:

InputCollect <- robyn_inputs(
  dt_input = data,
  date_var = "date",
  dep_var = "revenue",
  paid_media_spends = c("facebook_spend", "google_search_spend", "display_spend"),
  context_vars = c("competitor_index", "holiday_flag"),
  window_start = "2024-01-01",
  window_end = "2025-12-31",
  adstock = "geometric"  # или "weibull"
)

Диапазоны гиперпараметров: Robyn ищет для каждого канала значения theta адстока и alpha/K насыщения в заданном диапазоне. Default ranges обычно достаточны, но при наличии domain-knowledge можно добавить ограничения:

hyperparameters <- list(
  facebook_spend_alphas = c(0.5, 3),   # крутизна насыщения
  facebook_spend_gammas = c(0.3, 1),   # inflection point насыщения
  facebook_spend_thetas = c(0, 0.5)    # адсток-декей (geometric)
)

Запуск модели:

OutputModels <- robyn_run(
  InputCollect = InputCollect,
  iterations = 2000,     # iterations генетического алгоритма
  trials = 5,            # количество random seeds
  cores = 4
)

Этот шаг занимает 10–30 минут (зависит от объёма данных). На выходе — набор Pareto-оптимальных моделей, сбалансированных по NRMSE (качество подгонки) и DECOMP.RSSD (гладкость распределения вклада каналов).

Выбор модели: Robyn предлагает 10–20 Pareto-моделей. Выбор модели с минимальным NRMSE не всегда правилен — некоторые могут переучиться. Функция robyn_outputs() с параметром robyn_clusters позволяет кластеризовать модели и выбрать центр наиболее стабильного кластера.

Holdout-валидация: измерение надёжности модели

Одна из ключевых возможностей Robyn — встроенная holdout-валидация. Во время обучения последние N недель сохраняются как тестовый набор; затем модель делает прогноз на эти недели и сравнивает с фактическими значениями.

# Holdout последние 8 недель
OutputModels <- robyn_run(
  InputCollect = InputCollect,
  iterations = 2000,
  trials = 5,
  cores = 4,
  calibration_input = NULL,
  holdout_periods = 8  # последние 8 недель как тестовый набор
)

Результаты holdout-валидации в OutputModels$resultHypParam:

Model IDTrain NRMSEHoldout MAPEHoldout NRMSE
1_123_40.0812.3%0.14
2_456_10.0718.5%0.21

Holdout MAPE < 15% обычно считается production-ready. Значения выше 20% указывают на слабую предсказательную способность модели — либо проблемы с качеством данных, либо слишком широкие диапазоны гиперпараметров.

Практическая ловушка: Если в holdout-периоде произошло аномальное событие (outage платформы, вирусная кампания), модель не сможет его предсказать и MAPE резко возрастёт. В таких случаях сдвиньте holdout-период или пометьте неделю как аномалию.

Holdout-валидация имеет дополнительное применение: cross-check с результатами incrementality-тестов. Если MMM показывает 30% ROAS для Facebook, а прошлый geo-split-тест дал 15%, это сигнал, что MMM припишет Facebook часть макроэффекта (сезонность, органический тренд). Обнаружение таких несостыковок — часть процесса в CDP & retention engineering, где MMM-output подключается к dashboard'у экспериментов.

Оптимизация бюджета и планирование сценариев

После построения Robyn-модели открываются два основных применения: перераспределение бюджета (оптимальное распределение по каналам) и what-if сценарии (что если увеличить бюджет на 20%).

Budget allocator:

AllocatorCollect <- robyn_allocator(
  InputCollect = InputCollect,
  OutputCollect = OutputModels,
  select_model = "1_123_4",  # выбранная Pareto-модель
  scenario = "max_response",  # или "target_efficiency"
  channel_constr_low = 0.7,   # минимум 70% текущего бюджета на канал
  channel_constr_up = 1.5     # максимум 150%
)

Результат — рекомендованные новые расходы и ожидаемый incremental-до