基线融合排序算法

推荐系统的排序策略

排序策略起到的作用

  1. 将多种召回集的结果进行融合,挑出少量的推荐结果内容
  2. 返回结果应平衡多种来源的密度分布
  3. 根据排版要求进行精排序

问题和需求描述

假设我们现在拥有3个召回策略的来源的数据分别为

  1. 个性化内容约 200 个
  2. 新内容约 2000 个
  3. 热门内容约 200 个

我们希望达到如下目标

  1. 从这么多用户可能感兴趣的内容中挑出来10个内容返回
  2. 用户已经推荐过的不再进行推荐
  3. 用户的推荐结果中,结果保持多样性各种来源按照一定比例,个性化:新内容:热门内容比例为 3:4:3, 某种题材比例不足采用其他内容进行填充
    1
    2
    3
    4
    5
    6
    7
    8
    ;用scheme代码描述如下
    (define (recommendation user)
    (lambda (user)
    (cons (cond (personal? user) (get-personal user)
    (else '()))
    (if hottest? (get-hottest) '())
    (if lattest? (get-lattest) '())
    (if hot-spot? (get-hot-spot) '()))))

    朴素处理

  4. 直接从个性化内容里面 取100.3 个没有推荐过的,从新内容里面取 10 * 0.4 个, 从热门里面取 10 0.3 个放到一个队列返回
    1
    2
    3
    4
    5
    6
    7
    def rec(a:Set[个性化], b: Set[新], c: Set[热门],d: Set[看过]): Set[10] = {
    ans = Set()
    ans += a.forEach(v -> !d.contains(v)).take(3)
    ans += b.forEach(v -> !d.contains(v)).take(4)
    ans += c.forEach(v -> !d.contains(v)).take(3)
    ans
    }
    这种处理方法在遇到 某种策略内容不够的时候 就需要手动做判断,再从其他两种内容里面获取
    1
    2
    3
    while ans.size() < rec {
    ans += b.forEach(v -> !d.contains(v)).take(4)
    }
    假如其他集合还是不够的话还要继续处理,代码复杂度就直线上升

基线排序法

先选择一个数量较多的内容集合作为基线:例如 新内容。将基线集合的数据通过排名映射到[0,1]的空间之内

映射函数: $x$ 为在内容在该渠道召回分数, $rank$为内容在该渠道当前排名, $size$ 为该渠道内容总数

$$Score =1-\cfrac{x\cdot rank}{size}$$

1
2
3
4
5
6
; 新内容:假设我们有2000个新内容,那第400个的得分应该为
400
|
V
| | | | | |
1.0 0.8 0.6 0.4 0.2 0

然后我们把其他召回集内容也通过某个积分函数均匀散列在以上区间

由于我们要满足 3:4:3的概率所以我们要求在原来的40个新内容的范围里面,均匀混进去30个热门和30个个性化内容,这样比例就满足了

也就是说我们要满足如下公式, 由于内容密度要满足一定条件(3:4:3)

计算热门内容的分数范围的公示如下

$x = 0.02\cdot\cfrac{200}{30}=0.133$

也就是说热门内容分布的边界范围应该为 [1,0.867]

1
2
3
4
5
6
; 热门内容
30个 第100个 第200
| | |
V V V
| | | |
1.0 0.98 0.935 0.867

个性化内容也是同理
最后三者进行融合以后重新排序结果如下

1
2
3
4
5
6
7
; 融合之后的内容集合分布
800
|
V
| | | | | |
1.0 0.8 0.6 0.4 0.2 0
[1, 0.8]的空间内同时含有 400个新内容,200个热门内容和200个个性化内容

这种情况下顺序取前10个内容, 三种来源的比例就是 3:4:3
同样在这种算法情况下, 无论是调整 结果数量, 还是调整比例,都可以用同一个逻辑轻松实现需求, 实现优雅,性能也得到了保证