零基础入门推荐系统-新闻推荐-天池大赛-阿里云天池Task04【特征工程】
特征工程:制作特征和标签,转成监督学习问题
我们先分析一下基于原始的给定数据,有哪些特征可以直接使用:
1.文章的自身特征,category_id表示这文章的类型,created_at_ts表示文章建立的时间,这个关系着文章的时效性,words_count是文章字数,一般字数太长我们不太喜欢点击,也不排除有人就喜欢读长文。
2.文章的内容embedding特征,这个召回的时候用过,这里可以选择使用,也可以选择不用,也可以尝试其他类型的embedding特性,比如W2V等
3.用户的设备特征信息
上面这些直接可以用的特性,带做完特征工程之后,直接就可以使用根据article_id或者user_id把这些特征加入进去。但是我们需要先基于召回的结果,构造一些特征,然后制作标签,形成一个监督学习的数据集。
构造监督数据集的思路,根据召回结果,我们会得到一个{user_id:[可能点击的文章列表]}形式的字典。那么我们就可以对于每个用户,每篇可能点击的文章构造一个监督测试集,比如对于用户user1,假如得到的它的召回列表{user1:[item1.item2,item3]},我们就可以得到三行数据(user1,item1),(user1,item2),(user1,item3)的形式,这就是监督测试集时候的前两列特征。
构造特征的思路是这样,我们知道每一用户的点击文章是与其历史点击的文章信息是有很大关联的,比如同一个主题,相似等等。所以特征构造这块很重要的一系列特征是要结合用户的历史点击文章信息。我们已经得到了每个用户及点击候选文章的两列的一个数据集,而我们的目的是预测最后一次点击的文章,比较自然的一个思路就是和其最后几次点击文章产生关系,这样既考虑了其历史点击文章信息,又得离最后一次点击较近,因为新闻很大一个特点就是注重时效性。往往用户的最后一次点击会和其最后几次点击有很大的关联。所以我们就可以对于每个候选文章,做出与最后几次点击相关的特征如下:
1.候选item与最后几次点击的相似性特征(embedding内积)-这个直接关联用户历史行为
2.候选item与最后几次点击的相似性特征的统计特征——统计特征可以减少一些波动和异常
3.候选item与最后几次点击文章的字数差的特征 — 可以通过字数看用户偏好
4.候选item与最后几次点击的文章建立的时间差特征 — 时间差特征可以看出该用户对于文章的实时性的偏好
还需要考虑一下
5. 如果使用了youtube召回的话, 我们还可以制作用户与候选item的相似特征
当然, 上面只是提供了一种基于用户历史行为做特征工程的思路, 大家也可以思维风暴一下,尝试一些其他的特征。 下面我们就实现上面的这些特征的制作, 下面的逻辑是这样:
1.我们首先获得用户的最后一次点击操作和用户的历史点击, 这个基于我们的日志数据集做
2.基于用户的历史行为制作特征, 这个会用到用户的历史点击表, 最后的召回列表, 文章的信息表和embedding向量
3.制作标签, 形成最后的监督学习数据集
导包
df节省内存函数
# 节省内存的一个函数
# 减少内存
def reduce_mem(df):
starttime = time.time()
numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
start_mem = df.memory_usage().sum() / 1024**2
for col in df.columns:
col_type = df[col].dtypes
if col_type in numerics:
c_min = df[col].min()
c_max = df[col].max()
if pd.isnull(c_min) or pd.isnull(c_max):
continue
if str(col_type)[:3] == 'int':
if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
df[col] = df[col].astype(np.int8)
elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
df[col] = df[col].astype(np.int16)
elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
df[col] = df[col].astype(np.int32)
elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:
df[col] = df[col].astype(np.int64)
else:
if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
df[col] = df[col].astype(np.float16)
elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
df[col] = df[col].astype(np.float32)
else:
df[col] = df[col].astype(np.float64)
end_mem = df.memory_usage().sum() / 1024**2
print('-- Mem. usage decreased to {:5.2f} Mb ({:.1f}% reduction),time spend:{:2.2f} min'.format(end_mem,
100*(start_mem-end_mem)/start_mem,
(time.time()-starttime)/60))
return df
代码设计思想:
1.在每一列上迭代
2.确定列是否为数字
3.判断列是否可以用整数表示
4.找到最小值和最大值
5.确定并应用能够适应值范围的最小数据类型
可以参考:https://www.kaggle.com/arjanso/reducing-dataframe-memory-size-by-65
定义数据路径
数据读取
训练和验证集的划分
划分训练和验证集的原因是为了在线下验证模型参数的好坏,为了完全模拟测试集,我们这里就在训练集中抽取部分用户的所有信息来作为验证集。提前做训练验证集划分的好处就是可以分解制作排序特征时的压力,一次性做整个数据集的排序特征可能时间会比较长。
使用sort_values()结果如下:
使用groupby()结果如下:
使用reset_index()结果如下:
这里如果drop=False,会出现user_id碰撞,所以设置True,把多余user_id索引列删除。
python中[-1]、[:-1]、[::-1]、[n::-1]使用方法
参考:https://blog.csdn.net/weixin_42165786/article/details/89388403?utm_source=app
获取历史点击和最后一次点击
读取训练、验证及测试集
读取召回列表
读取各种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??查看
下面是个简单的测试样例:
注意:1.df.values.tolist()作用:DataFrame格式转换list
2.logging.basicConfig()详解参考:https://blog.csdn.net/The_Time_Runner/article/details/98650488?utm_source=app
读取文章信息
读取数据
对训练数据做负采样
通过召回我们将数据转换成三元组的形式(user1, item1, label)的形式,观察发现正负样本差距极度不平衡,我们可以先对负样本进行下采样,下采样的目的一方面缓解了正负样本比例的问题,另一方面也减小了我们做排序特征的压力,我们在做负采样的时候又有哪些东西是需要注意的呢?
1.只对负样本进行下采样(如果有比较好的正样本扩充的方法其实也是可以考虑的)
2.负采样之后,保证所有的用户和文章仍然出现在采样之后的数据中
3.下采样的比例可以根据实际情况人为的控制
4.做完负采样之后,更新此时新的用户召回文章列表,因为后续做特征的时候可能用到相对位置的信息。
其实负采样也可以留在后面做完特征在进行,这里由于做排序特征太慢了,所以把负采样的环节提到前面了。
注意:1.python items()用法:
items()方法的遍历:items()方法把字典中每对key和value组成一个元组,并把这些元组放在列表中返回。具体可以查阅:https://blog.csdn.net/qq_16792139/article/details/107912507?utm_source=app
2.tqdm是一个快速,可扩展的Python进度条,可以在Python长循环中添加一个进度提示信息,用户只需要封装任意的迭代器tqdm(iterator).具体可参考:https://blog.csdn.net/qq_3347265/article/details/82940843?utm_source=app
以上来自:http://datawhale.club/t/topic/302