【CTR模型】TensorFlow2.0 的 DCN(Deep & Cross Network) 实现与实战(附代码+数据)

  • Post author:
  • Post category:其他


CTR 系列文章:


  1. 广告点击率(CTR)预测经典模型 GBDT + LR 理解与实践(附数据 + 代码)

  2. CTR经典模型串讲:FM / FFM / 双线性 FFM 相关推导与理解

  3. CTR深度学习模型之 DeepFM 模型解读

  4. 【CTR模型】TensorFlow2.0 的 DeepFM 实现与实战(附代码+数据)

  5. CTR 模型之 Deep & Cross (DCN) 与 xDeepFM 解读

  6. 【CTR模型】TensorFlow2.0 的 DCN(Deep & Cross Network) 实现与实战(附代码+数据)

  7. 【CTR模型】TensorFlow2.0 的 xDeepFM 实现与实战(附代码+数据)

本篇文章讲解 DCN(Deep & Cross Network) 的 tensorflow2.0 实现,并使用 Criteo 数据集的子集加以实践。如果在看本文时有所困惑,可以看看 DCN(Deep & Cross Network) 的相关理论:

CTR 模型之 Deep & Cross (DCN) 与 xDeepFM 解读

本文使用的数据下载地址于代码获取地址在文末获取。

首先了解一下 Criteo数据集,它由有39个特征,1个label列,其中以I开头的为数值型特征,以C开头的为类别特征:

在这里插入图片描述

可以看到数据中有缺失值需要填充,并且类别变量需要进行类别编码(onehot 编码的任务交给模型),这部分预处理的代码不详细讲了。

为了方便后面建立模型,先将特征划分为 dense 特征与 sparse 特征两个类别:

# 数值型
dense_feats = [f for f in cols if f[0] == "I"]
# 类别型
sparse_feats = [f for f in cols if f[0] == "C"]



Deep & Cross Network

DCN网络结构如下:

在这里插入图片描述



构造模型输入

对于 dense 特征,按下面的代码构造输入:

# 构造每个 dense 特征的输入
dense_inputs = []
for f in dense_feats:
    _input = Input([1], name=f)
    dense_inputs.append(_input)
# 将输入拼接到一起
concat_dense_inputs = Concatenate(axis=1)(dense_inputs)  # ?, 13

对于 sparse 特征,按下面的代码构造输入:

# 这里单独对每一个 sparse 特征构造输入,
# 目的是方便后面构造二阶组合特征
sparse_inputs = []
for f in sparse_feats:
    _input = Input([1], name=f)
    sparse_inputs.append(_input)

# embedding size
k = 8
# 对sparse特征进行embedding
sparse_kd_embed = []
for _input in sparse_inputs:
    f = _input.name.split(':')[0]
    voc_size = total_data[f].nunique()
    _embed = Flatten()(Embedding(voc_size, k, embeddings_regularizer=tf.keras.regularizers.l2(0.7))(_input))
    sparse_kd_embed.append(_embed)

# 将sparse特征拼接在一起
concat_sparse_inputs = Concatenate(axis=1)(sparse_kd_embed)

然后将所有的 dense 输入和 sparse 输入拼接到一起:

embed_inputs = Concatenate(axis=1)([concat_sparse_inputs, concat_dense_inputs])



Cross Network

终于来到最核心的 Cross 部分,其中第



l

+

1

l + 1






l




+








1





层的计算过程为:

在这里插入图片描述





x

l

+

1

=

x

0

x

l

T

w

l

+

b

l

+

x

l

x_{l+1} =x_0x^T_lw_l + b_l + x_l







x











l


+


1





















=









x










0



















x










l








T



















w










l




















+









b










l




















+









x










l























此公式的实现代码如下:

def cross_layer(x0, xl):
    """
    实现一层cross layer
    @param x0: 特征embeddings
    @param xl: 前一层的输出结果
    """
    # 1.获取xl层的embedding size
    embed_dim = xl.shape[-1]
    # 2.初始化当前层的W和b
    w = tf.Variable(tf.random.truncated_normal(shape=(embed_dim,), stddev=0.01))
    b = tf.Variable(tf.zeros(shape=(embed_dim,)))
    # 3.计算feature crossing
    # 下面的reshape操作相当于将列向量转换为行向量
    x1_T = tf.reshape(xl, [-1, 1, embed_dim])
    # 行向量与列向量的乘积结果是一个标量
    x_lw = tf.tensordot(x1_T, w, axes=1)
    cross = x0 * x_lw 
    return cross + b + xl

这个代码的执行结果是第



l

+

1

l+1






l




+








1





层的输出,共分为三个步骤:

  1. 获取前一层的输出结果的嵌入维度
  2. 初始化本层的网络参数
  3. 进行特征交叉

关键在于第三步,计算



x

0

x

l

T

w

l

x_0x^T_lw_l







x










0



















x










l








T



















w










l





















时,如果先计算



x

0

x

T

x_0x^T







x










0



















x










T












则会得到一个矩阵,为了优化内存的使用,可以先计算



x

l

T

w

l

x^T_lw_l







x










l








T



















w










l





















得到标量,然后再与



x

0

x_0







x










0





















相乘得到 feature crossing。

接下来可以利用循环构建多层 crossing layer:

def build_cross_layer(x0, num_layer=3):
    """
    构建多层cross layer
    @param x0: 所有特征的embeddings
    @param num_layers: cross net的层数
    """
    # 初始化xl为x0
    xl = x0
    # 构建多层cross net
    for i in range(num_layer):
        xl = cross_layer(x0, xl)
    
    return xl
    
# cross net
cross_layer_output = build_cross_layer(embed_inputs, 3)



DNN 部分

这部分好理解,直接上代码吧:

fc_layer = Dropout(0.5)(Dense(128, activation='relu')(embed_inputs))
fc_layer = Dropout(0.3)(Dense(128, activation='relu')(fc_layer))
fc_layer_output = Dropout(0.1)(Dense(128, activation='relu')(fc_layer))



输出部分

代码如下:

stack_layer = Concatenate()([cross_layer_output, fc_layer_output])
output_layer = Dense(1, activation='sigmoid', use_bias=True)(stack_layer)



完善模型

model = Model(dense_inputs+sparse_inputs, output_layer)
model.compile(optimizer="adam", 
              loss="binary_crossentropy", 
              metrics=["binary_crossentropy", tf.keras.metrics.AUC(name='auc')])



训练模型

train_data = total_data.loc[:500000-1]
valid_data = total_data.loc[500000:]

train_dense_x = [train_data[f].values for f in dense_feats]
train_sparse_x = [train_data[f].values for f in sparse_feats]
train_label = [train_data['label'].values]

val_dense_x = [valid_data[f].values for f in dense_feats]
val_sparse_x = [valid_data[f].values for f in sparse_feats]
val_label = [valid_data['label'].values]

model.fit(train_dense_x+train_sparse_x, 
          train_label, epochs=5, batch_size=128,
          validation_data=(val_dense_x+val_sparse_x, val_label),
         )

最后,本文的代码链接在:https://github.com/zxxwin/tf2_DCN 。

数据下载地址为:链接:https://pan.baidu.com/s/1Qy3yemu1LYVtj0Wn47myHQ 提取码:pv7u

参考文章:


CTR预估模型:DeepFM/Deep&Cross/xDeepFM/AutoInt代码实战与讲解


NELSONZHAO/zhihu/ctr_models/DCN



版权声明:本文为VariableX原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。