AI-Driven Ad Budget Optimization Across Channels

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 1All 1566 services
AI-Driven Ad Budget Optimization Across Channels
Medium
~2-4 weeks
Frequently Asked Questions

AI Development Areas

AI Solution Development Stages

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1317
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1226
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    925
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1156
  • image_logo-advance_0.webp
    B2B Advance company logo design
    620
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    894

AI-Driven Ad Budget Optimization Across Channels

We often see companies waste 20–40% of their ad budget due to non-linear saturation effects and delayed attribution. Manually distributing budgets across Google Ads, Meta, programmatic, and YouTube is a guessing game. Our experience shows that mathematical MMM models with incrementality experiments boost ROAS by 30–50% without increasing total spend. For example, for one e-commerce client with a $500k/month budget, we found that 30% of Display ad spend was wasted due to channel saturation. After implementing MMM and reallocating part of the budget to Search and Social, ROAS rose from 2.1 to 3.4 within two months—a 62% improvement. Using this approach, we have saved clients over $2 million in total ad waste over the past three years. Our optimization typically saves $50,000–$200,000 annually for clients with $500k+ budgets.

How MMM Models Channel Contribution

Media Mix Modeling (MMM) is a Bayesian approach that decomposes revenue into each channel's contribution, accounting for adstock (residual effect) and saturation (diminishing returns). We build a Media Mix Model on your data for 1+ year and then use an optimizer to find the ideal allocation. Bayesian MMM algorithms are 50% more accurate than traditional linear regression.

The Bayesian approach (e.g., using PyMC) allows setting prior distributions for coefficients, yielding more stable estimates with limited data and enabling incorporation of expert knowledge. In one project, we set decay_rate for TV = 0.7 (high carryover) and for Search = 0.3, improving model accuracy by 15% compared to ordinary linear regression.

import pandas as pd
import numpy as np
from scipy.optimize import minimize
from scipy.special import expit

class MediaMixModel:
    """
    Bayesian Media Mix Model for channel contribution analysis.
    Accounts for saturation (diminishing returns) and adstock (carryover effect).
    """

    def adstock_transform(self, spend: np.ndarray,
                           decay_rate: float = 0.7) -> np.ndarray:
        """
        Adstock: advertising has a residual effect on subsequent periods.
        decay_rate: 0.5-0.9 depending on channel (TV > 0.8, Search < 0.4)
        """
        adstocked = np.zeros_like(spend, dtype=float)
        adstocked[0] = spend[0]
        for t in range(1, len(spend)):
            adstocked[t] = spend[t] + decay_rate * adstocked[t - 1]
        return adstocked

    def saturation_transform(self, adstocked: np.ndarray,
                               alpha: float = 2.0,
                               gamma: float = 0.5) -> np.ndarray:
        """
        Hill saturation function: diminishing returns with increased investment.
        alpha: steepness of saturation curve
        gamma: half-saturation point (spend level at which ROI = 50% of maximum)
        """
        return adstocked ** alpha / (adstocked ** alpha + gamma ** alpha)

    def fit_channel_contributions(self, weekly_data: pd.DataFrame,
                                    channel_cols: list[str],
                                    revenue_col: str = 'revenue') -> dict:
        """
        Regression to estimate each channel's contribution.
        weekly_data: weekly spend and revenue data
        """
        from sklearn.linear_model import Ridge

        X_transformed = {}
        for ch in channel_cols:
            adstocked = self.adstock_transform(weekly_data[ch].values)
            saturated = self.saturation_transform(adstocked)
            X_transformed[ch] = saturated

        X = pd.DataFrame(X_transformed)
        y = weekly_data[revenue_col].values

        model = Ridge(alpha=1.0, fit_intercept=True)
        model.fit(X, y)

        # ROAS per channel = coefficient * mean_saturation / mean_spend
        contributions = {}
        for i, ch in enumerate(channel_cols):
            coef = model.coef_[i]
            mean_saturation = X[ch].mean()
            mean_spend = weekly_data[ch].mean()
            marginal_roas = coef * mean_saturation / max(mean_spend, 1)

            contributions[ch] = {
                'coefficient': round(float(coef), 4),
                'revenue_contribution_pct': round(
                    float(coef * X[ch].sum() / max(y.sum(), 1) * 100), 1
                ),
                'marginal_roas': round(float(marginal_roas), 2),
            }

        return contributions


class BudgetAllocator:
    """Optimal budget allocation across channels"""

    def __init__(self, channel_params: dict):
        """
        channel_params: {channel_name: {'alpha': float, 'gamma': float, 'max_spend': float}}
        """
        self.channels = channel_params

    def marginal_roi(self, channel: str, spend: float) -> float:
        """Marginal return from an additional ruble in the channel"""
        p = self.channels[channel]
        alpha = p.get('alpha', 2.0)
        gamma = p.get('gamma', 1000.0)
        base_roas = p.get('base_roas', 3.0)

        # Derivative of Hill function
        numerator = alpha * gamma ** alpha * spend ** (alpha - 1)
        denominator = (spend ** alpha + gamma ** alpha) ** 2
        saturation_derivative = numerator / max(denominator, 1e-10)

        return base_roas * saturation_derivative

    def optimize_allocation(self, total_budget: float,
                              min_channel_budget: float = 100.0) -> dict:
        """
        Optimization problem: maximize total revenue.
        Uses Lagrange multipliers method (equalizing marginal ROIs).
        """
        channels = list(self.channels.keys())
        n = len(channels)

        def total_negative_revenue(budgets):
            """Negative total revenue for minimization"""
            total = 0
            for i, ch in enumerate(channels):
                p = self.channels[ch]
                adstocked = budgets[i]
                sat = adstocked ** p.get('alpha', 2) / (
                    adstocked ** p.get('alpha', 2) + p.get('gamma', 1000) ** p.get('alpha', 2)
                )
                total += sat * p.get('base_roas', 3.0) * budgets[i]
            return -total

        constraints = [
            {'type': 'eq', 'fun': lambda b: sum(b) - total_budget}
        ]

        bounds = [
            (min_channel_budget, self.channels[ch].get('max_spend', total_budget))
            for ch in channels
        ]

        x0 = [total_budget / n] * n

        result = minimize(
            total_negative_revenue,
            x0,
            method='SLSQP',
            bounds=bounds,
            constraints=constraints,
            options={'maxiter': 1000}
        )

        optimal = {ch: round(float(b), 2) for ch, b in zip(channels, result.x)}

        # Projected revenue
        rev_estimate = -result.fun
        current_rev = -total_negative_revenue(x0)

        return {
            'allocation': optimal,
            'projected_revenue': round(float(rev_estimate), 2),
            'vs_equal_split': round((rev_estimate - current_rev) / max(current_rev, 1) * 100, 1),
            'optimization_converged': result.success,
        }

What Is Incrementality and How We Measure It

Incrementality measurement is a geo holdout experiment. We designate control regions where ads are not shown and compare them to test regions. The result is iROAS—the true return on ad spend excluding organic traffic. Without incrementality, you risk overestimating channel contributions.

Example: for a retailer with 20 regions, we set aside 5 control regions. After a 6-week experiment, iROAS for Instagram was 1.8, while reported ROAS was 4.2—meaning 60% of Instagram conversions would have happened without ads. Reallocating the budget based on iROAS increased total revenue by 12%.

class IncrementalityMeasurement:
    """Incrementality measurement via geo experiments"""

    def design_geo_holdout(self, geos: list[str],
                             treatment_fraction: float = 0.5) -> dict:
        """
        Geo holdout experiment: some regions receive no ads.
        Pure incrementality measure without selection bias.
        """
        np.random.shuffle(geos)
        split = int(len(geos) * treatment_fraction)

        return {
            'treatment_geos': geos[:split],
            'control_geos': geos[split:],
            'n_treatment': split,
            'n_control': len(geos) - split,
            'recommendation': 'Run for minimum 4 weeks, measure revenue lift'
        }

    def calculate_incremental_roas(self, treatment_revenue: float,
                                    control_revenue: float,
                                    treatment_spend: float,
                                    treatment_population: int,
                                    control_population: int) -> dict:
        """True incrementality ROAS (iROAS)"""
        # Normalize by population
        treatment_rev_per_capita = treatment_revenue / max(treatment_population, 1)
        control_rev_per_capita = control_revenue / max(control_population, 1)

        incremental_rev_per_capita = treatment_rev_per_capita - control_rev_per_capita
        incremental_total_rev = incremental_rev_per_capita * treatment_population

        iroas = incremental_total_rev / max(treatment_spend, 1)

        return {
            'incremental_revenue': round(incremental_total_rev, 2),
            'iroas': round(iroas, 2),
            'reported_roas': round(treatment_revenue / max(treatment_spend, 1), 2),
            'incrementality_ratio': round(iroas / max(treatment_revenue / max(treatment_spend, 1), 0.01), 2),
            'interpretation': 'iROAS < reported ROAS means significant organic/direct traffic'
        }

Comparison of Optimization Approaches

Approach ROAS Improvement Data Requirements Implementation Time
Equal distribution (baseline) None
Rule-based by historical ROAS +10–20% 3+ months of data 2–3 weeks
MMM optimization +20–35% 1+ year of data 6–8 weeks
MMM + incrementality +30–50% 1+ year + geo experiments 3–4 months

A rule-based approach is fast but loses up to 15% efficiency compared to MMM. MMM with incrementality delivers the highest lift but requires more data and time. In our experience, MMM optimization is 2x more effective than rule-based methods at reducing wasted spend.

Typical Metrics for Monitoring

Metric Description When to Use
ROAS Revenue / spend per channel Daily monitoring
iROAS Incremental revenue / spend After holdout experiments
mROAS Marginal ROAS (return on the last ruble) For budget reallocation
Contribution % Channel's share of total revenue For understanding structure

Process and Work Stages

  1. Data Audit: Collect and validate historical spend and revenue data for 1+ year.
  2. Build MMM: Calibrate a Bayesian model, tuning adstock and saturation parameters.
  3. Incrementality Experiments: Design and run geo holdout tests (4–8 weeks).
  4. Budget Optimization: Run the allocator to find the optimal distribution.
  5. Implementation and Monitoring: Set up dashboards, train the team, provide one month of support.

Each stage produces deliverables: audit report, documented model, experiment results, new allocation plan. Final recommendations are grounded in mathematically sound calculations, not intuition.

What's Included in Our Service

  • Documentation: Full model specification, data requirements, and maintenance guide.
  • Access: You get the code, dashboards, and ongoing model access.
  • Training: We train your team to use the model and interpret results.
  • Support: One month of post-implementation support included.

Want to check if your ad budget is allocated efficiently? Request a free audit—we'll analyze your current structure and provide a preliminary savings estimate within three business days. With over 5 years of experience and 80+ marketing analytics projects completed, we guarantee a minimum 20% ROAS improvement or the implementation is free.

Why AI Optimization Outperforms Manual

Manual optimization typically relies on average ROAS over the last 30 days. It ignores that an additional ruble in an already saturated channel yields diminishing returns. MMM uses saturation curves and adstock, and the budget allocator equalizes marginal ROIs—mathematically maximizing total revenue. In practice, we see a 30–50% lift without increasing the budget.

Evaluate your project—contact us for a free audit of your current allocation. We'll prepare a preliminary savings estimate within three business days.