TensorFlow Ranking框架在海外推荐业务中的实践与应用

2020 年 11 月 16 日

TensorFlow Ranking框架在海外推荐业务中的实践与应用

前言

在当今互联网世界,推荐系统在内容分发领域扮演着至关重要的角色。如何尽可能的提升推荐系统的推荐效果,是每个推荐算法同学工作的核心目标。在爱奇艺海外推荐业务,引入TensorFlow Ranking(TFR)框架,并在此基础上进行了研究和改进,显著提升了推荐效果。本文将分享TFR框架在海外推荐业务中的实践和应用。


01 算法的迭代:从传统 CTR 预估到 LTR


长期以来,在推荐系统排序阶段广泛应用的 CTR 预估算法的研究重点在于,如何更加准确的估计一个用户对于一个 item 的点击概率。在这类算法中,我们将一组同时曝光在用户面前的 items,当做一个一个单独的个例看待,将用户的特征、环境特征和一个一个 item 的特征分别组合成为一条条训练数据,将用户对这个 item 的反馈(点击、未点击、播放时长等)作为训练数据的标签。这样看似合理的问题抽象其实并不能准确的表征推荐场景。


严格来讲,排序问题的本质(尤其是以瀑布流形式呈现的业务)并不是研究估计一个用户对于一个单独的 item 的点击概率,而是研究在一组 items 同时曝光的情况下,用户对这组 items 中哪个的点击概率更大的问题。


Learning-To-Rank(LTR)算法正是为解决这个问题而出现的。LTR 算法在训练时采用 pairwise 或者 listwise 的方式组织训练数据,将一组同时曝光在用户面前的 items,两两(pairwise)或者多个(listwise)items 和用户特征环境特征共同组成数据对,作为一条条的训练数据。相应的,在评估模型的指标上,LTR 算法更多采用 NDCG、ARP、MAP 等能够反映 items 顺序影响的指标。


同时,由于 LTR 算法的这种训练数据组织形式,使得这类算法在用户量相对不大的场景下,更容易取得比较好的效果。同样得益于这种数据组织形式,也很方便的实现更好的负样本采样。


(关于推荐业务中采样和模型评估指标之间还有一个有趣的研究可以参考,2020 KDD Best Paper Award ,On Sampled Metrics for Item Recommendation)


02 框架的设计:TensorFlow Ranking(TFR)


TensorFlow Ranking(TFR)是 Tensorflow 官方开发的 LTR 框架,旨在基于 TensorFlow 开发和整合 LTR 相关的技术,使开发人员可以更加方便的进行 LTR 算法的开发。


在实际使用过程中,可以体会到 TFR 框架为我们带来的收益。框架内抽象出了训练中不同层级的类,并开发了相关的 loss 函数,以方便我们进行 pairwise 和 listwise 的训练,同时整合了 arp、ndcg 等模型评估的 metrics,再结合 tf 高阶 api(Estimator),可以非常方便快捷的进行开发,而不用挣扎于各种实施上的细节。



如上图所示,蓝色框图中是在使用 TensorFlow Estimator 时,modelfn 参数内需要自己设计和开发的算法模型模块。在这个 modelfn 中,需要自行设计模型结构(Scoring Function),然后用模型计算的 logit 和 label 来计算 Loss 和 Metrics,最后利用 Optimizer 来进行模型的优化。图中,红色虚曲线下方的部分为使用 TFR 框架的整个流程。


从图中可以看出,其实 TFR 框架主要是做了两方面的工作:


  1. 把原有model_fn中Scoring Function和Loss、Metrics的计算进行了拆分,然后将原有流程中我们自行实现的Loss和Metrics替换为TFR框架中的LTR相关的Loss和Metrics。

  2. 为了配合TFR框架中的LTR相关的Loss和Metrics来实现LTR的训练,训练数据需要以listwise的形式组织。但由于需要使用原有model_fn中Scoring Function,在数据输入的部分通过LTR框架中的数据转换函数来对模型输入的训练数据进行转化,使得以listwise形式组织的数据能够利用Scoring Function来计算logit。


所以,在 TFR 框架中,从数据到模型训练完整的流程是:训练数据--》用户定义 featurecolumns--》transformfn 特征转换--》Scoring Function 计算 score--》rankinghead 的 lossfn 计算 loss–》rankinghead 的 evalmetric_fns 计算评价指标–》optimizer 进行优化。


从使用层面看,TFR 框架就是做了上面两件事,看起来似乎并不复杂。但是从框架开发的角度上看,为了实现上述流程,TFR 框架内在 losses.py 和 metrics.py 中开发了多个 LTR 相关的 Loss 和 Metrics,在 data.py 内实现了读取和解析以 listwise 形式组织数据的 tfrecords 文件的工具,还在 feature.py 中开发了兼容 TensorFlow 特征转换函数的特征处理工具。最后通过 head.py 和 model.py 中的类对上述功能进行了层层封窗和抽象,并与 TensorFlow Estimator 很好的结合起来。


具体一些来看,代码组织上,TFR 框架主要这样实现的:


第一:


tfr 通过


tfr.model.makegroupwiserankingfn 来对 Estimator 的 modelfn 进行了整体的封装。我们原有的基于 tf 的开发,在 Estimator 的 modelfn 这个参数内需要定义包括 Loss、Metrics 在内的完整的模型函数,但是在 tfr 这里就不需要了,makegroupwiserankingfn 会整体返回一个 Estimator 接收的 model_fn。


第二:


tfr.model.makegroupwiserankingfn 函数的第一个参数 groupscore_fn,这里是需要传入我们设计和开发的模型结构(Scoring Function),但是这个模型是之前我们提到的,只需要计算出 logit 的模型。


第三:


tfr.model.makegroupwiserankingfn 函数的第三个参数 transformfn,对应调用 feature.py 中开发了兼容 TensorFlow 特征转换函数来对以 listwise 形式组织的数据(由 data.py 中的工具读取进来的 Dataset)进行转换,确保输入 Scoring Function 的数据格式正确。


第四:


tfr.model.makegroupwiserankingfn 函数的第四个参数 rankinghead,对应调用了 tfr.head.createrankinghead 函数,里面的三个参数分别定义了 loss、metrics 和 optimizer。loss 和 metrics 分别从 TFR 的 losses.py 和 metrics.py 中选择我们需要的,而 optimizer 还是使用 TensorFlow 中的 optimizer。


以上就是 TFR 框架的整体架构,其实这个框架整体设计和代码实现,还是非常优雅和巧妙的。


03 遇到的问题和实践


TFR 框架的精巧设计和实现解决了我们基于 TensorFlow 做 LTR 算法中的 80%到 90%的问题。但是作为一个 2019 年才发布第一个版本的框架,TFR 还是存在一些待优化的地方。


在分享 TFR 框架上的实践前,首先介绍一下 TFR 框架的版本情况,目前 TFR 框架发布的版本中,0.1.x 版本支持 TensorFlow 1.X 版本,而 0.2.x 和 0.3.x 版本都只支持 TensorFlow 2.X 版本。考虑到 TensorFlow 2.X 版本还存在一些不确定性(如前段时间爆出使用 Keras 功能 API 创建的模型自定义层中的权重无法进行梯度更新的问题。


https://github.com/tensorflow/tensorflow/issues/40638),目前大量的算法开发人员其实还在用 TensorFlow 1.X 版本。我们目前也在使用 TensorFlow 1.X 版本,所以本文介绍的内容,描述的问题和给出的解决方案,都是基于 TensorFlow 1.14 版本,对应最新的 TFR 0.1.6 版本。


我们最开始使用 TFR 框架是 2019 年年中的时候,当时 TFR 框架的最新版本是 0.1.3。在使用的过程中,我们发现这个版本无法支持 sparse/embedding features。但是推荐的特征中,稀疏特征是不可或缺的一部分,并且可能大部分特征都是稀疏的,所以我们不得不放弃使用。但是很快,在稍后发布的 0.1.4 版本中这个问题就得到了解决。


我们正式开始使用 TFR 框架是从 0.1.4 版本开始的。但是到目前最新的 0.1.6 版本,还是有两个我们不得不用的特性还是没有在 TFR 0.1.x 版本上得到支持:


  • 训练过程中无法实施正则化


如前所述,TFR 框架通过 makegroupwiserankingfn 来对 Estimator 的 modelfn 进行了整体的封装。


我们自己设计和开发的模型(Scoring Function),定义了网络,输入输出节点,最后只需要输出一个 logit。这个和传统 TensorFlow Estimator 下 modelfn 模型开发不一样,传统的模型不仅仅要输出一个 logit,模型里面还需要定义如何计算 loss,怎样优化等内容。但是 TFR 框架将这部分内容已经进行了封装和整合,所以这里的 scorefn 就不需要这些了。这就带来一个问题,原来的模型设计中,我们可以直接拿出网络中需要正则化的参数,放在 loss 的计算中进行优化就可以了。但是使用 TFR 框架后,由于模型的设计和正向的计算在我们自己设计的模型函数中,而 loss 的计算在 TFR 框架内(rankinghead 中的 lossfn)进行,这样就没办法加入正则化项了。


已经有人提出了这个 issue,但是也没有很好的解决方案:(https://github.com/tensorflow/ranking/issues/52


当我们使用比较复杂的网络时,正则化是我们优化过程中必不可少的一环。不加入正则化项进行优化,将无法避免的陷入到严重的过拟合中,如下图所示:



为了能够方便的利用 TFR 框架其他功能,我们深入 TFR 框架源码中试图解决正则化问题。正如上面分析,TFR 框架无法实施正则化的原因在于,模型(Scoring Function)是我们自己设计和开发的,但是 loss 的计算是 TFR 框架帮我们封装好的。所以解决这个问题的核心就是如何在我们自己开发的模型中取出需要正则化的参数并传递给 TFR 框架中计算 loss 的部分就可以了。在 TFR 框架中,我们的模型计算好的 logit,是通过 rankinghead 的 createestimatorspec 方法,把 logit,labels 与 TFR 框架中定义的 loss 函数整合一起,来完成整个优化过程的。而在 0.1.5 版本的 TFR 框架中,这个 createestimatorspec 方法其实已经支持传入 regularizationlosses 了(估计未来版本一定会支持),而由于初始化 ranking_model 对象(我们的 Scoring Function)。


GroupwiseRankingModel 不支持我们拿到自己模型的正则化项,所以才无法实现。


理论上,只要我们重写 TFR 源码中 GroupwiseRankingModel 类的 computelogits 方法,就能够让 TFR 支持正则化了。具体的代码上如何处理可以参考这里(如何解决 TensorFlow Ranking 框架中的正则化问题)。在加入正则化项后,跟上图同样的模型训练时就没有那么严重的过拟合现象了:



  • 特征输入不支持Sequence Features


前边介绍过,在 TFR 框架中,模型输入的特征分为 contextfeatures 和 examplefeatures,分别对应于一次请求公共的特征(上下文特征、用户特征等)和 item 独有的特征。以 listwise 形式组织的数据(一般是由 data.py 中的工具读取 tfrecords 文件生成的 Dataset)需要经过 TFR 的特征转换函数(transformfn)转换后,再送入到我们的模型(Scoring Function)中。


而目前的特征转换函数(transformfn)只支持 numericcolumn、categoricalcolumn 等经典类型特征的转换,尚不支持 sequencecategoricalcolumn 类型特征的转换。要解决的 TFR 无法支持 SequenceFeatures 问题,主要是对 transform_fn 特征转换这一步进行调整。


在 transformfn 中,特征转换时用到的 tfr.feature.encodelistwise_features 和


tfr.feature.encodepointwisefeatures 函数都在 feature.py 中定义。


这两个函数的作用是在 listwise 或者 pointwise 模式下利用用户定义的 feature columns 生成输入模型的 dense tensors。这两个函数都是调用 encodefeatures 函数来具体执行 feature columns 生成输入模型的 dense tensors,而 encodefeatures 函数只支持 numericcolumn、categoricalcolumn 等经典类型特征的转换,尚不支持 sequencecategoricalcolumn 类型特征的转换。通过这里的分析,我们可以看到特征转换的过程全部是在 feature.py 中完成的,因此,解决 TFR 框架支持 SequenceFeatures 的问题核心思路就是修改 feature.py 中的几个涉及特征转换的函数,使这些函数能够实现 sequencecategoricalcolumn 类型特征的转换。


我们用到的 sequencecategoricalcolumn 类型特征都在 contextfeatures 中,所以我们的思路是,在处理特征的转换时,我先将 sequencecategoricalcolumn 从其中拿出来,处理完经典特征的转换后,单独增加一段处理 sequencecategoricalcolumn 转换的代码。待转换完成后,再合并回 contextfeatures 中,最终仍然保持 contextfeatures 和 examplefeatures 两部分输入到模型中。具体的代码上如何处理可以参考这里。(让 TensorFlow Ranking 框架支持 SequenceFeatures)


以上两个问题的解决方案都涉及到 TFR 框架对源码的修改。稍有不慎很容易引起稳定性兼容性问题以及意想不到的 bug。为了尽量保障代码的稳定可靠,我们主要考虑了两个主要的代码组织原则:


第一, 尽量缩小代码改动的范围,所有的改动都在尽可能少的几个函数内完成,不涉及 TFR 框架的其他模块代码。


第二, 对于不涉及上述两个问题的项目要做到完全的兼容。对于不使用 feature columns 的项目或者不使用正则化(应该很少),保证原有逻辑和计算结果不变。


04 实验:LTR 模型和原生模型的效果对比


究竟 TFR 框架训练的 LTR 排序模型对比同样网络结构的原生模型,能够带来多大的效果提升呢,我们也专门做了线上实验来分析。选取了一个业务场景,取出三个流量组分别做以下模型:


  • BaseB: 没有排序服务,为每个召回渠道配置优先级,系统按照优先级给出推荐结果。

  • Ranking: TensorFlow原生Estimator开发的排序算法。

  • TfrRankingB: 基于TFR框架开发的LTR排序算法。


其中,TfrRankingB 相比较于 Ranking,模型结构完全一致,也就是采用同一个 Scoring Function,训练数据集也完全一致。但是由于 TfrRankingB 采用 TFR 框架训练的 LTR 模型,模型优化上有以下几处不同:



以上的几处不同是模型训练方式和评估指标上的不同,这也正是采用 TFR 框架带给我们的。而训练数据和模型本身,包括正则化项在内,TfrRankingB 和 Ranking 是完全一样。两个模型训练后,与 BaseB 一起在线上真实流量环境下测试完整 4 天,其中 day1 和 day2 为平日,day3 和 day4 是休息日。线上实验考查用户的 CTR(点击率)、UCTR(用户点击率)和 LPLAY(长播放占比),效果如下:





考虑到业务保密性,我们对横纵坐标的具体取值不做展示。但是结论显而易见:


  1. 在CTR和UCTR指标上,TfrRankingB显著优于Ranking,Ranking显著优于BaseB。

  2. 在LPLAY指标上,TfrRankingB优于Ranking,Ranking优于BaseB。


总结

使用TFR框架后,可以非常方便的基于TensorFlow开发LTR模型或者将现有模型改造为LTR模型。同时,TFR框架的模块设计、代码逻辑都非常巧妙,诸如高内聚低耦合等大家常常挂在嘴边的规范也实实在在的落在了代码上。在接下来的工作中,逐步将现有的TensorFlow 1.X版本升级到2.X版本,并观察TFR框架对TensorFlow 2.X的支持情况。


参考文献


  1. Burges C J C. From ranknet to lambdarank to lambdamart: An overview[J]. Learning, 2010, 11(23-581): 81.

  2. Liu T Y. Learning to rank for information retrieval[M]. Springer Science & Business Media, 2011.

  3. Pasumarthi R K, Bruch S, Wang X, et al. Tf-ranking: Scalable tensorflow library for learning-to-rank[C]//Proceedings of the 25th ACM SIGKDD International Conference on Knowledge Discovery & Data Mining. 2019: 2970-2978.

  4. Krichene W, Rendle S. On Sampled Metrics for Item Recommendation[C]//Proceedings of the 26th ACM SIGKDD International Conference on Knowledge Discovery & Data Mining. 2020: 1748-1757.

  5. https://github.com/tensorflow/ranking


本文转载自公众号爱奇艺技术产品团队(ID:iQIYI-TP)。


原文链接


TensorFlow Ranking框架在海外推荐业务中的实践与应用


2020 年 11 月 16 日 10:11 547

评论

发布
暂无评论
发现更多内容

ARTS打卡第一周

GKNick

DDD 中的那些模式 — 使用 Specification 管理业务规则

Joshua

设计模式 领域驱动设计 DDD 架构模式

ARTS-01

NIMO

ARTS 打卡计划 ARTS活动

5G时代下应用的安全防御研究

Nick

5G 5G网络安全 5G安全

重学 Java 设计模式:实战单例模式

小傅哥

设计模式 编程思维 重构 优化代码

线程池续:你必须要知道的线程池submit()实现原理之FutureTask!

一枝花算不算浪漫

源码分析 并发编程

区块链技术大显身手,仅用20分钟就打完一场官司!

CECBC区块链专委会

CECBC 区块链技术 数字版权 存证

后疫情时代,区块链的发展迎来曙光!

CECBC区块链专委会

CECBC 区块链技术

深入计算机底层,从几本靠谱的书开始

HackMSF

计算机工作原理

关爱孩子的心理建设

Neco.W

人生 感悟 教育

眼中有码,心中无码

小眼睛聊技术

深度思考 学习方法 程序员 最佳实践 算法

我为什么开始技术写作?

架构精进之路

技术创作

ARTS打卡计划_第一周

叫不醒装睡的人

ARTS 打卡计划

体验一次简洁的代码

你当像鸟飞往你的山

Vue生态篇(一)

shirley

JavaScript Vue

如何做好Code Review?

架构精进之路

Code Review

如何设置线程池参数?美团给出了一个让面试官虎躯一震的回答。

why技术

Java 源码分析 面试题 线程池

MySQL 可重复读,差点就我背上了一个 P0 事故!

楼下小黑哥

Java MySQL

那些会阻碍程序员成长的细节[2]

码闻强

程序员 程序人生

像孩子一样认识新事物 —— 读《终身幼儿园》

YoungZY

读书笔记 学习方法 读书

爬虫框架Scrapy应用实践-淘宝保险频道数据抓取【2】-抓包分析

hadesxiong

Python 爬虫 保险 Scrapy

数据产品经理实战-数据门户搭建(上)

第519区

数据中台 开发数据

Java日志门面系统

泛泛之辈

Java 日志 slf4j

Mysql索引不会怎么办?6000字长文教会你

Super~琪琪

MySQL 数据库 sql 索引

我的编程之路 -6(新时代)

顿晓

android 编程之路 时代

ARTS week 3

刘昱

Flutter开发环境配置

玉龙BB

flutter android vscode

[ARTS打卡] week 01

Mau

ARTS 打卡计划

clang-format 使用与集成介绍

Geek_101627

2万字长文带你细细盘点五种负载均衡策略。

why技术

Java 负载均衡 源码分析 dubbo java面试

ARTS-1

你当像鸟飞往你的山

ARTS 打卡计划

微服务治理平台化探索

微服务治理平台化探索

TensorFlow Ranking框架在海外推荐业务中的实践与应用-InfoQ