AI Media Mix Modeling System

We design and deploy artificial intelligence systems: from prototype to production-ready solutions. Our team combines expertise in machine learning, data engineering and MLOps to make AI work not in the lab, but in real business.
Showing 1 of 1 servicesAll 1566 services
AI Media Mix Modeling System
Medium
~2-4 weeks
FAQ
AI Development Areas
AI Solution Development Stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823
  • image_logo-aider_0.jpg
    AIDER company logo development
    762
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    848

AI-медиамикс моделирование (Media Mix Modeling)

Media Mix Modeling (MMM) — эконометрический подход к оценке вклада каждого маркетингового канала в продажи. В условиях деградации cookie-трекинга и ограничений GDPR MMM переживает ренессанс как privacy-safe метод атрибуции. Современный MMM объединяет байесовскую статистику с ML для устойчивых, интерпретируемых оценок.

Основы MMM

Задача: Имея исторические данные о расходах по каналам (ТВ, digital, OOH, radio, search) и продажах — оценить вклад каждого канала и оптимальный бюджетный сплит.

Adstock трансформация: Реклама имеет отложенный эффект (carry-over). Adstock моделирует это:

def adstock_transform(spend_series, decay_rate=0.3, lag=None):
    """
    Adstock: каждая точка = spend + decay × предыдущий adstock
    decay_rate: 0 = нет памяти, 0.9 = долгая память
    """
    adstock = np.zeros(len(spend_series))
    adstock[0] = spend_series[0]
    for t in range(1, len(spend_series)):
        adstock[t] = spend_series[t] + decay_rate * adstock[t-1]
    return adstock

# Геометрический vs. Weibull adstock
def weibull_adstock(spend, shape=2.0, scale=4.0, max_lag=13):
    """
    Weibull PDF: гибкая форма распределения отложенного эффекта
    shape < 1: убывающий (мгновенное воздействие)
    shape > 1: delayed peak (реклама накапливается)
    """
    pdf = scipy.stats.weibull_min.pdf(np.arange(max_lag), shape, scale=scale)
    pdf = pdf / pdf.sum()
    return np.convolve(spend, pdf, mode='full')[:len(spend)]

Saturation (diminishing returns): Чем больше тратим, тем меньше прирост. Hill function:

def hill_saturation(spend, alpha=2.0, gamma=0.5):
    """
    alpha: форма кривой (крутизна)
    gamma: половина насыщения (при каком spend эффект = 50% максимума)
    """
    return spend**alpha / (gamma**alpha + spend**alpha)

Байесовский MMM

PyMC/Stan реализация:

import pymc as pm
import numpy as np

with pm.Model() as mmm_model:
    # Priors для параметров каждого канала
    beta_tv = pm.HalfNormal('beta_tv', sigma=1.0)
    beta_digital = pm.HalfNormal('beta_digital', sigma=1.0)
    beta_search = pm.HalfNormal('beta_search', sigma=1.0)

    # Decay priors (0-1)
    decay_tv = pm.Beta('decay_tv', alpha=3, beta=3)  # peak около 0.5
    decay_digital = pm.Beta('decay_digital', alpha=2, beta=5)  # < 0.5 для digital

    # Saturation priors
    gamma_tv = pm.HalfNormal('gamma_tv', sigma=0.5)

    # Transformed media
    tv_adstock = adstock_transform(tv_spend, decay_tv)
    tv_saturated = hill_saturation(tv_adstock, gamma=gamma_tv)

    # Baseline и тренды
    trend = pm.Deterministic('trend', np.arange(len(y)))
    seasonality = pm.Deterministic('seasonality', fourier_features(n_harmonics=4))
    intercept = pm.Normal('intercept', mu=y.mean(), sigma=y.std())

    # Модель
    mu = (intercept +
          beta_tv * tv_saturated +
          beta_digital * digital_saturated +
          beta_search * search_saturated +
          trend_coef * trend +
          seasonality_coefs @ seasonality)

    sigma = pm.HalfNormal('sigma', sigma=y.std() * 0.2)
    y_obs = pm.Normal('y_obs', mu=mu, sigma=sigma, observed=y)

trace = pm.sample(2000, tune=1000, target_accept=0.95)

Преимущества байесовского подхода:

  • Posterior распределение → доверительные интервалы для каждого вклада
  • Информативные priors от бизнеса: "TV decay обычно 0.3-0.7"
  • Устойчивость к мультиколлинеарности (частая проблема MMM)

Robyn (Meta MMM Framework)

# Открытый инструмент от Meta для автоматизированного MMM
# Ridge regression + Nevergrad оптимизатор + Pareto-optimal решения

from robyn import Robyn

robyn = Robyn(
    date_var='date',
    dep_var='revenue',
    dep_var_type='revenue',
    paid_media_spends=['tv_spend', 'digital_spend', 'search_spend'],
    paid_media_vars=['tv_imp', 'digital_clicks', 'search_clicks'],
    context_vars=['holidays', 'promotions'],
    season_var='yearmonth'
)

output = robyn.model_run(
    iterations=2000,
    trials=5,
    ts_validation=True
)

Pareto front решений: Robyn возвращает не одно решение, а фронт Парето по NRMSE (ошибка на трейне) и DECOMP.RSSD (сумма квадратов отклонений вкладов от априорных ожиданий).

Budget Optimization

Маржинальный ROI:

def marginal_roi_curve(channel, spend_range, channel_model):
    """
    При каком объёме расходов маржинальный ROI = 1?
    Это оптимальная точка инвестирования (при прочих равных)
    """
    revenues = [channel_model.predict(spend) for spend in spend_range]
    marginal_roi = np.diff(revenues) / np.diff(spend_range)
    optimal_spend = spend_range[np.argmin(np.abs(marginal_roi - 1))]
    return optimal_spend, marginal_roi

Portfolio оптимизация бюджета:

from scipy.optimize import minimize

def optimize_budget(total_budget, channel_models, current_allocation):
    """
    Максимизация суммарного lift по всем каналам
    при ограничении на суммарный бюджет
    """
    def total_revenue(allocation):
        return -sum(channel_models[c].predict(allocation[i])
                   for i, c in enumerate(channels))

    constraints = [
        {'type': 'eq', 'fun': lambda x: sum(x) - total_budget}
    ]
    bounds = [(0, total_budget)] * len(channels)

    result = minimize(total_revenue, x0=current_allocation,
                      bounds=bounds, constraints=constraints)
    return result.x

Валидация и ограничения

In-sample и out-of-sample:

  • MAPE на holdout-периоде: < 10% — хорошая модель
  • Decomposition check: суммарный вклад каналов + baseline = 100% продаж

Geo-experiments для калибровки: Изменение бюджета в одном регионе при прочих равных → "quasi-experiment" для верификации модельных оценок.

Принципиальные ограничения MMM:

  • Агрегированные данные → не работает на уровне пользователя
  • Короткие временные ряды (< 2 лет еженедельных данных) → нестабильные оценки
  • Новые каналы без исторических данных → prior-зависимость

Сроки: данные подготовка + Adstock трансформации + базовый Bayesian MMM — 4-5 недель. Robyn интеграция, saturation curves, budget optimizer, geo-calibration — 2-3 месяца.