google-meridian 1.2.1__py3-none-any.whl → 1.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- google_meridian-1.3.1.dist-info/METADATA +209 -0
- google_meridian-1.3.1.dist-info/RECORD +76 -0
- {google_meridian-1.2.1.dist-info → google_meridian-1.3.1.dist-info}/top_level.txt +1 -0
- meridian/analysis/__init__.py +2 -0
- meridian/analysis/analyzer.py +179 -105
- meridian/analysis/formatter.py +2 -2
- meridian/analysis/optimizer.py +227 -87
- meridian/analysis/review/__init__.py +20 -0
- meridian/analysis/review/checks.py +721 -0
- meridian/analysis/review/configs.py +110 -0
- meridian/analysis/review/constants.py +40 -0
- meridian/analysis/review/results.py +544 -0
- meridian/analysis/review/reviewer.py +186 -0
- meridian/analysis/summarizer.py +21 -34
- meridian/analysis/templates/chips.html.jinja +12 -0
- meridian/analysis/test_utils.py +27 -5
- meridian/analysis/visualizer.py +41 -57
- meridian/backend/__init__.py +457 -118
- meridian/backend/test_utils.py +162 -0
- meridian/constants.py +39 -3
- meridian/model/__init__.py +1 -0
- meridian/model/eda/__init__.py +3 -0
- meridian/model/eda/constants.py +21 -0
- meridian/model/eda/eda_engine.py +1309 -196
- meridian/model/eda/eda_outcome.py +200 -0
- meridian/model/eda/eda_spec.py +84 -0
- meridian/model/eda/meridian_eda.py +220 -0
- meridian/model/knots.py +55 -49
- meridian/model/media.py +10 -8
- meridian/model/model.py +79 -16
- meridian/model/model_test_data.py +53 -0
- meridian/model/posterior_sampler.py +39 -32
- meridian/model/prior_distribution.py +12 -2
- meridian/model/prior_sampler.py +146 -90
- meridian/model/spec.py +7 -8
- meridian/model/transformers.py +11 -3
- meridian/version.py +1 -1
- schema/__init__.py +18 -0
- schema/serde/__init__.py +26 -0
- schema/serde/constants.py +48 -0
- schema/serde/distribution.py +515 -0
- schema/serde/eda_spec.py +192 -0
- schema/serde/function_registry.py +143 -0
- schema/serde/hyperparameters.py +363 -0
- schema/serde/inference_data.py +105 -0
- schema/serde/marketing_data.py +1321 -0
- schema/serde/meridian_serde.py +413 -0
- schema/serde/serde.py +47 -0
- schema/serde/test_data.py +4608 -0
- schema/utils/__init__.py +17 -0
- schema/utils/time_record.py +156 -0
- google_meridian-1.2.1.dist-info/METADATA +0 -409
- google_meridian-1.2.1.dist-info/RECORD +0 -52
- {google_meridian-1.2.1.dist-info → google_meridian-1.3.1.dist-info}/WHEEL +0 -0
- {google_meridian-1.2.1.dist-info → google_meridian-1.3.1.dist-info}/licenses/LICENSE +0 -0
meridian/model/prior_sampler.py
CHANGED
|
@@ -48,15 +48,14 @@ def _get_tau_g(
|
|
|
48
48
|
parameter with zero at position `baseline_geo_idx` and matching
|
|
49
49
|
`tau_g_excl_baseline` elsewhere.
|
|
50
50
|
"""
|
|
51
|
-
|
|
52
|
-
shape = tau_g_excl_baseline.shape[:-1] + [1] if rank != 1 else 1
|
|
51
|
+
shape = tau_g_excl_baseline.shape[:-1] + (1,)
|
|
53
52
|
tau_g = backend.concatenate(
|
|
54
53
|
[
|
|
55
54
|
tau_g_excl_baseline[..., :baseline_geo_idx],
|
|
56
55
|
backend.zeros(shape, dtype=tau_g_excl_baseline.dtype),
|
|
57
56
|
tau_g_excl_baseline[..., baseline_geo_idx:],
|
|
58
57
|
],
|
|
59
|
-
axis
|
|
58
|
+
axis=-1,
|
|
60
59
|
)
|
|
61
60
|
return backend.tfd.Deterministic(tau_g, name="tau_g")
|
|
62
61
|
|
|
@@ -70,15 +69,13 @@ class PriorDistributionSampler:
|
|
|
70
69
|
def _sample_media_priors(
|
|
71
70
|
self,
|
|
72
71
|
n_draws: int,
|
|
73
|
-
|
|
72
|
+
rng_handler: backend.RNGHandler,
|
|
74
73
|
) -> Mapping[str, backend.Tensor]:
|
|
75
74
|
"""Draws samples from the prior distributions of the media variables.
|
|
76
75
|
|
|
77
76
|
Args:
|
|
78
77
|
n_draws: Number of samples drawn from the prior distribution.
|
|
79
|
-
|
|
80
|
-
see [PRNGS and seeds]
|
|
81
|
-
(https://github.com/tensorflow/probability/blob/main/PRNGS.md).
|
|
78
|
+
rng_handler: The backend-agnostic RNG handler managing the seed state.
|
|
82
79
|
|
|
83
80
|
Returns:
|
|
84
81
|
A mapping of media parameter names to a tensor of shape `[n_draws, n_geos,
|
|
@@ -89,31 +86,47 @@ class PriorDistributionSampler:
|
|
|
89
86
|
|
|
90
87
|
prior = mmm.prior_broadcast
|
|
91
88
|
sample_shape = [1, n_draws]
|
|
92
|
-
|
|
89
|
+
|
|
93
90
|
media_vars = {
|
|
94
|
-
constants.ALPHA_M: prior.alpha_m.sample(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
constants.
|
|
91
|
+
constants.ALPHA_M: prior.alpha_m.sample(
|
|
92
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
93
|
+
),
|
|
94
|
+
constants.EC_M: prior.ec_m.sample(
|
|
95
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
96
|
+
),
|
|
97
|
+
constants.ETA_M: prior.eta_m.sample(
|
|
98
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
99
|
+
),
|
|
100
|
+
constants.SLOPE_M: prior.slope_m.sample(
|
|
101
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
102
|
+
),
|
|
98
103
|
}
|
|
99
104
|
beta_gm_dev = backend.tfd.Sample(
|
|
100
105
|
backend.tfd.Normal(0, 1),
|
|
101
106
|
[mmm.n_geos, mmm.n_media_channels],
|
|
102
107
|
name=constants.BETA_GM_DEV,
|
|
103
|
-
).sample(
|
|
108
|
+
).sample(sample_shape=sample_shape, seed=rng_handler.get_next_seed())
|
|
104
109
|
|
|
105
110
|
prior_type = mmm.model_spec.effective_media_prior_type
|
|
106
111
|
if prior_type == constants.TREATMENT_PRIOR_TYPE_COEFFICIENT:
|
|
107
|
-
media_vars[constants.BETA_M] = prior.beta_m.sample(
|
|
112
|
+
media_vars[constants.BETA_M] = prior.beta_m.sample(
|
|
113
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
114
|
+
)
|
|
108
115
|
else:
|
|
109
116
|
if prior_type == constants.TREATMENT_PRIOR_TYPE_ROI:
|
|
110
|
-
treatment_parameter_m = prior.roi_m.sample(
|
|
117
|
+
treatment_parameter_m = prior.roi_m.sample(
|
|
118
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
119
|
+
)
|
|
111
120
|
media_vars[constants.ROI_M] = treatment_parameter_m
|
|
112
121
|
elif prior_type == constants.TREATMENT_PRIOR_TYPE_MROI:
|
|
113
|
-
treatment_parameter_m = prior.mroi_m.sample(
|
|
122
|
+
treatment_parameter_m = prior.mroi_m.sample(
|
|
123
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
124
|
+
)
|
|
114
125
|
media_vars[constants.MROI_M] = treatment_parameter_m
|
|
115
126
|
elif prior_type == constants.TREATMENT_PRIOR_TYPE_CONTRIBUTION:
|
|
116
|
-
treatment_parameter_m = prior.contribution_m.sample(
|
|
127
|
+
treatment_parameter_m = prior.contribution_m.sample(
|
|
128
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
129
|
+
)
|
|
117
130
|
media_vars[constants.CONTRIBUTION_M] = treatment_parameter_m
|
|
118
131
|
else:
|
|
119
132
|
raise ValueError(f"Unsupported prior type: {prior_type}")
|
|
@@ -125,7 +138,7 @@ class PriorDistributionSampler:
|
|
|
125
138
|
alpha=media_vars[constants.ALPHA_M],
|
|
126
139
|
ec=media_vars[constants.EC_M],
|
|
127
140
|
slope=media_vars[constants.SLOPE_M],
|
|
128
|
-
decay_functions=mmm.adstock_decay_spec.media
|
|
141
|
+
decay_functions=mmm.adstock_decay_spec.media,
|
|
129
142
|
)
|
|
130
143
|
linear_predictor_counterfactual_difference = (
|
|
131
144
|
mmm.linear_predictor_counterfactual_difference_media(
|
|
@@ -144,7 +157,7 @@ class PriorDistributionSampler:
|
|
|
144
157
|
)
|
|
145
158
|
media_vars[constants.BETA_M] = backend.tfd.Deterministic(
|
|
146
159
|
beta_m_value, name=constants.BETA_M
|
|
147
|
-
).sample()
|
|
160
|
+
).sample(seed=rng_handler.get_next_seed())
|
|
148
161
|
|
|
149
162
|
beta_eta_combined = (
|
|
150
163
|
media_vars[constants.BETA_M][..., backend.newaxis, :]
|
|
@@ -157,22 +170,20 @@ class PriorDistributionSampler:
|
|
|
157
170
|
)
|
|
158
171
|
media_vars[constants.BETA_GM] = backend.tfd.Deterministic(
|
|
159
172
|
beta_gm_value, name=constants.BETA_GM
|
|
160
|
-
).sample()
|
|
173
|
+
).sample(seed=rng_handler.get_next_seed())
|
|
161
174
|
|
|
162
175
|
return media_vars
|
|
163
176
|
|
|
164
177
|
def _sample_rf_priors(
|
|
165
178
|
self,
|
|
166
179
|
n_draws: int,
|
|
167
|
-
|
|
180
|
+
rng_handler: backend.RNGHandler,
|
|
168
181
|
) -> Mapping[str, backend.Tensor]:
|
|
169
182
|
"""Draws samples from the prior distributions of the RF variables.
|
|
170
183
|
|
|
171
184
|
Args:
|
|
172
185
|
n_draws: Number of samples drawn from the prior distribution.
|
|
173
|
-
|
|
174
|
-
see [PRNGS and seeds]
|
|
175
|
-
(https://github.com/tensorflow/probability/blob/main/PRNGS.md).
|
|
186
|
+
rng_handler: The backend-agnostic RNG handler managing the seed state.
|
|
176
187
|
|
|
177
188
|
Returns:
|
|
178
189
|
A mapping of RF parameter names to a tensor of shape
|
|
@@ -183,31 +194,47 @@ class PriorDistributionSampler:
|
|
|
183
194
|
|
|
184
195
|
prior = mmm.prior_broadcast
|
|
185
196
|
sample_shape = [1, n_draws]
|
|
186
|
-
|
|
197
|
+
|
|
187
198
|
rf_vars = {
|
|
188
|
-
constants.ALPHA_RF: prior.alpha_rf.sample(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
constants.
|
|
199
|
+
constants.ALPHA_RF: prior.alpha_rf.sample(
|
|
200
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
201
|
+
),
|
|
202
|
+
constants.EC_RF: prior.ec_rf.sample(
|
|
203
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
204
|
+
),
|
|
205
|
+
constants.ETA_RF: prior.eta_rf.sample(
|
|
206
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
207
|
+
),
|
|
208
|
+
constants.SLOPE_RF: prior.slope_rf.sample(
|
|
209
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
210
|
+
),
|
|
192
211
|
}
|
|
193
212
|
beta_grf_dev = backend.tfd.Sample(
|
|
194
213
|
backend.tfd.Normal(0, 1),
|
|
195
214
|
[mmm.n_geos, mmm.n_rf_channels],
|
|
196
215
|
name=constants.BETA_GRF_DEV,
|
|
197
|
-
).sample(
|
|
216
|
+
).sample(sample_shape=sample_shape, seed=rng_handler.get_next_seed())
|
|
198
217
|
|
|
199
218
|
prior_type = mmm.model_spec.effective_rf_prior_type
|
|
200
219
|
if prior_type == constants.TREATMENT_PRIOR_TYPE_COEFFICIENT:
|
|
201
|
-
rf_vars[constants.BETA_RF] = prior.beta_rf.sample(
|
|
220
|
+
rf_vars[constants.BETA_RF] = prior.beta_rf.sample(
|
|
221
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
222
|
+
)
|
|
202
223
|
else:
|
|
203
224
|
if prior_type == constants.TREATMENT_PRIOR_TYPE_ROI:
|
|
204
|
-
treatment_parameter_rf = prior.roi_rf.sample(
|
|
225
|
+
treatment_parameter_rf = prior.roi_rf.sample(
|
|
226
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
227
|
+
)
|
|
205
228
|
rf_vars[constants.ROI_RF] = treatment_parameter_rf
|
|
206
229
|
elif prior_type == constants.TREATMENT_PRIOR_TYPE_MROI:
|
|
207
|
-
treatment_parameter_rf = prior.mroi_rf.sample(
|
|
230
|
+
treatment_parameter_rf = prior.mroi_rf.sample(
|
|
231
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
232
|
+
)
|
|
208
233
|
rf_vars[constants.MROI_RF] = treatment_parameter_rf
|
|
209
234
|
elif prior_type == constants.TREATMENT_PRIOR_TYPE_CONTRIBUTION:
|
|
210
|
-
treatment_parameter_rf = prior.contribution_rf.sample(
|
|
235
|
+
treatment_parameter_rf = prior.contribution_rf.sample(
|
|
236
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
237
|
+
)
|
|
211
238
|
rf_vars[constants.CONTRIBUTION_RF] = treatment_parameter_rf
|
|
212
239
|
else:
|
|
213
240
|
raise ValueError(f"Unsupported prior type: {prior_type}")
|
|
@@ -240,7 +267,7 @@ class PriorDistributionSampler:
|
|
|
240
267
|
rf_vars[constants.BETA_RF] = backend.tfd.Deterministic(
|
|
241
268
|
beta_rf_value,
|
|
242
269
|
name=constants.BETA_RF,
|
|
243
|
-
).sample()
|
|
270
|
+
).sample(seed=rng_handler.get_next_seed())
|
|
244
271
|
|
|
245
272
|
beta_eta_combined = (
|
|
246
273
|
rf_vars[constants.BETA_RF][..., backend.newaxis, :]
|
|
@@ -253,22 +280,20 @@ class PriorDistributionSampler:
|
|
|
253
280
|
)
|
|
254
281
|
rf_vars[constants.BETA_GRF] = backend.tfd.Deterministic(
|
|
255
282
|
beta_grf_value, name=constants.BETA_GRF
|
|
256
|
-
).sample()
|
|
283
|
+
).sample(seed=rng_handler.get_next_seed())
|
|
257
284
|
|
|
258
285
|
return rf_vars
|
|
259
286
|
|
|
260
287
|
def _sample_organic_media_priors(
|
|
261
288
|
self,
|
|
262
289
|
n_draws: int,
|
|
263
|
-
|
|
290
|
+
rng_handler: backend.RNGHandler,
|
|
264
291
|
) -> Mapping[str, backend.Tensor]:
|
|
265
292
|
"""Draws samples from the prior distributions of organic media variables.
|
|
266
293
|
|
|
267
294
|
Args:
|
|
268
295
|
n_draws: Number of samples drawn from the prior distribution.
|
|
269
|
-
|
|
270
|
-
see [PRNGS and seeds]
|
|
271
|
-
(https://github.com/tensorflow/probability/blob/main/PRNGS.md).
|
|
296
|
+
rng_handler: The backend-agnostic RNG handler managing the seed state.
|
|
272
297
|
|
|
273
298
|
Returns:
|
|
274
299
|
A mapping of organic media parameter names to a tensor of shape
|
|
@@ -279,27 +304,37 @@ class PriorDistributionSampler:
|
|
|
279
304
|
|
|
280
305
|
prior = mmm.prior_broadcast
|
|
281
306
|
sample_shape = [1, n_draws]
|
|
282
|
-
|
|
307
|
+
|
|
283
308
|
organic_media_vars = {
|
|
284
|
-
constants.ALPHA_OM: prior.alpha_om.sample(
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
constants.
|
|
309
|
+
constants.ALPHA_OM: prior.alpha_om.sample(
|
|
310
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
311
|
+
),
|
|
312
|
+
constants.EC_OM: prior.ec_om.sample(
|
|
313
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
314
|
+
),
|
|
315
|
+
constants.ETA_OM: prior.eta_om.sample(
|
|
316
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
317
|
+
),
|
|
318
|
+
constants.SLOPE_OM: prior.slope_om.sample(
|
|
319
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
320
|
+
),
|
|
288
321
|
}
|
|
289
322
|
beta_gom_dev = backend.tfd.Sample(
|
|
290
323
|
backend.tfd.Normal(0, 1),
|
|
291
324
|
[mmm.n_geos, mmm.n_organic_media_channels],
|
|
292
325
|
name=constants.BETA_GOM_DEV,
|
|
293
|
-
).sample(
|
|
326
|
+
).sample(sample_shape=sample_shape, seed=rng_handler.get_next_seed())
|
|
294
327
|
|
|
295
328
|
prior_type = mmm.model_spec.organic_media_prior_type
|
|
296
329
|
if prior_type == constants.TREATMENT_PRIOR_TYPE_COEFFICIENT:
|
|
297
330
|
organic_media_vars[constants.BETA_OM] = prior.beta_om.sample(
|
|
298
|
-
|
|
331
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
299
332
|
)
|
|
300
333
|
elif prior_type == constants.TREATMENT_PRIOR_TYPE_CONTRIBUTION:
|
|
301
334
|
organic_media_vars[constants.CONTRIBUTION_OM] = (
|
|
302
|
-
prior.contribution_om.sample(
|
|
335
|
+
prior.contribution_om.sample(
|
|
336
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
337
|
+
)
|
|
303
338
|
)
|
|
304
339
|
incremental_outcome_om = (
|
|
305
340
|
organic_media_vars[constants.CONTRIBUTION_OM] * mmm.total_outcome
|
|
@@ -321,7 +356,7 @@ class PriorDistributionSampler:
|
|
|
321
356
|
organic_media_vars[constants.BETA_OM] = backend.tfd.Deterministic(
|
|
322
357
|
beta_om_value,
|
|
323
358
|
name=constants.BETA_OM,
|
|
324
|
-
).sample()
|
|
359
|
+
).sample(seed=rng_handler.get_next_seed())
|
|
325
360
|
else:
|
|
326
361
|
raise ValueError(f"Unsupported prior type: {prior_type}")
|
|
327
362
|
|
|
@@ -337,22 +372,20 @@ class PriorDistributionSampler:
|
|
|
337
372
|
)
|
|
338
373
|
organic_media_vars[constants.BETA_GOM] = backend.tfd.Deterministic(
|
|
339
374
|
beta_gom_value, name=constants.BETA_GOM
|
|
340
|
-
).sample()
|
|
375
|
+
).sample(seed=rng_handler.get_next_seed())
|
|
341
376
|
|
|
342
377
|
return organic_media_vars
|
|
343
378
|
|
|
344
379
|
def _sample_organic_rf_priors(
|
|
345
380
|
self,
|
|
346
381
|
n_draws: int,
|
|
347
|
-
|
|
382
|
+
rng_handler: backend.RNGHandler,
|
|
348
383
|
) -> Mapping[str, backend.Tensor]:
|
|
349
384
|
"""Draws samples from the prior distributions of the organic RF variables.
|
|
350
385
|
|
|
351
386
|
Args:
|
|
352
387
|
n_draws: Number of samples drawn from the prior distribution.
|
|
353
|
-
|
|
354
|
-
see [PRNGS and seeds]
|
|
355
|
-
(https://github.com/tensorflow/probability/blob/main/PRNGS.md).
|
|
388
|
+
rng_handler: The backend-agnostic RNG handler managing the seed state.
|
|
356
389
|
|
|
357
390
|
Returns:
|
|
358
391
|
A mapping of organic RF parameter names to a tensor of shape
|
|
@@ -363,27 +396,37 @@ class PriorDistributionSampler:
|
|
|
363
396
|
|
|
364
397
|
prior = mmm.prior_broadcast
|
|
365
398
|
sample_shape = [1, n_draws]
|
|
366
|
-
|
|
399
|
+
|
|
367
400
|
organic_rf_vars = {
|
|
368
|
-
constants.ALPHA_ORF: prior.alpha_orf.sample(
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
constants.
|
|
401
|
+
constants.ALPHA_ORF: prior.alpha_orf.sample(
|
|
402
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
403
|
+
),
|
|
404
|
+
constants.EC_ORF: prior.ec_orf.sample(
|
|
405
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
406
|
+
),
|
|
407
|
+
constants.ETA_ORF: prior.eta_orf.sample(
|
|
408
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
409
|
+
),
|
|
410
|
+
constants.SLOPE_ORF: prior.slope_orf.sample(
|
|
411
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
412
|
+
),
|
|
372
413
|
}
|
|
373
414
|
beta_gorf_dev = backend.tfd.Sample(
|
|
374
415
|
backend.tfd.Normal(0, 1),
|
|
375
416
|
[mmm.n_geos, mmm.n_organic_rf_channels],
|
|
376
417
|
name=constants.BETA_GORF_DEV,
|
|
377
|
-
).sample(
|
|
418
|
+
).sample(sample_shape=sample_shape, seed=rng_handler.get_next_seed())
|
|
378
419
|
|
|
379
420
|
prior_type = mmm.model_spec.organic_media_prior_type
|
|
380
421
|
if prior_type == constants.TREATMENT_PRIOR_TYPE_COEFFICIENT:
|
|
381
422
|
organic_rf_vars[constants.BETA_ORF] = prior.beta_orf.sample(
|
|
382
|
-
|
|
423
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
383
424
|
)
|
|
384
425
|
elif prior_type == constants.TREATMENT_PRIOR_TYPE_CONTRIBUTION:
|
|
385
426
|
organic_rf_vars[constants.CONTRIBUTION_ORF] = (
|
|
386
|
-
prior.contribution_orf.sample(
|
|
427
|
+
prior.contribution_orf.sample(
|
|
428
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
429
|
+
)
|
|
387
430
|
)
|
|
388
431
|
incremental_outcome_orf = (
|
|
389
432
|
organic_rf_vars[constants.CONTRIBUTION_ORF] * mmm.total_outcome
|
|
@@ -406,7 +449,7 @@ class PriorDistributionSampler:
|
|
|
406
449
|
organic_rf_vars[constants.BETA_ORF] = backend.tfd.Deterministic(
|
|
407
450
|
beta_orf_value,
|
|
408
451
|
name=constants.BETA_ORF,
|
|
409
|
-
).sample()
|
|
452
|
+
).sample(seed=rng_handler.get_next_seed())
|
|
410
453
|
else:
|
|
411
454
|
raise ValueError(f"Unsupported prior type: {prior_type}")
|
|
412
455
|
|
|
@@ -422,22 +465,20 @@ class PriorDistributionSampler:
|
|
|
422
465
|
)
|
|
423
466
|
organic_rf_vars[constants.BETA_GORF] = backend.tfd.Deterministic(
|
|
424
467
|
beta_gorf_value, name=constants.BETA_GORF
|
|
425
|
-
).sample()
|
|
468
|
+
).sample(seed=rng_handler.get_next_seed())
|
|
426
469
|
|
|
427
470
|
return organic_rf_vars
|
|
428
471
|
|
|
429
472
|
def _sample_non_media_treatments_priors(
|
|
430
473
|
self,
|
|
431
474
|
n_draws: int,
|
|
432
|
-
|
|
475
|
+
rng_handler: backend.RNGHandler,
|
|
433
476
|
) -> Mapping[str, backend.Tensor]:
|
|
434
477
|
"""Draws from the prior distributions of the non-media treatment variables.
|
|
435
478
|
|
|
436
479
|
Args:
|
|
437
480
|
n_draws: Number of samples drawn from the prior distribution.
|
|
438
|
-
|
|
439
|
-
see [PRNGS and seeds]
|
|
440
|
-
(https://github.com/tensorflow/probability/blob/main/PRNGS.md).
|
|
481
|
+
rng_handler: The backend-agnostic RNG handler managing the seed state.
|
|
441
482
|
|
|
442
483
|
Returns:
|
|
443
484
|
A mapping of non-media treatment parameter names to a tensor of shape
|
|
@@ -448,23 +489,27 @@ class PriorDistributionSampler:
|
|
|
448
489
|
|
|
449
490
|
prior = mmm.prior_broadcast
|
|
450
491
|
sample_shape = [1, n_draws]
|
|
451
|
-
|
|
492
|
+
|
|
452
493
|
non_media_treatments_vars = {
|
|
453
|
-
constants.XI_N: prior.xi_n.sample(
|
|
494
|
+
constants.XI_N: prior.xi_n.sample(
|
|
495
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
496
|
+
),
|
|
454
497
|
}
|
|
455
498
|
gamma_gn_dev = backend.tfd.Sample(
|
|
456
499
|
backend.tfd.Normal(0, 1),
|
|
457
500
|
[mmm.n_geos, mmm.n_non_media_channels],
|
|
458
501
|
name=constants.GAMMA_GN_DEV,
|
|
459
|
-
).sample(
|
|
502
|
+
).sample(sample_shape=sample_shape, seed=rng_handler.get_next_seed())
|
|
460
503
|
prior_type = mmm.model_spec.non_media_treatments_prior_type
|
|
461
504
|
if prior_type == constants.TREATMENT_PRIOR_TYPE_COEFFICIENT:
|
|
462
505
|
non_media_treatments_vars[constants.GAMMA_N] = prior.gamma_n.sample(
|
|
463
|
-
|
|
506
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
464
507
|
)
|
|
465
508
|
elif prior_type == constants.TREATMENT_PRIOR_TYPE_CONTRIBUTION:
|
|
466
509
|
non_media_treatments_vars[constants.CONTRIBUTION_N] = (
|
|
467
|
-
prior.contribution_n.sample(
|
|
510
|
+
prior.contribution_n.sample(
|
|
511
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
512
|
+
)
|
|
468
513
|
)
|
|
469
514
|
incremental_outcome_n = (
|
|
470
515
|
non_media_treatments_vars[constants.CONTRIBUTION_N]
|
|
@@ -485,7 +530,7 @@ class PriorDistributionSampler:
|
|
|
485
530
|
)
|
|
486
531
|
non_media_treatments_vars[constants.GAMMA_N] = backend.tfd.Deterministic(
|
|
487
532
|
gamma_n_value, name=constants.GAMMA_N
|
|
488
|
-
).sample()
|
|
533
|
+
).sample(seed=rng_handler.get_next_seed())
|
|
489
534
|
else:
|
|
490
535
|
raise ValueError(f"Unsupported prior type: {prior_type}")
|
|
491
536
|
non_media_treatments_vars[constants.GAMMA_GN] = backend.tfd.Deterministic(
|
|
@@ -493,7 +538,7 @@ class PriorDistributionSampler:
|
|
|
493
538
|
+ non_media_treatments_vars[constants.XI_N][..., backend.newaxis, :]
|
|
494
539
|
* gamma_gn_dev,
|
|
495
540
|
name=constants.GAMMA_GN,
|
|
496
|
-
).sample()
|
|
541
|
+
).sample(seed=rng_handler.get_next_seed())
|
|
497
542
|
return non_media_treatments_vars
|
|
498
543
|
|
|
499
544
|
def _sample_prior(
|
|
@@ -509,21 +554,28 @@ class PriorDistributionSampler:
|
|
|
509
554
|
if seed is not None:
|
|
510
555
|
backend.set_random_seed(seed)
|
|
511
556
|
|
|
557
|
+
rng_handler = backend.RNGHandler(seed)
|
|
558
|
+
|
|
512
559
|
prior = mmm.prior_broadcast
|
|
513
560
|
# `sample_shape` is prepended to the shape of each BatchBroadcast in `prior`
|
|
514
561
|
# when it is sampled.
|
|
515
562
|
sample_shape = [1, n_draws]
|
|
516
|
-
sample_kwargs = {constants.SAMPLE_SHAPE: sample_shape, constants.SEED: seed}
|
|
517
563
|
|
|
518
|
-
tau_g_excl_baseline = prior.tau_g_excl_baseline.sample(
|
|
564
|
+
tau_g_excl_baseline = prior.tau_g_excl_baseline.sample(
|
|
565
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
566
|
+
)
|
|
519
567
|
base_vars = {
|
|
520
|
-
constants.KNOT_VALUES: prior.knot_values.sample(
|
|
521
|
-
|
|
568
|
+
constants.KNOT_VALUES: prior.knot_values.sample(
|
|
569
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
570
|
+
),
|
|
571
|
+
constants.SIGMA: prior.sigma.sample(
|
|
572
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
573
|
+
),
|
|
522
574
|
constants.TAU_G: (
|
|
523
575
|
_get_tau_g(
|
|
524
576
|
tau_g_excl_baseline=tau_g_excl_baseline,
|
|
525
577
|
baseline_geo_idx=mmm.baseline_geo_idx,
|
|
526
|
-
).sample()
|
|
578
|
+
).sample(seed=rng_handler.get_next_seed())
|
|
527
579
|
),
|
|
528
580
|
}
|
|
529
581
|
|
|
@@ -534,49 +586,53 @@ class PriorDistributionSampler:
|
|
|
534
586
|
backend.to_tensor(mmm.knot_info.weights),
|
|
535
587
|
),
|
|
536
588
|
name=constants.MU_T,
|
|
537
|
-
).sample()
|
|
589
|
+
).sample(seed=rng_handler.get_next_seed())
|
|
538
590
|
|
|
539
591
|
# Omit gamma_c, xi_c, and gamma_gc parameters from sampled distributions if
|
|
540
592
|
# there are no control variables in the model.
|
|
541
593
|
if mmm.n_controls:
|
|
542
594
|
base_vars |= {
|
|
543
|
-
constants.GAMMA_C: prior.gamma_c.sample(
|
|
544
|
-
|
|
595
|
+
constants.GAMMA_C: prior.gamma_c.sample(
|
|
596
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
597
|
+
),
|
|
598
|
+
constants.XI_C: prior.xi_c.sample(
|
|
599
|
+
sample_shape=sample_shape, seed=rng_handler.get_next_seed()
|
|
600
|
+
),
|
|
545
601
|
}
|
|
546
602
|
|
|
547
603
|
gamma_gc_dev = backend.tfd.Sample(
|
|
548
604
|
backend.tfd.Normal(0, 1),
|
|
549
605
|
[mmm.n_geos, mmm.n_controls],
|
|
550
606
|
name=constants.GAMMA_GC_DEV,
|
|
551
|
-
).sample(
|
|
607
|
+
).sample(sample_shape=sample_shape, seed=rng_handler.get_next_seed())
|
|
552
608
|
base_vars[constants.GAMMA_GC] = backend.tfd.Deterministic(
|
|
553
609
|
base_vars[constants.GAMMA_C][..., backend.newaxis, :]
|
|
554
610
|
+ base_vars[constants.XI_C][..., backend.newaxis, :] * gamma_gc_dev,
|
|
555
611
|
name=constants.GAMMA_GC,
|
|
556
|
-
).sample()
|
|
612
|
+
).sample(seed=rng_handler.get_next_seed())
|
|
557
613
|
|
|
558
614
|
media_vars = (
|
|
559
|
-
self._sample_media_priors(n_draws,
|
|
615
|
+
self._sample_media_priors(n_draws, rng_handler)
|
|
560
616
|
if mmm.media_tensors.media is not None
|
|
561
617
|
else {}
|
|
562
618
|
)
|
|
563
619
|
rf_vars = (
|
|
564
|
-
self._sample_rf_priors(n_draws,
|
|
620
|
+
self._sample_rf_priors(n_draws, rng_handler)
|
|
565
621
|
if mmm.rf_tensors.reach is not None
|
|
566
622
|
else {}
|
|
567
623
|
)
|
|
568
624
|
organic_media_vars = (
|
|
569
|
-
self._sample_organic_media_priors(n_draws,
|
|
625
|
+
self._sample_organic_media_priors(n_draws, rng_handler)
|
|
570
626
|
if mmm.organic_media_tensors.organic_media is not None
|
|
571
627
|
else {}
|
|
572
628
|
)
|
|
573
629
|
organic_rf_vars = (
|
|
574
|
-
self._sample_organic_rf_priors(n_draws,
|
|
630
|
+
self._sample_organic_rf_priors(n_draws, rng_handler)
|
|
575
631
|
if mmm.organic_rf_tensors.organic_reach is not None
|
|
576
632
|
else {}
|
|
577
633
|
)
|
|
578
634
|
non_media_treatments_vars = (
|
|
579
|
-
self._sample_non_media_treatments_priors(n_draws,
|
|
635
|
+
self._sample_non_media_treatments_priors(n_draws, rng_handler)
|
|
580
636
|
if mmm.non_media_treatments_normalized is not None
|
|
581
637
|
else {}
|
|
582
638
|
)
|
meridian/model/spec.py
CHANGED
|
@@ -203,14 +203,13 @@ class ModelSpec:
|
|
|
203
203
|
non-media value will be scaled by population. If `None`, then no non-media
|
|
204
204
|
variables are scaled by population. Default: `None`.
|
|
205
205
|
adstock_decay_spec: A string or mapping specifying the adstock decay
|
|
206
|
-
function for each media, RF, organic media and organic RF channel.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
Default: `'geometric'`.
|
|
206
|
+
function for each media, RF, organic media and organic RF channel. If a
|
|
207
|
+
string, must be either `'geometric'` or `'binomial'`, specifying that
|
|
208
|
+
decay function for all channels. If a mapping, keys should be channel
|
|
209
|
+
names and values should be `'geometric'` or `'binomial'`, with each
|
|
210
|
+
key-value pair denoting the adstock decay function to use for that
|
|
211
|
+
channel. Channels that are not specified in the mapping default to using
|
|
212
|
+
'geometric'. Default: `'geometric'`.
|
|
214
213
|
enable_aks: A boolean indicating whether to use the Automatic Knot Selection
|
|
215
214
|
algorithm to select an optimal number of knots for running the model
|
|
216
215
|
instead of the default 1 for national models and n_times for geo models.
|
meridian/model/transformers.py
CHANGED
|
@@ -196,11 +196,19 @@ class KpiTransformer(TensorTransformer):
|
|
|
196
196
|
each geo, used to to compute the population scale factors.
|
|
197
197
|
"""
|
|
198
198
|
self._population = population
|
|
199
|
-
|
|
199
|
+
self._population_scaled_kpi = backend.divide_no_nan(
|
|
200
200
|
kpi, self._population[:, backend.newaxis]
|
|
201
201
|
)
|
|
202
|
-
self._population_scaled_mean = backend.reduce_mean(
|
|
203
|
-
|
|
202
|
+
self._population_scaled_mean = backend.reduce_mean(
|
|
203
|
+
self._population_scaled_kpi
|
|
204
|
+
)
|
|
205
|
+
self._population_scaled_stdev = backend.reduce_std(
|
|
206
|
+
self._population_scaled_kpi
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
@property
|
|
210
|
+
def population_scaled_kpi(self):
|
|
211
|
+
return self._population_scaled_kpi
|
|
204
212
|
|
|
205
213
|
@property
|
|
206
214
|
def population_scaled_mean(self):
|
meridian/version.py
CHANGED
schema/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Copyright 2025 The Meridian Authors.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Module containing MMM schema library."""
|
|
16
|
+
|
|
17
|
+
from schema import serde
|
|
18
|
+
from schema import utils
|
schema/serde/__init__.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Copyright 2025 The Meridian Authors.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""A serialization and deserialization library for Meridian models.
|
|
16
|
+
|
|
17
|
+
For entry points API, see `meridian_serde` module docs.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from schema.serde import constants
|
|
21
|
+
from schema.serde import distribution
|
|
22
|
+
from schema.serde import eda_spec
|
|
23
|
+
from schema.serde import hyperparameters
|
|
24
|
+
from schema.serde import inference_data
|
|
25
|
+
from schema.serde import meridian_serde
|
|
26
|
+
from schema.serde import serde
|