零基础入门推荐系统-新闻推荐 Task4 特征工程
本篇为Datawhale组队学习笔记,Datawhale推荐系统实践
天池比赛地址:零基础入门推荐系统
制作特征和标签, 转成监督学习问题
我们先捋一下基于原始的给定数据, 有哪些特征可以直接利用:
- 文章的自身特征, category_id表示这文章的类型, created_at_ts表示文章建立的时间, 这个关系着文章的时效性, words_count是文章的字数, 一般字数太长我们不太喜欢点击, 也不排除有人就喜欢读长文。
- 文章的内容embedding特征, 这个召回的时候用过, 这里可以选择使用, 也可以选择不用, 也可以尝试其他类型的embedding特征, 比如W2V等
- 用户的设备特征信息
上面这些直接可以用的特征, 待做完特征工程之后, 直接就可以根据article_id或者是user_id把这些特征加入进去。 但是我们需要先基于召回的结果, 构造一些特征,然后制作标签,形成一个监督学习的数据集。
构造监督数据集的思路, 根据召回结果, 我们会得到一个{user_id: [可能点击的文章列表]}形式的字典。 那么我们就可以对于每个用户, 每篇可能点击的文章构造一个监督测试集, 比如对于用户user1, 假设得到的他的召回列表{user1: [item1, item2, item3]}, 我们就可以得到三行数据(user1, item1), (user1, item2), (user1, item3)的形式, 这就是监督测试集时候的前两列特征。
构造特征的思路是这样, 我们知道每个用户的点击文章是与其历史点击的文章信息是有很大关联的, 比如同一个主题, 相似等等。 所以特征构造这块很重要的一系列特征是要结合用户的历史点击文章信息。我们已经得到了每个用户及点击候选文章的两列的一个数据集, 而我们的目的是要预测最后一次点击的文章, 比较自然的一个思路就是和其最后几次点击的文章产生关系, 这样既考虑了其历史点击文章信息, 又得离最后一次点击较近,因为新闻很大的一个特点就是注重时效性。 往往用户的最后一次点击会和其最后几次点击有很大的关联。 所以我们就可以对于每个候选文章, 做出与最后几次点击相关的特征如下:
- 候选item与最后几次点击的相似性特征(embedding内积) — 这个直接关联用户历史行为
- 候选item与最后几次点击的相似性特征的统计特征 — 统计特征可以减少一些波动和异常
- 候选item与最后几次点击文章的字数差的特征 — 可以通过字数看用户偏好
- 候选item与最后几次点击的文章建立的时间差特征 — 时间差特征可以看出该用户对于文章的实时性的偏好
还需要考虑一下
5. 如果使用了youtube召回的话, 我们还可以制作用户与候选item的相似特征
当然, 上面只是提供了一种基于用户历史行为做特征工程的思路, 大家也可以思维风暴一下,尝试一些其他的特征。 下面我们就实现上面的这些特征的制作, 下面的逻辑是这样:
- 我们首先获得用户的最后一次点击操作和用户的历史点击, 这个基于我们的日志数据集做
- 基于用户的历史行为制作特征, 这个会用到用户的历史点击表, 最后的召回列表, 文章的信息表和embedding向量
- 制作标签, 形成最后的监督学习数据集
数据读取
训练和验证集的划分
划分训练和验证集的原因是为了在线下验证模型参数的好坏,为了完全模拟测试集,我们这里就在训练集中抽取部分用户的所有信息来作为验证集。提前做训练验证集划分的好处就是可以分解制作排序特征时的压力,一次性做整个数据集的排序特征可能时间会比较长。
Word2Vec训练及gensim的使用
Word2Vec主要思想是:一个词的上下文可以很好的表达出词的语义。通过无监督学习产生词向量的方式。word2vec中有两个非常经典的模型:skip-gram和cbow。
- skip-gram:已知中心词预测周围词。
- cbow:已知周围词预测中心词。
在使用gensim训练word2vec的时候,有几个比较重要的参数
- size: 表示词向量的维度。
- window:决定了目标词会与多远距离的上下文产生关系。
- sg: 如果是0,则是CBOW模型,是1则是Skip-Gram模型。
- workers: 表示训练时候的线程数量
- min_count: 设置最小的
- iter: 训练时遍历整个数据集的次数
注意
- 训练的时候输入的语料库一定要是字符组成的二维数组,如:[[‘北’, ‘京’, ‘你’, ‘好’], [‘上’, ‘海’, ‘你’, ‘好’]]
- 使用模型的时候有一些默认值,可以通过在Jupyter里面通过
Word2Vec??
查看
下面是个简单的测试样例:
from gensim.models import Word2Vec
doc = [['30760', '157507'],
['289197', '63746'],
['36162', '168401'],
['50644', '36162']]
w2v = Word2Vec(docs, size=12, sg=1, window=2, seed=2020, workers=2, min_count=1, iter=1)
# 查看'30760'表示的词向量
w2v['30760']
skip-gram和cbow的详细原理可以参考下面的博客:
- word2vec原理(一) CBOW与Skip-Gram模型基础
- word2vec原理(二) 基于Hierarchical Softmax的模型
- word2vec原理(三) 基于Negative Sampling的模型
对训练数据做负采样
通过召回我们将数据转换成三元组的形式(user1, item1, label)的形式,观察发现正负样本差距极度不平衡,我们可以先对负样本进行下采样,下采样的目的一方面缓解了正负样本比例的问题,另一方面也减小了我们做排序特征的压力,我们在做负采样的时候又有哪些东西是需要注意的呢?
- 只对负样本进行下采样(如果有比较好的正样本扩充的方法其实也是可以考虑的)
- 负采样之后,保证所有的用户和文章仍然出现在采样之后的数据中
- 下采样的比例可以根据实际情况人为的控制
- 做完负采样之后,更新此时新的用户召回文章列表,因为后续做特征的时候可能用到相对位置的信息。
其实负采样也可以留在后面做完特征在进行,这里由于做排序特征太慢了,所以把负采样的环节提到前面了。
制作与用户历史行为相关特征
对于每个用户召回的每个商品, 做特征。 具体步骤如下:
- 对于每个用户, 获取最后点击的N个商品的item_id,
- 对于该用户的每个召回商品, 计算与上面最后N次点击商品的相似度的和(最大, 最小,均值), 时间差特征,相似性特征,字数差特征,与该用户的相似性特征
用户和文章特征
用户相关特征
这一块,正式进行特征工程,既要拼接上已有的特征, 也会做更多的特征出来,我们来梳理一下已有的特征和可构造特征:
- 文章自身的特征, 文章字数,文章创建时间, 文章的embedding (articles表中)
- 用户点击环境特征, 那些设备的特征(这个在df中)
- 对于用户和商品还可以构造的特征:
- 基于用户的点击文章次数和点击时间构造可以表现用户活跃度的特征
- 基于文章被点击次数和时间构造可以反映文章热度的特征
- 用户的时间统计特征: 根据其点击的历史文章列表的点击时间和文章的创建时间做统计特征,比如求均值, 这个可以反映用户对于文章时效的偏好
- 用户的主题爱好特征, 对于用户点击的历史文章主题进行一个统计, 然后对于当前文章看看是否属于用户已经点击过的主题
- 用户的字数爱好特征, 对于用户点击的历史文章的字数统计, 求一个均值
分析一下点击时间和点击文章的次数,区分用户活跃度
如果某个用户点击文章之间的时间间隔比较小, 同时点击的文章次数很多的话, 那么我们认为这种用户一般就是活跃用户, 当然衡量用户活跃度的方式可能多种多样, 这里我们只提供其中一种,我们写一个函数, 得到可以衡量用户活跃度的特征,逻辑如下:
- 首先根据用户user_id分组, 对于每个用户,计算点击文章的次数, 两两点击文章时间间隔的均值
- 把点击次数取倒数和时间间隔的均值统一归一化,然后两者相加合并,该值越小, 说明用户越活跃
- 注意, 上面两两点击文章的时间间隔均值, 会出现如果用户只点击了一次的情况,这时候时间间隔均值那里会出现空值, 对于这种情况最后特征那里给个大数进行区分
这个的衡量标准就是先把点击的次数取到数然后归一化, 然后点击的时间差归一化, 然后两者相加进行合并, 该值越小, 说明被点击的次数越多, 且间隔时间短。