作者:Heorhii Skovorodnikov
深入研究TikTok令人惊叹的实时推荐系统的内部工作原理,了解是什么使它成为该领域最好的产品之一。
为什么TikTok的feed如此让人上瘾?秘诀在于他们的推荐引擎,这正是使TikTok成为最大的社交媒体平台之一的原因。
似乎feed可以读取你的思想,让你在应用程序中停留更长时间。最近,TikTok决定让每个人都知道一个秘密,并在一篇题为 “Monolith:带有无碰撞嵌入表的实时推荐系统[https://arxiv.org/pdf/2209.07663.pdf]” 的论文中发布了它的模型Monolith。
在线推荐系统是一种算法,用于根据用户的兴趣和喜好向用户提供个性化的建议。这些系统通常被在线零售商和媒体公司用于向用户推荐产品或内容。
在这篇文章中,我们将深入研究TikTok令人惊叹的推荐系统的内部工作原理,并了解是什么,让它成为该领域最好的系统之一.
目前的设计存在什么问题?
构建可扩展的实时推荐系统对于许多企业在其产品或网站中构建良好的体验至关重要。然而,目前的深度学习框架(TensorFlow或PyTorch)不能很好地用于实时生产场景。这是因为:
- 在依赖动态稀疏特征的推荐系统中,基于静态参数和密集计算的模型更新并不适合较好的推荐性能。
- 常见的方法是将批量训练阶段和服务阶段(在用户与产品交互期间)完全分开设计,防止模型与客户反馈实时交互。
TikTok的团队通过3个步骤解释了他们的解决方案:
- 他们制作了一个无碰撞的嵌入表,同时通过添加可exponable embedding 和 frequency filtering(频率过滤)来进一步优化它,以减少其内存消耗,让其高效,并适合分发到用户;
- 他们提供了一个可用于生产环境的,且具有高容错性在线训练架构;
- 他们通过实验证明,系统的可靠性可以与实时学习来互相平衡;
听起来有点吓人吗? 不要担心,我们将通过对每个组件的拆解分析,在本文结束时,你将有信心地理解,为什么你可以在应用程序中浪费大量时间。准备好了吗? 我们要发车啦。
Embeddings and Hash maps
TikTok的研究人员观察到,对于推荐系统来说,数据大多是categorical(分散) 和sparse(稀疏)的。
这意味着,如果我们使用像单词嵌入这样的ML方法嵌入数据,我们将无法通过推荐数据提供的独特特性数量来实现,相比之下,由于词汇量有限,语言模型可以做到这一点。
根据YouTube和Instagram推荐系统的实际经验,哈希技巧被认为是大规模推荐系统的最佳方法。让我们深入研究《Monolith》中所使用的细节。
那么HashMap呢?
哈希映射是一种数据结构,它允许通过一个特殊的哈希函数将数据片段快速映射到一个值。
哈希映射速度很快,被大型平台用于高效编码数据,那么单体应用如何使其更好呢? 哈希映射有一个固有的权衡,这个数据结构的原始设计称为碰撞(collision)。
当两个或多个数据通过哈希函数映射到相同的输出值时,就会发生冲突。当使用哈希函数索引数据时,这可能会导致问题,因为多个数据块将被映射到相同的位置。TikTok的团队开发了一个 cuckoo hashmap,来解决这个问题。
在 cuckoo hashmap 中,就像在标准hash map中一样,每个数据都被分配一个唯一的键,并且键被哈希以确定它在数组中的位置。如果该位置已经被另一段数据占据,则现有数据将被“踢出”(类似于现实生活中杜鹃对巢中蛋的行为),并且必须使用第二个哈希函数在数组中找到一个新的位置。这个过程将继续,直到所有数据都成功插入数组,或者直到达到最大迭代次数为止。上面给出了一个例子。这里两个哈希表T0和T1用于存储哈希数据。值A被散列并插入到T0中,但是由于B已经占据了这个位置,然后将其逐出,并试图将其插入到T1中,这个过程将重复,直到插入所有值或重新散列以避免循环插入。这个过程可以避免碰撞,对生产模型的性能有重要影响。
为了完成他们的embedding系统设计,研究人员添加了一些附加功能来进一步优化过程,特别是减少哈希所需要的内存需求:
-
用于过滤hashmap中的id的概率过滤器。由于一个重要的观察是,在来自TikTok id的数据中,id是长尾分布的,热门id可能出现数百万次,而不受欢迎的id出现不超过10次,因此可以合理地假设它们不会影响最终的模型质量,因此可以清除。
-
一个ID存在计时器,控制旧ID和过期ID的删除。这可能是由于用户不再活跃,或短视频过时。为这些id存储嵌入不能以任何方式帮助模型,因此清除内存是明智的。
在线训练
现在,由于我们已经了解了数据在模型中是如何表示的,我们需要了解如何训练和更新数据。Monolith在线训练的系统架构的总体示意图如下:
它看起来很复杂,但实际上,它都围绕着一个非常简单的过程,这个过程是更大架构的基础,推动了整个训练系统架构的核心。
TensorFlow的分布式Worker-ParameterServer(或简称PS)模型是以分布式方式训练机器学习模型的一种方式,其中多台机器(或一台机器上的进程)一起工作来训练模型,如下图所示:
在这个模型中,有两种类型的进程:工作进程和参数服务器进程。
- 工作进程负责执行训练模型所需的计算,例如计算梯度或更新模型参数。
- 参数服务器负责存储模型的当前状态,例如模型权重或偏差。
训练分为批量训练和在线训练两个阶段:
-
批量训练阶段。 该阶段的工作原理如下:在每个训练步骤中,训练工作者从存储中读取一个小批量的训练样例,向PS请求参数,计算向前和向后传递,最后将更新后的参数推入训练PS。当需要修改模型架构并重新训练模型时,批量训练对于训练历史数据非常有用;
-
在线训练阶段。 模型部署到在线服务后,训练不会停止,而是进入在线训练阶段。训练工作者不再从存储中读取小批量示例,而是实时地使用实时数据并更新训练PS,训练PS定期将其参数同步到服务PS,这将立即在用户端生效。
Streaming引擎
为了确保Monolith能够在批量训练和在线训练之间无缝切换,它使用了一个Streaming引擎组件:
为了收集实时用户反馈,研究团队使用Kafka队列,其中一个队列记录用户操作(点击,点赞等),另一个队列记录来自模型服务器的功能。然后使用Apache Flink joiner连接两个,这些打包的数据被转换成训练数据,然后由另一个Kafka队列读取,这些训练示例用于批处理训练和在线训练:
- 在批量训练过程中,Kafka队列中的数据被转储到Hadoop分布式文件存储(HDFS)中,在积累了一定数量的训练数据后,再发送给训练工作者
-在线训练的过程更简单:数据直接从Kafka队列中读取
训练操作完成后,PS收集参数,并根据选定的同步计划更新服务PS,而服务PS又更新用户端的模型。
在线 Joiner
Joiner 过程实际上有点复杂,我们应该注意一些事情:
内存缓存和KV(Key-Value)存储,是两个有助于稳定用户操作和来自服务器的功能之间的延迟的组件,这是因为它们都到达,而不考虑彼此的到达时间,因此需要缓存来正确地配对它们。但是如果用户需要很长时间才能完成一个操作呢? 那么缓存就不是一个好主意,因此一些值存储在磁盘上,以便再次配对。当用户操作日志到达时,它首先查找内存中的缓存,然后查找键值存储,以防缺少缓存。
还要注意最后一步,即 负例采样(Negative Sampling)。因为在训练过程中有积极和消极的例子。在推荐系统中,正例是用户喜欢或表现出兴趣的项目,而负例是用户不喜欢或表现出兴趣的项目。但是它们的数量可能是不平衡的,因此纠正数据集中的这种偏差是很重要的。
就是这样。你已经了解了Monolith中的所有组件。现在是最后一个部分,研究人员证明了在线学习的有效性。
实时学习
在这里,团队还比较了模型,在不同同步时间间隔下的性能,以验证其性能:
正如我们在上面看到的,在线训练,对于具有动态反馈的推荐系统拥有更好的性能,是至关重要的。
写在最后
感谢阅读我对TikTok实时推荐系统工作原理的深入研究。
我希望你觉得有趣,并学到了一点新的东西。
原文:https://www.shaped.ai/blog/the-secret-sauce-of-tik-toks-recommendations
论文:Monolith:带有无碰撞嵌入表的实时推荐系统,https://arxiv.org/pdf/2209.07663.pdf