pinterest在《PinnerFormer: Sequence Modeling for User Representation at Pinterest》提出了它们的PinnerFormer方法:
摘要
近年来,序列模型在个性化推荐系统中变得越来越流行。这些传统的方法将用户在网站上的行为建模为一个序列,以预测用户的下一个行为。虽然理论上简单,但这些模型在生产环境中部署起来相当具有挑战性,通常需要流式基础设施来反映最新的用户活动,并可能管理可变数据以编码用户的隐藏状态。在这里,我们介绍了 PinnerFormer,这是一个训练用来预测用户未来长期参与度的用户表示,它基于用户近期行为的序列模型。与之前的方法不同,我们通过我们的新密集全动作损失(dense all-action loss)适应批量基础设施,模拟长期未来行为而不是下一个行为预测。我们展示了这样做可以显著缩小每天生成一次的批量用户嵌入和用户采取动作时生成的实时用户嵌入之间的差距。我们通过广泛的离线实验和消融研究来描述我们的设计理念,并在 A/B 实验中验证了我们方法的有效性,与我们之前用户表示相比,PinnerFormer 在 Pinterest 的用户留存和参与度方面显示出了显著的改进。PinnerFormer 自 2021 年秋季起已在生产环境中部署。
1 引言
每月有超过4亿用户使用Pinterest来从我们数十亿的Pins内容库中发现想法和灵感。一个Pin通常以一张图片开始,经常包括文本、一个网页链接和一个板子,将单个Pin连接到用户策划的Pins集合中。灵感是Pinterest的关键,主要通过我们的搜索和推荐系统实现,使用户能够通过:
- (a) Homefeed,我们的个性化推荐产品,
- (b) Related Pins,与查询Pin相关的推荐,
- (c) Search,与用户文本查询相关的推荐来找到内容
用户通过保存Pins到板子(Repin)、点击底层链接、放大一个Pin(特写)、隐藏不相关内容等互动方式给出反馈。为了实现我们让每个人都能得到创造他们所爱生活灵感的使命,我们需要根据用户的兴趣和上下文个性化我们的内容,考虑到用户在Pinterest旅程中给出的反馈;即,我们需要对我们的用户有一个强大的表示。
学习user embedding(表示)已成为提高推荐质量的越来越流行的方法。这种嵌入已被采用以推动排名和候选生成在行业中,被用来在YouTube[6]、Google Play[26]、Airbnb搜索[8]、JD.com搜索[30]、Alibaba[12, 18]等多个平台上推动个性化推荐。除了学习个性化嵌入的工作外,还有一系列工作集中在直接使用序列信息构建排序模型[4, 18, 19, 31],使推荐基于用户最近的参与度进行个性化。
用户在网站上的行为往往是顺序性的;动作可以按照他们被采取的时间来排序,这自然导致了序列建模方法。已经有多种方法被提出来基于用户的历史互动序列预测未来的参与度[3, 9, 17, 20, 21]。更近的工作已经应用了各种深度学习模型,包括循环神经网络(RNN)和变换器(transformers)来进行这种序列推荐,并取得了有希望的结果[3, 7, 9, 10, 21, 25, 29]。序列模型传统上专注于实时设置,旨在从所有导致该点的动作中预测用户的下一个动作或参与度。
在实践中,将现有的序列建模方法部署到大型网络规模应用中存在两个关键挑战:
- (a) 计算成本
- (b) 基础设施复杂性
现有的序列建模方法大致分为两类:无状态模型和有状态模型。无状态模型可能具有高计算成本,因为必须在用户采取的每个动作之后从头开始计算嵌入,而有状态模型需要健壮可靠的流式基础设施来处理给定用户模型状态中的潜在错误或数据损坏[18]。
在这里,我们介绍了PinnerFormer,这是一个在Pinterest生产环境中部署的端到端学习的用户表示。与先前关于序列用户建模的工作类似,PinnerFormer直接基于用户过去的Pin参与度学习表示。我们提出了一个密集全动作损失(dense all-action loss),这使我们的嵌入能够捕捉用户的长期兴趣,而不仅仅是预测下一个动作。这使我们的embedding可以在离线、批量设置中计算,并大大简化了基础设施。
我们还解决了Pinterest中表现的基础设施复杂性挑战:有数十个排序模型可以从个性化中受益,但为每个模型开发定制解决方案是不可行的。我们选择不针对每个模型生产一个用户嵌入(这将增加复杂性),而是投资于开发一个可以用于多个下游任务的单一高质量用户嵌入。尽管在某些情况下可能会牺牲特定任务的性能,但对于大多数用例来说,复杂性权衡使得共享嵌入更受欢迎。
我们在离线和在线A/B实验中评估PinnerFormer。在离线实验中,我们展示了这种训练目标几乎将每日推断模型和实时推断模型之间的性能差距减半,并且比其他方法更好地反映了用户的长期兴趣。然后,我们展示了PinnerFormer作为特征的效用,证明当它作为多个不同领域排序模型中的特征使用时,它能够实现显著的在线收益。
2 设计选择
我们首先讨论PinnerFormer中的关键设计选择。
设计选择1:单个用户使用单个还是多个嵌入。 大多数生成用户表示的方法产生单个嵌入[3, 7, 9, 10, 21, 25, 29],但有些专注于学习固定或可变数量的用户嵌入[12, 13, 17, 24]。在我们之前用户表示PinnerSage中,我们决定允许一个可变的数量,可能是大量的嵌入,允许模型明确表示用户的多样化兴趣[17]。
尽管使用多个嵌入可以让模型更明确地捕捉用户兴趣,并且在检索中表现良好,但这在使用下游模型时可能会导致问题:在训练数据中存储20+个256维float16嵌入并不具有很好的扩展性,特别是当数据集可能包含数十亿行时,正如排序模型中的情况。此外,这也增加了模型训练和推理的成本;处理5000+个浮点数可能会引入非微不足道的延迟,特别是如果它们在聚合前被转换。在训练时,大型样本也可以增加加载数据所需的时间。为了避免这些问题,在使用PinnerSage进行排序模型时,我们通常会使用用户嵌入的加权聚合作为最终用户表示。因为我们希望PinnerFormer能够轻松用作特征,我们产生一个捕获用户兴趣的单个嵌入,允许在下游模型中无痛使用。在离线评估中,我们展示了我们的单个嵌入能够比PinnerSage更好地反映用户的长期兴趣,同时只需要一小部分存储空间。
设计选择2:实时与离线推理。 大多数关于序列用户建模的先前工作集中在实时或近实时操作的模型上。在实践中,这至少会导致以下之一:
- 高计算成本: 对于用户采取的每一个动作,系统必须获取用户历史中的所有事件,并经常推断一个可能复杂的模型。
- 高基础设施复杂性: 用户的隐藏状态或嵌入可以逐步更新,但这需要一个健壮的系统来恢复和预热模型的状态,以防任何数据损坏[18]。
在Pinterest,用户可能在一天内采取数十或数百个动作,因此一个每天最多更新一次用户嵌入的模型只需要同等规模实时模型计算资源的一小部分。在离线评估中,我们展示了我们的损失函数显著减少了实时和每日推断模型之间的差距,并且在A/B实验中,我们展示了PinnerFormer极大地提高了下游排序模型的性能。
3 我们的方法:PinnerFormer
在本节中,我们介绍了自2021年秋季以来在Pinterest生产环境中使用的PinnerFormer,描述了我们的模型(如图1所示)及其部署方式。
图1 PinnerFormer架构的概览如下:特征通过带有因果掩蔽的transformer传递,并且在每个timestep返回embedding。需要注意的是,训练窗口(28天)超过了我们未来评估目标窗口(14天)
我们从:
- 一个庞大的Pins语料库:$P = \lbrace 𝑃_1, 𝑃_2, \cdots, 𝑃_𝑁 \rbrace$,其中:𝑁非常大,大约有数十亿
- 一组用户:$U = \lbrace 𝑈_1, 𝑈_2, \cdots \rbrace$,其中:$\mid U \mid > 500M$。
- 对于语料库中的每个Pin,我们有一个PinSage embedding: $𝑝_𝑖 \in R^{256}$,这是Pin $𝑃_𝑖$的视觉、文本和参与度信息的聚合。
- 对于每个用户,我们有一个用户在网站上采取的动作序列:$A_𝑈 = {𝐴_1, 𝐴_2, …, 𝐴_𝑆}$,按时间戳升序排列。
在这项工作中,我们将动作序列限制为用户对Pins的参与,包括过去一年中对Pin的保存、点击、反应和评论。基于这个假设,动作可以由PinSage嵌入表示,以及一些关于动作的元数据。实际上,对于给定的用户,𝑆可能非常大,对于某些用户来说可能是数千或数万,因此我们使用用户的M个最新动作来计算用户的嵌入。
给定这些定义,我们的目标是:学习一个用户表示$𝑓 : U → R^d$,与某些Pin表示$𝑔 : P → R^d$在余弦相似性下兼容。我们联合学习𝑓和𝑔,使用动作序列$A_𝑈$作为模型的唯一输入特征,并限制为仅最新的M个。
在一个用户的完整动作序列中,可能有许多类型的动作,其中一些是正向的(例如长时间点击),而另一些是中性的或负向的(例如隐藏或短时间点击)。在这项工作中,我们专注于学习预测正向参与的表示,我们将其定义为Pin保存(”Repin”)、持续超过10秒的Pin特写(”Closeup”)或对Pin下链接的长时间点击(>10s)(”Click”)。我们只将Homefeed上的参与视为正向的;在搜索或相关Pins等表面上,查询提供了大量上下文,而在Homefeed上,用户提供了主要的上下文。
我们的主要目标是:学习一个模型,能够预测用户在生成嵌入后的14天内的未来正向参与,而不是传统的序列建模任务,后者的嵌入只会预测下一个动作。换句话说,我们的目标是:学习嵌入$𝑢_𝑖$和$𝑝_𝑖$,使得如果$𝑑(𝑢_𝑘, 𝑝_𝑖) < 𝑑(𝑢_𝑘, 𝑝_𝑗)$,则$𝑝_𝑖$比$𝑝_𝑗$更有可能被$𝑢_𝑘$代表的用户在生成$𝑢_𝑘$后的14天内正向参与。我们选择14天的范围是为了可行性,并假设用户在两周内采取的动作足以代表用户的长期兴趣。图1展示了PinnerFormer架构,我们将在下面更详细地展开每个组件。
3.1 特征编码
对于用户序列中的每个动作,我们有一个PinSage嵌入(256维)[28]和元数据特征:动作类型(action type)、表面(surface)、时间戳(timestamp)和动作持续时间(action duration)。我们使用小型可学习的嵌入表来编码动作类型和表面,这两个分类特征,并丢弃这两个特征中任一的词汇表外术语的序列元素。我们用单个标量值$log(𝑑𝑢𝑟𝑎𝑡𝑖𝑜𝑛)$来编码动作持续时间。
为了表示动作发生的时间,我们使用2个衍生值以及原始的绝对时间戳:自用户采取的最新动作以来的时间,以及动作之间的时间间隔。对于这些时间特征,我们遵循使用不同周期的正弦和余弦变换编码时间的常见做法,类似于Time2vec[11],但使用固定的𝑃周期,而不是学习周期,以及对时间进行对数变换,而不是线性变换。这产生了2𝑃 + 1个特征(来自时间戳的周期变换的2𝑃)。
所有特征被连接成一个单独的向量,结果是一个维度为$𝐷_{in}$的输入向量。对应于动作𝐴𝑖的表示记为$𝑎_𝑖 \in R^{𝐷_{in}}$。
3.2 模型架构
在PinnerFormer中,我们使用transformer模型架构[22]来模拟用户动作序列。我们选择使用PreNorm残差连接,在每个块之前应用层归一化,因为这种方法已被证明可以提高训练的稳定性[16, 23]。我们首先构造输入矩阵$𝐴 = [𝑎_𝑇, …, 𝑎_{𝑇−𝑀+1}]^T \in R^{𝑀×𝐷_{in}}$,使用作为用户序列的导致动作$𝐴_{𝑇+1}$的𝑀个动作。然后,我们将这些投影到transformer的隐藏维度,添加一个完全可学习的位置编码,并应用一个标准transformer,由交替的前馈网络(FFN)和多头自注意力(MHSA)块组成。transformer在每个位置的输出通过一个小型MLP和L2归一化,结果得到一组嵌入$𝐸 = [𝑒_1, … , 𝑒_𝑀]^T \in R^{𝑆×𝐷}$,其中𝐷是最终嵌入维度。
为了表示Pins,我们学习一个MLP,它只接受PinSage作为输入,并L2归一化输出嵌入。我们发现使用L2归一化的嵌入来表示用户和Pins可以带来最稳定的训练,并且不会牺牲离线性能。
3.3 度量学习
为了训练我们的表示,我们需要由用户嵌入和目标Pin嵌入组成的配对${(𝑢1, 𝑝1), …, (𝑢𝐵, 𝑝𝐵)}$,其中用户和Pins可能被重复选择。我们选择不在这项工作中使用显式的负例(即我们没有为负参与,如隐藏,设置损失项)。在设计我们的模型时,有几个考虑因素:
- (a) 我们如何选择这些配对?
- (b) 对于给定的$(𝑢_𝑖, 𝑝_𝑖)$配对,我们如何选择负例?
- (c) 给定一个$(𝑢_𝑖, 𝑝_𝑖)$配对和一组负例,我们如何计算损失?
我们首先描述(b)和(c),然后在3.4节详细讨论(a)。
3.3.1 负例选择。
我们考虑两个来源的负例:批内负例和随机负例。在选择给定用户的批内负例时,我们选择批内所有正例作为负例,遮蔽对该用户有正参与的Pins。这种方法高效简单,但如果天真地实施,可能会导致流行Pin的降级,因为参与度高的Pin比参与度低的Pin更可能出现为负例。批内负例的另一个缺点是负例的分布与用于检索的真实Pins底层分布不同,导致训练和服务之间的差异。第二个负例来源是我们可能展示给Homefeed的所有Pins语料库中均匀采样的负例,但单独使用这些可能导致模型崩溃,因为负例可能太容易。我们考虑的第三个选项是结合随机和批内负例,通过将批内和随机负例池合并为一个,包含批内和随机负例的组合,以利用两者的独特特征[26]。
在实践中,更大的负例池可以提高学习嵌入的质量,因此我们从训练中使用的所有的GPU中收集负例,选择可以舒适地适应GPU内存的最大可能池。
3.3.2 损失函数。
在选择负例来源后,我们可以为给定的用户和正嵌入配对$(𝑢_𝑖, 𝑝_𝑖)$产生一组负嵌入${𝑛_1, \cdots, 𝑛_𝑁}$。我们为每对计算损失,然后计算加权平均值,使得每个GPU上的批内每个用户都有相等的权重。
我们发现最好的损失函数是采样softmax带logQ修正[2, 27],我们根据给定负例在批中出现的概率对每个logit应用修正。我们还学习一个温度$\tau \in [0.01, \infty)$,限制稳定性的下界。如果我们让$𝑠(𝑢, 𝑝) = ⟨𝑢, 𝑝⟩/𝜏$,一个没有样本概率修正的采样softmax损失将被定义如下:
\[L (𝑢𝑖, 𝑝𝑖) = − \log \frac{𝑒^{𝑠(𝑢𝑖,𝑝𝑖)}}{𝑒^{𝑠(𝑢𝑖,𝑝𝑖)} + \sum_{𝑗=1}^{𝑁} 𝑒^{𝑠(𝑢𝑖,𝑛𝑗)}}\]…(1)
当负例不是均匀分布时,应应用一个修正项𝑄𝑖(𝑣) = 𝑃 (Pin 𝑣在批中 | 用户𝑈𝑖在批中)来纠正采样偏差,其中𝑣可能是正例或负例。对于单个配对的带样本概率修正的softmax损失然后被定义如下: |
…(2)
为了简化,我们使用计数-最小草图[5]来近似𝑄。
3.4 训练目标
给定我们的损失函数,我们解决如何选择配对$(𝑢_𝑖, 𝑝_𝑖)$的问题。我们的模型应该能够预测三种形式的正向参与:Repins、Closeups和Clicks。这些动作都有价值,而不是像多任务学习文献[1, 14]中常见的那样学习任务特定的头,我们选择以多任务的方式训练单个嵌入,直接学习一个可以有效检索不同类型的正向参与的嵌入。我们不在损失计算函数中显式地对不同参与进行不同的加权。我们考虑的四个训练目标如下描述,并在图2中描绘。
图2
3.4.1 下一个动作预测。
序列建模任务的朴素目标是下一个动作预测,我们给定用户序列$\lbrace 𝐴_𝑇, 𝐴_{𝑇−1}, …, 𝐴_{𝑇−𝑀+1} \rbrace$预测$𝐴_{𝑇+1}$(如果$𝐴_{𝑇+1}$是正向参与)。这个目标对于实时序列模型来说很直观,因为在在线设置中,$𝐴_𝑇$总是用户采取的最新动作。SASRec[10]通过在模型的每一步预测下一个动作,而不仅仅是预测最近的正向参与来扩展这个简单的训练目标。我们在实验中稍微修改这一点,只允许正向参与对模型的损失做出贡献。
与这些传统目标不同,我们的目标不是预测用户的下一个即时动作;相反,我们每天推断用户嵌入,并旨在捕捉用户的长期兴趣。为此,我们引入了两个替代训练目标。
3.4.2 所有动作预测。
基于我们不仅希望预测用户将采取的下一个动作的观察,我们构建了一个朴素的训练目标,使用最终用户嵌入𝑒1预测用户在接下来的𝐾天内将采取的所有动作。假设用户在𝑇+3、𝑇+8和𝑇+12的动作中有正向参与,所有这些都落在𝑇的𝐾天窗口内,我们的目标是从用户序列{𝑎𝑇, 𝑎𝑇−1, …, 𝑎𝑇−𝑆+1}预测所有3个动作:𝐴𝑇+3、𝐴𝑇+8、𝐴𝑇+12。这个目标迫使模型学习长期兴趣,而不仅仅关注用户将采取的下一个动作,这应该减少来自每天离线推断的陈旧性效应。为了计算可行性,我们在这个𝐾天时间窗口中为每个用户随机采样多达32个动作。
3.4.3 密集所有动作预测。
为了进一步提高每批提供的信号,我们从SASRec[10]中汲取灵感,修改所有动作预测目标。我们不是使用只有𝑒1的最新用户嵌入在𝐾天窗口内预测动作,而是选择一组随机索引{𝑠𝑖},并对每个𝑒𝑠𝑖,旨在从接下来𝐾天内所有正向参与的集合中预测一个随机选择的正向参与。为确保这种方法从数据的顺序中学习,我们对transformer的自注意力块应用因果掩蔽,以便每个动作只能关注过去或现在的动作,而不能关注未来的动作。我们观察到这种掩蔽在这项任务上显著提高了模型的性能。为了减少内存使用,我们不旨在预测所有正向参与动作,而只是为每个𝑒𝑠𝑖预测一个正向参与动作。
3.5 数据集设计(续)
我们利用压缩格式来存储训练序列。我们观察到,给定单个用户的时间线,可以构建许多独立的用户序列和正例。给定一个完整的用户序列 ( A_U = {Y_1, \ldots, Y_S} ) 和某个最大序列长度 ( M ),我们可以构建多达 ( S - M - 1 ) 个长度恰好等于 ( M ) 的训练样本(假设所有动作都是正向的)。例如,序列 ({Y_5, \ldots, Y_{5+M-1}}) 以及正参与 ({Y_{5+M}, Y_{7+M}}) 可以从完整的时间线 ( A_U ) 中提取出来。存储这些数据的一种潜在方式是提前具体化所有长度为 ( M )(或更短)的相关序列,以及每个序列对应的未来正参与集合。这在尝试不同的采样策略时会遇到问题,因为调整参数将需要重新生成训练数据——这是一个缓慢的过程。为了提高生产力,我们改为将每个用户的序列存储为数据集中的单行,并在训练期间即时采样示例。这明显的好处是允许在训练期间进行定制采样,但代价是减少了训练数据的混洗。
具体来说,我们使用这种策略调整了几个参数,这些参数都显著影响模型的整体性能:
- 最大序列长度
- 从用户时间线中采样的可能用户序列的比例
- 每个用户采样的最大序列数
- 每个序列作为标签采样的最大正例数
3.6 模型服务
由于我们专注于PinnerFormer的离线、批量设置中的推理,我们在一个每日增量的工作流程中推断模型,如图3所示。
图3
这个工作流程为过去一天与任何Pin互动的用户生成新的嵌入,将它们与前一天的嵌入合并,然后上传到键值特征存储中,以便在线服务。因为我们只为最近一天有互动的用户生成新的嵌入,并且在没有延迟限制的情况下离线运行推理,我们能够使用比否则可能的更大的模型,这增加了我们的嵌入可以捕获的信息量。如果输入特征有任何损坏(例如,由于日志记录错误),我们可以轻松地为自损坏以来更新了嵌入的所有用户运行推理,假设上游数据已经修复,第二天的数据将是正确的。
Pin嵌入的计算成本很低,只需要对现有特征进行小型MLP转换,因此我们每天从头开始生成它们,然后编译一个HNSW[15]图,该图可以使用保存在特征存储中用户嵌入在线查询。
4.
略