CTR学习笔记&代码实现5-深度ctr模型 DeepCrossing -> DCN
之前总结了PNN,NFM,AFM这类两两向量乘积的方式,这一节我们换新的思路来看特征交互。DeepCrossing是最早在CTR模型中使用ResNet的前辈,DCN在ResNet上进一步创新,为高阶特征交互提供了新的方法并支持任意阶数的特征交叉。
以下代码针对Dense输入更容易理解模型结构,针对spare输入的代码和完整代码
https://github.com/DSXiangLi/CTR
Deep Crossing
Deep Crossing结构比较简单,和最原始的Embedding+MLP的模型结果相比,差异在于之后跟的不是全连接层而是残差层。模型结构如下

简单说说残差网络,基本的网络结构如下

残差网络解决了什么,为什么有效?这篇博客讲得很清楚,核心是解决网络退化的问题,既随着网络深度增加,网络的表现先是逐渐增加至饱和,然后迅速下降。这里的下降并非指过拟合。理论上如果20层的网络是最优解,那30层的网络会包含20层的网络,后面10层只需做恒等映射\(a^{l} = a^{l-1}\)即可,因此更多怀疑是MLP不易拟合恒等映射。而上述残差网络因为做了identity mapping,当\(F(a^{l-1}, w^l)=0\)时,就直接沿用上一层数据也就是进行了恒等变换。
那把ResNet放到CTR模型里又有什么特殊的优势呢?老实说感觉像是把那个时期比较牛的框架直接拿来用。。。不过能想到的一种是MLP学习的是高阶泛化特征,而ResNet做的identity mapping会保留更多的原始低阶特征信息,有点类似Wide&Deep又不完全是,因为输入已经是Embedding而不是原始的离散特征了。真棒又强行解释了一波。。。
代码实现
def residual_layer(x0, unit, dropout_rate, batch_norm, mode):
# f(x): input_size -> unit -> input_size
# output = relu(f(x) + x)
input_size = x0.get_shape().as_list()[-1]
# input_size -> unit
x1 = tf.layers.dense(x0, units = unit, activation = 'relu')
if batch_norm:
x1 = tf.layers.batch_normalization( x1, center=True, scale=True,
trainable=True,
training=(mode == tf.estimator.ModeKeys.TRAIN) )
if dropout_rate > 0:
x1 = tf.layers.dropout( x1, rate=dropout_rate,
training=(mode == tf.estimator.ModeKeys.TRAIN) )
# unit -> input_size
x2 = tf.layers.dense(x1, units = input_size )
# stack with original input and apply relu
output = tf.nn.relu(tf.add(x2, x0))
return output
@tf_estimator_model
def model_fn(features, labels, mode, params):
dense_feature = build_features()
dense = tf.feature_column.input_layer(features, dense_feature)
# stacked residual layer
with tf.variable_scope('Residual_layers'):
for i, unit in enumerate(params['hidden_units']):
dense = residual_layer( dense, unit,
dropout_rate = params['dropout_rate'],
batch_norm = params['batch_norm'], mode = mode)
add_layer_summary('residual_layer{}'.format(i), dense)
with tf.variable_scope('output'):
y = tf.layers.dense(dense, units=1)
add_layer_summary( 'output', y )
return y
Deep&Cross
Deep&Cross带着Wide&Deep的风格,在保留全联接的Deep部分的同时,Deep&Cross借鉴了上述ResNet的思路,创新了显式的高阶特征交互方式。之前的模型要么像DeepFM直接依赖全连接层来捕捉高阶特征交互,要么像PNN,NFM,AFM先基于向量两两做内/外/element-wise乘积学习二阶交互特征,再依赖全联接层来学习更高阶的交互信息。两两交互式的方法很难扩展到更高阶,因为会存在维度爆炸的问题。
模型细节
DCN的输入是Embedding和连续特征拼接而成的Dense输入,因为不像PNN,AFM等需要两两向量内积,因此对每个特征Embedding的维度是否一致没有要求,然后Cross部分和Deep部分共享输入,进行联合训练,最终把两个part进行拼接后预测ctr。模型结构如下

Deep部分没啥好说的和DeepFM,Wide&Deep一样就是多个全联接层用来学习泛化特征。Cross部分由多层的cross_layer组成,输入有N个特征,为简化Embedding维度统一是为K,每层cross_layer的计算如下

1. 特征共享:控制复杂度
特征共享的存在,保证了Cross每增加一层,新增的参数都是\(O(NK)\)
FM视角(式4): FM是每个离散特征共享一个隐向量v,向量交互的权重为隐向量内积,但这种操作只局限于两两交互。而Cross是Embedding的每一个元素和其余所有元素交互时共享一个权重w。(这里感觉cross直接用原始的one-hot也是可以的,只不过用Embedding可以进一步降低复杂度)
OPNN视角(式5): OPNN两两向量做外积得到\(N^2\)个\(K^2\)外积矩阵,拼在一起其实就是Cross不区分Field直接做外积得到的大外积矩阵。不过不像OPNN采用简单粗暴的sum_pooling来解决维度爆炸的问题,Cross采用每行共享一个权重的方式来降维。保留更多信息的同时保证了Cross-layer的复杂度不会随层数上升而上升, 每层的维度都是最初的\(NK\), 复杂度也是\(O(NK)\)
2. 多项式内核:任意阶数特征交互
为简化我们先忽略截距项,看下两层的cross-layer
会发现ResNet加上cross,类似于对输入向量进行了多项式计算,Cross的部分每深一层,就可以捕捉更高一阶的特征交互信息。因此高级特征交互信息的捕捉不再简单依赖MLP而是人为可控。同时ResNet的存在也保证了不会随着Cross的加深而导致模型过于泛化,因为最初的输入特征始终保留。
DCN已经很优秀,只能想到可以吐槽的点
- 对记忆信息的学习可能会有不足,虽然有ResNet但输入已经是Embedding特征,多少已经是泛化后的特征表达,不知道再加入Wide部分是不是会有提升。
代码实现
在上面参数共享讨论的两种视角,刚好对应到cross layer的两种计算方式。按照原始顺序Embedding先做外积再加权求和(特征共享中的OPNN视角),会需要存储巨大的临时矩阵,代码如下
def cross_op_raw(xl, x0, weight, feature_size):
# (x0 * xl) * w
# (batch,feature_size) - > (batch, feature_size * feature_size)
outer_product = tf.matmul(tf.reshape(x0, [-1, feature_size,1]),
tf.reshape(xl, [-1, 1, feature_size])
)
# (batch,feature_size*feature_size) ->(batch, feature_size)
interaction = tf.tensordot(outer_product, weight, axes=1)
return interaction
而通过调整向量乘积的顺序\((x_0 * x_l) *w \to x_0 * (x_l * w)\)我们可以避免外积矩阵的运算(特征共享中的FM视角),也就是paper中提到的利用\(x_0x_l^T\)是秩为1的矩阵特性。
def cross_op_better(xl, x0, weight, feature_size):
# x0 * (xl * w)
# (batch, 1, feature_size) * (feature_size) -> (batch,1)
transform = tf.tensordot( tf.reshape( xl, [-1, 1, feature_size] ), weight, axes=1 )
# (batch, feature_size) * (batch, 1) -> (batch, feature_size)
interaction = tf.multiply( x0, transform )
return interaction
完整代码如下
def cross_layer(x0, cross_layers, cross_op = 'better'):
xl = x0
if cross_op == 'better':
cross_func = cross_op_better
else:
cross_func = cross_op_raw
with tf.variable_scope( 'cross_layer' ):
feature_size = x0.get_shape().as_list()[-1] # feature_size = n_feature * embedding_size
for i in range( cross_layers):
weight = tf.get_variable( shape=[feature_size],
initializer=tf.truncated_normal_initializer(), name='cross_weight{}'.format( i ) )
bias = tf.get_variable( shape=[feature_size],
initializer=tf.truncated_normal_initializer(), name='cross_bias{}'.format( i ) )
interaction = cross_func(xl, x0, weight, feature_size)
xl = interaction + bias + xl # add back original input -> (batch, feature_size)
add_layer_summary( 'cross_{}'.format( i ), xl )
return xl
@tf_estimator_model
def model_fn_dense(features, labels, mode, params):
dense_feature = build_features()
dense_input = tf.feature_column.input_layer(features, dense_feature)
# deep part
dense = stack_dense_layer(dense_input, params['hidden_units'],
params['dropout_rate'], params['batch_norm'],
mode, add_summary = True)
# cross part
xl = cross_layer(dense_input, params['cross_layers'], params['cross_op'])
with tf.variable_scope('stack'):
x_stack = tf.concat( [dense, xl], axis=1 )
with tf.variable_scope('output'):
y = tf.layers.dense(x_stack, units =1)
add_layer_summary( 'output', y )
return y
CTR学习笔记&代码实现系列
https://github.com/DSXiangLi/CTR
CTR学习笔记&代码实现1-深度学习的前奏LR->FFM
CTR学习笔记&代码实现2-深度ctr模型 MLP->Wide&Deep
CTR学习笔记&代码实现3-深度ctr模型 FNN->PNN->DeepFM
CTR学习笔记&代码实现4-深度ctr模型 NFM/AFM
资料
- Gang Fu,Mingliang Wang, 2017, Deep & Cross Network for Ad Click Predictions
- Ying Shan, T. Ryan Hoens, 2016, Deep Crossing: Web-Scale Modeling without Manually Crafted Combinatorial Features
- https://blog.csdn.net/Dby_freedom/article/details/86502623
- https://zhuanlan.zhihu.com/p/80226180
CTR学习笔记&代码实现5-深度ctr模型 DeepCrossing -> DCN的更多相关文章
- CTR学习笔记&代码实现3-深度ctr模型 FNN->PNN->DeepFM
这一节我们总结FM三兄弟FNN/PNN/DeepFM,由远及近,从最初把FM得到的隐向量和权重作为神经网络输入的FNN,到把向量内/外积从预训练直接迁移到神经网络中的PNN,再到参考wide& ...
- CTR学习笔记&代码实现4-深度ctr模型 NFM/AFM
这一节我们总结FM另外两个远亲NFM,AFM.NFM和AFM都是针对Wide&Deep 中Deep部分的改造.上一章PNN用到了向量内积外积来提取特征交互信息,总共向量乘积就这几种,这不NFM ...
- CTR学习笔记&代码实现6-深度ctr模型 后浪 xDeepFM/FiBiNET
xDeepFM用改良的DCN替代了DeepFM的FM部分来学习组合特征信息,而FiBiNET则是应用SENET加入了特征权重比NFM,AFM更进了一步.在看两个model前建议对DeepFM, Dee ...
- CTR学习笔记&代码实现2-深度ctr模型 MLP->Wide&Deep
背景 这一篇我们从基础的深度ctr模型谈起.我很喜欢Wide&Deep的框架感觉之后很多改进都可以纳入这个框架中.Wide负责样本中出现的频繁项挖掘,Deep负责样本中未出现的特征泛化.而后续 ...
- CTR学习笔记&代码实现1-深度学习的前奏LR->FFM
CTR学习笔记系列的第一篇,总结在深度模型称王之前经典LR,FM, FFM模型,这些经典模型后续也作为组件用于各个深度模型.模型分别用自定义Keras Layer和estimator来实现,哈哈一个是 ...
- GIS案例学习笔记-明暗等高线提取地理模型构建
GIS案例学习笔记-明暗等高线提取地理模型构建 联系方式:谢老师,135-4855-4328,xiexiaokui#qq.com 目的:针对数字高程模型,通过地形分析,建立明暗等高线提取模型,生成具有 ...
- 【PyTorch深度学习】学习笔记之PyTorch与深度学习
第1章 PyTorch与深度学习 深度学习的应用 接近人类水平的图像分类 接近人类水平的语音识别 机器翻译 自动驾驶汽车 Siri.Google语音和Alexa在最近几年更加准确 日本农民的黄瓜智能分 ...
- [原创]java WEB学习笔记44:Filter 简介,模型,创建,工作原理,相关API,过滤器的部署及映射的方式,Demo
本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...
- cips2016+学习笔记︱简述常见的语言表示模型(词嵌入、句表示、篇章表示)
在cips2016出来之前,笔者也总结过种类繁多,类似词向量的内容,自然语言处理︱简述四大类文本分析中的"词向量"(文本词特征提取)事实证明,笔者当时所写的基本跟CIPS2016一 ...
随机推荐
- Java 导出Excel xlsx、xls, CSV文件
通用导出功能: 1.支持Excel xlsx.xls 2.支持CSV文件导出 3.数据库查询分页导出.内存导出 4.支持大批量数据导出 使用步骤如下 导入jar <dependency> ...
- 【FishFX】花式撩骚,打造TypeScript易用框架。
· 栗子入手 假设有以下foo数组,数组中每个对象都拥有id,name两个属性,现在需要查找id > 0的对象数量. const foo: Array<{ id: number, name ...
- 在同一个服务器上同时配置xxxxxxxxxx1个tomcat
下面我们把配置的详细过程写在下面,以供参考:(此例以配置三个Tomcat为例) 下载apache-tomcat-7.0.63,下载下来的文件为apache-tomcat-7.0.63.zip. 解压该 ...
- Jenkins(1)- centos7.X下安装Jenkins
如果想从头学起Jenkins的话,可以看看这一系列的文章哦 https://www.cnblogs.com/poloyy/category/1645399.html 下载安装包 wget http:/ ...
- 关于Swiper和vue数据顺序加载问题处理
在使用swiper插件的时候,常常因为异步加载数据产生的顺序问题而使插件不能正常实行,所以可以使用vue的updated来解决. 问:什么时候 进updated方法? 答:只有事先设置好的data变量 ...
- 2019-2020-1 20199310《Linux内核原理与分析》第六周作业
1.问题描述 在前面的文章中,学习了系统调用的相关理论知识,并使用库函数API和C代码中嵌入汇编代码两种方式使用getpid()系统调用.本文将内容围绕系统调用system_call的处理过程,在Me ...
- sql注入学习笔记
1.什么是SQL注入 SQL注入就是指web应用程序对用户输入的数据的合法性没有判断,前端传入后端的参数带有数据库查询的语句,攻击者可以构造不同的SQL语句来实现对数据库的操作. 2.SQL注入原理 ...
- 二, 连接Oracle 二
一,sqlplus操作 文件操作命令 1.start和@ 说明: 运行sql脚本 案例: sql>@ home/a.sql或是sql>start home/a.sql 2.edit 说明: ...
- javescrip内嵌样式与外联样式怎么做?
对于前端初学者,个人JS样式常用的有两种:内嵌样式 ,外联样式:下面通过一个简单的鼠标点击出现设定的验证数字为例进行演示: 先看下效果: 鼠标点击前效果: 鼠标点击后效果: 图中的这个ojbk是我js ...
- 【shell】Shell变量基础及深入
1. 什么是变量 变量就是用一个固定的字符串(也可能是字符数字等的组合),替代更多更复杂的内容,这个内容里可能还会包含变量和路径,字符串等其他内容. 变量的定义是存在内存中. x=1 y=2 2. 变 ...