FM、DeepFM原理和Tensorflow代码解读

本文主要讲述FM的推导,DeepFM结构与DeepFM的tensorflow代码解读

FM算法

以往我们在预测点击时,常用的技术是GBDT+LR(逻辑回归),但这种方法并没有考虑特征交叉项对目标的影响。
假如我们认为点击时符合户线性归回模型时,当我们加入交叉项时,多元线性回归变为以下形式:
在这里插入图片描述
(注,在计量中,上面形式仍为线性回归,最小二乘依然是有效的)
这样做的好处考虑的特征之间的相互效应,模型的精度会上升。但代价是模型参数量大量增加(算法由O(n)变为O(n^2)。对此,可尝试对nn的二次项矩阵系数W,进行分解,变为NK的矩阵T与其转置相乘。有:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
此时无需在估计nn的二次项矩阵系数W,转为估计nk的矩阵T,通常k<<n。进一步的对公式二次项进行拆开有:
在这里插入图片描述
于是可以提出FM的结构如下:
在这里插入图片描述
注:由于经过计算训练好的embedding层的系数矩阵刚好等于向量Vi,因此可用embedding层计算Vi,并且embedding的输出刚好为ViXi。图中输入层为one-hot编码方式吗,每个Field中红色点代表该位置为1,蓝色点代表该位置为0,是非常稀疏的。经过embedding计算后,embedding层输出是稠密的。由于embedding的输出刚好为ViXi,由FM公式可知,需将不同ViXi两两组合。图中embedding层到FM层两两组合的红线即代表这一过程。图中一次项为输入层直接到FM层的黑线。
由于特征有很多是类别变量,需要进行one-hot处理,这导致输入是稀疏的。因此提出了FFM概念,FFM与FM的主要区别是FFM提出了类别Field的概念。同一类别下的特征(比如男:[1,0],女[0,1]都属于性别类)属于一个类别,FFM中每一维特征 xi,针对其它特征的每一种field fj,都会学习一个隐向量 vi,fj。因此,隐向量不仅与特征相关,也与field相关。
FM中,每个特征i对其他特征学习一个隐向量vi,j。FFM中,每个特征i对不同的Field学习不同的隐向量vi,fj。
假设样本的 n个特征属于 f个field,那么FFM的二次项有 nf个隐向量,而在FM模型中,每一维特征的隐向量只有一个。FM可以看作FFM的特例,是把所有特征都归属到一个field时的FFM模型。FFM中需要求解的隐向量更多,求解更加困难。

DeepFM算法

DeepFM是FM结构和DNN结构并连组成的结构。上面FM结构中embedding的输出可直接输入到DNN中。因此可设计如下DNN结构
在这里插入图片描述
将FM结构和DNN结构并联,遍得到了DeepFM:
在这里插入图片描述

DeepFM代码解读

本文代码提取自:DeepFM.git

#声明placeholder
self.feat_index = tf.placeholder(tf.int32,shape=[None,None],name='feat_index')
self.feat_value = tf.placeholder(tf.float32,shape=[None,None],name='feat_value')

self.label = tf.placeholder(tf.float32,shape=[None,1],name='label')
self.dropout_keep_fm = tf.placeholder(tf.float32,shape=[None],name='dropout_keep_fm')   #存放FM层的dropout概率
self.dropout_keep_deep = tf.placeholder(tf.float32,shape=[None],name='dropout_deep_deep')  #存放FM层的dropout概率

#embeddings构建权重
weights['feature_embeddings'] = tf.Variable( tf.random_normal([self.feature_size,self.embedding_size],0.0,0.01),name='feature_embeddings')   #权重项
weights['feature_bias'] =tf.Variable(tf.random_normal([self.feature_size,1],0.0,1.0),name='feature_bias')  #偏置项

#构建底层field到embedding层
# model
self.embeddings = tf.nn.embedding_lookup(self.weights['feature_embeddings'],self.feat_index) # N * F * K,embedding层
feat_value = tf.reshape(self.feat_value,shape=[-1,self.field_size,1])  #对输入向量展开
self.embeddings = tf.multiply(self.embeddings,feat_value)  #输入向量与embedding矩阵相乘,得到embedding向量,即(Vi*Xi)

# 构建FM层
# FM公式中一次项的构建
self.y_first_order = tf.nn.embedding_lookup(self.weights['feature_bias'],self.feat_index)   #一次项Xi前面的次数,与输入特征维度一致
self.y_first_order = tf.reduce_sum(tf.multiply(self.y_first_order,feat_value),2)  #计算一次向的输出,W0*x
self.y_first_order = tf.nn.dropout(self.y_first_order,self.dropout_keep_fm[0])  #对一次向dropout,防止过拟合

#FM公式中二次项的构建
# 二次项中和的平方的构建,即(sum(Vi*Xi))^2
self.summed_features_emb = tf.reduce_sum(self.embeddings,1) # None * k  构建FM模型中的Vi*Xi,即对embedding求和
self.summed_features_emb_square = tf.square(self.summed_features_emb) # None * K ,求平方

# 二次项中平方和项的构建。即sum(Vi^2*Xi^2)
self.squared_features_emb = tf.square(self.embeddings)  #求平方
self.squared_sum_features_emb = tf.reduce_sum(self.squared_features_emb, 1)  # None * K,求和

#二次项的构建,(sum(Vi*Xi))^2-sum(Vi^2*Xi^2),即summed_features_emb_square -squared_sum_features_emb 
self.y_second_order = 0.5 * tf.subtract(self.summed_features_emb_square,self.squared_sum_features_emb)  #相减,构建FM二次项层
self.y_second_order = tf.nn.dropout(self.y_second_order,self.dropout_keep_fm[1])  #FM二次项层的dropout层

#DNN结构编写
#首先利用embedding层的输入当多DNN的输入
self.y_deep = tf.reshape(self.embeddings,shape=[-1,self.field_size * self.embedding_size])
self.y_deep =tf.nn.dropout(self.y_deep,self.dropout_keep_deep[0])  #dropout层

#都用循环构建多层DNN网络
for i in range(0,len(self.deep_layers)):
    self.y_deep = tf.add(tf.matmul(self.y_deep,self.weights["layer_%d" %i]), self.weights["bias_%d"%I])
    self.y_deep = self.deep_layers_activation(self.y_deep)
    self.y_deep = tf.nn.dropout(self.y_deep,self.dropout_keep_deep[i+1])

#将FM的一次向,二次项输出和DNN输出,用concat并行起来,构成最终输出
concat_input = tf.concat([self.y_first_order, self.y_second_order, self.y_deep], axis=1)


# 定义损失函数
#二分类损失用logloss
if self.loss_type == "logloss":
    self.out = tf.nn.sigmoid(self.out)
    self.loss = tf.losses.log_loss(self.label, self.out)
#连续值用MSE
elif self.loss_type == "mse":
    self.loss = tf.nn.l2_loss(tf.subtract(self.label, self.out))

# l2 regularization on weights添加l2正则项
if self.l2_reg > 0:
    self.loss += tf.contrib.layers.l2_regularizer(
        self.l2_reg)(self.weights["concat_projection"])
    if self.use_deep:
        for i in range(len(self.deep_layers)):
            self.loss += tf.contrib.layers.l2_regularizer(
                self.l2_reg)(self.weights["layer_%d" % I])

#定义不同的优化器
if self.optimizer_type == "adam":
    self.optimizer = tf.train.AdamOptimizer(learning_rate=self.learning_rate, beta1=0.9, beta2=0.999,epsilon=1e-8).minimize(self.loss)
elif self.optimizer_type == "adagrad":
    self.optimizer = tf.train.AdagradOptimizer(learning_rate=self.learning_rate,
initial_accumulator_value=1e-8).minimize(self.loss)
elif self.optimizer_type == "gd":
    self.optimizer = tf.train.GradientDescentOptimizer(learning_rate=self.learning_rate).minimize(self.loss)
elif self.optimizer_type == "momentum":
    self.optimizer = tf.train.MomentumOptimizer(learning_rate=self.learning_rate, momentum=0.95).minimize(self.loss)
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值