DCN全称Deep & Cross Network,是谷歌和斯坦福大学在2017年提出的用于Ad Click Prediction的模型。DCN(Deep Cross Network)在学习特定阶数组合特征的时候效率非常高,而且同样不需要特征工程,引入的额外的复杂度也是微乎其微的。
DCN架构图如上图所示:最开始是Embedding and stacking layer,然后是并行的Cross Network和Deep Network,最后是Combination Layer把Cross Network和Deep Network的结果组合得到Output。
接下来,我们对DCN网络的各个部分分别总结分析。
为什么要Embed?
这是CTR模型的常用套路了,稀疏数据变稠密,降维度。
具体的:在web-scale的推荐系统比如CTR预估中,输入的大部分特征都是类别型特征,通常的处理办法就是one-hot,但是one-hot之后输入特征维度非常高非常系数。
所以有了Embedding来大大的降低输入的维度,就是把这些binary features转换成dense vectors with real values。
Embedding操作其实就是用一个矩阵和one-hot之后的输入相乘,也可以看成是一次查询(lookup)。这个Embedding矩阵跟网络中的其他参数是一样的,是需要随着网络一起学习的。
为什么要Stack?
这也是常用套路了,数据有连续的,有稀疏的,稀疏的embedding,处理完了类别型特征,还有连续型特征没有处理。所以我们把连续型特征规范化之后,和嵌入向量stacking到一起,就得到了原始的输入:
x 0 = [ x e m b e d , 1 T , . . . , x e m b e d , k T , x d e n s e T ] x_0=[x_{embed,1}^T,...,x_{embed,k}^T,x_{dense}^T] x0=[xembed,1T,...,xembed,kT,xdenseT]
Cross Network是整篇论文的核心。它被设计来高效的学习组合特征,关键在于如何高效的进行feature crossing。形式化如下:
x l + 1 = x 0 x l T w l + b l + x l = f ( x l , w l , b l ) + x l x_{l+1} = x_0x_l^Tw_l + b_l + x_l = f(x_l,w_l,b_l)+x_l xl+1=x0xlTwl+bl+xl=f(xl,wl,bl)+xl
x l x_l xl和 x l + 1 x_l+1 xl+1 分别是第l层和第 l + 1 l+1 l+1层cross layer的输出, w l w_l wl和 b l b_l bl是这两层之间的连接参数。注意上式中所有的变量均是列向量,W也是列向量,并不是矩阵。每一层的输出,都是上一层的输出加上feature crossing f。而f就是在拟合该层输出和上一层输出的残差。 针对one cross layer可视化如下:
Cross Network特殊的网络结构使得cross feature的阶数随着layer depth的增加而增加。相对于输入x0来说,一个l层的cross network的cross feature的阶数为l+1。
复杂度分析: 假设一共有Lc层cross layer,起始输入x0的维度为d。那么整个cross network的参数个数为:dLc2
因为每一层的W和b都是d维度的。从上式可以发现,复杂度是输入维度d的线性函数。所以相比于deep network,cross network引入的复杂度微不足道。这样就保证了DCN的复杂度和DNN是一个级别的。论文中表示,Cross Network之所以能够高效的学习组合特征,就是因为x0 * xT的秩为1,使得我们不用计算并存储整个的矩阵就可以得到所有的cross terms。
但是,正是因为cross network的参数比较少导致它的表达能力受限,为了能够学习高度非线性的组合特征,DCN并行的引入了Deep Network。
Combination Layer把Cross Network和Deep Network的输出拼接起来,然后经过一个加权求和后得到logits,然后经过sigmoid函数得到最终的预测概率。形式化如下:
p = σ ( [ x L 1 T , h L 2 T ] w l o g i t s ) p = \sigma([x_{L_1}^T,h_{L_2}^T]w_{logits}) p=σ([xL1T,hL2T]wlogits)
p是最终的预测概率;XL1是d维的,表示Cross Network的最终输出;hL2是m维的,表示Deep Network的最终输出;Wlogits是Combination Layer的权重;最后经过sigmoid函数,得到最终预测概率。
另外,针对Cross Network和Deep Network,DCN是一起训练的,这样网络可以知道另外一个网络的存在。
FM是一个非常浅的结构,并且限制在表达二阶组合特征上,DeepCrossNetwork(DCN)把这种参数共享的思想从一层扩展到多层,并且可以学习高阶的特征组合。但是和FM的高阶版本的变体不同,DCN的参数随着输入维度的增长是线性增长的。
与前一篇DeepFM实现方式相同,使用的代码主要采用开源的DeepCTR,相应的API文档可以在这里阅读。DeepCTR是一个易于使用、模块化和可扩展的深度学习CTR模型库,它内置了很多核心的组件从而便于我们自定义模型,它兼容tensorflow 1.4+和2.0+
使用方法很简单,生成指定格式的数据,得到linear_feature_columns与dnn_feature_columns,然后compile——fit——pre即可,感兴趣的可以去看下我在DeepFM中的解释。
import pandas as pd
from sklearn.preprocessing import LabelEncoder, MinMaxScaler
from sklearn.model_selection import train_test_split
from deepctr.models.dcn import DCN
from deepctr.inputs import SparseFeat, DenseFeat,get_feature_names
data = pd.read_csv('./criteo_sample.txt')
sparse_features = ['C' + str(i) for i in range(1, 27)]
dense_features = ['I'+str(i) for i in range(1, 14)]
data[sparse_features] = data[sparse_features].fillna('-1', )
data[dense_features] = data[dense_features].fillna(0,)
target = ['label']
for feat in sparse_features:
lbe = LabelEncoder()
data[feat] = lbe.fit_transform(data[feat])
mms = MinMaxScaler(feature_range=(0,1))
data[dense_features] = mms.fit_transform(data[dense_features])
sparse_feature_columns = [SparseFeat(feat, vocabulary_size=data[feat].nunique(),embedding_dim=4)
for i,feat in enumerate(sparse_features)]
# 或者hash,vocabulary_size通常要大一些,以避免hash冲突太多
# sparse_feature_columns = [SparseFeat(feat, vocabulary_size=1e6,embedding_dim=4,use_hash=True)
# for i,feat in enumerate(sparse_features)]#The dimension can be set according to data
dense_feature_columns = [DenseFeat(feat, 1)
for feat in dense_features]
dnn_feature_columns = sparse_feature_columns + dense_feature_columns
linear_feature_columns = sparse_feature_columns + dense_feature_columns
feature_names = get_feature_names(linear_feature_columns + dnn_feature_columns)
train, test = train_test_split(data, test_size=0.2)
train_model_input = {name:train[name].values for name in feature_names}
test_model_input = {name:test[name].values for name in feature_names}
model = DCN(linear_feature_columns,dnn_feature_columns,task='binary')
model.compile("adam", "binary_crossentropy",
metrics=['binary_crossentropy'], )
history = model.fit(train_model_input, train[target].values,
batch_size=256, epochs=10, verbose=2, validation_split=0.2, )
pred_ans = model.predict(test_model_input, batch_size=256)
参考文献
https://deepctr-doc.readthedocs.io/en/latest/deepctr.models.dcn.html
https://mp.weixin.qq.com/s/lF_WLAn6JyQqf10076hsjA
https://www.cnblogs.com/wmx24/p/10341332.html