zoukankan      html  css  js  c++  java
  • Deepctr框架代码阅读

    DeepCtr是一个简易的CTR模型框架,集成了深度学习流行的所有模型,适合学推荐系统模型的人参考。我在参加比赛中用到了这个框架,但是效果一般,为了搞清楚原因从算法和框架两方面入手。在读代码的过程中遇到一些不理解的问题,所以记录在这里。

    1. DeepFM模型的整体流程

    preprocess_input_embedding:
    	create_singlefeat_inputdict:
    		搞成Feat是为了整体封装好,然后输入到Input的时候可以一一对应
    		dense和spare直接放入keras的Input,格式是dict{key是feat名字,value是Input层结果}
    	create_varlenfeat_inputdict
    		序列是直接用max len放入keras的Input
    	get_inputs_embedding
    		create_embedding_dict(embedding层)
    			稀疏特征:自动指定embeddingsize,6 * int(pow(feat.dimension, 0.25),否则按照指定的embeddingsize,使用L2正则化
    			序列特征和稀疏特征的流程一样,封装Embedding多了mask_zero
    			结果都是dict{key是feat名字,value是Embedding层结果},处理的是稀疏和序列特征
    		get_embedding_vec_list(是embedding值)
    			如果指定是hash,用自己写的Hash函数将特征的索引(是Input层的结果,也就是原始是数据输入)转换成hash函数,如果不是就直接用原始的特征索引。用特征索引和对应的特征在Embedding获取输出。
    			这里只处理sparse
    		merge_sequence_input
    			这里处理序列
    			get_varlen_embedding_vec_dict
    				hash的时候全部填充0,和之前的区别是之前指定的才填充0
    				这里和处理的sparse的方式一样,区别是用sequence_input_dict,但是这个和sparse都是用OrderedDict,区别是用最大长度,名字加seq
    			get_pooling_vec_list
    				如果没有最长长度或者长度序列为空,不填充。在SequencePoolingLayer对序列特征进行pool
    			把结果加到之前sparse的结果上
    			返回全部结果		
    		merge_dense_input
    			把原始的embedding拼接成列向量,加到之前的结果上
    		如果有线性:
    			稀疏向量的embedding长度是1,只做融合稀疏和序列,流程和之前一样
    		inputs_list是稀疏、稠密、序列和序列长度
    返回deep_emb_list, linear_emb_list, dense_input_dict, inputs_list
    get_linear_logit
    	如果有linear embedding,就是dense input经过全连接层之后加linear embedding,否则是dense input经过全连接层之后直接输出。默认创造linear embedding
    embedding拉平放入FM和Deep中,然后是linear+FM+Deep,如果都有的话,有一个加一个
    	FM:和的平方-平方的和
    	DNN:默认两层128*128
    最后所有的结果放入PredictionLayer,也就是连一个softmax或者sigmoid	
    

    2. 框架优点

    • 整体结构清晰灵活,linear返回logit,FM层返回logit,deep包含中间层结果,在每一种模型中打包deep的最后一层,判断linear,fm和deep是否需要,最后接入全连接层。
    • 主要用到的模块和架构: keras的Concatenate(list转tensor),Dense(最后的全连接层和dense),Embedding(sparse,dense,sequence),Input(sparse,dense,sequce)还有常规操作:优化器,正则化项
    • 复用了重载了Layer层,重写了build,call,compute_output_shape,compute_mask,get_config

    3. 框架缺点

    • 给定的参数都是论文提供的参数,实际使用存在问题,都需要自己修改!
    • 好多参数没有留接口,比如回归问题的loss 是mean_squared_error,只能通过硬写来修改参数
    • 如果想实现自己的模型,复用这个框架,需要了解keras,同时改很多接口,时间代价较大。

    运行模型,每次结果不一样:
    这个属于正常现象,尤其是数据不够充分的情况下,
    主要原因是由于Tensorflow底层的多线程运行机制以及一些具有随机性的op和random seed导致的。
    如果想让每次运行的结果尽量一致,可以考虑使用CPU运行程序,并且指定单线程运行,同时固定random seed,包括python自身的,Numpy的还有tensorflow的

    4. 思考的问题

    1. 为什么获取Embedding的时候要hash?为什么是在这个地方hash?
      慢慢想!
    2. 为什么linear需要全连接+Embedding,为什么默认为Linear加入Embeding操作?
      慢慢想!
    3. FM模型的连续特征是怎么处理的?
      1. 离散化后在输入模型,事实上离散化后的模型更适合工业流水线环境。
      2. embedding一般是表示特征,embedding的话一般不能用来表示连续变量,拿年龄举例来说就是不能让10岁和40岁用同一个vector来表示。所以,还是要做one-hot处理的,也可以改动一下FM的结构,在一阶部分保留原始的连续特征。
      3. w&d是将连续值特征转换成累计分布形式,只针对离散特征去做fm和特征交叉;而它的连续值直接当作embedding向量和离散特征的embedding是拼接起来输入到神经网络里面去的。这里DeepFM模型也是这样做的。
    4. 关于embedding降维的思考:
      比如用户是50个binary特征,广告有100个binary特征,那预测用户是否会点击某条广告: 用fm把这些特征都抽象成10维的embedding,而且只做用户和广告的特征交叉,那把用户侧的embedding对应元素相加,这就压缩成10维了,广告侧也这么做,也变成10维了。这时候衡量用户和广告的相关性就直接拿这两个10的向量内积一下。这样实现降维的目的。
    5. 序列化embedding的方式是pooling的方式
      这是参考youtobe的做法
    6. FM的embedding size通常设置的不大比如4或者8这个量级,但是在深度学习中一般设置的会相对大,比如32、64,128。这是为什么?
      实验决定embeddingsize,有专家说过embeddingsize对最后的结果影响不大,所以只是一般使用128.
    7. 是否能实现有sparse的DeepFM
      为了稀疏输入,可以直接替换Tensor的类型
      ids = tf.SparseTensor(sparse_index, sparse_ids, sparse_shape) values = tf.SparseTensor(sparse_index, sparse_values, sparse_shape)
    8. 在这个帖子中提到embedding look up性能非常低,没有解决方案。
      https://zhuanlan.zhihu.com/p/39774203
    9. concat_fun 这里是concat什么?fm的输入为什么需要concat?
      原来是list,每一行是一个tensor,concat之后是tensor,每一行是tensor
    10. tf.keras.layers.Flatten()(fm_input)
      原先的embedding输入是[d,f,k],deep embedding是[d,f*k]
    11. 这里的实现和我的实现不一样:
      我的linear+interact+deep接入全连接层,将所有的特征接入全连接层, 但是根据根据论文和多家的博客来看,我之前理解的是错误的,正确的应该是 fm logit+deep logit,最后接全连接层。 同时AFM等多个模型都是这么处理的。

    5. 学习到的python知识

    1. cls:表示类本身,和静态函数很像,但是比静态函数多一个功能,就是调用一般的函数。
    2. namedtuple:表示可访问属性的tuple
    3. isinstance:判断对象是否是一个已知类型
    4. 使用__import__ + list 导入需要的包
    5. new和init的区别:一个是创建,一个是初始化

    这里写框架阅读写的有点乱,下次可以按照以下三个要点来记录:

    1. 输入数据的方式
    2. 对不同数据的处理
    3. 最后的结合
  • 相关阅读:
    入门菜鸟
    FZU 1202
    XMU 1246
    Codeforces 294E Shaass the Great 树形dp
    Codeforces 773D Perishable Roads 最短路 (看题解)
    Codeforces 814E An unavoidable detour for home dp
    Codeforces 567E President and Roads 最短路 + tarjan求桥
    Codeforces 567F Mausoleum dp
    Codeforces 908G New Year and Original Order 数位dp
    Codeforces 813D Two Melodies dp
  • 原文地址:https://www.cnblogs.com/x739400043/p/11417625.html
Copyright © 2011-2022 走看看