CTR展现打散去重的设计

大家看今日头条的推荐会发现,每次刷新都会推荐不同的内容,而且翻页也很少出现重复内容。

那么这种个性化推荐系统的架构是什么样的呢?

粗排召回

所有文章都会存在一个大候选池中,比如用Elasticsearch存储。

我们难以对大候选池中的所有文章做CTR模型预估,也难以在这么大的候选池中实现业务要求的排序策略,所以大候选池的作用只是粗粒度的文章召回。

在粗排阶段,我们召回的文章集合应该在千级别,比如:3000篇。

最简单的召回策略就是返回最近24小时的热门文章,当然如果可以结合用户偏好的文章类型更精细化的召回也可以考虑结合进来。

另外,大候选池只是数据源之一,我们还需要取一些运营位,广告位,它们整体作为我们的召回集合。

精排

这个阶段的输入文章规模是3000篇,我们遍历3000篇文章做CTR预估得到模型打分并排序,靠前的文章点击率越高。

配比

单纯依靠CTR得分排序,会造成内容不均衡,比如靠前的可能是大量的娱乐花边新闻,导致用户没法看到重要的时事政治新闻。

所以这个阶段需要结合业务特点,首先将3000篇文章按照业务类型等策略需求划分到多个小候选池,单个类型的候选池内可以进一步根据业务策略精细化排序。

最后,我们的目标是从众多候选池中,通过合理的分类配比生成要返回的20篇文章,也就是一页的内容。

这里配比就靠策略指定了,比如2篇体育的,2篇娱乐的,2篇政治的,大家可以根据公司需要决定。

广告和运营位在这个阶段可以插入到20篇文章的中间合适位置,一起返回给客户端。

去重

我们从3000篇粗排结果中甄选出20篇文章,返回给客户端。

那么当用户再次下拉或者刷新的时候,我们需要推荐与这20篇不同文章,这样用户才不会觉得重复推荐,所以这里需要去重。

所以当我们返回20篇文章之前,需要在redis里记录下这20篇文章ID。

当再次请求推荐接口时,我们仍旧从粗排召回3000篇文章(也许仍旧有部分文章属于我们之前返回的20篇),然后从redis里取出已经推荐过的文章ID,从3000篇中过滤掉推荐过的文章。

对于剩余的文章,我们继续走精排 -> 配比的逻辑即可。

因为大候选池内容不断生成新的,用户不断访问推荐接口,那么该用户的redis访问历史集合会越来越大,对我们去重计算会造成很大压力(也占用了很多内存),所以我们需要让每个用户的redis历史推荐集合自动淘汰,在设计时可以利用滑动窗口的方式,只保存最近N小时推荐过的文章ID即可。

极端情况

如果业务的内容生产速度较慢,而某用户持续的请求推荐接口,那么粗排召回的3000篇可能全部都被用户读过,导致去重后没有内容可以推荐。

这种极端情况下,代码可以直接取redis中的历史推荐文章ID(可以用zset保存,key是文章ID,score是推荐的时间点),直接按历史推荐集合timeline展现给用户即可。

 

 

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~