【王树森推荐系统】排序05:排序模型的特征
用户画像 (User Profile)
- 用户ID(在召回,排序中做embedding)
- 是用户最重要的特征之一,它本身不携带任何有用的信息,但是模型学到的ID embedding 向量,对召回和排序模型有很重要的影响
- 召回和排序都会用ID 做 embedding,通常 32 维或者 64 维向量
- 人口统计学属性:性别,年龄
- 不同性别,不同年龄的兴趣差别点很大
- 账号信息:新老,活跃度······
- 新老用户,高活低活的用户行为区别也很大,模型需要对新用户和低活用户做优化
- 感兴趣的类目,关键词,品牌
- 可以是用户填写的,也可以是算法提取的
- 这些用户兴趣的信息对排序也是很有用的
物品画像 (Item Profile)
- 物品ID (在召回,排序中做embedding)
- 物品ID的重要性非常高
- 发布时间(或者年龄)
- 也是重要的特征
- 在小红书,一个物品发表的时间越久,价值越低,尤其是明星出轨和电商打折这样的强时效性话题,热度只有几天
- GeoHash(经纬度编码),所在城市
- 对召回和排序都有用
- GeoHash 是对经纬度的编码,表示地图上一个长方形的区域
- 标题,类目,关键词,品牌······
- 通常对这些离散的内容特征做 embedding 变成向量
- 字数,图片数,视频清晰度,标签数······
- 这些属性反映出笔记的质量
- 物品的点击和交互指标和这些属性相关
- 内容信息量,图片美学······
- 是算法打的分数
- 事先用人工标注的数据训练 cv 和 nlp 模型,当新笔记发布的时候给模型和笔记打分,把内容信息量,图片美学这些分数写到物品画像中,这些分数可以作为排序模型的特征
用户统计特征
- 用户最近 30 天(7 天,1 天,1 小时)的曝光数,点击数,点赞数,收藏数······
- 用各种时间粒度,可以反映出用户的实时兴趣,短期兴趣和中长期兴趣
- 按照笔记图文/视频分桶(比如最近 7 天,该用户对图文笔记的点击率,对视频笔记的点击率)
- 对视频和笔记分别统计,可以反映出用户对两类笔记的偏好
- 按照笔记类目分桶(比如最近 30 天,用户对美妆笔记的点击率,对美食笔记的点击率,对科技数码笔记的点击率)
笔记统计特征
-
笔记最近 30 天(7 天,1 天,1 小时)的曝光数,点击数,点赞数,收藏数······
- 这些统计量反映出笔记的受欢迎程度,如果点击率和点赞率等指标都很高,说明笔记质量高,算法应该给这样的笔记更多的流量
- 使用不同的事件粒度也是有道理的,有些笔记的时效性强,30 天指标很高,但是最近 1 天的指标很差,说明这样的笔记已经过时了,不应该给更多的流量
-
按照用户性别分桶,按照用户年龄分桶······
- 比如把受众分成男女两个桶,分别计算男性的点击率和女性的点击率,这样的点击率可以反应是更受男性欢迎还是更受女性欢迎
- 比如一篇笔记是对粉色键盘的测评,笔记的总体点击点赞指标都很高,但是来自男性用户的点击率很低,说明不应该给将这个粉色键盘推荐给男性用户
- 还有地域分桶等,用途都是类似的
-
作者特征:
- 发布笔记数
- 粉丝数
- 消费指标(曝光数,点击数,点赞数,收藏数)
- 这些特征反映了作者的受欢迎程度,以及他作品的平均品质。显然,如果一个作者已有作品的普遍品质很高,它新发布的作品的品质大概率也会很高
场景特征 (Context)
-
场景特征是随着推荐请求传来的,不用像用户画像和物品画像那样需要从数据库中获取
-
用户定位 GeoHash(经纬度编码),城市
- 当用户登录小红书的时候,如果用户给小红书权限,允许使用地理定位。那么小红书就会收到用户的经纬度和所在城市,这些信息对召回和排序都有用,因为用户可能会对附近发生的事情感兴趣
-
当前时刻(分段,做 embedding)
- 一个人在同一天不同时刻的兴趣可能有所区别。在上班路上,在办公室或者在晚上睡觉前,用户想看的东西可能不一样
-
是否是周末、是否是节假日
- 周末和节假日的时候,用户可能对特定的话题感兴趣
-
手机品牌、手机型号、操作系统
- 安卓用户和苹果用户的点击和点赞这些指标差异非常显著,所以设备信息也是有用的特征
特征处理
- 离散特征:做 embedding
- 用户ID,笔记ID,作者ID,都是几千万几亿的级别,消耗内存很大
- 类目,关键词,城市,手机品牌。处理起来很容易,比方说笔记的类目也就几百个,关键词有几百万个,给它们做 embedding 比较容易,消耗内存不多
- 连续特征:做分桶,变成离散特征
- 年龄,笔记字数,视频长度。比方说把连续的年龄变成 10 个年龄段做 One-Hot 编码或者做 embedding
- 连续特征:其他变换
- 曝光数,点击数,点赞数等都是长尾分布,以曝光数为例,大多数笔记只有几百次曝光,而极少数笔记能有上百万次曝光,假如直接把曝光数作为特征输入模型,一旦出现几十万几百万这些特别大的数值,计算会出现异常,比如训练的时候梯度会很离谱,做推理的时候预估值会很奇怪。所以对于这样的特征一般是做 log(1+x)log(1+x)log(1+x),这样会解决异常值的问题
- 转化为点击率,点赞率等值,并做平滑,去掉偶然性造成的波动
- 在实际的推荐系统中,两种变换的连续特征都作为模型的输入
特征覆盖率
- 在理想情况下,每个特征都应该覆盖 100% 样本,不存在特征缺失
- 但是现实是很多特征无法覆盖 100% 的样本,大多数特征都有缺失
- 例:很多用户不填年龄,因此用户的年龄特征的覆盖率远小于 100%
- 例:很多用户设置隐私权限,APP 不能获得用户地理定位,所以场景特征缺失
- 提高特征覆盖率,可以让精排模型更准。如果一个特征很重要,那么提高它的覆盖率肯定可以提升模型表现
- 做特征工程的时候还需考虑当特征缺失的时候用什么默认值
数据服务
- 推荐系统用到三个数据源,包括:
- 用户画像 (User Profile)
- 物品画像 (Item Profile)
- 统计数据
- 三个数据源都存储在内存数据库中,在排序的时候,排序服务器会从 3 个数据源取回所需的数据,然后把读取的数据做处理,作为特征喂给模型,模型就能预估出点击率点赞率等指标
简化的系统架构
-
当用户刷小红书的时候,用户请求会被发送到推荐系统的主服务器上
-
主服务器会把请求发到召回服务器上
-
做完召回服务后,召回服务器会把几十路的召回结果做归并,把几千篇笔记的 ID 返回给主服务器
-
召回需要用到用户画像,这里不展开
-
主服务器把物品ID,用户ID,场景特征等发送给排序服务器
-
这里有 1 个用户ID和几千个笔记ID,笔记ID是召回的结果,用户ID和场景特征都是从用户请求中获取的。场景特征包括当前的时刻,用户所在的地点以及手机的型号和操作系统
-
接下来排序服务器需要从多个数据源中取回排序所需的特征,主要是:用户画像,物品画像,统计数据。分别取回用户特征,物品特征,统计特征。
-
用户画像数据库线上压力比较小,因为每次只读一个用户的特征
-
而物品画像数据库压力非常大,粗排要给几千篇笔记做排序,读取几千篇笔记的物品特征
-
同理,纯用户统计数据的数据库压力小。而物品统计数据的数据库压力很大
-
在工程实现的时候,用户画像里面存什么都可以,特征可以很多很大。但尽量不要往物品画像里面塞很大的向量,否则物品画像会承受过大压力
-
用户画像较为静态,像性别年龄这样的属性几乎不会发生变化,用户活跃度,兴趣标签这些属性基本就是天级别的刷新,变化很慢
-
物品画像上的变化更少,可以认为是完全静态的。物品自身的属性和算法给物品打的标签,在很长一段时间内不会发生任何变化
-
对于用户画像和物品画像,最重要的是读取速度要快,而不太需要考虑时效性,因为它们都是静态的。有时甚至可以让用户画像和物品画像缓存在排序服务器本地,让读取变得更快
-
但是不能让统计数据在本地缓存,统计数据是动态变化的,时效性很强。比如用户刷小红书,往下刷了 30 篇笔记,点击了 5 篇,点赞了一篇。那么这个用户的曝光,点击,点赞等统计量都发生了变化,要尽快刷新数据库
-
在收集到排序所需的特征之后,排序服务器把特征打包,传递给 TF Serving,TensorFlow 会给笔记打分,分数返回给排序服务器
-
排序服务器会用融合的分数,多样性分数还有业务的规则给笔记做排序,把排名最高的几十篇服务器返回给主服务器,这些就是最终给用户曝光的笔记