TikTok 推荐引擎强大的秘密
作者: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
TikTok 推荐引擎强大的秘密的更多相关文章
- AI时代:推荐引擎正在塑造人类
We shape our tools and afterwards our tools shape us. ------Marshall McLuhan 麦克卢汉说:"我们塑造了工具,反过来 ...
- 机器学习实战(Machine Learning in Action)学习笔记————10.奇异值分解(SVD)原理、基于协同过滤的推荐引擎、数据降维
关键字:SVD.奇异值分解.降维.基于协同过滤的推荐引擎作者:米仓山下时间:2018-11-3机器学习实战(Machine Learning in Action,@author: Peter Harr ...
- 机器学习 101 Mahout 简介 建立一个推荐引擎 使用 Mahout 实现集群 使用 Mahout 实现内容分类 结束语 下载资源
机器学习 101 Mahout 简介 建立一个推荐引擎 使用 Mahout 实现集群 使用 Mahout 实现内容分类 结束语 下载资源 相关主题 在信息时代,公司和个人的成功越来越依赖于迅速 ...
- 从源代码剖析Mahout推荐引擎
转载自:http://blog.fens.me/mahout-recommend-engine/ Hadoop家族系列文章,主要介绍Hadoop家族产品,常用的项目包括Hadoop, Hive, Pi ...
- 基于Azure构建PredictionIO和Spark的推荐引擎服务
基于Azure构建PredictionIO和Spark的推荐引擎服务 1. 在Azure构建Ubuntu 16.04虚拟机 假设前提条件您已有 Azure 帐号,登陆 Azure https://po ...
- [转] 基于 Apache Mahout 构建社会化推荐引擎
来源:http://www.ibm.com/developerworks/cn/java/j-lo-mahout/index.html 推荐引擎简介 推荐引擎利用特殊的信息过滤(IF,Informat ...
- 基于Spark ALS构建商品推荐引擎
基于Spark ALS构建商品推荐引擎 一般来讲,推荐引擎试图对用户与某类物品之间的联系建模,其想法是预测人们可能喜好的物品并通过探索物品之间的联系来辅助这个过程,让用户能更快速.更准确的获得所需 ...
- JVM调优(这里主要是针对优化基于分布式Mahout的推荐引擎)
优化推荐系统的JVM关键参数 -Xmx 设定Java允许使用的最大堆空间.例如-Xmx512m表示堆空间上限为512MB -server 现代JVM有两个重要标志:-client和-server,分别 ...
- 基于lucene实现自己的推荐引擎
基于lucene实现自己的推荐引擎 推荐常用算法之-基于内容的推荐 推荐算法
- 转】从源代码剖析Mahout推荐引擎
原博文出自于: http://blog.fens.me/mahout-recommend-engine/ 感谢! 从源代码剖析Mahout推荐引擎 Hadoop家族系列文章,主要介绍Hadoop家族产 ...
随机推荐
- JDK卸载
JDK卸载 从环境变量里的JAVA_HOME里找到jdk的主程序,删掉 把JAVA_HOME删掉,在把path里跟java_home相关的也删掉 在cmd里运行java-version
- C++面向对象程序设计期末复习笔记[吉林大学](结合历年题速成85)
1.头文件 头文件的作用就是被其他的.cpp包含进去的.它们本身并不参与编译,但实际上,它们的内容却在多个.cpp文件中得到了编译.根据"定义只能一次"原则我们知道,头文件中不能放 ...
- 对一个序列双重argsort的含义
学习笔记:由numpy.argsort()引发的思考 一.numpy.argsort() 函数定义 函数的定义 首先函数的定义比较简洁: argsort()函数是将x中的元素从小到大排列,提取其对应的 ...
- 2022弱口令实验室招新赛CTF赛道WriteUp
Misc 签到 下载附件,得到一张二维码. 扫码,然后根据提示"linux"操作系统,直接cat /flag,得到flag. EasyMisc 下载得到EasyMisc附件,压缩包 ...
- [OpenCV实战]22 使用EigenFaces进行人脸重建
目录 1 背景 1.1 什么是EigenFaces? 1.2 坐标的变化 2 面部重建 2.1 计算新面部图像的PCA权重 2.2 使用EigenFaces进行面部重建 3 参考 在这篇文章中,我们将 ...
- (6)go-micro微服务consul配置、注册中心
目录 一 Consul介绍 1. 注册中心Consul基本介绍 2.注册中心Consul关键功能 3.注册中心Consul两个重要协议 二 Consul安装 1.使用docker拉取镜像 三 Conf ...
- SSM框架——SpringMVC
SpringMVC MVC三层架构 Controller层:取得前端数据.调用相关业务逻辑.转发/重定向到其他页面 Model层:实现业务逻辑.保存数据 View层:显示页面 1.第一个MVC程序 新 ...
- 移动端微信小程序开发学习报错记录--toast.js:41 未找到 van-toast 节点,请确认 selector 及 context 是否正确
这个问题仔细检查了一下代码引入,是没有问题的, 根本原因是在页面上忘了加<van-toast id="van-toast" /> 具体引入代码如下: app.json ...
- Xmake v2.7.6 发布,新增 Verilog 和 C++ Modules 分发支持
Xmake 是一个基于 Lua 的轻量级跨平台构建工具. 它非常的轻量,没有任何依赖,因为它内置了 Lua 运行时. 它使用 xmake.lua 维护项目构建,相比 makefile/CMakeLis ...
- Java 进阶P-8.5+P-8.6
抛出异常 异常的抛出与声明 如果你的函数可能抛出异常,就必须在函数头部加以声明 你可以声明并不会真的抛出得异常 什么能扔? 任何继承了Throw able类的对象 Exception类继承了Throw ...