主要是对实习期间涉及到的知识进行梳理。
背景:视频推荐(手机端,TV端等)。
场景:feed 流,瀑布流(猜你喜欢),短视频 Tab 等。
召回涉及到的方法(以短视频 Tab 为例):
youtubednn u2i, 插播2i, 频道热门召回,up主2i, 热门内容,FM i2i等。
排序涉及到的方法 (以猜你喜欢为例):
afm, dcn, deepfm, wide&deep, deep_crossing, xgb等。
数据源表(自己日常接触到的表):
show(展示) click(点击) vv(播放次数) vt(播放时长)
都是hive分区表 通过 dt 和 scene 进行限制。
关注的线上指标:
一般情况下都是通过对不同的字段进行group by聚合来求不同维度下的指标。
通常用来聚合的字段有:channel_id(频道), bucket(实验分桶), app_version(版本) ,dt(日期)
通过show表求: show_pv , show_uv
通过click表求: click_pv , click_uv
通过vv表求:vv
通过vt表求:vt
ctr = click_pv / show_pv
uctr = click_uv / show_uv
ppui = vt / show_uv
次留
以端指标为核心,日常关注的方面:
- 召回,排序模型的优化。这其中包括 特征丰富,多场景,多目标,冷启动,实时性,指标加权,长尾分发,软保量硬保量,debiasing, 新颖性等。每个方面都值得之后再具体展开(更新中)。
- 对1中的方法设计实验,进行A/B test, 接下来几天再进行场景指标,端指标的观察和对比,验证实验的效果。
- 线上问题的跟踪与排查。拿到用户以及up主的负面反馈(bad case),对于具体的问题分析成因,设计实验方法进行改进。改进的方面无非也是1中那些方法。
- 在关注推荐系统用户消费指标之外,针对部分推荐场景,基于各产品的业务定位把评估体系进一步完善。
以参与过的TV端 猜你喜欢场景的A/B Test为例,主要为达成以下三个目标:
1.长视频端播放时长,端次留(也就是说这两个指标是核心指标,验证实验是否成功)
2.对电影/电视剧/综艺的分发量进行扶持
3.猜你喜欢场景同样存在追剧的席位,降低追剧占比,提升新颖性和探索性。
主要设计的实验如下:
规则层面:
1.追剧内容打压儿童、游戏策略实验(就是把儿童,游戏频道的追剧内容直接减少)
2.按小时兴趣频道提权,分时段重排减少儿童、游戏内容占比
3.保留前三卡追剧,后面位置个性化探索(该场景就是电视端的猜你喜欢的十多个栏位,把前三个拦住都设置为追剧,即用户最近在看的剧,后面的栏位自动分发。)
召回层面:
4.高消费指数召回
5.豆瓣高分召回
模型层面:
6.训练样本影剧综提权
之后再统计几天内每个实验桶下的场景指标和端指标,一般来讲时间越长说服力越好,单独拿某一天的指标进行实验的对比并没有什么意义。而且这里并没有使用置信度的概念。
短视频场景逻辑梳理:
拿到原始数据,先进行简单的过滤,例如付费内容,色情,政治敏感,时效性(去除老旧的内容)等,写入Corpus。
从Corpus中取数据进行多路召回,方法各种各样有很多,除了深度模型大多可以通过sql完成。
每路召回的数量也不一定(每种召回路径中的物料id也可作为之后模型学习的特征,如果该物料排名较好,可以理解为该路召回效果不错,可考虑加权重)
所有召回的内容加在一起,之后一起过粗排。
在做粗排前(pre_train),我们拿到了召回得到的物料id. 拿这些id去特征库中(couchbase)匹配,(特征是全部做好的,我们只需要拿召回结果去匹配就好了)。
同时我们还需要拿到label,在有的场景下,label可以自定义,比如说观看时长>15s的.
有的场景不适合这样定义。这时候就需要pingback了,我们可以通过pingback看用户是否点击了,进而拿来做label 0和1 。
拿到特征和label后(七天的数据)做特征的组合,生成train_dataset, 再生成tfrecord格式.
opal调用gitlab代码进行离线训练。训练完成后,保存模型。模型在http服务上进行预测
(预测时,也是从根据id从couchbase中拿到对应的特征,以及实时特征,直接送至模型进行预测)
得到粗排后的结果,是物料id 设备id 还有排序。
根据粗排得到的结果,再去cb中匹配特征,与粗排同样的步骤进行精排,模型在http服务上进行预测.
之后我们只需给引擎那边 http地址,他们就可以继续做下去,得到精排后的结果。
再进行重排,(MMR,强插等策略,最终可能只留下top5) 进行推荐。
特征回放好像就是指 特征到CB,根据id从CB中取特征的过程。(不确定)
(瞎BB版,不改动了,有很多逻辑梳理错误,理解错误的地方,回看的时候看能不能找到。刚好保留之前自己琢磨时陷入的思维陷阱 )
TV推荐场景
corpus根据什么来定义:
首先从 videoflow_al_da_snap_hive_episode_album_ppc_meta_latest_view 第一步筛选,
例如 过滤掉 推荐等级为0的,过滤掉 所有的付费内容, 过滤掉所有不可试看内容等等,这样生成了第一步基础的corpus -- tv_basic_short_corpus
在tv_basic_short_corpus中进一步限制掉 政治,色情的内容,并scene=‘short_tab’,生成短视频tab的高质量的corpus数据.
在tv_basic_short_corpus中进一步限制掉 政治,色情的内容,并scene=‘aiplay’,生成随心看的高质量的corpus数据.
把上面生成的两个 union all起来 后,对不同的频道需要控制 不同的 时效性。 有的频道需要限制在一周内,有的需要控制一个月内,有的则需要更久。
对时效性进行限制后,重写入 tv_basic_short_corpus
再通过几个 视频 质量分的特征 进一步筛选 高质量的视频 存入 tv_short_tab_corpus_quality
以上是corpus的生成过程。
之后进入召回流程:
召回分为 i2i 标签召回 TGI召回 模型召回 trigger 兜底召回 u2i
每个分类下面又有好多的方法。
(标签召回就很简单 从不同的维度取出 热度较高 的视频或者是 up主 再取出排名前100or前200的
这种方法是统一的 因为没有利用到 具体用户和视频的交互信息 所以做不到 个性化)
通过以上构建的tv_short_tab_corpus 进行多种标签召回 和 倒排 (例如 hot_up hot_channel hot_search hot_short)
以hot_up为例,用到这个语句,以粉丝量做排名 从多到少
row_number() over (partition by domain_name order by fans_following_count desc) as rank
粉丝量这个特征是从videoflow_al.videoflow_al_da_snap_hive_iqiyi_user_meta_view取得(这个源表中还有视频质量分等特征) 因此同时需要 corpus表和这个源表 做join
取出 up rank < 100的 domain, uploader_id 和其对应的quality_score 并存入 tv_dwd_dd_recall_invert_qiguan这张表。
同样的方法 根据其他几路召回的定义 取出 channel rank < 200的 channel-id, qipu_id, 以及其对应的hot 并存入tv_dwd_dd_recall_invert_qiguan这张表。 不同召回方式的数据 用 reverse_type = '' 做分区。 (不同的召回的方式 可以见 锋哥 以前发的截图)
还有一种up2up 通过该up进行相同domain其他up的推荐。
以上是标签召回 还有很多方式 具体见 babel - tv推荐 - 基础数据 - 标签召回及倒排
通过标签召回的数据都存入tv_dwd_dd_recall_invert_qiguan 的不同分区
以上是 标签召回的过程
(再以i2i 召回为例)
以 fm i2i为例 fm i2i 的路径分为两条 tv i2i 和 short tab i2i
tv i2i 用到了表 dwd_base_recall_i2i_fm_prediction_dd dwd_kpp_dump_column_related_rec_hh tv_dwd_dd_view_long_video_corpus 并写入 tv_dwd_dd_fm_i2i_recall
short tab i2i 用到了表 dwd_base_recall_i2i_fm_prediction_dd tv_short_tab_corpus 并写入 tv_dwd_dd_recall_i2i_qiguan 的某个分区
为了跟之前的逻辑对照 还是以短视频的这个路径为例
先看dwd_base_recall_i2i_fm_prediction_dd 哪来的 这个表是成都那边产的 。。
看一下该表的结构:
hive> desc dwd_base_recall_i2i_fm_prediction_dd;
tv_id string
rec_tv_id string
score double
channel_id int
dt string
即已经给出了 tv_id 和 根据其 推荐的 rec_tv_id 还有二者的得分 score
接着看 short_tab i2i 这里就很简单了 取出 dwd_base_recall_i2i_fm_prediction_dd中的 tv_id rec_tv_id score 并与 tv_short_tab_corpus中的qipu_eposide_id join
得到的 数据 写入到 tv_dwd_dd_recall_i2i_qiguan 的某个分区
以上是i2i召回的过程
(再以youtubednn召回为例)
从 tv_dwd_dd_user_click 拿到用户的点击记录 再与 tv_dwd_dd_corpus_merged 的前一天的数据 通过album_id join 之后再通过 tv_dwd_dd_black_device 限制设备
最后拿到 device_id ,entity_id , 1 as label, 1 as weight , concat('last_20_click:', concat_ws(',',collect_list(entity_id))) as cf . .存入tv_dwd_dd_train_dataset
根据entity_id 出现的次数 对其进行 row_number()排序 得到一列新特征 并连同之前的特征写入 tv_dwd_dd_train_dataset_merged中。
之后进入到 gen_data 模块 ,子工作流为 tv_common_sub_gen_train_data_without_sample_libsvm
gen_data模块:
对tv_dwd_dd_train_dataset_merged中的数据进行以下操作:
cate_fea > gen_idmapping > feature_field_extract > feature_field_to_mysql
num_fea > numerical_feature_info > update_numerical_columns > numerical_feature_column_to_mysql
之后join 再进行 tfrecord_gen 得到 clean_data 位于 hadoop fs -rm -r /data/bi_uaa/rec/tv-rec/tfrecord/{DT_BEFORE}/
即通过模型召回得到的数据以 tfrecord格式 放在 hdfs上
前面的标签召回和i2i召回后 生成tv_dwd_dd_recall_i2i_qiguan ,这张表下一步紧接着干嘛呢 ?
tv_dwd_dd_recall_i2i_qiguan 生成后的步骤 暂没找到
进入排序模型训练部分
这一部分 包括 feature_extract label_extract train
这部分操作 都是用的 dwd_dd_show click vv等表 并没有用到corpus表
feature extract 从三个角度 entity_feature device_feature cross_feature 做特征 做的特征 通过feature_type 进行区分cate num
分别存入不同的表 并通过dt scene 进行分区
例如 tv_dwd_dd_entity_feature_by_scene
再进入一个子工作流 通过feature_type = num cate把num cate feature分别提取出来 用concat_ws 进行拼接 as categorical_featuers numerical_features
再存入 tv_dwd_dd_entity_feature_by_scene_combined 之后再跟一个 spark 模块 名为import 暂不清楚这个模块是不是用来生成 tfrecord 的。。
label_extract 也是通过定义 (t1.item is not null and t1.playtime > 15, 1, 0) as label 来进行label的定义。
train模块不只是train 主要包括以下几个模块 make_sample gen_data gen_tf_record
make_sample : 规范的从feature表和label表拿到每天的数据 拼接 写入tv_dwd_dd_train_dataset
gen_data:放在make_sample 之后, 从 tv_dwd_dd_train_dataset 拿到七天内的数据 ,写入到 tv_dwd_dd_train_dataset_merged
之后再进行 cate_fea > gen_idmapping > feature_field_extract > feature_field_to_mysql
num_fea > numerical_feature_info > update_numerical_columns > numerical_feature_column_to_mysql
再生成 tfrecord.
根据上面的总结 还存在几点问题 Corpus主要是用来干嘛的
应该是主要做 非模型的召回等方式的吧 做完生成tv_dwd_dd_recall_i2i_qiguan之后呢。
在线训练是从哪进行的 是从生成的tdrecord 开始训练的嘛 tfrecord不是存的7天数据吗。 在线训练的任务流在哪。
之后不是在opal上训练模型吗 模型的预测结果写入哪里了 是不是保存模型文件后 拿去线上预测 保存的模型文件不是用当天以及以前7天的数据训练的吗
拿到线上的话 不存在时间跨度的问题吗
还是说opal上的模型只是用来对模型离线训练 用来调整模型的结构和参数 等调好了 再拿到线上用吗 ? 线上还需要训练吗 还是直接调用模型进行预测 线上预测的代码在哪 是python实现的吗?
如果线上预测的代码不是python 是java的话 应该是把离线的模型 调用后 在java中预测给出预测结果把 或者不是java呢 这部分的代码在哪里呢
为啥我觉得做排序的时候 做train_dataset用的表不是 召回后的结果 而且直接用 dwd_dd_ click show ..detail表 加工而来的
往前看的话 用户进入app 有展示,点击,观看记录 经过开发岗的加工 我们才拿到比较干净的数据。。
此外 在模型上线前 我们经常需要找开发 进行沟通 是为了什么?
https://zhuanlan.zhihu.com/p/81752025 对于 推荐系统架构 文章的一些疑问:
如文章所说,模型存储在 在线存储模块(用于存储模型和模型需要的特征信息供实时计算模块调用), 预测的数据流接受业务的预测请求,通过 A/B 测试模块访问 实时计算模块 获取预测结果。
实时计算模块:(实时推荐模块的功能是对来自业务的新请求进行预测。1.获取用户特征;2.调用推荐模型;3.结果排序。)
那么app的 在线存储模块 和 实时计算模块在哪里(是什么平台) 代码又在哪里