营销归因传统上是向后看的:分析过去的客户旅程,以了解哪些接触点对转化做出了贡献。但是,如果我们能利用这些历史数据来设计最佳的未来客户旅程会怎样呢?在这篇文章中,我将展示如何将深度学习与优化技术相结合,设计出高转化率的客户旅程,同时考虑到现实世界的限制。我们将通过使用长短期记忆网络(LSTM)来预测具有高转化概率的旅程,然后使用集束搜索来寻找有良好转化机会的序列。
介绍
客户在与企业的互动中经历着我们所说的客户旅程。在这个旅程中,他们通过所谓的接触点(如社交媒体、谷歌广告等)与企业接触。在任何时候,用户都有可能转化(例如,通过购买你的产品)。我们想知道在这个旅程中哪些接触点对转化做出了贡献,以便优化转化率。
传统归因的局限性
在深入探讨我们的解决方案之前,重要的是要了解为什么传统的归因模型存在不足。
位置无关归因
传统的归因模型(首次接触、最后一次接触、线性等)通常会给每个渠道分配一个单一的重要性分数,而不考虑它出现在客户旅程中的位置。这从根本上是有缺陷的,因为:
上下文盲目性
大多数归因模型(甚至是数据驱动的模型)都忽略了关键的上下文因素:
客户特征:年轻且技术娴熟的客户可能对数字渠道的响应与传统渠道不同,
Customer 1 (Young, Urban): Social → Video → Purchase
Customer 2 (Older, Rural): Print → Email → Purchase
以前的购买历史:现有客户通常需要与新客户不同的参与策略
一天/一周的时间:渠道有效性可能因时间的不同而有很大差异
设备/平台:同一渠道在不同平台上的表现可能会有所不同
地理/文化因素:在一个市场行之有效的方法在另一个市场可能行不通
静态渠道值
传统模型假设渠道有效性可以表示为一个单一数字,而其他所有影响有效性的因素都被边缘化。如上所述,渠道有效性高度依赖于上下文,并且应该是这些上下文(如位置、其他接触点等)的函数。
深度学习登场
客户旅程本质上是顺序的——接触点的顺序和时间很重要。我们可以将归因建模视为一个二元时间序列分类任务,即我们希望从接触点序列中预测客户是否转化。这使得它们成为使用循环神经网络(RNN),特别是长短期记忆(LSTM)网络进行序列建模的理想候选者。这些模型能够捕获序列数据中的复杂模式,包括:
从历史数据中学习
第一步是在历史客户旅程数据上训练一个LSTM模型。对于每个客户,我们需要:
LSTM学习根据任何接触点序列预测转化概率。这给了我们一个强大的“模拟器”,可以评估任何提出的客户旅程的可能有效性。
由于我没有找到合适的数据集(特别是包含作为上下文数据的客户特征的数据集),我决定生成我自己的合成数据。我们为每位客户生成了一些特征和随机数量的客户旅程。这些旅程的长度是随机的。在旅程的每个点上,客户都会与一个接触点互动,并有转化的概率。这个概率由多个因素组成:
然后,我们对数据进行预处理,包括合并两张表、对数值特征进行缩放以及对类别特征进行独热编码(OneHotEncoding)。接着,我们可以设置一个LSTM模型,用于处理嵌入后的接触点序列。在最终的全连接层中,我们还会加入客户的上下文特征。
然后,我们可以用二元交叉熵损失来训练神经网络。我在下面绘制了测试集上实现的召回率。在这种情况下,我们更关心召回率而不是准确率,因为我们希望检测尽可能多的转化客户。错误地预测某些客户会转化并不像错过高潜力客户那么糟糕。
此外,我们会发现大多数客户旅程并不会导致转化。我们通常看到的转化率在2%到7%之间,这意味着我们的数据集是高度不平衡的。出于同样的原因,准确率并不是那么有意义。总是预测多数类(在这种情况下是“未转化”)会让我们获得非常高的准确率,但我们将无法找到任何转化的用户。
从预测到优化
一旦我们有了训练好的模型,就可以利用它来设计最优的客户旅程。我们可以在一组客户上施加一系列渠道(在下面的示例中是渠道1然后是渠道2),并查看模型预测的转化概率。我们已经可以看到,这些概率会根据客户的特征而有很大差异。因此,我们希望为每个客户单独优化其旅程。
此外,我们不能仅仅选择概率最高的序列。现实世界的营销活动受到各种限制:
因此,我们将此问题构建为一个受约束的组合优化问题:在满足所有约束条件的同时,找到能够最大化模型预测的转化概率的接触点序列。在这种情况下,我们只会对旅程中某些位置的接触点出现进行约束。也就是说,我们有一个从位置到接触点的映射,它指定了某个接触点必须在给定的位置出现。
还需要注意的是,我们的目标是优化预定长度的旅程,而不是任意长度的旅程。由于模拟的性质,如果我们在每个接触点都有一个非零的转化概率,那么整体转化概率将严格单调递增。因此,在大多数情况下,更长的旅程(即更多的非零条目)会胜过较短的旅程,而我们将会构建出无限长的旅程。
使用集束搜索进行优化
以下是使用递归实现的集束搜索。在每一层,我们优化旅程中的某个特定位置。如果该位置在约束中并且已经固定,则我们跳过它。如果我们已经达到了想要优化的最大长度,则停止递归并返回结果。
在每一层,我们查看当前的解决方案并生成候选方案。在任何时候,我们都保留由集束宽度定义的前K个最佳候选方案。然后,这些最佳候选方案将作为下一轮集束搜索的输入,用于优化序列中的下一个位置。
def beam_search_step(
model: JourneyLSTM,
X: torch.Tensor,
pos: int,
num_channels: int,
max_length: int,
constraints:dict[int, int],
beam_width: int = 3
):
if pos > max_length:
return X
if pos in constraints:
return beam_search_step(model, X, pos + 1, num_channels, max_length, constraints, beam_width)
candidates = [] # List to store (sequence, score) tuples
for sequence_idx in range(min(beam_width, len(X))):
X_current = X[sequence_idx:sequence_idx+1].clone()
# Try each possible channel
for channel in range(num_channels):
X_candidate = X_current.clone()
X_candidate[0, extra_dim + pos] = channel
# Get prediction score
pred = model(X_candidate)[0].item()
candidates.append((X_candidate, pred))
candidates.sort(key=lambda x: x[1], reverse=True)
best_candidates = candidates[:beam_width]
X_next = torch.cat([cand[0] for cand in best_candidates], dim=0)
# Recurse with best candidates
return beam_search_step(model, X_next, pos + 1, num_channels, max_length, constraints, beam_width)
这种优化方法是贪婪的,我们很可能会错过高概率的组合。然而,在许多场景中,特别是当渠道众多时,由于可能旅程的数量随着旅程长度的增加而呈指数级增长,因此强行求解最优解可能并不可行。
在上面的图像中,我们为单个客户优化了转化概率。在位置0,我们已将“电子邮件”指定为固定的接触点。然后,我们探索与电子邮件可能的组合。由于我们的集束宽度为五,因此所有组合(例如,电子邮件 -> 搜索)都会进入下一轮。在那一轮中,我们发现了高潜力的旅程,即向用户展示两次电子邮件,最后进行重定向。
结论
在归因建模中,从预测转向优化意味着我们从预测性建模转向规范性建模,即模型会告诉我们应采取的行动。这有可能实现更高的转化率,尤其是在我们面临具有众多渠道和上下文变量的高度复杂场景时。
同时,这种方法也存在几个缺点。首先,如果我们没有一个能够充分准确地识别转化客户的模型,那么我们可能会损害转化率。此外,模型输出的概率必须得到良好的校准。否则,我们正在优化的转化概率可能毫无意义。最后,当模型需要预测超出其数据分布的旅程时,我们会遇到问题。因此,使用强化学习(RL)方法也是可取的,因为模型可以主动生成新的训练数据。