From 3b4881e90017e5541305a4b721724872918b8fdc Mon Sep 17 00:00:00 2001 From: Santoso Wijaya Date: Tue, 15 Sep 2026 10:23:33 -0700 Subject: [PATCH] Add declarative ROI calibration, holdout, and scaling schemas to `Hyperparameters` proto. PiperOrigin-RevId: 981897759 --- proto/CHANGELOG.md | 19 ++ .../v1/model/meridian/meridian_model.proto | 208 +++++++++++++++++- 2 files changed, 219 insertions(+), 8 deletions(-) diff --git a/proto/CHANGELOG.md b/proto/CHANGELOG.md index 05bce3591..6de331e46 100644 --- a/proto/CHANGELOG.md +++ b/proto/CHANGELOG.md @@ -23,6 +23,25 @@ To release a new version (e.g. from `1.0.0` -> `2.0.0`): ## [Unreleased] +* Add `CalibrationConfig` and `ChannelDateRangeCalibration` protos, together + with the `roi_calibration_config` and `rf_roi_calibration_config` fields in + `Hyperparameters`, expressing ROI calibration windows declaratively as date + intervals over named channels. +* Add `HoldoutConfig` and `GeoDateRangeHoldout` protos, together with the + `holdout_config` field in `Hyperparameters`, expressing holdouts + declaratively as global date intervals, geo x date-interval windows, or a + random draw. `HoldoutConfig.resolved` records the materialized draw for a + random holdout, which a seed alone cannot reproduce across computational + backends, library versions, or input data coordinate orderings. +* Add `population_scaled_controls` and `population_scaled_non_media_channels` + to `Hyperparameters`, selecting population-scaled variables by name. +* Add `non_media_baseline_values_map` to `Hyperparameters`, decoupling + non-media baseline configuration from channel ordering. +* Deprecate the raw tensor and positional `Hyperparameters` fields superseded + by the above: `roi_calibration_period`, `rf_roi_calibration_period`, + `holdout_id`, `holdout_ratio`, `control_population_scaling_id`, + `non_media_population_scaling_id`, and `non_media_baseline_values`. + ## [1.3.1] - 2026-09-01 * Update EDA outcome severity documentation in README_internal.md. diff --git a/proto/mmm/v1/model/meridian/meridian_model.proto b/proto/mmm/v1/model/meridian/meridian_model.proto index e4782c2b9..743d5ca17 100644 --- a/proto/mmm/v1/model/meridian/meridian_model.proto +++ b/proto/mmm/v1/model/meridian/meridian_model.proto @@ -16,6 +16,7 @@ edition = "2023"; package mmm.v1.model.meridian; +import "mmm/v1/common/date_interval.proto"; import "mmm/v1/model/meridian/eda/eda_spec.proto"; import "mmm/v1/model/meridian/review/results.proto"; import "tensorflow/core/framework/tensor.proto"; @@ -377,6 +378,150 @@ message KnotLocations { repeated int32 locations = 1; } +// Specifies ROI calibration date intervals for a subset of channels. +// +// In Marketing Mix Modeling, ROI calibration incorporates experimental ground +// truth (e.g. lift test results) into the model's Bayesian prior. This message +// maps specific treatment channels to the non-contiguous date intervals during +// which experiments occurred. +// +message ChannelDateRangeCalibration { + // Channel names where `date_intervals` apply for ROI calibration. + // These names must match the coordinate labels in `InputData.media_channel` + // (for paid media) or `InputData.rf_channel` (for reach and frequency). + repeated string channels = 1; + + // Non-contiguous date intervals where ROI calibration is applied for the + // specified channels. Each interval contains an inclusive `start_date` and an + // exclusive `end_date`. These date intervals act as declarative selectors + // over the discrete date coordinates present in the input data (e.g. + // `InputData.media_time`). Each interval must encompass one or more observed + // dates in the dataset to be valid. + repeated mmm.v1.common.DateInterval date_intervals = 2; +} + +// Declarative ROI calibration specification supporting both global and +// channel-specific date intervals. +// +message CalibrationConfig { + // Specifies global date intervals applied across all channels of this + // treatment type. + message GlobalCalibrationDateRanges { + // Non-contiguous date intervals applied across all channels. + // These date intervals act as declarative selectors over the discrete date + // coordinates present in the input data (e.g. `InputData.media_time`). Each + // interval must encompass one or more observed dates in the dataset to be + // valid. + repeated mmm.v1.common.DateInterval date_intervals = 1; + } + + // Specifies channel-specific date intervals. + message ChannelDateRanges { + // Calibration date intervals for specific channels or channel groups. + repeated ChannelDateRangeCalibration channel_date_ranges = 1; + } + + oneof spec { + // Global calibration date intervals across all channels. + GlobalCalibrationDateRanges global_date_ranges = 1; + + // Channel-specific calibration windows. + ChannelDateRanges channel_date_ranges = 2; + } +} + +// Specifies holdout date intervals for a subset of geos. +// +// Used in spatio-temporal holdout configurations to hold out specific date +// intervals for specific geographic units. +// +message GeoDateRangeHoldout { + // Geo names where `date_intervals` apply for holdout. + // These names must match the coordinate labels in `InputData.geo`. + repeated string geos = 1; + + // Non-contiguous date intervals where holdout applies for the specified geos. + // Each interval contains an inclusive `start_date` and an exclusive + // `end_date`. These date intervals act as declarative selectors over the + // discrete date coordinates present in the input data (e.g. + // `InputData.time`). Each interval must encompass one or more observed dates + // in the dataset to be valid. + repeated mmm.v1.common.DateInterval date_intervals = 2; +} + +// Declarative holdout specification supporting temporal, spatial, and +// spatio-temporal holdout windows, as well as randomized holdout sampling. +// +// This message is self-contained: `spec` declares the holdout *intent*, and +// `resolved` records the materialized draw when that intent is +// non-deterministic. Together they fully determine the holdout without +// resorting to a raw, input-data-shaped tensor. +// +message HoldoutConfig { + // Specifies global temporal holdouts applied to all geos. + message GlobalHoldoutDateRanges { + // Non-contiguous date intervals held out across all geos. + // These date intervals act as declarative selectors over the discrete date + // coordinates present in the input data (e.g. `InputData.time`). Each + // interval must encompass one or more observed dates in the dataset to be + // valid. + repeated mmm.v1.common.DateInterval date_intervals = 1; + } + + // Specifies spatio-temporal holdouts for specific geos. + message GeoDateRanges { + // Holdout date intervals for specific geos. + repeated GeoDateRangeHoldout geo_date_ranges = 1; + } + + // Specifies randomized holdout sampling with an optional PRNG seed. + message RandomHoldout { + // Proportion of observations randomly selected for holdout evaluation + // (e.g. 0.2 for a 20% random holdout sample). Must be in the range + // (0.0, 1.0). + double ratio = 1; + + // Pseudo-random number generator (PRNG) seed used to sample holdout + // observations deterministically. If unset, an unpredictable seed from OS + // entropy is used during model compilation. + // + // Note that a seed alone does NOT reproduce a draw: the drawn sample also + // depends on the computational backend (TensorFlow vs. JAX), library + // version, and input data coordinate ordering (e.g. geo order in + // `InputData.geo`). The authoritative record of a draw is `resolved`. + int64 seed = 2 [features.field_presence = EXPLICIT]; + } + + // The declarative holdout intent. + oneof spec { + // Global temporal holdouts (applied to all geos). + GlobalHoldoutDateRanges global_date_ranges = 1; + + // Spatio-temporal holdouts (geo x time windows). + GeoDateRanges geo_date_ranges = 2; + + // Randomized holdout sampling, materialized into `resolved` at fit time. + RandomHoldout random_holdout = 3; + } + + // The materialized result of a non-deterministic `spec`, expressed + // declaratively as geo x time windows. + // + // Populated ONLY when `spec` is `random_holdout`. A random draw cannot be + // reproduced from `ratio` and `seed` alone across computational backends, + // library versions, or input data coordinate orderings, so the drawn sample + // is recorded explicitly here. + // + // For the deterministic variants (`global_date_ranges`, `geo_date_ranges`), + // `spec` reproduces the holdout exactly on every read, so this field is + // redundant and MUST be left unset. + // + // When set, this field is authoritative and MUST NOT be re-drawn on + // deserialization; `spec` is retained as provenance describing how the + // holdout was originally requested. + GeoDateRanges resolved = 4; +} + // Hyperparameters for the MMM model. message Hyperparameters { reserved 5; @@ -448,13 +593,17 @@ message Hyperparameters { // This indicates the subset of `time` coordinates in the model for media ROI // calibration. If unset, all time coordinates are used for media ROI // calibration. - tensorflow.TensorProto roi_calibration_period = 9; + // + // Deprecated. Use `roi_calibration_config` instead. + tensorflow.TensorProto roi_calibration_period = 9 [deprecated = true]; // A boolean tensor in the shape `(n_media_times, n_rf_channels)`. // This indicates the subset of `time` coordinates in the model for reach and // frequency ROI calibration. If unset, all time coordinates are used for R&F // ROI calibration. - tensorflow.TensorProto rf_roi_calibration_period = 10; + // + // Deprecated. Use `rf_roi_calibration_config` instead. + tensorflow.TensorProto rf_roi_calibration_period = 10 [deprecated = true]; // A (single-value) integer or a list of integers, indicating the knots used // to estimate time effects. @@ -496,26 +645,37 @@ message Hyperparameters { // This indicates which observations are part of the holdout sample, which // are excluded from the training sample. For more details on the holdout // sample, see: `meridian.model.spec.ModelSpec` - tensorflow.TensorProto holdout_id = 11; + // + // Deprecated. This leaks a raw, input-data-shaped tensor into the schema. + // Use `holdout_config` instead, which expresses the same holdout + // declaratively in terms of geo names and date intervals. + tensorflow.TensorProto holdout_id = 11 [deprecated = true]; // The ratio of holdout data to use for the goodness of fit check. Used as // inputs and this will be implemented in training module right before // feeding into Meridian. - // The holdout only applied to date as of Q1 2025. - double holdout_ratio = 16; + // + // Deprecated. Use `holdout_config.random_holdout` instead, which also + // records the resolved draw in `holdout_config.resolved`. + double holdout_ratio = 16 [deprecated = true]; } // A boolean tensor in the shape `(n_controls,)`. // This indicates the control variables for which the control value will be // scaled by population. // If unset, no control variables are scaled by population. - tensorflow.TensorProto control_population_scaling_id = 12; + // + // Deprecated. Use `population_scaled_controls` instead. + tensorflow.TensorProto control_population_scaling_id = 12 [deprecated = true]; // A boolean tensor in the shape `(n_non_media_channels,)`. // This indicates the non-media treatments channels for which the value will // be scaled by population. // If unset, no non-media treatments channels are scaled by population. - tensorflow.TensorProto non_media_population_scaling_id = 14; + // + // Deprecated. Use `population_scaled_non_media_channels` instead. + tensorflow.TensorProto non_media_population_scaling_id = 14 + [deprecated = true]; // Specifies the adstock decay function for each media, RF, organic media and // organic RF channel. Default is 'geometric'. @@ -549,7 +709,39 @@ message Hyperparameters { // If `None`, the minimum value is used as baseline for each non-media // treatments channel. This attribute is used as the default value for the // corresponding argument to `Analyzer` methods. - repeated NonMediaBaselineValue non_media_baseline_values = 25; + // + // Deprecated. Use `non_media_baseline_values_map` instead. + repeated NonMediaBaselineValue non_media_baseline_values = 25 + [deprecated = true]; + + // Declarative ROI calibration specification for paid media channels. + CalibrationConfig roi_calibration_config = 30; + + // Declarative ROI calibration specification for reach and frequency channels. + CalibrationConfig rf_roi_calibration_config = 31; + + // Declarative holdout specification defining temporal (all geos), + // spatio-temporal (geo-specific), or random holdout configurations. + // + // This is the preferred representation of the holdout sample, superseding the + // deprecated `holdout_id` and `holdout_ratio`. It is self-contained: for + // deterministic specifications it fully determines the holdout, and for + // random specifications it additionally records the resolved draw. + HoldoutConfig holdout_config = 32; + + // Names of control variables selected for population scaling. + // If empty, no control variables are scaled by population. + repeated string population_scaled_controls = 33; + + // Names of non-media treatment channels selected for population scaling. + // If empty, no non-media treatment channels are scaled by population. + repeated string population_scaled_non_media_channels = 34; + + // Named baseline values for non-media treatment channels. + // Maps channel names to their respective baseline configuration (either a + // fixed float value or a baseline function like MIN or MAX). Channels not + // specified in the map default to using MIN. + map non_media_baseline_values_map = 35; } // A named tensor parameter.