特征样本构造工具的基本实现
本系列文章:
【推荐算法】推荐算法中常用的样本处理方式及其实现
【推荐算法】推荐系统中的特征工程(一):特征处理
【推荐算法】推荐系统中的特征工程(二):特征选取及重要性分析
【推荐算法】推荐系统中的特征工程(三):特征样本构造工具的实现1
【推荐算法】推荐系统中的特征工程(四):特征样本构造工具的实现2
本文主要介绍特征样本构造工具的基本实现思路,从各自独立的特征和样本,最终产出模型可读的数据,大致流程如下:
之前文章中介绍了特征的基本处理方式,这个版块则是更细粒度的介绍不同类型特征的处理及编码方式。
对于不同类型的特征,具体的转换方式不同:
id类特征:作为模型学习的强相关特征,通常情况下id特征的处理方式有两种,一是将id特征进行one-hot编码,每个id特征占一个维度,产出一个维度庞大的稀疏向量;二是先对id特征进行建模,产出id的embedding向量,这个向量不仅可以反应id的自身特征,还可以描述不同id的相关性信息。前者作为id特征进行描述,后者的处理方式放到embedding类型特征中介绍。
category特征:这类特征比较多,通常重要程度也比较高,像性别、标签、用户等级这些,直接获取到该特征值集合,进行one-hot映射即可。
数值特征:之前讨论过数值特征的分桶方式,提供对应的分桶阈值,按照阈值进行分桶操作,将原本的连续特征转为离散型特征,这类特征和category特征相似,直接进行one-hot编码即可。还有一类统计特征,其本身的数值区间变化不大,可以直接使用,就不需要进行分桶,直接将该特征值做为某一维度的值即可。
序列特征:这类特征比较常见,处理方式也比较多,尤其是din这类序列建模的模型,对序列特征的信息提取更有效;此外,普通模型也可以使用序列特征进行建模,有两种方式,一个是把序列中的每个id按照id特征的映射方式进行one-hot编码,这种方式是以牺牲编码空间换性能,会造成编码空间暴增,最后收效不可预见;另一种方式实际上是忽略了序列特征中的id先后顺序,直接将序列进行multi-hot编码。
向量特征:这类特征的产出方式很多,比如预训练产出向量信息、双塔产出、GNN产出等方式,这类特征使用灵活,可以将其作为输入特征与其他特征一起丢给模型训练,也可以放在模型中与中间向量concat操作后继续训练,加强模型的信息提取能力。在编码时,直接映射为固定长度的dense向量
kv特征:这类特征相当于是value特征的复合结果,直接进行multi-hot即可
具体的特征编码方式如下:
特征名 | 特征类型 | key | value | 编码方式 | 备注 |
---|---|---|---|---|---|
user_id | id | 12345 | 12345 | one-hot | - |
gender | category | 12345 | male | one-hot | - |
click_num | value | 12345 | 203 | one-hot | 提供分桶 |
ctr | value | 12345 | 0.2 | dense | 不分桶 |
click_seq | seq | 12345 | 1,2,5,7 | multi-hot | - |
user_vector | embedding | 12345 | 0.1,0.3,…,0.05 | dense | - |
preference | kv | 12345 | 1:0.2,4:0.9,6:0.3 | multi-hot | - |
在特征编码阶段直接产出一份统计文件,包括样本量、特征维度、每个特征的编码空间等,后续在模型训练过程中有需要则可以直接读取这个统计文件,获得相应的统计量。
这个统计文件也可以辅助验证特征样本构造数据的准确性,用于例行化的监控数据生产流程。
在特征、样本构造阶段,维护编码映射文件的一致性是非常重要的问题。
由于编码映射与模型训练是分开进行的,模型在离线评估和线上预测阶段,保证编码映射的一致性才能确保服务正常;此外,增量模型迭代过程中,增量训练时大概率会引入新特征,如何将新特征的编码融合到base模型产出的编码映射文件中显得尤为重要。
其实,对于大多数的特征,本身特征值的key不会有太大改变,只是value在不断的变化,比如:性别、标签等特征,用户确定,基本上性别随之确定;商品确定,基本上运营or模型打出的标签也是一层不变的,这些特征即使是在增量训练时候,基本上也不会影响整个特征编码空间的改变;再比如:ctr、偏好这类特征,本身数值的改变只是影响对应维度的value变化,对于编码空间也是没有影响;但是,新的用户或是新的item引入,则会改变编码空间——综上,需要注意的基本就是id类特征引起编码空间改变这种情况。
针对上面的分析,提出两种解决方式:
预留编码空间
这种方式比较好理解,既然id特征的引入会改变编码空间,那提前预留出一定的编码空间,当有新id进来时,将该id向预留的编码空间进行映射即可,这样就可以保证增量模型编码映射的一致性:相当于新id来之前,预留空间都是0,新id进来时,预留空间中的一维发生变化(id特征进行one-hot编码)
预留的编码空间多少比较合适呢?这个可以先对数据进行一个基础的分析,以用户id为例,base模型是30天的行为数据,可以先比对增量的用户id与base的差集(相当于是冷启动用户了),再根据这个差集确定预留空间的大小。
LRU更新编码空间
上面预留编码空间的方式只对新的id特征提出了映射方案,在实际的业务场景中,id的引入和剔除是同时进行的,比如某个id只在30天前发生过行为、某个item在30天前已经下架,后续不会再出现该item这中情况,增量的编码映射文件更新时就需要采用LRU更新的方式。
LRU是一种常见的页面置换算法,通过维护一个时序的栈,将最新使用的特征放到栈顶,当id来的时候进行判断,如果id不在栈中,则将id放到栈顶,同时移除栈底的id;如果id在栈中,更新id的统计次数,移到栈顶。这里,对LRU感兴趣的可以看一下这篇博客,本文中就不展开介绍了:LRU缓存算法的实现
资源足够的情况下,上面两种方式可以同时进行,这样可以最大限度的容纳特征值,尤其是id,预留空间不为空时,有限占用预留空间的维度;预留空间为空时,再采用LRU更新的方式进行替换。但如果资源紧张的情况下,可以只使用LRU更新的方式。
综上,特征、样本构造的流程就全部介绍完了,实际开发逻辑比较细节,没有进行详细介绍,可以根据业务实际情况实现。样本、特征准备好,就开始进行模型训练流程了,后续的系列文章中会介绍模型训练框架的一些想法~