使用Python和LightweightMMM衡量广告效果

Python
282
0
0
2024-02-29

摘要:

媒体组合建模,也称为市场组合建模(MMM),是一种帮助广告商量化多个市场投资对销售的影响的技术。

LightweightMMM是一个考虑媒体饱和度和广告库存的Python库,但在尝试MMM时可能需要进行多次试验和错误。为了获得实际见解和行动,不断努力获取更好的数据、建立更好的模型和进行更好的实验。

1.简介

当你听到广告这个词时,你会想到什么?

让我给你举几个例子。电视广告是一种常见的方法。社交媒体广告,例如在社交媒体平台上查看朋友的帖子或视频时,你可能会看到很多广告。此外,如果你在谷歌搜索某个内容,通常会在结果的顶部看到一些广告。此外,在公交车、机场、火车或出租车上,以及在建筑物上,被称为OOH(户外广告)的广告也相当普遍。

媒体优化长期以来一直是一个挑战。你们中的一些人可能熟悉市场开拓者约翰·瓦纳梅克。他据说曾说过:“我在广告上花的一半钱都是浪费的,问题是我不知道哪一半是浪费的。”

解决这个问题的一种统计方法称为媒体组合建模或市场组合建模,通常简称为MMM。MMM的目的是了解每个营销媒体对销售的贡献有多大,应该在每个媒体上花费多少资金。

几十年来,饮料、消费品、汽车和时尚行业的大型广告预算公司一直在致力于改进MMM。此外,谷歌和Meta等广告技术公司近年来也一直在积极关注MMM。

2.什么是MMM?

MMM是一种统计模型,有助于量化多个营销投入对销售的影响。

粗略地说,它有三个目标。

  • 第一个目标是“理解和衡量投资回报率(ROI)”。例如,模型会告诉你去年电视广告的投资回报率。
  • 第二个目标是模拟。例如,通过这个模型,你可以回答一个商业问题,比如“如果明年在电视广告上花费更多或更少的资金,我们的销售额会是多少?”你将能够找出如果明年在电视广告上花费更多或更少的资金,你的销售额会是怎样的。
  • 第三个目标是优化媒体预算。这一步将帮助你优化预算分配,从而最大化销售额。

媒体优化的主要挑战

你可能想知道为什么衡量投资回报率如此困难,或者为什么不只是查看每个媒体发布的报告中的投资回报率。

这些都是很好的问题。但现实情况要复杂一些。

第一个原因是最终用户有多个媒体接触点,媒体渠道的影响相互交织。

其次,跟踪准确性如今并不总是正确的。离线媒体渠道的影响很难追踪。例如,对于报纸或杂志等印刷媒体,我们无法追踪到实际看到该媒体广告的人数。更糟糕的是,即使在数字世界中,诸如GDPR和苹果的IDFA淘汰等隐私规定也对跟踪准确性产生了影响。

第三,已知为Lift测试的随机实验是不切实际的。回答因果问题的黄金标准是进行随机实验,将人群随机分成测试组和对照组,其中测试组没有广告。然而,这并不实际,因为公司不愿意长时间限制广告,这可能导致机会流失。

3.数据准备

3.1 输入数据

我们使用时间序列数据,不使用任何涉及隐私的数据。正如你所见,我们有一周的销售额、媒体支出和其他数据列。

3.2 需要什么样的数据?

第一部分是最重要的指标,即你的业务关键绩效指标(KPI),它将成为一个因变量。如果你是零售商或制造商,销售额是一个常见的选择。然而,如果你是一家移动应用公司,安装应用的数量可能是KPI。

接下来,解释变量是影响销售的潜在因素。媒体数据是必需的,因为我们希望优化这些分配。非媒体营销数据,如价格、促销或产品分销,也会影响销售。季节性、假日、天气或宏观经济数据等外部因素对提高模型的准确性也很重要。

3.3 数据应该有多精细?

关于时间,MMM通常需要两到三年的每周级别数据。然而,如果你没有那么多数据,每日数据也是可以接受的,但在这种情况下,你需要更仔细地审查异常值。

接下来是业务的粒度。常见的方法是收集品牌或业务单元级别的数据。例如,宝洁公司在头发护理类别中拥有潘婷、海飞丝和草本精华等品牌。每个品牌团队都有不同的销售、营销和媒体策略。请确保根据产品线、组织和决策过程确定数据的粒度。

在查看媒体支出数据时,常见的粒度是媒体渠道级别,如电视、印刷品、户外和数字媒体。但这取决于你在每个媒体上的花费情况。例如,如果你在数字广告上花费很多,最好将数字渠道细分为更具体的群体,如谷歌搜索广告、谷歌展示广告、YouTube广告、Facebook广告等,因为谷歌搜索广告和YouTube广告具有不同的销售漏斗和角色。

4.建模

4.1 简单的传统方法——线性回归

首先,让我们考虑简单的建模方法。观测数据上的线性回归是一种常用的传统方法。

在这里,销售额是目标变量,媒体支出因素和控制因素是解释变量。这些系数表示对销售额的影响。因此,beta_m是媒体变量的系数,beta_c是季节性或价格变动等控制变量的系数。

这种方法最重要的优点是每个人都可以快速运行,因为即使Excel也有回归函数。此外,对于包括非技术主管在内的所有人来说,直观地理解结果也很容易。然而,这种方法没有根据市场营销行业广泛接受的关键营销原则进行基础。

4.2 广告中的两个原则

在广告中有两个需要考虑的原则:饱和度和广告库存。

饱和度:随着支出的增加,一个媒体渠道的广告效果会下降。换句话说,你在一个媒体渠道的广告上花费的金额越多,它的效果就越小。饱和度也被称为形状效应。

广告库存:广告对销售的影响可能滞后于初始曝光,并延续数周,因为消费者通常会记住广告很长时间,但他们有时会推迟采取行动。造成这种滞后效应的原因有几个:如果消费者已经有库存的商品,他们不会立即购买。或者,如果他们计划购买昂贵的商品,如个人电脑、家具或电视,可能需要几天到几周的时间来考虑购买这些商品。这些例子就是导致滞后效应的原因。

4.3 Jin等人提出的Google研究者模型

Google的研究人员在2017年提出了一种反映这两个特征的方法。下面的公式是反映滞后效应和饱和度的最终模型。

基本方法与我之前分享的简单模型相同。销售额可以分解为基线销售额、媒体因素、控制因素和白噪声。在这个公式中,系数beta代表每个因素的影响。这里的变化是对媒体支出的时间序列应用了两个转换函数:饱和度函数和广告库存函数。

4.4 有用的MMM库(LightweightMMM与Robyn)

在这里,让我介绍两个非常棒的开源库,它们将帮助你尝试MMM:LightweightMMM是由谷歌开发人员主要开发的基于Python的库,而Robyn是由Meta开发的基于R的库。

LightweightMMM使用Numpyro和JAX进行概率编程,从而使建模过程更快。除了标准方法外,LightweightMMM还提供了一种层次化方法。如果你有州级或区域级别的数据,这种基于地理的层次化方法可以得到更准确的结果。

而Robyn则利用了Meta的AI库生态系统。使用Nevergrad进行超参数优化,采用Prophet处理时间序列数据。

5.示例代码

让我演示一下如何使用LightweightMMM实际进行建模。完整的代码可以在我的Github上找到。我的示例代码基于lightweight_mmm的官方演示脚本。

https://github.com/takechanman1228/mmm_pydata_global_2022/blob/main/simple_end_to_end_demo_pydataglobal.ipynb

首先,让我们使用pip命令安装lightweight_mmm库。这应该需要大约1-2分钟的时间。如果你收到“重新启动运行时”的错误提示,你需要点击“重新启动运行时”按钮。

!pip install --upgrade git+https://github.com/google/lightweight_mmm.git

此外,让我们导入一些库,如JAX、numpryro和库的必要模块。

# Import jax.numpy and any other library we might need.
import jax.numpy as jnp
import numpyro


# Import the relevant modules of the library
from lightweight_mmm import lightweight_mmm
from lightweight_mmm import optimize_media
from lightweight_mmm import plot
from lightweight_mmm import preprocessing
from lightweight_mmm import utils

接下来,让我们准备数据。官方的示例脚本使用了库的函数生成模拟数据集来创建虚拟数据。然而,在本次会话中,我将使用更真实的数据。

我在GitHub存储库上找到了一个好的数据集:sibylhe/mmm_stan。我不确定这个数据集是真实的、虚拟的还是模拟的数据,但对我来说,它看起来比我在互联网上找到的任何其他数据更真实。

import pandas as pd

# I am not sure whether this data set is real, dummy, or simulated data, but for me, it looks more realistic than any other data I found on the internet.
df = pd.read_csv("https://raw.githubusercontent.com/sibylhe/mmm_stan/main/data.csv")

# 1. media variables
# media spending (Simplified media channel for demo)
mdsp_cols=[col for col in df.columns if 'mdsp_' in col and col !='mdsp_viddig' and col != 'mdsp_auddig' and col != 'mdsp_sem']

# 2. control variables
# holiday variables
hldy_cols = [col for col in df.columns if 'hldy_' in col]
# seasonality variables
seas_cols = [col for col in df.columns if 'seas_' in col]

control_vars =  hldy_cols + seas_cols

# 3. sales variables
sales_cols =['sales']

df_main = df[['wk_strt_dt']+sales_cols+mdsp_cols+control_vars]
df_main = df_main.rename(columns={'mdsp_dm': 'Direct Mail', 'mdsp_inst': 'Insert', 'mdsp_nsp': 'Newspaper', 'mdsp_audtr': 'Radio', 'mdsp_vidtr': 'TV', 'mdsp_so': 'Social Media', 'mdsp_on': 'Online Display'})
mdsp_cols = ["Direct Mail","Insert", "Newspaper", "Radio", "TV", "Social Media", "Online Display"]

让我们快速看一下这个数据集。该数据包含四年的每周级别的记录数据。为了简单起见,我使用了七个媒体渠道的媒体支出数据,以及假期和季节信息作为控制变量。

df_main.head()

接下来,我将对数据进行预处理。我们将数据集分成训练集和测试集。在这种情况下,我只保留了最后24周的数据作为测试集。

SEED = 105
data_size = len(df_main)

n_media_channels = len(mdsp_cols)
n_extra_features = len(control_vars)
media_data = df_main[mdsp_cols].to_numpy()
extra_features = df_main[control_vars].to_numpy()
target = df_main['sales'].to_numpy()
costs = df_main[mdsp_cols].sum().to_numpy()

# Split and scale data.
test_data_period_size = 24
split_point = data_size - test_data_period_size

# Media data
media_data_train = media_data[:split_point, ...]
media_data_test = media_data[split_point:, ...]

# Extra features
extra_features_train = extra_features[:split_point, ...]
extra_features_test = extra_features[split_point:, ...]

# Target
target_train = target[:split_point]

此外,这个库提供了一个用于预处理的CustomScaler函数。在这个示例代码中,我们通过它们的均值将媒体支出数据、额外特征数据和目标数据除以均值,以确保结果的均值为1。这使得模型对输入的尺度无关。

media_scaler = preprocessing.CustomScaler(divide_operation=jnp.mean)
extra_features_scaler = preprocessing.CustomScaler(divide_operation=jnp.mean)
target_scaler = preprocessing.CustomScaler(divide_operation=jnp.mean)
cost_scaler = preprocessing.CustomScaler(divide_operation=jnp.mean, multiply_by=0.15)

media_data_train = media_scaler.fit_transform(media_data_train)
extra_features_train = extra_features_scaler.fit_transform(extra_features_train)
target_train = target_scaler.fit_transform(target_train)
costs = cost_scaler.fit_transform(costs)p

下一步是训练。我们可以从三种选项中选择一种广告库存函数进行建模:Hill-广告库存、广告库存和滞后效应。一般建议比较这三种方法,并选择最好的方法。

mmm = lightweight_mmm.LightweightMMM(model_name="hill_adstock")
mmm.fit( media=media_data_train, media_prior=costs, target=target_train, extra_features=extra_features_train, number_warmup=number_warmup, number_samples=number_samples, media_names = mdsp_cols, seed=SEED)

训练完成后,你可以检查跟踪结果的摘要:在运行贝叶斯建模时,重要的是检查所有参数的r hat值是否小于1.1。这是一个检查点。

mmm.print_summary()

我们可以可视化媒体效果的后验分布。

plot.plot_media_channel_posteriors(media_mix_model=mmm, channel_names=mdsp_cols)

现在,让我们进行拟合检查。可以使用plot_model_fit函数检查模型对训练数据的拟合情况。

图表中显示了R2和MAPE(平均绝对百分比误差)。在这个例子中,R2为0.9,MAPE为23%。一般来说,如果R2大于0.8,则被认为是良好的。此外,对于MAPE,目标是保持在20%以下。

plot.plot_model_fit(mmm, target_scaler=target_scaler)

这是预测结果的可视化。R2为0.62,MAPE为23%。老实说,这里的R2和MAPE值并不理想。

然而,我没有任何额外的数据,而且我甚至不确定这个数据集是真实的还是虚拟的。尽管如此,我仍然要使用这个数据集和模型来向你展示洞察力。稍后我将详细介绍如何改进模型。

plot.plot_out_of_sample_model_fit(out_of_sample_predictions=new_predictions,
                                 out_of_sample_target=target_scaler.transform(target[split_point:]))

输出

我们可以通过使用这个函数快速可视化估计的媒体和基线贡献随时间的变化。下图显示约70%的销售额是基线销售额,用蓝色区域表示。其他颜色显示了媒体对剩余销售额的贡献。

media_contribution, roi_hat = mmm.get_posterior_metrics(target_scaler=target_scaler, cost_scaler=cost_scaler)
plot.plot_media_baseline_contribution_area_plot(media_mix_model=mmm,
                                                target_scaler=target_scaler,
                                                fig_size=(30,10),
                                                channel_names = mdsp_cols
                                                )

plot.plot_bars_media_metrics(metric=roi_hat, metric_name="ROI hat", channel_names=mdsp_cols)

这张图显示了每个媒体渠道的估计投资回报率。每个柱形图表示媒体的回报率效率。在这种情况下,电视和在线展示比其他媒体更高效。

我们可以可视化优化的媒体预算分配。图表显示了先前的预算分配和优化后的预算分配。在这种情况下,直邮和广播应该减少,其他媒体应该增加。

plot.plot_pre_post_budget_allocation_comparison(media_mix_model=mmm, 
                                                kpi_with_optim=solution['fun'], 
                                                kpi_without_optim=kpi_without_optim,
                                                optimal_buget_allocation=optimal_buget_allocation, 
                                                previous_budget_allocation=previous_budget_allocation, 
                                                figure_size=(10,10),
                                                channel_names = mdsp_cols)

6.如何提高模型的准确性?

为了获得更好的洞察力和行动,需要量身定制的模型,因为没有一种“一刀切”的模型,每个企业都处于不同的情况中。

那么,如何提高模型的准确性,以获得更好的洞察力和行动呢?

更好的数据:根据你的业务,你需要选择影响销售的控制变量。一般来说,销售会根据促销活动、价格变动和折扣而波动。缺货信息对销售也有重大影响。谷歌的研究人员发现,相关查询的搜索量可以在MMM中用于适当控制付费搜索广告的影响。

如果你在特定的媒体渠道上花费很多钱,最好将媒体渠道细分为更具体的群体。

更好的模型:下一个建议是改进建模。当然,超参数调整很重要。除此之外,尝试使用基于地理层次化方法是提高准确性的好方法。

更好的实验:第三个建议是与你的营销团队一起进行实际实验,即所谓的提升测试。如前所述,在所有媒体上进行随机实验是不现实的。然而,在关键点进行实验对于获得真实情况并改进模型是有用的。Meta最近发布了Geo Lift,这是一个能够用于基于地理位置的实验的开源解决方案。

7.结论

让我们总结一些关键要点。

  • MMM是一种统计模型,有助于量化几种营销投入对销售的影响。
  • 在广告中,饱和和广告存量是关键原则。它们可以使用转换函数来建模。
  • 如果你熟悉Python, LightweightMMM是一个很好的开端。
  • 为了获得更好的见解和行动,不断尝试更好的数据,建立更好的模型,做更好的实验。

参考资料

  • Jin, Y., Wang, Y., Sun, Y., Chan, D., & Koehler, J. (2017). Bayesian Methods for Media Mix Modeling with Carryover and Shape Effects. Google Inc.
  • Chan, D., & Perry, M. (2017). Challenges and Opportunities in Media Mix Modeling.
  • LightweightMMM : https://github.com/google/lightweight_mmm
  • Robyn : https://github.com/facebookexperimental/Robyn
  • mmm_stan : https://github.com/sibylhe/mmm_stan

谢谢阅读!

✄-----------------------------------------------