tensorflow2.0实现Deep & Cross Network(DCN)


本文基于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

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值