本文基于tensorflow2.0实现 Deep & Cross Network(DCN)结构,数据集:Criteo 的500000条数据子集。
预处理
必要的库:
import numpy as np
import pandas as pd
import tensorflow as tf
from tensorflow.keras.layers import *
from tensorflow.keras.models import Model
import tensorflow.keras.backend as K
import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelEncoder
数据读取:
data = pd.read_csv('criteo_sampled_data.csv')
cols = data.columns.values
数据预处理代码如下:
# 定义特征组
dense_feats = [f for f in cols if f[0] == "I"]
sparse_feats = [f for f in cols if f[0] == "C"]
# 处理dense特征
def process_dense_feats(data, feats):
d = data.copy()
d = d[feats].fillna(0.0)
for f in feats:
d[f] = d[f].apply(lambda x: np.log(x+1) if x > -1 else -1)
return d
data_dense = process_dense_feats(data, dense_feats)
# 处理sparse特征
def process_sparse_feats(data, feats):
d = data.copy()
d = d[feats].fillna("-1")
for f in feats:
label_encoder = LabelEncoder()
d[f] = label_encoder.fit_transform(d[f])
return d
data_sparse = process_sparse_feats(data, sparse_feats)
数据预处理具体细节可参考:tensorflow2.0实现DeepFM
模型的构建
DCN模型的结构如下:
由上图可知这个网络有四个部分组成:模型的输入,Cross network,Deep network,模型的输出。
part1—模型的输入
输入部分包括dense_feats和sparse_feats两个部分,对于dense特征,直接构造输入。对于sparse特征需要先进行embedding。
dense_feats
# 构造每个 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_feats
# 这里单独对每一个 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 i, _input in enumerate(sparse_inputs):
f = sparse_feats[i]
voc_size = data[f].nunique()
reg = tf.keras.regularizers.l2(0.7)
_embed = Embedding(voc_size+1,k,embeddings_regularizer=reg)(_input)
_embed = Flatten()(_embed)
sparse_kd_embed.append(_embed)
# 将sparse特征拼接在一起
concat_sparse_inputs = Concatenate(axis=1)(sparse_kd_embed)
输入层—组合sparse和dense 特征
embed_inputs = Concatenate(axis=1)([concat_sparse_inputs, concat_dense_inputs])
part2和part3共享输入层的组合输入。
part2—Cross network
cross network 的核心如下图示所示:
x
l
+
1
=
x
0
x
l
T
w
l
+
b
l
+
x
l
x_{l+1} = x_0 x_{l}^Tw_l + b_l + x_l
xl+1=x0xlTwl+bl+xl
在代码实现过程中,对于:
x
0
x
l
T
w
l
x_0 x_{l}^Tw_l
x0xlTwl 为了优化计算,一般先计算
x
l
T
w
l
x_{l}^Tw_l
xlTwl,然后在与
x
0
x_0
x0相乘得到最终结果。
def cross_layer(x0, xl):
"""
实现一层cross layer
@param x0: 特征embeddings
@param xl: 前一层的输出结果
"""
# 1.获取xl层的embedding size,用于生成初始化的参数w,b的shape
embed_dim = xl.shape[-1] # 221
# 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]) # (None,1,221)
# 行向量与列向量的乘积结果是一个标量
x_lw = tf.tensordot(x1_T, w, axes=1) #(None,1)
cross = x0 * x_lw
return cross + b + xl
通过循环构建多层:
def build_cross_layer(x0,num_layer=3):
"""
构建多层 cross layer
param x0: 所有特征的embeddings
param num_layers: cross net 层数
"""
# 初始化 x1为x0
x1 = x0
# 构建多层cross net
for i in range(num_layer):
x1 = cross_layer(x0,x1)
return x1
cross_layer_output = build_cross_layer(embed_inputs, 3)
part3—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))
part4—输出部分
将part3和part4进行concatenate然后经过一个全连接层,输出最终结果。
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')])
注:可以通过model.summary()查看模型细节。
切分数据。训练集:数据集中的前500000条,验证集数据集的最后100000条:
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://pan.baidu.com/s/1Qy3yemu1LYVtj0Wn47myHQ 提取码:pv7u