返回列表

5th place Solution

532. OTTO – Multi-Objective Recommender System | otto-recommender-system

开始: 2022-11-01 结束: 2023-01-31 商品推荐 数据算法赛
第5名方案

第5名方案

作者: NikhilMishra (Grandmaster)
发布时间: 2023-02-01

首先感谢大家在这个问题上分享了这么多。我从你们所有人身上学到了很多。

特别感谢(按字母顺序排列):

  1. Carno:感谢分享你的 numba 流程。我所有的候选生成和许多特征都是使用 numba 创建的。我在这次比赛之前几乎从未使用过 numba,所以在这次比赛中学到了很多 numba 的知识。
  2. Chris:感谢从比赛开始到结束分享了这么多内容。我认为每位参赛者都欠你一份人情。我记得你在比赛的最后一天还在回答关于排名模型的问题,除了尊敬别无他言。
  3. Radek:感谢向我们介绍 polars,polars 在连接表时的速度确实帮助我加快了实验速度。
  4. Senkin:感谢你在 H&M 比赛中的第 1 名方案。我在 otto 比赛中的许多想法都受到了该方案的启发。

我在 Private LB 上表现最好的方案几乎是单模型(得分几乎与集成模型相同),所以我将描述这个单模型(Public LB: 0.604,Private LB: 0.603)。

流程架构:候选生成 -> 特征创建 (Numba, Polars) -> 排序模型 -> 推理

候选生成

我认为拥有一个强大的候选生成方法对我帮助很大,所以我是这样做的。

每个会话的候选数量: 在比赛的大部分时间里,我生成了 80 个候选,然后在最后一周将其增加到 120 个候选,以获得一些分数提升(约 0.001)。对于 80 个候选,我有相当不错的最大召回率 0.648(在验证集上)。我也曾在一个实验中尝试了 200 个候选,但这并没有帮助提高分数。

另外,如果我从候选生成模型中取前 20 个候选,我在 LB 上的得分将是 0.585。

对于候选生成,我使用了类似于共访矩阵的方法,我将一个会话中任意两个 aid 的用户行为分为不同的类别,例如:

  • a. 任意动作到任意动作
  • b. 点击到加购
  • c. 加购到下单
  • d. 下单到下单
  • ... 等等

为了保持较低的内存使用量,我只选择了前 (k * 100) 个候选。这里的 K 是我想要生成的候选数量。

此外,我通过第一个项目的频率对权重进行了归一化。所以这基本上就像:在购买牛奶的 100 次中,有多少次同时也购买了鸡蛋。

矩阵中的权重通过我们要讨论的两个 aid 之间访问的项目数量进行了归一化。

假设我们有 5 个 aid:aid1, aid2, aid3, aid4, aid5。

那么 (aid1, aid5) 的权重将是 (5-1)/(aid1 的频率)。

另外 (aid5, aid1) 的权重将是 ((aid1, aid5) 的权重)/2(只是为了表达 aid5 的购买是由 aid1 的购买驱动的,而不是反过来)。

在推理时,为了决定应该取哪些前 k 个候选,我使用了 Optuna,将每个共访矩阵的权重、项目的时效性权重、项目的整体归一化频率等作为参数进行优化。

特征生成

  • 基础特征:如项目频率、点击与加购比率等、访问项目的时效性(如果会话中的候选数量超过 20 个,这会有很大帮助)。
  • 关联特征:生成的候选与会话中已见过的 aid 的关联。这可以通过使用共访矩阵权重来创建。深入挖掘此类特征极大地提高了我的分数。其思想是可以用不同的方式创建共访矩阵来建立两个项目之间的关系,例如:
    • a. 仅取两个 aid 之间的平均距离(中间的 aid 数量)。
    • b. 距离也可以通过时间戳差异来衡量。
    • c. 仅考虑第一邻域中的候选(直接候选)。
    • d. 仅考虑最后一周的关系等。

训练排序模型

我使用了 LightGBM,采用 5% 的负采样和大约 400 个特征,数据为最后两周的数据。添加倒数第二周的数据使分数提高了约 0.0005。

一些对我有效的方法或技巧:

  1. 单模型训练:使用单个模型训练所有的点击、加购和下单(而不是 3 个独立的模型)。在本地可以明显看到分数提高了约 0.001 到 0.002(数据按会话分组,而不是按会话和类型分组)。
同比赛其他方案