Datawhale第23期组队学习—深度学习推荐系统—task3 DeepFM

DeepFM

  • 1. 引言
    • 1.1 学习总结
    • 1.2 研究背景
    • 1.3 已有模型的介绍
  • 2. DeepFM模型结构与原理
    • 2.1 FM
    • 2.2 Deep
    • 2.3 DeepFM
  • 3. 代码实现

本文主要参考来源-Datawhale:https://github.com/datawhalechina/team-learning-rs/blob/master/DeepRecommendationModel/DeepFM.md

1. 引言

1.1 学习总结

通过这段时间的学习,逐渐发现了学习过程中的一些规律。可能也是巧合,很多模型的名字都是自然而然的concate,如Wide&Deep就是Wide和Deep组合,DeepFM就是Deep+FM。

而在学习过程中,经常会遇到一些不了解的名词,这些纯粹是因为自己基础不牢固而产生的问题,随着学习的深入,慢慢的就会熟悉一些名词,从而更容易去学习新的理论知识。

虽说有些模型命名简单,通过名字便可窥探一二,但每个模型的提出,都是为了解决一定的问题,背后都隐含着工程师们的精巧设计,值得令人推敲和学习。

1.2 研究背景

对于CTR问题1,被证明的最有效的提升任务表现的策略是特征组合(Feature Interaction)2, 在CTR问题的探究历史上来看就是如何更好地学习特征组合,进而更加精确地描述数据的特点。可以说这是基础推荐模型到深度学习推荐模型遵循的一个主要的思想。而组合特征大牛们研究过组合二阶特征,三阶甚至更高阶,但是面临一个问题就是随着阶数的提升,复杂度就成几何倍的升高。这样即使模型的表现更好了,但是推荐系统在实时性的要求也不能满足了。所以很多模型的出现都是为了解决另外一个更加深入的问题:如何更高效的学习特征组合?

为了解决上述问题,出现了FM和FFM来优化LR的特征组合较差这一个问题。并且在这个时候科学家们已经发现了DNN在特征组合方面的优势,所以又出现了FNN和PNN等使用深度网络的模型。但是DNN也存在局限性。

1. CTR问题
CTR(Click-Through-Rate)即点击通过率,是互联网广告常用的术语,指网络广告(图片广告/文字广告/关键词广告/排名广告/视频广告等)的点击到达率,计算公式为:CTR = 实际点击次数/展示量,即 Click / Show content。
2. 特征组合
特征组合 (feature cross),通过将单独的特征进行组合(相乘或求笛卡尔积)而形成的合成特征。特征组合有助于表示非线性关系。

1.3 已有模型的介绍

参考来源:https://zhuanlan.zhihu.com/p/97847698
该文章简单介绍了CTR问题和处理该问题的常用算法,想了解更多的可戳。

LR
由于在处理回归问题中经常会用到LR方法,而且LR也是一类原始推荐算法,那么顺带在这里介绍一下LR。
LR(linear regression)即线性回归,在另一篇博客(
https://blog.csdn.net/ahdaizhixu/article/details/114917264?spm=1001.2014.3001.5501)中做了简要介绍。
在推荐系统中,我们既然拥有了用户和点击条目等各个特征的信息,那么使用LR来预测用户是否会点击就成了首先想到的方法。但实际做出来的效果却不太理想,究其原因会发现,在CTR问题中,用户是否点击一个item,不仅仅是单个特征决定,还与背后隐藏的特征组合有关,所以LR在处理该类问题中并无优势。

DNN
在万物皆可DNN的时代,把这个问题完全交给机器(你自己去跑吧,我也不管你为啥,你给用户个结果就行),似乎也成了一种选择。诚然,DNN在一定程度上解决了问题,但不可否认,新的问题又产生了,因为在进行特征处理的时候需要使用one-hot编码来处理离散特征,这会导致输入的维度猛增,如图:
Datawhale第23期组队学习—深度学习推荐系统—task3 DeepFM_第1张图片

FM
既然LR的缺点是由于LR没有捕获到用户的组合特征信息,那么我们使用人工的方式进行一些特征组合,这样会不会可以改善模型效果呢。但是这样做又会出现了新的问题,人工特征组合的效果一部分取决于进行组合的人的先验知识,一方面不一定能挖掘出我们想要的信息,另一方面,可能会遗漏一些重要信息。而且,由于需要处理的数据维数较多,比较复杂,通过人工组合的方式费时费力。

我们依旧是想机器自动的学习出特征组合,我们索性直接让所有的特征都进行一一组合,然后让LR学习各个特征组合的参数权重就好了,这样的话,如果权重很小说明该组合没有什么意义,反之则意义很大。

那么,把这一部分工作交给机器来完成呢?如果交给机器来做,可以把所有特征进行一一组合,然后让LR学习各个特征组合的参数权重就好了,这样的话,如果权重很小说明该组合没有什么意义,反之则意义很大。因此有了二项式模型:
f ( x ) = s i g m o i d ( < w , x > , ∑ i = 0 n − 1 ∑ j = i + 1 n w i j x i x j ) = s i g m o i d ( < w , x > , x T W ( 2 ) x ) f(x) = sigmoid(,\sum_{i=0}^{n-1}\sum_{j = i+1}^{n}w_{ij}x_ix_j) = sigmoid(,x^TW^{(2)}x) f(x)=sigmoid(<w,x>,i=0n1j=i+1nwijxixj)=sigmoid(<w,x>,xTW(2)x)
由以上公式可知,由于我们的训练矩阵是一个稀疏矩阵,而 w i j w_{ij} wij只与 x i x j x_ix_j xixj有关,只要有有一个 x x x为0,则该参数将不会得到更新,所以,我们使用矩阵分解的思想引入FM算法。

sigmoid函数
sigmoid函数也叫Logistic函数,用于隐层神经元输出,取值范围为(0,1),它可以将一个实数映射到(0,1)的区间,可以用来做二分类。Sigmoid函数由下列公式定义:
S ( x ) = a 1 + e − x S(x) = \frac {a} {1+e^{-x}} S(x)=1+exa

其中 W ( 2 ) W^{(2)} W(2)为二阶特征组合的权重矩阵,是对称矩阵。而这个矩阵参数非常多,为O ( n 2 ) ( n^2 ) (n2)。为了降低该矩阵的维度,可以将其因子分解(Factorization)为两个低维(比如n*k)矩阵的相乘。则此时W矩阵的参数就大幅降低,为O ( n k ) (nk) (nk)。公式如下:
W ( 2 ) = W T ⋅ W W ( 2 ) = W T ⋅ W W ( 2 ) = W T ⋅ W W ^ { ( 2 ) } = W ^ { T } \cdot W W(2)=WTWW(2)=WTW
化简上式,去掉点击后的重复项和对角线元素:
f ( x ) = s i g m o i d ( < w , x > , ∑ i = 0 n − 1 ∑ j = i + 1 n < v i , v j > x i x j ) f(x) = sigmoid(,\sum_{i=0}^{n-1}\sum_{j = i+1}^{n}x_ix_j) f(x)=sigmoid(<w,x>,i=0n1j=i+1n<vi,vj>xixj)
我们可以看出来,FM的关键是对一个高维向量x与一个矩阵w相乘后变成一个低维向量,这个过程我们其实可以理解为embedding(将离散特征经过矩阵乘法变成一个低维的稠密向量),即先embedding,然后进行内积( < w x , w x > <wx,wx> ),内积的作用是二阶特征组合,然后加上线性项。
总结来说FM=embedding+内积
Datawhale第23期组队学习—深度学习推荐系统—task3 DeepFM_第2张图片

FNN和PNN
由引言可知,很多算法的原理根据名字可窥见一二,所以FNN的意思是不是就是:FNN+DNN?bingo!那么PNN呢?PNN就是在FNN的基础上增加了product层。
Datawhale第23期组队学习—深度学习推荐系统—task3 DeepFM_第3张图片
如图所示,红框中圈出的便是product层。FNN是使用预训练好的FM模块,得到隐向量,然后把隐向量作为DNN的输入,但是经过实验进一步发现,在Embedding layer和hidden layer1之间增加一个product层(如上图所示)可以提高模型的表现,所以提出了PNN,使用product layer替换FM预训练层。红框上的为全连接层,即MLP。

Wide&Deep
FNN和PNN模型仍然有一个比较明显的尚未解决的缺点:对于低阶组合特征学习到的比较少,这一点主要是由于FM和DNN的串行方式导致的,也就是虽然FM学到了低阶特征组合,但是DNN的全连接结构导致低阶特征并不能在DNN的输出端较好的表现。看来我们已经找到问题了,将串行方式改进为并行方式能比较好的解决这个问题。于是Google提出了Wide&Deep模型(task2对该模型的原理做了相应介绍:https://blog.csdn.net/ahdaizhixu/article/details/114983197?spm=1001.2014.3001.5501),但是如果深入探究Wide&Deep的构成方式,虽然将整个模型的结构调整为了并行结构,在实际的使用中Wide Module中的部分需要较为精巧的特征工程,换句话说人工处理对于模型的效果具有比较大的影响。

2. DeepFM模型结构与原理

DeepFM整体结构图
Datawhale第23期组队学习—深度学习推荐系统—task3 DeepFM_第4张图片

2.1 FM

Datawhale第23期组队学习—深度学习推荐系统—task3 DeepFM_第5张图片
根据1.3提到的FM的公式我们可以看到,FM是由一阶特征和二阶特征组合再一起,再用Sigmoid函数激活得到结果的,所以在实现的时候需要单独考虑linear部分和FM交叉特征部分。

2.2 Deep

Datawhale第23期组队学习—深度学习推荐系统—task3 DeepFM_第6张图片
如图是Deep部分,从图中可以看到,所有的特征都会被转化成embedding向量作为Deep部分的输入。这里的Dense Embeddings也很好理解,在Wide&deep中也出现过,是为了处理输入的稀疏矩阵维数爆炸的问题,因其后便是DNN,DNN的输入当维数太多时,训练时间会显著增长。

2.3 DeepFM

Datawhale第23期组队学习—深度学习推荐系统—task3 DeepFM_第7张图片

3. 代码实现

DeepFM在模型的结构图中显示,模型大致由两部分组成,一部分是FM,一部分就是DNN, 而FM又由一阶特征部分与二阶特征交叉部分组成,所以可以将整个模型拆成三部分,分别是一阶特征处理linear部分,二阶特征交叉FM以及DNN的高阶特征交叉。在下面的代码中也能够清晰的看到这个结构。此外每一部分可能由是由不同的特征组成,所以在构建模型的时候需要分别对这三部分输入的特征进行选择。

def DeepFM(linear_feature_columns, dnn_feature_columns):
    # 构建输入层,即所有特征对应的Input()层,这里使用字典的形式返回,方便后续构建模型
    dense_input_dict, sparse_input_dict = build_input_layers(linear_feature_columns + dnn_feature_columns)

    # 将linear部分的特征中sparse特征筛选出来,后面用来做1维的embedding
    linear_sparse_feature_columns = list(filter(lambda x: isinstance(x, SparseFeat), linear_feature_columns))

    # 构建模型的输入层,模型的输入层不能是字典的形式,应该将字典的形式转换成列表的形式
    # 注意:这里实际的输入与Input()层的对应,是通过模型输入时候的字典数据的key与对应name的Input层
    input_layers = list(dense_input_dict.values()) + list(sparse_input_dict.values())

    # linear_logits由两部分组成,分别是dense特征的logits和sparse特征的logits
    linear_logits = get_linear_logits(dense_input_dict, sparse_input_dict, linear_sparse_feature_columns)

    # 构建维度为k的embedding层,这里使用字典的形式返回,方便后面搭建模型
    # embedding层用户构建FM交叉部分和DNN的输入部分
    embedding_layers = build_embedding_layers(dnn_feature_columns, sparse_input_dict, is_linear=False)

    # 将输入到dnn中的所有sparse特征筛选出来
    dnn_sparse_feature_columns = list(filter(lambda x: isinstance(x, SparseFeat), dnn_feature_columns))

    fm_logits = get_fm_logits(sparse_input_dict, dnn_sparse_feature_columns, embedding_layers) # 只考虑二阶项

    # 将所有的Embedding都拼起来,一起输入到dnn中
    dnn_logits = get_dnn_logits(sparse_input_dict, dnn_sparse_feature_columns, embedding_layers)
    
    # 将linear,FM,dnn的logits相加作为最终的logits
    output_logits = Add()([linear_logits, fm_logits, dnn_logits])

    # 这里的激活函数使用sigmoid
    output_layers = Activation("sigmoid")(output_logits)

    model = Model(input_layers, output_layers)
    return model

参考来源

  1. https://zhuanlan.zhihu.com/p/97847698
  2. https://blog.csdn.net/han_xiaoyang/article/details/81031961
  3. https://github.com/datawhalechina/team-learning-rs/blob/master/DeepRecommendationModel/DeepFM.md

你可能感兴趣的:(推荐系统,深度学习,python,人工智能)