使用 TensorFlow 对推文进行排名
2019 年 3 月 4 日
来自 Twitter Cortex 和主页时间线团队的 Yi Zhuang、Arvind Thiagarajan 和 Tim Sweeney 的客座文章

作为全球性的公共通信平台,Twitter 致力于通过提供相关且健康的內容来让用户了解最新信息。最初,Twitter 以倒序时间順序显示推文。随着社区变得更加紧密,用户主页时间线中的内容量显著增加。用户会关注 Twitter 上的数百人,甚至数千人,当他们打开 Twitter 时,他们会错过一些最重要的推文。

为了解决这个问题,我们推出了“排名时间线”,它在时间线的顶部显示最相关的推文,确保用户永远不会错过他们最好的推文。一年后,我们分享了机器学习如何大规模地为排名时间线提供支持。从那时起,我们重新调整了机器学习平台以使用 TensorFlow。这带来了显著的生产力提升,同时也使我们能够利用最新的行业研究成果。在这篇文章中,我们将解释我们选择 TensorFlow 的原因,讨论时间线排名用例的独特复杂性,最后总结 TensorFlow 如何改变了 Twitter 中机器学习模型的开发方式。

从 Torch 迁移到 TensorFlow

我们之前的机器学习平台基于 Lua Torch,允许用户使用 YAML 配置文件来定义模型和优化过程。然而,随着机器学习行业的不断发展,人才市场也培养了更多模型专家,我们决定重新评估我们基于 Lua Torch 的平台。

使用 YAML 作为主要模型 API 对模型能力造成了限制。例如,如果一个 ML 从业人员想要构建一个非常深的深度神经网络(例如 ResNets-152),他们需要在 yaml 文件中定义 152 层,而不是编写一个参数化的 for 循环。从技术上讲,需要灵活性和表达能力的 ML 从业人员可以直接使用 Torch 编写 Lua 代码。然而,Lua 对大多数 Twitter 工程师来说是一种陌生的语言,而且并不是 Twitter 官方推荐的语言。除了模型限制之外,我们还遇到了大量代码重复、单元测试困难以及调试挑战。

此外,Torch 的大规模生产支持没有满足我们的需求。利用 Lua 创建了复杂的生产系统,这些系统涉及通过内部编写的 JNI 桥接在 JVM 中运行 Lua VM。为了调试生产性能问题,我们需要精通 JVM/JNI、Lua 和 C 性能优化的工程师。由于设置复杂,我们也缺乏良好的端到端分析工具。这导致性能和质量问题难以诊断和修复。

在调查了各种选择之后,TensorFlow 和 PyTorch 成为我们首选的候选者。我们在 2017 年底做出了决定,当时 TensorFlow 发布了 1.0 生产版本,并提供了 API 稳定性保证,而 PyTorch 仍然处于测试阶段。这是一个艰难的决定,因为 TensorFlow 具有更好的稳定性,但另一方面,我们内部的 Torch 执行引擎专业知识可以更好地迁移到 PyTorch(例如,Torch 和 PyTorch 都使用 THNN)。TensorFlow 在大规模用例中经过了考验,并且可以使用一种熟悉的语言:Python。它还拥有出色的 Java API,这非常有用,因为 Twitter 的生产系统大多是基于 JVM 的。最后,TensorFlow 的表达能力和灵活性有望支持当前的用例、研究工作以及尚未诞生的架构。生产稳定性保证、更好的 Java API 以及来自 Google 的强大支持最终促使 Twitter 决定用 TensorFlow 替换 Lua Torch。

案例研究:使用 TensorFlow 对主页时间线上的推文进行排名

本节将更深入地介绍我们使用具体示例(即我们用于对 Twitter 主页时间线进行排名的机器学习系统)从 Torch 迁移到 Tensorflow 的过程。该系统是 Twitter 上运行的规模最大的机器学习应用程序之一,服务于数亿活跃用户,并为每个用户评分数千条推文。

Twitter 的主页时间线是大多数 Twitter 用户的默认起点,也是他们花费大部分时间的地方。时间线的核心功能是通过帮助用户查看最相关的推文来提供公共对话,并让用户了解 Twitter 上发生的实时对话。我们的指标和用户调查都表明,当我们在第一时间显示最好的推文时,用户对其 Twitter 时间线更满意。此外,当我们对时间线进行排名以显示最相关的推文时,人们会进行更多对话,并且更有可能回到 Twitter,因为他们发现 Twitter 更有用、更有信息量和更有趣。

在排名时,每个候选推文都会被相关性模型评分,以预测它对每个用户的相关性。“相关性”由多个因素决定,包括用户与推文互动的可能性,以及它是否可能促进健康的公共对话。该模型使用来自三个实体的数千个特征:推文、作者和查看用户。一些示例特征包括

  • 推文: 它的发布时间、是否存在媒体(图片或视频)、总互动量(例如转发或点赞次数)、推文上的实时互动。
  • 作者: 查看用户与该作者的过去互动、用户与该作者的关系强度、关系的来源。
  • 用户: 用户在过去发现相关的推文、他们使用 Twitter 的频率和强度。

有趣的是,推文排名不同于传统的深度学习研究(例如图像分类)。这是因为数据本身是稀疏的,许多最重要的特征只存在于用于训练或推理的样本子集中。这也意味着对稀疏特征之间的特征交互进行建模对于实现良好的预测质量至关重要。

我们最初用于推文相关性预测任务的 ML 模型依赖于逻辑回归、手工制作的特征交叉和浅层决策树。随着时间的推移,当我们将数百到数千个新特征添加到时间线预测模型中时,我们了解到我们使用的手动特征交叉和决策树方法无法扩展以找到所有重要的特征交互。在 2017 年,我们迁移了时间线模型以使用基于深度学习的架构,这种架构更擅长寻找复杂的特征交互。这个最初的初代深度学习系统是基于 Lua Torch 构建的。时间线 ML 模型的层使用如下代码片段所示的 YAML 模板文件进行手工编码
- modules.FullDenseLayer:
    dropout         : 0
    inputFeatures   : 100
    outputFeatures  : 200
    activations     : PReLU
- modules.FullDenseLayer:
    dropout         : 0
    inputFeatures   : 200
    outputFeatures  : 400
    activations     : PReLU
我们手工编码的 YAML + Lua 设置能够胜过基于逻辑回归/决策树的解决方案,并在生产中提供显著的短期用户价值。基于 Lua 的设置在能够向模型添加新特征方面也相对灵活,并且为我们的 ML 工程师释放了继续进行高价值特征工程以提高主页时间线质量的工作。但是,它在探索新的和创新的模型架构方面的灵活性很差。

例如,在训练模型以预测不同的参与度(具有不同程度的稀疏性)时,我们需要测试一系列架构和超参数。具体来说,来回的对话比推文的点赞更稀疏,我们有理由相信,每个目标的最佳模型复杂度和架构根据目标的稀疏性有很大差异。但是,即使是这个简单的任务,即调整主页时间线上不同参与度目标的层配置和超参数,对于我们不灵活的基于 YAML 的模板系统来说也是非常困难的。由于 YAML 不是一个功能齐全的编程语言,因此我们的模型层无法以编程方式定义或优化。

与 Twitter 的中央机器学习和 AI 团队 Cortex 合作,我们确定将时间线的机器学习模型迁移到基于 TensorFlow 的训练和服务栈将帮助我们直接解决与我们内部基于 Lua Torch 的平台相关的挑战。使用 TensorFlow,主页时间线相关性模型的整个模型图可以以编程方式表示为一系列 Python 函数。图 1 显示了我们模型中的一部分,其中包含一系列由 “PreLu” 激活函数激活的密集层
Dense Network Section Of Timelines Model
图 1:时间线模型的密集网络部分
下面的 Python 代码片段展示了如何使用 TensorFlow 中的普通 for 循环构建这个网络(与上面所示的手工编码 YAML 相比)
for i, dense_size in enumerate(dense_layer_sizes):
  dense_layers.add(layers.FullDense(dense_size,
    dtype=tf.float32,
    name='full_dense_' + str(i),
    activation=tf.keras.layers.PReLU() if (i < len(dense_layer_sizes) - 1) else None)
)
接下来,我们将讨论 TensorFlow 支持的时间线质量模型的两个重要创新:拆分网络架构热启动

拆分网络: 我们为 Twitter 时间线运行的第一个深度学习模型是简单的全连接网络。深度学习的一个好处是,这种简单架构能够在给定足够训练数据和时间的情况下学习复杂的特征交互。然而,关于问题领域的先验知识可以帮助找到更优化的模型架构,该架构更快或更容易训练。

我们渴望利用我们对 Twitter 时间线的了解来设计更适合我们问题空间的架构。回想一下,我们用来对推文进行评分的特征可以分为几组:推文本身的特征,与推文作者和用户与他们关系相关的特征,以及仅与用户查看其时间线相关的特征。虽然我们每一组原始特征可能包含数千个组成特征,但我们假设我们可以为每个组成 *逻辑* 实体(例如用户、作者、推文)学习一个紧凑的低维表示,它可以捕获来自原始特征的大部分关键信号。这使我们探索了 TensorFlow 中的“分层网络”架构,其中每一组特征都被分别馈送到其自己的“分层网络”,该网络学习对相关逻辑实体的紧凑表示。
Split Network Architecture For Timelines Model
图 2:时间线模型的分层网络架构
图 2 中所示的“分层”网络比具有相应输入层大小的全连接网络快得多,因为它需要学习较少的超参数(模型权重)并且需要较少的梯度更新,从而显着减少处理能力。新的架构易于在 TensorFlow 中以编程方式实现。以下 Python 代码片段说明了如何在时间线模型中实现分层网络逻辑。
tensors_to_concat = []
for split in input_split_config:
  input_tensor = input_tensors[split.name]
  output_size_bits = split.output_size_bits
  full_sparse_layer = construct_full_sparse(input_tensor, mode,
output_size_bits, params)
  tensors_to_concat.append(full_sparse_layer)
我们使用 TensorFlow 来快速验证我们特定的产品假设,即时间线可以使用分层网络而不会牺牲质量。下表提供了一个全连接网络与“用户、用户-作者、推文”分层网络架构在模型质量和训练时间方面(在相同数量的训练样本上进行公平比较)的对比。
The table shows that the split network architecture achieves better model quality than the fully connected network
该表表明,分层网络架构实现了比全连接网络更好的模型质量,同时将训练时间缩短了约 22%。更快的训练时间对我们的 ML 工程团队来说是一个重要的胜利,因为它们会导致更短的迭代反馈循环,并允许团队改进模型,以便更快地为最终用户提供更相关的时间线。在时间线的背景下,分层网络架构的另一个好处是,可以使用 TensorFlow 仅导出用户和用户-作者拆分的 *嵌入子图*,并使用子图通过定期安排的作业来 *预计算* 近似、紧凑的用户和用户-作者嵌入。如图 2 所示,嵌入可以比生成它们的原始特征紧凑得多。这种优化使我们能够大幅减少特征水化在推理时消耗的网络流量,并显着降低我们服务基础设施的复杂性和成本。

我们现在已将分层网络架构部署到 TensorFlow 中,为所有 Twitter 时间线的生产流量提供服务,我们希望利用 TensorFlow 可编程平台的表达能力,在未来进一步探索更复杂的图和架构。

热启动: 我们使用旧版 Lua 平台训练的第一个深度学习模型是从头开始训练的,为网络的所有参数使用随机初始权重。TensorFlow 平台解锁的一个关键技术创新是能够“热启动”我们的模型,方法是使用以前训练的模型版本中的权重作为比随机权重更好的起点。此功能可从 TensorFlow 中直接获得。任何 TensorFlow 估算器都可以从提供的“模型检查点”中热启动其参数。

https://tensorflowcn.cn/api_docs/python/tf/estimator/WarmStartSettings

我们可以在 TensorFlow 中使用此功能定期刷新时间线预测模型(例如,以每周、每天或每小时的频率),每次刷新迭代都从模型的先前版本停止的地方继续训练。在时间线环境中热启动的好处是双重的

(a) 通过记住模型先前训练中的参数,该模型实际上能够从比“冷启动”模型大得多的训练数据量中学习,从而带来质量上的提升。

(b) 能够以更频繁的频率更新模型。这对于使模型适应用户行为的变化、首页时间线上的产品发布以及实时突发事件非常有用。后者尤其重要,因为 Twitter 本质上是一个实时平台,我们的产品目标是实时将用户连接到他们最合适的对话。

在为时间线模型实现热启动时,我们遇到的一个关键技术挑战是如何使系统足够灵活,以便能够随着时间的推移添加新特征。我们最初在 Lua 上冷启动的模型会通过一个离散器处理实值特征,该离散器会为每个这样的特征生成一组箱,并将每个特征映射到为生成的输出箱生成的一组唯一标识符。这种离散器实现不能直接用于热启动,因为模型先前训练中“记住”的权重特定于先前模型训练中生成的精确箱标识符。我们较旧的离散器无法保证在重复运行时相同特征值将映射到相同的箱索引。幸运的是,TensorFlow 的 *模块化* 特性,作为一个基于 Python 的编程平台,使我们能够轻松地用一个调整后的离散器实现版本替换使用的离散器实现,该版本保证跨多次运行离散器保持箱标识符。我们能够通过一个包含 4 行 Python 代码的代码片段来实现交换,如下所示
if enable_warm_starting:
    return PreserveBinsDiscretizerCalibrator(n_bin=num_bins, out_bits=num_output_bits)
else:
    return DiscretizerCalibrator(n_bin=num_bins, out_bits=num_output_bits)
我们使用 TensorFlow 来评估热启动对时间线 ML 模型的质量益处。我们评估的主要发现是

  1. 从预先训练的模型权重进行热启动,仅使用“冷启动”模型 20% 的训练样本就可以在新的数据上实现相同或更好的模型质量。
  2. 通过调整热启动模型的参数,我们还看到与冷启动相比,质量显著提升(相对对数损失降低 0.4%)。
  3. 当在多个时间段内连续热启动模型时,我们看到与冷启动相比,质量持续提升。

总之,在我们的 ML 模型中采用 TensorFlow 平台,为 Twitter 时间线团队在多个方面解锁了重大优势。我们已经看到了模型质量和 Twitter 用户时间线质量的提升,减少了训练和模型迭代时间,我们的 ML 工程团队也从平台本身的扩展性和可维护性的提升中受益。我们期待在下一代基于该强大平台功能的模型中进一步创新。

TensorFlow 生态系统

采用 TensorFlow 为 Twitter 带来了许多直接好处,从更稳定的生产系统到更具表现力的建模能力。除了这些直接好处外,我们还能够通过解锁对由大型繁荣社区支持的大型工具生态系统的访问来实现间接好处。例如,我们现在能够利用与 TensorFlow 相关的生态系统中的工具,例如 TensorBoard、TensorFlow Hub、TensorFlow 模型分析、TensorFlow 数据验证等。在本节中,我们将展示成为这个繁荣社区的一部分的好处。

| TensorBoard | 借助 Lua Torch,我们依赖文本日志来监控训练行为,这对于显示来自时间序列的模式来说并不理想。TensorBoard 乍一看可能只是一个微不足道的便利工具。实际上,我们发现它在揭示模型训练行为方面非常有用。这转化为性能更好的模型,因为我们能够更好地检测收敛问题,更好地调整学习和衰减率,以及更好地决定何时提前停止。

| TensorFlow Hub | TensorFlow 的模块化特性使我们能够使用 Google 提供的常用层和模块,以及将我们自己的模块发布到 Twitter 的其他部分。在 TensorFlow Hub 之前,我们维护着我们自己的模块共享内部格式(子图及其相关权重)。自 TensorFlow Hub 发布以来,我们已经淘汰了我们自己的内部格式,并采用了一种由 TensorFlow 生态系统支持的更标准的格式。我们现在使用 TensorFlow Hub 在 Twitter 上共享模块(例如,用于发布预训练的词嵌入)。这将我们的模块序列化格式迁移到了一个开源标准,同时还解锁了对 TensorFlow Hub 存储库 中现有模块的访问。除了共享模块外,我们还使用 TensorFlow Hub 来执行其他任务,例如多阶段训练、微调(例如,微调预训练嵌入)以及将多个训练过的模型堆叠在一起。

| TensorFlow 模型分析 | 在我们以前的 Lua Torch 解决方案中,我们实现了一个名为“特征汇总”的功能。它使我们能够分析训练过的 ML 模型在给定数据集的不同片段上的性能。这些片段是通过在输入数据集中指定一个二进制或离散特征来定义的。拥有相同的功能是我们排名时间线团队采用 TensorFlow 之前的一个阻碍性功能请求。在 TensorFlow 模型分析发布后,我们意识到它提供了我们想要的功能的超集。除了“特征汇总”外,TensorFlow 模型分析还带来了基于更复杂逻辑定义片段(切片)的能力以及执行分布式处理以加快模型评估的能力。

| TensorFlow 性能分析器和 Chrome 跟踪 | 我们以前的基于 Lua Torch 的解决方案经过高度优化。当我们将排名时间线模型训练迁移到 TensorFlow 时,我们观察到与 Lua 相比,速度下降了 10 倍以上。TensorFlow 附带的性能分析器帮助我们解决了这个问题。性能分析器以标准格式生成输出,可以在 Chrome 中可视化。这些漂亮的图表(图 3 中显示的示例屏幕截图)使我们能够快速定位瓶颈,并将我们的优化工作集中在“最具性价比”的位置。借助 TensorFlow 性能分析器,我们能够将训练速度恢复到与我们以前解决方案相同的水平,该性能分析器引导我们发现效率低下之处。
screenshot showing an example of TensorFlow profiler’s output helping us compare different model optimization.
图 3:屏幕截图显示了 TensorFlow 性能分析器的输出示例,帮助我们比较不同的模型优化。
| 招聘 | 最后,成为 TensorFlow 生态系统的一部分的副作用是,招聘 ML 专家变得更加容易。与 Lua Torch 相比,现在有更多的人才已经了解 TensorFlow 或愿意在工作中学习 TensorFlow。我们的团队规模在 2018 年显著增长,并且我们正在继续为 Cortex 和排名时间线(应用 ML)团队招聘人员。

结论

TensorFlow(和 TensorFlow Extended)已被证明是一个可靠且强大的工具生态系统,使我们的团队能够更快地为用户提供价值。一般而言,我们的工程师更喜欢 TensorFlow 建模 API 比传统的 YAML API。我们期待在即将发布的 TensorFlow 2.0 版本中采用基于 Keras 的建模 API。这种灵活性使高级建模人员能够发挥创造力,同时也能被希望获得 ML 经验的工程师所接受。围绕 TensorFlow 的工具套件紧密集成到我们的核心 ML 平台中,并提供了无与伦比的开箱即用价值。TensorFlow 在生产环境中很稳定,性能问题很容易进行性能分析和修复。

在时间线团队(以及 Twitter 上超过 50% 的其他应用 ML 团队)成功采用 TensorFlow 后,Twitter 的每个机器学习团队都将在 2019 年采用 TensorFlow 来替换他们以前的模型。我们正在积极投资于支持分布式训练和集成剩余的 TFX 产品。如上所述,各团队渴望采用 TensorFlow 2.0 以访问 Keras 风格的建模和 Eager 模式。总的来说,TensorFlow 在开发人员生产力和满意度方面取得了重大进步。

致谢

我们要感谢 Google 与我们持续合作,帮助推动 Twitter 的 ML 发展。我们还要感谢 Twitter 的首页时间线团队和 Cortex 在将 TensorFlow 用于时间线排名方面的成功合作。
下一篇
Ranking Tweets with TensorFlow

来自Twitter Cortex和Home Timeline团队的Yi Zhuang、Arvind Thiagarajan和Tim Sweeney的客座文章

作为全球性的公共通信平台,Twitter致力于通过相关、健康的内容为用户提供信息。最初,Twitter以倒序的方式呈现推文。随着社区的连接度越来越高,用户时间线上的内容数量也显著增加…