YAML Runner Settings

This page documents the runner and pipeline keys: data loading, Stage 20 fitting, attached inference data, Stage 70 optimisation and Stage 35 blocked holdout validation. The YAML configuration overview lists all root keys, YAML model specification covers the modelling blocks, and YAML advanced blocks covers prior_sensitivity, ai_advisor and diagnostics.

data

The runner loads data before building the model. It supports two CSV layouts.

Combined dataset

data:
  dataset_path: "dataset.csv"

The runner reads the CSV, removes the target column from X, and uses that column as y.

Separate feature and target files

data:
  x_path: "X.csv"
  y_path: "y.csv"

When loading y_path:

  • if the configured target column exists, the runner uses that column
  • otherwise, if the file has exactly one column, the runner uses that column and renames it to the target name

Target column resolution

The runner resolves the target column in this order:

  1. PipelineRunConfig.target_column or CLI --target-column
  2. target.column
  3. "y"

Use the CLI override only when you want to change how the runner reads the CSV. Keep it consistent with target.column in YAML.

fit

fit controls Stage 20 fitting because the fit stage calls:

context.model.fit(X=context.X, y=context.y, progressbar=False)

The runner merges these CLI or PipelineRunConfig overrides onto the YAML fit block when they are provided:

  • draws
  • tune
  • chains
  • cores
  • random_seed

The public YAML schema currently supports these fit keys:

  • draws
  • tune
  • chains
  • cores
  • random_seed
  • target_accept
  • progressbar
  • compute_convergence_checks

Unknown fit keys are rejected when the YAML is loaded.

inference_data

inference_data.path is passed through to the YAML builder. If the file exists, AMMM3 attaches that InferenceData after graph construction. This occurs in Stage 00 for unlabelled models and Stage 10 for named estimators.

Important: the structured runner still executes Stage 20 and fits the model again. inference_data.path does not currently skip fitting.

optimization

Add an optimization block when you want Stage 70 to run. If this block is absent, Stage 70 is marked skipped.

The YAML builder validates this block when the config is loaded. start_date and end_date are always required, and you must provide exactly one of:

  • optimization.budget for the preferred user-facing budget spec
  • optimization.total_budget for the legacy per-period budget input

Unknown top-level optimization keys are rejected.

Preferred example:

optimization:
  start_date: "2024-11-11"
  end_date: "2025-01-27"
  budget:
    mode: relative
    value: 1.10
    basis: reference_window_total

Optional keys read by Stage 70:

KeyDefaultMeaning
budgetNonePreferred user-facing budget spec: absolute or relative
total_budgetNoneLegacy per-period budget input kept for backward compatibility
response_variabletotal_media_contribution_original_scaleOptimisation objective variable
budget_distribution_over_periodNoneTime weights over the optimisation window
budget_boundsDerived or defaultExplicit spend bounds
spend_constraint_lower0.3 when deriving boundsRelative lower bound around scaled reference spend
spend_constraint_upper0.3 when deriving boundsRelative upper bound around scaled reference spend
default_constraintstrueWhether to add the default equality budget constraint
noise_level0.001Noise level for simulated response samples
include_last_observationsfalseWhether posterior predictive sampling includes trailing observed rows
include_carryovertrueWhether simulated response sampling extends the window for carryover

Budget spec modes:

  • budget.mode: absolute budget.value is total spend over the full optimisation horizon.
  • budget.mode: relative budget.value is a multiplier on the chosen basis.
  • budget.basis: reference_window_total AMMM3 resolves the budget against the same reference-window total spend it already uses for current-plan comparison and default bound derivation.

Important budget-unit note

The preferred optimization.budget block uses total horizon spend. Stage 70 converts that to the wrapper’s per-period contract internally before calling PanelBudgetOptimizerWrapper.optimize_budget(...).

The legacy optimization.total_budget field is still supported, but it keeps the old wrapper-facing per-period spend contract.

See Budget Optimisation.

Xarray-like optimisation values in YAML

For panel bounds or time distributions, use the xarray-like mapping shape that Stage 70 expects:

optimization:
  start_date: "2025-02-03"
  end_date: "2025-02-24"
  budget:
    mode: absolute
    value: 100000.0
  budget_distribution_over_period:
    values:
      - [[0.25, 0.25], [0.25, 0.25]]
      - [[0.25, 0.25], [0.25, 0.25]]
      - [[0.25, 0.25], [0.25, 0.25]]
      - [[0.25, 0.25], [0.25, 0.25]]
    dims: ["date", "geo", "channel"]
    coords:
      date: [0, 1, 2, 3]
      geo: ["UK", "FR"]
      channel: ["channel_1", "channel_2"]

The same shape works for budget_bounds, but with an additional "bound" dimension containing "lower" and "upper".

validation

Use the optional validation block when you want Stage 35 blocked holdout scoring. This is the runner’s out-of-sample tail check: AMMM3 refits a clean model on the earlier dates and scores only the final blocked window.

validation:
  enabled: true
  holdout_observations: 8
  include_last_observations: true
  coverage_levels: [0.5, 0.8, 0.94]
  sampler:
    draws: 500
    tune: 500
    chains: 2
    cores: 2
    random_seed: 42

Supported keys:

KeyMeaning
enabledSet to false to skip Stage 35 while keeping the stage in the manifest
holdout_observationsNumber of unique dates to reserve for the blocked holdout window
include_last_observationsKeep lag history for carryover-sensitive holdout scoring
coverage_levelsCoverage levels reported in Stage 35; use the fixed 50, 80, and 94 percent defaults
samplerOptional validation-only sampler overrides for the train-window refit

Stage 35 reports coverage as coverage_50, coverage_80, and coverage_94. Keep those defaults unless the implementation and tests are updated together.

The validation stage builds a clean train-window model for holdout scoring and ignores inference_data.path so the refit does not inherit attached posterior state from the full-sample model.

For a full explanation of why the split is blocked, how to read crps and coverage, and rules of thumb for weekly MMM, see Blocked Holdout Validation.