推荐系统系列:xDeepFM理论和代码实现

本文深入解析xDeepFM模型,这是2018年KDD上提出的DeepFM的升级版。文章详细介绍了xDeepFM如何结合显式和隐式特征交互进行推荐系统优化,并通过CIN组件实现高级特征学习。此外,还提供了TensorFlow实现的代码片段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

DeepFM的升级版本xDeepFM,发表于2018年KDD,论文地址为:xDeepFM: Combining Explicit and Implicit Feature Interactions for Recommender Systems

论文对应的代码地址:https://github.com/Leavingseason/xDeepFM

另外文中如果出现错误,欢迎大家指正出来,我好进行修改,免得误导他人,毕竟个人能力有限,好的博客应该大家一起来维护。

 

二、算法原理

(1)xDeepFM的特征交互发生在向量级别,自动学习高阶的显示特征,模型的整体结构如下:也是分为三个部分,线性部分,CIN部分以及DNN部分,其中值得注意的地方就是,CIN部分的输入还是对稀疏矩阵中每个field的特征进行词嵌入之后拼接组合,DNN部分的输入还是稀疏矩阵进行词嵌入进行Flatten之后和连续特征进行拼接得到的,这部分学习的隐式的特征;

(2)模型中最难的部分就是CIN了,需要好好理解一下,实际上先介绍两个部分:

最后的通用公式如下:

通过如上的公式就能很好的表示特征之间的交互,从而能够显式的学习特征;

(3)很多人可能看公式看的头晕,包括我也一样,通过图像来理解整个过程可能比较好;

(4)xDeepFm模型依然也是包括线性交叉部分,CIN部分和DNN部分,后面两部分使得模型能够分别显式的学习和隐式的学习数据的特征;

(5)损失函数的设计依然是log损失,在推荐系统中还是很常见的;

 

四、实验结果:

 

五、代码实现

这部分代码参考了几份代码实现,其中变量的名称也是尽量和其他人的一致,因为看了几份代码中的变量名称也是差不多的,甚至是一致的,所以我也尽量不改变;

import tensorflow as tf
from tensorflow.python.keras.layers import Layer
from tensorflow.python.keras.initializers import glorot_normal
from tensorflow.python.keras.regularizers import l2

class cin(Layer):
    def __init__(self, layers=(128, 128),  if_direct=False, l2_reg=1e-5, seed=1024, **kwargs):
        self.layers = layers
        self.if_direct = if_direct
        self.l2_reg = l2_reg
        self.seed = seed
        super(cin, self).__init__(**kwargs)

    def build(self, input_shape):
        self.filed_nums = [input_shape.shape[1].value]
        self.filters = []
        self.bias = []
        for i, field_size in enumerate(self.layers):
            self.filters.append(self.add_weight(name='filters'+str(i), 
                                                shape=(1, self.field_nums[-1] * self.filed_nums[0]),
                                                dtype=tf.float32, 
                                                initializer=glorot_normal(seed=self.seed + i), 
                                                regularizer=l2(self.l2_reg)))
            self.bias.append(self.add_weight(name='bias'+str(i), shape=(field_size), dtype=tf.float32, 
                                             initializer=tf.keras.initializer.Zeros()))
            if self.if_direct:
                self.filed_nums.append(field_size // 2)
            else:
                self.filed_nums.append(field_size)
        super(cin, self).build(input_shape)
    
    def call(self, inputs, **kwargs):#inputs: (batch, filed_size, emb_size)
        emb_size = inputs.get_shape()[-1].value
        hidden_nn_layers = [inputs]
        final_res = []
        split_tensor0 = tf.split(inputs, [1] * emb_size, axis=2) #(emb_size, batch, field_size, 1)
        for i, field_size in enumerate(self.layers):
            split_tensor = tf.split(hidden_nn_layers[-1], [1] * emb_size, axis=2)
            dot_tensor_m = tf.matmul(split_tensor, split_tensor0, transpose_b=True) #(emb_size, batch, field_size, field_size)
            dot_tensor_o = tf.reshape(dot_tensor_m, shape=[emb_size, -1, self.filed_nums[0] * self.filed_nums[i]])#(emb_size, batch, field_size * field_size)
            dot_tensor_res = tf.transpose(dot_tensor_o, perm=[1, 0, 2])#(batch, emb_size, field_size * field_size)
            cur_out = tf.nn.conv1d(dot_tensor_res, self.filters[i], stride=1, padding='VALID')#(batch, emb_size, field_size)
            cur_out = tf.nn.bias_add(cur_out, self.self.bias[i])
            cur_out = tf.transpose(cur_out, perm=[0, 2, 1])#(batch, field_size, emb_size)
            #diect方式:当前输出作为下一层的输入,非direct方式:当前前一半作为下一层的输入,后一半作为输出结果的一部分;
            if self.if_direct:
                direct_connect = cur_out
                next_hidden = cur_out
            else:
                if i != len(self.layers) - 1:
                    next_hidden, direct_connect = tf.split(cur_out, 2 * [field_size], axis=1)
                else:
                    direct_connect = cur_out
                    next_hidden = 0
            final_res.append(direct_connect)
            hidden_nn_layers.append(next_hidden)
        result = tf.concat(final_res, axis=1)
        result = tf.reduce_sum(result, axis=-1, keep_dims=False)
        return result

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值