一、推荐系统中的排序公式
首先,我们来探讨排序公式在推荐系统中的重要地位。对于眼界广阔的算法工程师来说,他们在各种大公司中的工作可能看起来像是在进行数据分析,或者进行开发,或者策略制定,但实际上他们所做的一切都是为了一件事——服务于最终的排序。这是因为,推荐系统的最终输出就是排序结果。
无论是从事特征工程、模型训练,还是推荐策略的人,又或是那些努力将业务理念融入推荐结果的人,他们所有的努力,都是为了将个性化的差异体现在最终的排序结果中。为了实现这个目标,必须依靠排序公式。排序公式是实现我们所有目标,并最终展现出推荐结果的关键。
由于排序公式在推荐系统中占据着核心地位,大公司对其的研究和迭代都非常深入且广泛。排序公式主要包括两种融合方式:序融合和值融合。
首先,序融合主要关注各个模型或其提供的特征以及给出的排序信号。这种方法只关注排名,而不关心其具体的数值。这样做的优点是,由于我们直接在排名(rank)维度上进行排序和融合,这个过程已经摆脱了各个子模块之间尺度的巨大差异影响。这种技术更适合处理那些最后只需要排名,而无法最后确定精确评分的推荐算法,如混排的场景。然而,这种方法的缺点是会丢失很多信息。
很多大公司也在做各种尝试,有的公司以前可能主要迭代值融合的算法,但后来觉得不能减小方差或差异性,因此改为迭代序融合,尝试了一段时间后又回到了迭代值融合。这两种方法并没有绝对的好与坏,需要结合业务需求选择最佳的结合方式,或将二者组合使用。
接下来介绍序融合。序融合中最简单最常见的是 rank product 方法,该方法引自遗传算法,用于研究哪个基因最重要,之后很多融合算法都受到了生物信息学的启发。在实际应用中,序融合的研究和使用数量更多。
值融合是直接对各个推荐子模块的输出值进行操作。这种方法的优点是易于理解和调整。调整时,可以清晰看到所调整的子模块或公式部分如何影响结果范围,如何产生作用,如何挤压其它信号,以及如何改变整个融合分的均值。这些都可以通过离线或在线的方式预估。因此,值融合不仅易于实现,而且对于进行迭代的研究者而言,它降低了迭代的压力,使得整个迭代过程变得更容易。但是,其缺点在于如果某些排序因子预估值分布是在 0.1,那么会导致融合效果不理想。
有些目标如观看时长或打赏金额等,具有明确的物理含义,而它们本身的尺度(scale)差距非常大。在这种情况下,直接融合无法获得良好的效果。因此,我们需要采用某种标准化方法或映射来解决这个问题。在数据来源相对统一,例如同一模型得到的不同预估值,或者尺度接近的情况下,值融合的方式会比较好,操作起来也更加简单。
为了更好地理解推荐系统中的排序公式融合方式,我们应明确融合公式需要解决的问题以及应如何定义优秀的融合公式。在本文中,优良的融合公式应至少满足以下基本要求:
- 一是具有区分度,因为融合公式融合了各种先验信息、预估信号以及业务需求得到的综合评分,若评分没有区分度,那么排序机制将失效。
- 其次,我们需要最大限度地保留各排序因子的信息量,因为如果融合公式因设计问题而无法保留原始排序因子的信息,就等同于抛失了这个排序因子的信息。
- 最后一点,若排序公式过于复杂,如包含过多的超参数,这将增加人工调参的难度,使我们可能沉溺于局部最优而无法看清整体趋势,为此,我们将在后续提供一些方法,以减少参数冗余,并更好地理解数据分布,从而更有效地进行参数调整。
在实际工作中,优化并非仅限于单一指标,实际上,优化过程中涉及到的因素或指标间既有可能同向优化,又有可能不相关或相互制约。因此,认识和处理好这些关系,对于推荐系统的优化具有重要意义。
二、业务夹角与多目标权衡
在实际工作中,调整模型参数的过程经常“牵一发而动全身”,某个业务的微调可能会对其他相关业务产生影响,这也使得参数优化过程中压力倍增,要实现“帕累托优化”(在保障所有业务不受影响的同时,仍能取得收益)实际上是一项艰巨的任务,除非我们关注的指标集合相对较少。然而,在现实环境下,各业务方都有自己的需求,我们需要尽可能地满足这些需求。因此,问题转变为我们需要牺牲多少及何种业务来换取我们需要的指标,以及针对这些指标进行多少折算。总的来说,对于融合公式的迭代来说,这个权衡是必须要面对的问题。
在进行权衡时,我们需要基于两种判断:价值判断和事实判断。价值判断来自于高层决策,例如公司策略决定大力发展短视频或直播等,这种决策反映了公司的价值取向,并非我们可以主观改变的。事实判断则是一种客观存在,例如即使我们决定将短视频业务去置换直播业务的某种收益,但这两项业务之间的置换难度是客观存在的。为此,我们需要在多目标优化中尽可能感知到这种置换的难易度。这可以通过某种工具或调参方式来实现,使我们能够发现业务间置换的实际难度。在制定多目标权衡设计框架时,我们需要同时考虑公司战略的价值判断和事实判断,这是优化权衡的基本出发点。
以上介绍了调整推荐系统排序公式融合过程中面临的挑战和需要做出的决策。
三、排序公式离线寻参原理
下面要探讨的问题是如何迭代排序公式,进而提升技术效能。对此,我们需要首先考虑为什么要进行离线迭代及其必要性。许多公司投入大量人力资源在调整排序公式上,算法工程师们可能需要投入大量精力在这样的任务上。这是一项费时、费心、费力的工作,却又非常必要。一旦参数调整成功,其效果将十分显著。
那么,为什么我们会选择离线调整参数呢?事实上,也有公司会选择将强化学习接入到线上系统中,以实时学习和调整参数。然而,我们并没有看到这种方法成为主流,原因主要有以下三点:
- 首先,我们很难确保线上调参的质量。由于线上调参实际上是在实时运行的系统中试错,不能使用大规模流量进行广范围的搜索,因此无法有效覆盖整个样本空间。而且,由于这是一个实时交互的过程,结果具有很大的不确定性,且几乎没有试错的空间。因此,使用强化学习在实时交互的环境中进行训练是非常困难和缓慢的。
- 其次,线上调参的效率无法保证。在线上调参的过程中,我们无法保证系统能快速收敛。而且,由于系统状态在不断变化,无法预知系统的最终状态。考虑到排序公式并无必要实时更新,如果将其视为实时模型,便意味着需要在现有的排序结果中嵌入自己的业务理念。由于排序公式的更新一般并不需要达到分钟级,我们完全有条件进行离线调参。
- 最后,线上过程很难处理多目标优化问题。如前文所述,多目标优化需要同时考虑价值判断和事实判断,即需要同时考虑上层的业务需求和当前系统的实际状态。在线上环境下难以实现这种优化,因为我们无法进行反复或交叉验证。例如,我们无法获取 A 置换 B、B 置换 C 等等所需的置换难度。而在离线环境下,由于其封闭性,我们可以测定各种置换的边际效益和置换效果。
基于以上三个方面的考虑,我们提出了离线调整排序公式的优化路径。
接下来介绍离线调参框架的基本原理。首先我们需要考虑要构造什么样的融合公式,有些工程师可能倾向于加法公式,有些可能倾向于乘法公式,甚至可能有精心设计的、具有特定物理含义的公式,这种公式在广告行业比较常见,还有一些复合公式,即基于某个公式输出进行再次配置的公式,这种可以看作是一种树状结构的公式,可以写成 JSON 格式,这也是一种融合公式。
我们首先需要考虑要迭代的公式是什么形式,一旦确定了这个,我们就可以知道有多少超参数需要进行迭代。实际上,迭代公式就是迭代超参数,我们希望知道在新的公式形式下,这些超参数被调整到什么样子,线上的某些指标将如何变化,这是我们要解答的问题。确定了融合公式的形式后,就可以开始收集离线样本了。
在收集离线样本时,需要考虑关注哪些指标,比如点击率、观看时长、分享数、打赏数等。这是在每次迭代时都要明确定义的。对于这些目标,我们会定义自己的计算方式。例如,对于离散变量点击或未点击,可以使用 AUC 来衡量。对于连续变量,例如观看时长,可以进行分桶或使用其他评估方法来进行度量。因此,第二步就是确定希望优化的目标,并决定怎么计算。
确定了离线目标后,就要考虑如何将这些目标进行融合,这就涉及到我们刚刚讨论的多目标融合,即价值判断和事实判断。你可能认为点击的 AUC 和分享的 AUC 同样重要,那么在我们优化的目标中,这两个指标的权重就可以设为 1:1。这就是价值判断部分。事实判断部分就是,当我们完成了离线寻参,我们一定会发现有些指标降不下去,有些很容易改进。根据这个认知更新,我们需要调整我们的优化目标设计。这就是事实判断部分。
根据我们定义的结果合并方法及子目标的方法,可以使用算法工具,比如 Optuna 进行寻优。这就是我们进行 sorted top-K prediction 问题中的求解方法。
综上所述,离线调参框架至少需要四个功能:确定融合公式、评估子目标、融合子目标,还需要有一个优化器来求解。这就是我们从技术角度拆解的离线调参框架。
四、融合排序量化寻参实战
接下来介绍一款业界广泛使用的开源框架——ParaDance(项目链接:https://github.com/yinsn/ParaDance),该框架由我个人开发。这是一款开箱即用的、全自动的排序公式迭代框架。该框架在 Linux 和 Mac 上都可以通过 pip 工具进行安装。
ParaDance 是一个开源项目,主要在业余时间进行开发和维护。因此,如果你对此感兴趣,也可以贡献你的代码。该框架的特点在于其开箱即用的特性,它非常适合一线算法工程师进行排序公式的调整。其次,它支持离线计算,并且已实现并行处理,可以很快地运行。
由于得到了许多同行的帮助和建议,ParaDance 已经具备了丰富的评估手段,不仅支持如 AUC 这样的评估方式,也支持其他各种子目标的计算方式。此外,它还支持丰富的公式形式,不仅仅是加法和乘法公式,你甚至可以将自己想要的公式以 JSON 的形式传入,ParaDance 可以很好地支持这些操作。
此外,ParaDance 提供全程监控功能,可以监控每个子目标和综合目标在迭代过程中的变化情况,让你一目了然地知道哪些指标有明显的改进,哪些指标难以迭代等信息。安装了该库后,我们就可以着手准备离线数据了。
例如,这里我们模拟了一些数据,包括四种类型的 XTR(XTR 0 到 XTR 3),并关注了“点击”和“分享”两种子目标。我们更关注“点击”这个目标,认为它比“分享”更重要,这是由实际经验和业务需求所决定的。如果我们决定按照这种方式进行调参,那么就需要收集相应的离线数据。这种基础设施在各公司应该都已经存在。只需对离线数据进行采集和均匀采样,这是非常容易实现的。根据经验,大约需要几百万条数据,才能获取到一个相对稳定的调参状态。
然后,我们要进行的第一步是确定融合公式的形式。实际上,很多公司在加法公式和乘法公式之间反复调试。为了方便大家使用,这里还支持了一种称为"freestyle"的类型,可以在其中任意编写各种形式的公式,甚至支持函数类型推导,还可以在其中加入条件语句等。甚至,还支持一种非常复杂的形式,即直接传入一个 JSON 来定义你的融合公式。对于那些有几千行的复杂公式都能够支持。无论你想调整公式中的一部分,还是整体,我们的框架都能够支持。确定了融合公式的形式后,我们就可以开始构造我们的融合公式了。
代码实现上,首先选择了四个排序因子作为考虑对象,并使用融合公式进行组合。在这个例子中,采用了"freestyle"形式的融合公式,该公式具有一定的逻辑意义和物理解释。例如,“weight”乘以紧随其后的两个排序因子(即,"columns 1"和"columns 2"),它们恰是对应的排序因子。这个公式是根据实际情况、专业知识和经验判断定义的,代表着预期公式应该是什么样的形式。
在框架中,根据确定的公式形式,会评估并挑选出最佳的公式组合。例如,在这个场景中,公式形式为“freestyle”,目前有四个超参数需要调整,即 weights[0] 到 weights[3],共计四个超参数。迭代完成后,框架会给出最佳的参数组合,从而使公式达到预期的效果。
此后,将离线数据传递给模型,指定所需的排序因子,并指定融合公式的类型和形式,这些都是事先定义好的。这是在确定融合公式形式后的必要步骤,且其代码实现极其直接。
此外,还需要确定子目标的评估方式以及融合方式。前述中提到的"点击"和"分享" 是分别作为子目标进行考虑的,对于如何评估这些子目标需要提前确定。例如,对于"点击"这一指标,可以采用 AUC(Area under the ROC Curve,ROC 曲线下的面积)进行评估。值得注意的是评估方式可以多种多样,AUC 只是其中一种相对易于接受和理解的评估方式。然后就是确定子目标之间的融合方式,即如何设定综合目标的计算公式。
一个业务决策的例子是,我们可能认为"点击"指标的 AUC 更重要,因此我们给它赋予更大的权重。在这种情况下,我们可能会将其权重设为 2,这个决定完全来自于我们的业务洞察,如老板觉得"点击"指标是其他指标重要性的两倍。这将作为我们的初始设定。然后我们创建一个多目标优化器对象,其中包含刚刚创建的融合公式和我们希望优化的目标方向。如这里我们设置了"maximum",表示我们希望 AUC 尽可能大。然后,我们将融合目标输入,设定所需的超参数数量,这些都可以有默认值。如果有具体需求,我们可以为每个超参数设置精确的范围,如大于 0、小于 1;大于 -1、小于10;大于 1、小于 100;大于 -1、小于 50 等,都可以自由设定。
另外,"study name"选项会生成一个包含所有结果的文件夹。这有助于我们准确了解每次实验的内容,尤其是当我们在一天中进行多次尝试的时候,很容易混淆每次实验的目标和结果。因此,我们提供了一个便利的命名功能,可为你的实验记录添加具体描述。
在定义了融合公式和目标后,剩下的代码实现就仅有一行,即"run"。"run"后的参数设定了循环的次数,这里设置了 300 次。实际的轮次可能根据具体的问题和难度有所不同,但是通常几百次的迭代就已足够使得结果收敛。这是因为排序公式也有自身的容量限制,不能无限地优化一个指标。
代码运行结束后会输出一个文件夹,第一个输出的文件是我们这个实验的配置信息。第二个文件 paradance_best_trials.csv 中记录了它优化的过程,包括两个子目标的优化过程,以及每一个超参对于每一个非列解等于多少,一般情况下是取最后一行,即最好的一组参数进行使用。文件 paradance_storage.db 和 paradance.log 分别是 check_point 和完整的迭代记录。
*可通过以下链接获取示例数据和代码:
https://pan.baidu.com/s/1AI9MkYhiJwd182gmtHL7fQ?pwd=1234
五、Q&A
Q1:离线评估如何构建样本数据?有些候选集在线上的排序方式可能压根没有下发,没有这部分候选集的后验数据如何保障离线评估是否准确的呢?
A1:你提出的问题非常重要。我们目前的方式的确必须需要有落盘的数据。如果没有落盘的数据,这部分数据就被视为缺失,在离线环境下确实无法解决。离线评估是分阶段的,因为我们的排序也是从召回一直到最后重排,中间经历了多个阶段。实际上,离线数据采集的越靠近真正曝光的环节,这段数据空间对我们的公式的要求越接近,线上线下一致性效果越好。但如果数据完全偏离,没有落盘,确实无法进行离线评估。
Q2:readme 文件上缺少使用的文档和示例,这是因为何原因?
A2:readme 文件已经非常久没更新了,我们有自己的内部文档和示例库,这些无法对外公布。如果你在使用过程中遇到具体问题,可以直接找到我,我们有相关的用户群,提供一些共享数据和示例,但我目前没有太多精力去更新 readme 文档。
Q3:如何处理多样性的流行度高的商品问题,也就是说可能有些商品在多个目标上的得分都很高?
A3:我们可以直接设计一个多样性的评估算子,在收盘的时候进行约束。实际上任何想法都可以转化为算子,然后在迭代过程中进行约束。多样性也不例外,你可以用熵或者基尼系数作为它的约束算子。
Q4:不同的用户可能有不同的个性化需求,但离线寻参对所有用户都是一样的,你们有对这点进行比较吗?
A4:我们的总体情况并不是对所有用户一样。我们的算子支持 group by 方法,这样就可以以用户为单位进行聚类,实现千人千面。