方案规划和未来预算优化

本部分介绍了如何使用 Meridian 进行方案规划,包括如何将数据和关于未来的假设纳入预算优化方案。

在进行未来预算优化时,Meridian 会根据一组关于未来的假设预测增量结果。Meridian 不会预测未来的结果值本身,只会预测增量部分。如需了解详情,请参阅 Meridian 为什么不预测结果

什么是方案规划?

方案规划是一种 MMM 分析,可以纳入关于未来的假设。对于投资回报率 (ROI)、响应曲线和预算优化等建模后分析,Meridian 会使用历史数据做出默认假设。有时,这些假设对于未来规划来说是合理的,但并非总是如此。您可以根据需要将新数据纳入分析中,以修改假设。

以下是未来策略可能与历史策略不同的一些方面。

  • 每个媒体单位的费用:渠道每个媒体单位的费用可能已发生变化或预计会发生变化。(请参阅代码编写示例 1示例 2
  • 每个 KPI 单位的收入:每个 KPI 的收入(例如,单位价格或生命周期价值)可能已发生变化或预计会发生变化。(请参阅代码编写示例 3
  • 排期模式:您的历史排期模式和未来排期模式可能不一致。例如,您可能在模型训练窗口期间引入了新的媒体渠道。新渠道的历史排期模式可能包含零值,或者显示出预计不会持续的“逐步增加”趋势。(请参阅代码编写示例 4

建模后分析指标

方案规划会影响指标定义,但不会影响形参估计。为了说明这些概念,我们来看一个假设性案例,在该案例中,我们使用普通最小二乘法来估计某种药物的治疗效果,并假设药物剂量(以毫克单位,X)对结果 (Y) 具有线性影响,如以下模型所示:

\[ Y = \alpha + X \beta + \epsilon \ . \]

治疗效果取决于剂量系数 $\beta$,该系数是根据观测到的数据集估计的形参。治疗效果可以有多种可能的指标定义。例如,您可以将药物的效果定义为 10 毫克剂量 ($10 \beta$) 或 15 毫克剂量 ($15 \beta$) 所导致的预期结果变化。您可以使用相同的模型和系数估计值 $\hat{\beta}$ 对这两种定义下的治疗效果进行估计。

在 Meridian 中,建模后分析指标的定义取决于某些数据特征。例如,ROI 取决于指定的时间范围、地理位置集、排期模式(媒体单位在各个地理位置和时间段的相对分布)、每个渠道的媒体单位总数、每个媒体单位的费用和每个 KPI 的收入。其他建模后分析指标包括预期结果、增量结果、边际 ROI、CPIK、响应曲线和最佳预算分配比例。默认情况下,这些指标是使用传递给 Meridianinput_data 定义的;不过,您可以提供 new_data 来指定替代指标定义。表 1 列出了影响每个指标定义的数据属性。

表 1:影响每种方法指标定义的数据属性列表。

时间范围 地理位置集 排期模式 每个渠道的媒体单位总数 每个媒体单位的费用 每个 KPI 的收入(如适用) 控制值
expected_outcome X X X X X X
incremental_outcome X X X X X
roimarginal_roicpik X X X X X X
response_curves X X X X X
BudgetOptimizer.optimize X X X X X

‡ 预算优化功能会使用每个渠道的媒体单位总数(结合固定的排期模式和每个媒体单位的费用假设)来分配默认的总预算(仅限固定预算优化)和渠道级预算限制。如果使用 BudgetOptimizer.optimizebudgetpct_of_spend 实参替换这些设置,则每个渠道的媒体单位总数不会影响优化。

每项建模后分析指标的估计值取决于模型形参的后验分布。后验分布基于传递给 Meridian 对象构造函数的 input_data(不包括 ModelSpec.holdout_id 中指定的地理位置和时间段的 KPI)。当调用 sample_posterior 时,系统会估计后验分布,并且此估计值会用于所有建模后分析。

new_data 实参

表 1 中每个数据属性的默认值都派生自传递给 Meridianinput_data。在建模后分析中,用户可以使用大多数方法中提供的 new_data 实参替换输入数据。每个方法都仅使用一部分 new_data 属性。表 2 列出了每个方法使用的 new_data 属性。

表 2:每个方法使用的 new_data 属性的列表。

mediareachfrequency revenue_per_kpi media_spendrf_spend organic_mediaorganic_reachorganic_frequencynon_media_treatments controls time 时间维度是否必须与 input_data 一致
expected_outcome X X X X
incremental_outcome X X X
roicpikmarginal_roi X X X
response_curves X X X X
BudgetOptimizer.optimize X X X X

数据属性从 new_data 派生的方式与从输入数据派生一样。例如,对于每个地理位置和时间段,每个媒体单位的费用的计算方式为,支出除以媒体单位数。

如果 new_data 的时间范围与输入数据的时间范围一致,则您无需指定所调用方法使用的所有 new_data 属性。您可以提供任意一部分属性,其余属性将从输入数据中获取。

不过,如果 new_data 的时间范围与输入数据的时间范围不同,则您必须传递所调用方法使用的所有 new_data 属性。所有 new_data 属性都必须具有相同的时间维度。只有 expected_outcome 方法要求时间维度必须与输入数据一致。

对于 response_curvesoptimize 方法,如果时间维度与输入数据不一致,您还需要向 new_data.time 传递日期标签。这些日期标签不影响计算,但会在某些可视化图表中用作轴标签。

用于创建 new_data 来优化预算的辅助方法

渠道级支出既是 BudgetOptimizer.optimize 的输出,也是其 new_data 输入,这可能看起来有悖常理。输出是最佳支出分配比例,而作为输入的支出与媒体单位数输入一起,用于为每个地理位置和时间段设置假设的每个媒体单位的费用。输入的支出还用于为固定预算优化方案设置总预算,以及设置渠道级预算限制,但您可以使用 budgetpct_of_spend 实参替换这些设置。

用户如果更希望直接输入每个媒体单位的费用数据,可以使用 optimizer.create_optimization_tensors 方法。此方法会创建一个 new_data 对象,专门用于通过以下输入数据选项传递给 BudgetOptimizer.optimize 方法。

非 R&F 渠道:

  1. 媒体单位数和每个媒体单位的费用
  2. 支出和每个媒体单位的费用

R&F 渠道(当 use_optimal_frequency=True 时):

  1. 展示次数和每次展示费用
  2. 支出和每次展示费用

R&F 渠道(当 use_optimal_frequency=False 时):

  1. 展示次数、频次和每次展示费用
  2. 支出、频次和每次展示费用

仅作说明之用的示例

本部分旨在说明如何为最重要的建模后分析函数(Analyzer.incremental_outcomeAnalyzer.roiAnalyzer.response_curvesBudgetOptimizer.optimize)执行计算。

具体而言,这些示例展示了如何使用 input_datanew_data 进行每个方法的计算。在这些示例中,input_data 包含媒体单位数据的“建模前窗口”,而 new_data 不包含。“建模前窗口”包含第一个“建模窗口”时间段之前的媒体单位数据,这让模型能够正确考虑这些单位的滞后效应。如果 new_data 涵盖的时间范围与 input_data 不同(如这些示例所示),则媒体单位数据的时间段数必须与所有其他新数据相同。

除了 new_data 之外,这些方法都有一个 selected_times 实参。这些实参用于自定义输出指标的定义,而不是形参估计(如需了解详情,请参阅什么是方案规划?

Analyzer.incremental_outcome 方法还具有 media_selected_times 实参,可用于进一步自定义增量结果定义。此实参使 Analyzer.incremental_outcome 比其他方法更为灵活。其他方法没有此实参,因为它们的计算涉及将增量结果与一些相关联的费用进行匹配,而当 selected_timesmedia_selected_times 均可自定义时,这种匹配可能变得不明确。不过,您可以手动匹配 incremental_outcome 输出与费用数据,以创建自定义的 ROI 定义,等等。

在地理位置级模型中,每个地理位置都有自己的增量结果、ROI 和响应曲线。您可以将每个示例视为代表一个地理位置。国家级结果是通过汇总各个地理位置的结果获得的。这些方法都有一个 selected_geos 实参,用户可以通过该实参指定要纳入指标定义中的一部分地理位置。

增量结果

对于每个媒体渠道,incremental_outcome 方法会比较两种反事实情景下的预期结果。在一种情景中,媒体单位数设置为历史值。在另一种情景中,部分或全部时间段的媒体单位数设置为零。

media_selected_times 实参用于确定媒体单位数设置为零的时间段。

selected_times 实参用于确定衡量增量结果的时间范围。

使用 input_data

默认的增量结果定义将 media_selected_times 设置为所有时间段,包括“建模窗口”和“建模前窗口”。(input_data 媒体单位可选择性地包含“建模前窗口”,以便模型适当考虑这些单位的滞后效应。)

默认 selected_times 是“建模窗口”中的所有时间段,这意味着增量结果是在“建模窗口”中的所有时间段内汇总的。

input_data 可以包含“建模前窗口”期间的媒体单位,以考虑滞后效应。“建模前窗口”不包含除媒体单位外的任何数据,因此图示中的单元格为空白。

scenario-planning1

使用 selected_timesmedia_selected_times 经过修改的 input_data

若要了解 new_dataincremental_outcome 和其他方法中的用法,请务必了解 selected_timesmedia_selected_times 实参。

在此示例中,media_selected_times 设置为单个时间段(第 0 周)。同时,selected_times 设置为第 0 周到第 3 周。此示例展示了一个方案,其中 max_lagModelSpec 中设置为 3,因而第 4 周及以后的增量结果必定为零。因此,media_selected_timesselected_times 的这种组合可全面反映第 0 周媒体单位的长期影响。

scenario-planning2

使用具有新时间窗口的 new_data

如果传递的 new_data 的时间段数与 input_data 的时间段数不同,则不存在“建模前窗口”。假设在 new_data 时间窗口之前的所有时间段内,媒体单位数为零。

默认的增量结果定义将 media_selected_timesselected_times 都设置为 new_data 时间窗口中的所有时间段。

scenario-planning3

ROI

对于每个媒体渠道,roi 方法会将 selected_times 期间产生的增量结果除以 selected_times 期间的支出。roi 方法没有 media_selected_times 实参。增量结果比较的是以下两种反事实情景:一种是将媒体单位数设置为历史值,另一种是将所有时间段内的媒体单位数都设置为零。

使用 input_data

默认情况下,selected_times 设置为整个“建模窗口”。反事实情景将“建模窗口”和“建模前窗口”中所有时间段的媒体单位数都设置为零。

scenario-planning4

使用具有新时间窗口的 new_data

如果传递的 new_data 的时间段数与 input_data 的时间段数不同,则不存在“建模前窗口”。假设在 new_data 时间窗口之前的所有时间段内,媒体单位数为零。

scenario-planning5

响应曲线和预算优化

response_curves 方法与 roi 类似,因为增量结果和支出都在 selected_times(默认设置为整个“建模窗口”)内进行汇总。响应曲线 x 轴上的每个点均代表 selected_times 范围内历史支出的某个百分比。此方法通过按相同比例缩放历史媒体单位来计算相应的增量结果(y 轴)。缩放比例会应用于“建模窗口”和“建模前窗口”的媒体单位。

预算优化基于响应曲线,因此同一图示适用于响应曲线和预算优化。请注意,BudgetOptimizer.optimize 已弃用 selected_times 实参,现在改用 start_dateend_date 实参。

使用 input_data

默认情况下,selected_times 设置为整个“建模窗口”。在该反事实情景中,“建模窗口”和“建模前窗口”中所有时间段内的媒体单位数都是按比例缩放的。

在此图示中,响应曲线是根据 input_data 预算的 50% 计算得出的。

scenario-planning6

使用具有新时间窗口的 new_data

如果传递的 new_data 的时间段数与 input_data 的时间段数不同,则不存在“建模前窗口”。假设在 new_data 时间窗口之前的所有时间段内,媒体单位数为零。

在此图示中,响应曲线是根据 new_data 预算的 50% 计算得出的。

scenario-planning7

预算优化代码示例

以下示例展示了 new_data 在预算优化和未来方案规划方面的强大之处。为便于说明,每个示例都侧重于输入数据的一个关键属性,该属性可以使用 new_data 进行修改。不过,所有这些假设(以及更多假设)都可以合并为一个优化方案。

假设您想优化未来一个季度的预算,而该季度的排期模式、每个媒体单位的费用和每个 KPI 的收入预计与上个季度的输入数据类似。您可以使用上个季度的输入数据来表示未来方案,并调整预计会发生变化的方面。这一点在每个示例中都有所体现。

在更复杂的未来方案中,最好完全用新数据替换输入数据。这可以通过用 Python 构建数组来完成,也可以通过从 CSV 文件加载数据来完成。

在这些示例中,有三个媒体渠道。KPI 是销量,并且提供了 revenue_per_kpi。为便于说明,每个示例都运行基于 2024 年第四季度的优化方案。每个示例都会修改方案的一个关键元素,并使用 new_data 实参来纳入相应更改。

每个示例中的代码都假定已将 Meridian 模型初始化为 mmm,已调用 sample_posterior 方法,并且已将 BudgetOptimizer 初始化为 opt

mmm = model.Meridian(...)
mmm.sample_posterior(...)
opt = optimizer.BudgetOptimizer(mmm)

示例 1 - 新的每个媒体单位的费用(单个渠道)

假设第一个渠道的每个媒体单位的费用预计在不久的将来会翻倍,因此您希望在优化中将此渠道的假设每个媒体单位的费用翻倍。为此,您可以创建一个与输入数据一致的支出数组,但将第一个渠道的支出翻倍。该数组会传递给 DataTensors 构造函数,后者又会传递给优化的 new_data 实参。

修改 media_spend 还会影响固定预算优化的默认总预算以及默认支出限制。可以使用 budgetpct_of_spend 优化实参替换这些默认设置。请务必了解这些实参,并根据需要进行自定义。

# Create `new_data` from `input_data`, but double the spend for channel 0.
new_spend = mmm.input_data.media_spend
new_spend[:, :, 0] *= 2
new_data = analyzer.DataTensors(media_spend=new_spend)
# Run fixed budget optimization on the last quarter of 2024, using customized
# total budget and constraints.
opt_results = opt.optimize(
    new_data=new_data,
    budget=100,
    pct_of_spend=[0.3, 0.3, 0.4],
    start_date="2024-10-07",
    end_date="2024-12-30",
)

示例 2 - 新的每个媒体的费用(所有渠道)

假设所有渠道的每个媒体单位的费用预计在不久的将来会发生变化,因此您希望在优化中更改所有渠道的假设每个媒体单位的费用。对于每个渠道,预期费用是一个已知常量,在不同地理位置和时间段内保持不变。create_optimization_tensors 辅助方法可创建 DataTensors 对象,非常适合此任务,因为每个媒体单位的费用 (cpmu) 是直接输入。

create_optimization_tensors 方法要求传递所有优化实参。您可以传递 mediamedia_spend(包含地理位置和时间维度)来指定排期模式。create_optimization_tensors 的所有实参的时间维度必须一致(media 不能包含用于考虑滞后效应的其他时间段)。

# Create `new_data` using the helper method. The cost per media unit (cpmu) is
# set to a constant value for each channel.
new_cpmu = np.array([0.1, 0.2, 0.5])
media_excl_lag = mmm.input_data.media[:, -mmm.n_times:, :]
new_data = opt.create_optimization_tensors(
    time=mmm.input_data.time,
    cpmu=new_cpmu,
    media=media_excl_lag,
    revenue_per_kpi=mmm.input_data.revenue_per_kpi,
)
# Run fixed budget optimization on the last quarter of 2024, using customized
# total budget and constraints.
opt_results = opt.optimize(
    new_data=new_data,
    budget=100,
    pct_of_spend=[0.3, 0.3, 0.4],
    start_date="2024-10-07",
    end_date="2024-12-30",
)

您也可以直接创建 DataTensors 对象来完成相同的任务。为此,支出是通过将每个渠道的媒体单位数按相应渠道的每个媒体单位的费用进行缩放来计算的。

# Create `new_data` without the helper method.
# In this example, `mmm.n_media_times > mmm.n_times` because the `media` data
# contains additional lag history. These time periods are discarded to create
# the new spend data.
media_excl_lag = mmm.input_data.media[:, -mmm.n_times:, :]
new_spend = media_excl_lag * np.array([0.1, 0.2, 0.5])
new_data = analyzer.DataTensors(media_spend=new_spend)
# Run fixed budget optimization on the last quarter of 2024, using customized
# total budget and constraints.
opt_results = opt.optimize(
    new_data=new_data,
    budget=100,
    pct_of_spend=[0.3, 0.3, 0.4],
    start_date="2024-10-07",
    end_date="2024-12-30",
)

示例 3 - 新的每个 KPI 的收入

假设每个 KPI 的收入(例如单位价格或生命周期价值)预计在不久的将来会发生变化。新的每个 KPI 的收入假设可以纳入优化中。需要明确的是,此操作会更改假设的每个增量 KPI 单位产生的收入,但不会更改模型对 KPI 本身的拟合度。

# Create `new_data` from `input_data`, but double the revenue per kpi.
new_data = analyzer.DataTensors(
    revenue_per_kpi=mmm.input_data.revenue_per_kpi * 2
)
# Run fixed budget optimization on the last quarter of 2024, using customized
# total budget and constraints.
opt_results = opt.optimize(
    new_data=new_data,
    budget=100,
    pct_of_spend=[0.3, 0.3, 0.4],
    start_date="2024-10-07",
    end_date="2024-12-30",
)

示例 4 - 新的排期模式

您可能正在考虑其他未来排期模式(媒体单位在各个地理位置或时间段的相对分配比例)。这样做的一个常见原因是,您计划将预算从一个地理位置转投到另一个地理位置。另一个常见原因是,需要考虑在输入数据窗口期间引入的新媒体渠道。如果引入了新渠道,那么在该渠道引入的时间段之前,历史排期模式的媒体单位数为零。如果您计划在未来持续执行新渠道,则应将排期模式中的零值替换为其他值,以便更好地反映未来计划的排期模式。

在此示例中,假设第一个媒体渠道是在 2024 年第四季度引入的。也许支出最初为零,但在整个季度内逐步增加。不过,您预计未来会在不同地理位置和时间段设置恒定不变的人均媒体单位数。DataTensorsmedia 实参用于指定排期模式。为地理位置级模型指定此实参时,最好基于人均媒体单位数考虑预期的排期模式。

确切的人均媒体单位数(在此例中为 100)不会影响排期模式。排期模式是指媒体单位在各个地理位置和时间段的相对分配比例,因此,如果分配的人均媒体单位数为 10,也可以实现相同的排期模式。不过,媒体单位数还会影响每个媒体单位的费用假设,该假设基于每个地理位置和时间段内每个媒体单位的支出比率。在此示例中,传递了新的支出数据,以实现如下目的:所有地理位置和时间段的每个媒体单位的费用为 0.1。

修改 media_spend 还会影响固定预算优化的默认总预算以及默认支出限制。可以使用 budgetpct_of_spend 优化实参替换这些默认设置。请务必了解这些实参,并根据需要进行自定义。

# Create new media units data from the input data, but set the media units per
# capita to 100 for channel 0 for all geos and time periods.
new_media = mmm.input_data.media.values
new_media[:, :, 0] = 100 * mmm.input_data.population.values[:, None]
# Set a cost per media unit of 0.1 for channel 0 for all geos and time periods.
new_media_spend = mmm.input_data.media_spend.values
new_media_spend[:, :, 0] = 0.1 * new_media[:, -mmm.n_times:, 0]
new_data = analyzer.DataTensors(
    media=new_media,
    media_spend=new_spend,
)
# Run fixed budget optimization on the last quarter of 2024, using customized
# total budget and constraints.
opt_results = opt.optimize(
    new_data=new_data,
    budget=100,
    pct_of_spend=[0.3, 0.3, 0.4],
    start_date="2024-10-07",
    end_date="2024-12-30",
)

Meridian 为什么不预测结果

Meridian 无需预测未来的预期结果,其因果推理即可用于未来的规划。事实上,Meridian 提供了多种方法来帮助您进行未来规划,包括 Optimizer 类和许多方法,例如 roimarginal_roiincremental_outcome。这些方法中的 new_data 实参支持使用 Meridian 的因果推理来估计任何假设性媒体执行或排期模式(包括未来执行)的数量。

Meridian 的目标是进行因果推理。更确切地说,其目标是推断处理变量对结果的增量效应。我们使用术语库中的术语,将增量结果定义简化为

\[ \text{Incremental Outcome} = \text{Expected Outcome} - \text{Counterfactual} \]

其中,反事实情景的含义取决于处理类型。(如需了解详情,请参阅术语库;如需了解更精确的定义,请参阅增量结果定义。)

控制变量会对预期结果产生影响,但不会对增量结果产生影响(控制变量对消除媒体效应偏差的影响除外)。这是因为 Meridian 模型规定,控制变量对“预期结果”和“反事实情景”都有加性效应,而这种效应在计算差值时会抵消。若要预测预期结果,我们需要预测控制数据。这可能相当困难,因为许多控制变量干扰因素多、不可预测,并且完全不受广告客户的控制。预测控制数据与 Meridian 的因果推理目标背道而驰,因为我们无需预测预期结果,即可推断增量结果,甚至对其进行优化。

同样,由结形参化的时间效应也是加性的。因此,“预期结果”和“反事实情景”取决于结值,但“增量结果”不是。Meridian 用于将时间模式纳入模型的基于结的方法并非专为预测而设计。相反,它旨在为时间模式提供更灵活的模型,因此非常适合因果推理问题。