Trip Of Factorization Machine And Advance

因子分解机的基础和进阶

  • 什么是因子分解机?
    • 因子分解机的定义
    • 因子分解机的复杂度分析
  • 因子分解机有什么用?
  • 如何实现因子分解机?
  • 参考文献

什么是因子分解机?

因子分解机的定义 (Define)

FM(Factorization Machine) 是由Konstanz大学Steffen Rendle 在 2010年提出的$^{[1]}$,目的是为了解决稀疏数据(sparsity data)下的特征组合问题。下面我们通过几个栗子来看看什么是因子分解机。

例一$^{[3]}$:

我们观测到的数据的图表形式如下:

rating User Movie rate-date
5 Alice Titanic 2010-1
4 Bob Notting Hill 2010-2
3 Charlie Star War 2009-12

这个例子是一个电影评分系统数据集。考虑电影评分系统种的数据,它的每一条记录都包含了哪个用户 $u \in U$在什么时候$t \in R$,对哪部电影$i \in I$打了多少分$r \in {1,2,3,4,5}$ 这样的信息。

数据集S的列表形式就是:

$S = { (Alice, Titanic, 2010-1, 5), (Bob, Notting Hill, 2010-2, 4)}$

那么我可以使用数据集S来进行预测任务、推荐等任务,比如找到一个函数$\hat{y}$来预测某个用户在某个时刻对某个电影的打分行为。

有了数据集S,通过下图的方式,我们可以构建一个特征向量和标签的示例:

sparse matrix

上图中的标签部分比较简单,就是直接的让当前用户对电影的评分作为标签,比如第一条测试记录中Alice 对 Titanic的评分是 5,那么$y^{(1)} = 5$,而特征向量$X$由以下五部分组成:

1、第一部分(蓝色方框)表征的是当前评分用户的信息,其维度为$|U|$,这里使用的是One-hot Encoding的方法。也就是除了当前位置是1,其余位置都是0。

2、第二部分(红色方框)表征的是当前被评分电影信息,其维度为$|I|$,这里使用的也是One-hot Encoding的方法。

3、第三部分(黄色方框)表征的是当前评分用户评分过的所有电影信息,其维度是$|I|$,假设当前用户评论过的电影个数为$n_I$,那么这$n_I$个电影位置上的数值为$\frac{1}{n_I}$,其他位置上都是0。

4、第四部分(绿色方框)表征的是评分日期信息,维度为1,我们将最早的记录日期——2009年1月开始基数为1,以后没增加一个月,就加一,比如2009年5月就是5。

5、第五部分(棕色方框)表征的是当前评分用户最近评分过的一部电影的信息,其维度为$|I|$,同样的也是使用One-hot Encoding的方法。比如Alice现在评价的是Titanic,之前还评价过一次Notting Hill,那么Notting Hill位置的为1,其余为0,如果之前没有评价过任何电影,就全部取0。

所以,X的总维度是 $|U| + |I| + |I| + 1 + |I| = |U| + 3|I| + 1$

显然,在一个真实的电影评分系统中,用户的数目$|U|$和电影的数目$|I|$通常是很大的,而每个用户参与评论的电影数目则相对而言比较小。于是,对于每一条记录对应的特征向量将会非常的稀疏。

所以,我们需要有一个办法来降低这种稀疏性。

例二$^{[2]}$:

假设一个广告分类的问题,根据用户和广告位相关的特征,预测用户是否点击了广告。源数据如下:

Clicked? Country Day Ad_type
1 USA 3/3/15 Movie
0 China 1/7/14 Game
1 China 3/3/15 Game

“Clicked?”是label,Country、Day、Ad_type是特征。由于三种特征都是categorical类型的,需要经过(One-Hot Encoding)转换成数值型特征。

Clicked? USA China 26/11/15 1/7/14 19/2/15 Movie Game
1 1 0 1 0 0 1 0
0 0 1 0 1 0 0 1
1 0 1 0 0 1 0 1

从上面那张表。可以看出,我们在使用了One-hot Encoding之后,显然也会出现跟例一中一样的问题,那就是稀疏性非常明显。

同时,我们观察数据样本,也可以发现,有一些特征,相互关联之后,跟label的相关性也会提升。

比如“USA”与“Thanksgiving”、“China”与“Chinese New Year”这样的有很强关联性的特征,它们对用户是否点击,会产生正面的影响

用一个小例子来解释就是:来自“China”的用户在“Chinese New Year”这一天会有大量的浏览、购买行为,但是再“Thanksgiving”这天,却不会有特别的消费行为。相反地是,来自“USA”的用户很可能在“Thanksgiving”这天有大量的浏览、购买行为,但是在“Chinese New Year”却可能没有特别的消费行为。

这种关联特征与label的正向相关性在实际问题中是普遍存在的,如“化妆品”类商品与“女”性,“球类运动配件”的商品与“男”性,“电影票”的商品与“电影”品类偏好等。因此,引入两个特征的组合是非常有意义的

我们可以使用多项式来直观地表现多项式模型,让特征$x_i$和$x_j$组合,得到如下表达式,这里我们只讨论二阶多项式模型:

$$y(x) = w_0 + \sum_{i=1}^n w_i x_i + \sum_{i=1}^n \sum_{j=i+1}^n w_{ij} x_i x_j$$

$$= w_0 + W_1^TX + X^TW_2X \ \ \ \ \ \ (1)$$

可以看到,当$x_i$和$x_j$有一个为零,这时的组合特征$x_ix_j$是没有意义的。

其中$n$表示样本的特征数量,$x_i$表示第i个特征的值,$w_0、w_i、w_{ij}$是参数模型。

显然,我们写出这个模型的过程,并不复杂,然后我们只要能求出 $w_0、w_i、w_{ij}$就能够建立起一个很好用的,引入了组合特征的模型。

但是事实真的是这样么?这里我们依旧会遇到跟例一中,一样的问题,就是样本太少了,稀疏性太强,哪怕强行求出了$w_{ij}$,得到的模型也会是欠拟合的那种。

那么,我们如何解决这个问题呢?

一个可以想到的办法就是通过矩阵分解

在推荐系统中,我们把一个评分矩阵,分解成一个user和一个item,每个user和item都可以采用一个隐向量表示。比如在下图中的例子中,我们把每个user表示成一个二维向量,同时把每个item表示成一个二维向量,两个向量的点积就是矩阵中user对item的打分。

矩阵分解

按照,这个思想,我们可以对中间的权重$W_2$做一次矩阵分解。

$$W_2 = V^TV, V \in R^{k \times n}$$

那么有

$$w_{ij} = V_i^TV_j, V_i \in R^k$$

最后公式$(1)$可以化成

$$y(\mathbf{x}) = w_0+ \sum_{i=1}^n w_i x_i + \sum_{i=1}^n \sum_{j=i+1}^n \langle \mathbf{v}_i, \mathbf{v}_j \rangle x_i x_j (2)$$

这里的$\langle \mathbf{v}_i, \mathbf{v}_j \rangle$表示的是两个长度为k的向量的点乘

$$\langle \mathbf{v}_i, \mathbf{v}j \rangle = \sum{f=1}^k v_{i,f} \cdot v_{j,f}$$

这样一来得到的隐向量的长度为$k$,包含$k$个描述特征的因子。且二次项的参数的个数就成了$kn$远远少于$n^2$。另外参数因子化使得 $x_h x_i$ 的参数和 $x_ix_j$ 的参数不再相互独立。
因此我们可以在样本稀疏的情况下相对合理地估计FM的二次项参数。具体来说,$x_hx_i$ 和 $x_ix_j$ 的系数分别为 $\langle v_h, v_i \rangle$ 和 $\langle v_i, v_j \rangle$,它们之间有共同项 $v_i$。也就是说,所有包含“ $x_i$ 的非零组合特征”(存在某个 $j \ne i$,使得 $x_i x_j \ne 0$ )的样本都可以用来学习隐向量 $v_i$,这很大程度上避免了数据稀疏性造成的影响。而在多项式模型中,$w_{hi}$ 和 $w_{ij}$ 是相互独立的。

数学角度看差异

接下来,我们从数学的角度看看(1)和(2)的主要区别在哪里,对于交叉项 $x_i x_j$ 的系数,前者用的是 $w_{ij}$,后者用的是 $\hat w_{ij}$ ,为了能够更好的说明,我们引入几个矩阵。

交互矩阵和差异

由此可见,(1) 和 (2)分别采用了交互矩阵 $W$ 和$\hat W$的非对角线元素作为$w_ix_j$的系数.由于$\hat W = VV^T$对应于一种矩阵分解,因此,我们将这种以(2)作为模型的方法称为 Factorization Machine.

这里,我们需要补充一个条件 “当k足够大的时候,对于任意对称正定的实矩阵 $\hat W \in R^{n \times k}$,会使得 $\hat W=VV^T$ 成立.

因子分解机的复杂度分析

自然,$(2)$是一个通用的拟合方程,我们可以采用不同的损失函数【我们用来对结果进行判断的函数】用于解决回归、二元分类等问题,比如:

1、 采用MSE(Mean Square Error)损失函数来求解回归问题,

2、 采用Hinge/Cross-Entropy损失来求解分类问题。

看得出来(2)的时间复杂度是$O(kn^2)$。但是,有意思的是我们通过使用一个trick,可以把时间减到$O(kn)$,具体细节证明如下,详情可以看论文$^{[3]}$

proof

由此可见,FM可以在线性时间对新样本作出预测。

$\sum_{i=1}^n \sum_{j=i+1}^n \langle \mathbf{v}_i, \mathbf{v}_j \rangle x_i x_j = \frac{1}{2} \sum_{f=1}^k \left(\left( \sum_{i=1}^n v_{i, f} x_i \right)^2 - \sum_{i=1}^n v_{i, f}^2 x_i^2 \right)$

我们再来看一下FM的训练复杂度,利用SGD(Stochastic Gradient Descent)训练模型。模型各个参数的梯度如下

$$\frac{\partial}{\partial\theta} y (\mathbf{x}) = \left{\begin{array}{ll} 1, & \text{if}\; \theta\; \text{is}\; w_0 \ x_i, & \text{if}\; \theta\; \text{is}\; w_i \ x_i \sum_{j=1}^n v_{j, f} x_j - v_{i, f} x_i^2, & \text{if}\; \theta\; \text{is}\; v_{i, f} \end{array}\right.$$

其中$v_{j,f}$是隐向量$V_j$的第$f$个元素。由于$\sum_{j=1}^nv_{j,f}x_j$只与$f$有关,而与$i$无关,在每次迭代的过程中,只需要计算一次所有$f$的$\sum_{j=1}^nv_{j,f}x_j$就能够得到所有$v_{j,f}$的梯度。显然,计算所有$f$的$\sum_{j=1}^nv_{j,f}x_j$的复杂度是$O(kn)$;已知$\sum_{j=1}^nv_{j,f}x_j$时,计算每个参数梯度的复杂度时$O(1)$;得到梯度后,更新每个参数的复杂度为$O(1)$;模型参数一共有$nk + n + 1$个。因此,FM参数训练的复杂度也是$O(kn)$。综上可知,FM可以在线性时间训练和预测,是一种非常高效的模型。

因子分解机有什么用?

1、 FM和矩阵分解

mfcf

在推荐系统中,我们经常使用的就是基于矩阵分解的协同过滤$^{[4]}$,我们会使用到 user 对 Item 的评分,但是由于 user 和 item 的数量非常多,但是 user 对 item 打分的个数却十分稀少。基于矩阵分解的协同过滤就是用来预测 user 对那些没有过消费记录的 item 的打分行为,以及打多少分。实际上可以看成一个评分预测的问题。矩阵分解的方法假设 user 对 item 的打分 $\hat r_{ui}$ 由下式决定:

$$\hat r_{ui} = q_i^Tp_u + \mu + b_i + b_u$$

其中,$q_i$ 是第 $i$ 个 item 所对应的隐向量,$p_u$ 是第 $u$ 个 user 所对应的隐向量,$b_i$ 代表 item 的偏差,解释了商品本身的冷门、热门程度。 $b_u$ 代表了 user 的偏差,解释了用户本身的打分偏好,$\mu$ 是常数。也就是将评分矩阵分解为 user 矩阵、 Item 矩阵的乘积再加上线性项和常数项,且这两个矩阵都是低秩的。并且这些参数可以通过最小化经验误差得到:

$$\min_{p,q,b} \sum_{u,i \in K}(r_{ui} - \hat r_{ui})^2 + \lambda(||p_u||^2 + ||q_i||^2 + b_u^2 + b_i^2)$$

从上面的叙述来看,FM的二阶矩阵也使用了矩阵分解的技巧,那么基于矩阵分解的协同过滤和FM有什么关系呢?

以 user 对 item 的评分预测问题为例,基于矩阵分解的协同过滤可以看成是FM的一个特殊例子

在协同过滤中,对于每一个样本,FM可以将其看作特征只有 userid 和 itemid。

但是在实际FM中,我们的每个样本,可以有除了 userid, itemid 之外的很多特征组合而成。能够学到更多的组合模式。

2、 FM和神经网络

神经网络天然地难以直接处理高维稀疏的离散特征,因为这将导致神经元的连接参数太多。但是低维嵌入(embedding)技巧可以解决这个问题,词的分布式表达就是一个很好的例子。事实上 FM就可以看做对高维稀疏的离散特征做 embedding

上面举的例子其实也可以看做将每一个 user 和每一个 item 嵌入到一个低维连续的 embedding 空间中,然后在这个 embedding 空间中比较 user 和 item 的相似性来学习 user 对 item 的偏好。

这跟 word2vec$^{[8]}$词向量学习类似,word2vec 将词 embedding 到一个低维连续空间,词的相似性通过两个词向量的相似性来度量。神经网络对稀疏离散特征做 embedding 后,可以做更复杂的非线性变换,具有比FM更大的潜力学习到更深层的非线性关系!基于这个思想,2016年,Google提出 wide and deep 模型用作 Google Play的app推荐$^{[9]}$它利用神经网络做离散特征和连续特征之间的交叉,神经网络的输出和人工组合较低维度的离散特征一起预测,并且采用端到端的学习,联合优化DNN和LR。如图所示,Catergorial 特征 embedding 到低维连续空间,并和连续特征拼接,构成了1200维的向量,作为神经网络的输入,神经网络采用三隐层结构,激活函数都是采用 ReLU,最后一层神经元的输出$a^{(lf)}$和离散特征$X$及人工叉积变换后的特征$\phi(\mathbf{x})$,一起预测

$$P(Y=1|\mathbf{x}) = \sigma\left(\mathbf{w}{wide}^T[\mathbf{x}; \phi(\mathbf{x})] + \mathbf{w}{deep}^T a^{(lf)} + b \right)$$

wide and deep

注意到,在 wide and deep 模型中,wide 部分是通过对用户安装过的 APP id 和用户 Impression App id 做叉积变换,解决 embedding 的过泛化问题。 所谓的过泛化,实际上是因为用户的偏好本身就很集中,即使相似的一些 item,用户也只偏好其中一部分,使得 query-item 矩阵稀疏但是高秩。 而这些信息实际上已经反映在用户已有的行为当中了,因此可以利用这部分信息,单独建立wide部分,解决deep部分的过泛化。

从另一个角度来看,wide 和 deep 部分分别在学习不同阶的特征交叉,deep 部分学到高阶交叉,而 wide 部分学到的是二阶交叉。 后来,有人用FM替换了这里wide部分的二阶交叉,使得模型对高度稀疏的特征的建模更加有效,因为高度稀疏特征简单的叉积变换也难以有效地学到二阶交叉, 这在前面已经叙述过了。因此,很自然的想法就是,用FM替换这里的二阶交叉,得到DeepFM模型$^{[10]}$

deepfm

事实上,对于连续特征和非高度稀疏特征的高阶交叉,决策树似乎更加擅长。因此,很自然的想法是将GBDT也加到模型中。 但是问题是,决策树的优化方法和神经网络之类的不兼容,因此无法直接端到端学习。一种解决方案是,利用Boost融合的方案, 将神经网络、FM、LR当做一个模型,先训练一个初步模型,然后在残差方向上建立GBDT模型,实现融合。 微软的一篇文献$^{[10]}$也证实,Boost方式融合DNN和GBDT方案相比其他融合方案更优,因此这也不失为一种可行的探索方向!

如何实现因子分解机?

在我们动手写代码之前,我们先考虑API,我们需要哪里API,才能构成一个完整的Factorization Machine呢?

FM主要处理以下三种问题:

  • 回归问题(Regression)

  • 二分类问题(Binary Classification)

  • 排序(Ranking)

这里,我们主要介绍二分类问题。

二分类问题的loss function为:

$$loss(\hat y , y) = \sum_{i=1}^m-ln \sigma{(\hat y , y)}$$

其中

$$\sigma(x) = \frac{1}{1 + e^{-x}}$$

使用基于随机梯度的方式求解:

我们可以设计API如下:

loadDataSet(data):

    '''导入训练数据
    input:  data(string)训练数据
    output: dataMat(list)特征
            labelMat(list)标签
    '''


sigmoid(inx):

    ''' sigmoid 函数
    '''

initialize_v(n, k):

    '''初始化交叉项
    input:  n(int)特征的个数
            k(int)FM模型的超参数
    output: v(mat):交叉项的系数权重
    '''


stocGradAscent(dataMatrix, classLabels, k, max_iter, alpha):

    '''利用随机梯度下降法训练FM模型
    input:  dataMatrix(mat)特征
            classLabels(mat)标签
            k(int)v的维数
            max_iter(int)最大迭代次数
            alpha(float)学习率
    output: w0(float),w(mat),v(mat):权重
    '''

getCost(predict, classLabels):

    '''计算预测准确性
    input:  predict(list)预测值
            classLabels(list)标签
    output: error(float)计算损失函数的值
    '''



getPrediction(dataMatrix, w0, w, v):

    '''得到预测值
    input:  dataMatrix(mat)特征
            w(int)常数项权重
            w0(int)一次项权重
            v(float)交叉项权重
    output: result(list)预测的结果
    '''


getAccuracy(predict, classLabels):

    '''计算预测准确性
    input:  predict(list)预测值
            classLabels(list)标签
    output: float(error) / allItem(float)错误率
    '''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

import numpy as np
from random import normalvariate # 使用正态分布进行初始化

def loadDataSet(path):
'''导入训练数据
input: path(string)训练数据
output: dataMat(list)特征
labelMat(list)标签
'''
dataMat = []
labelMat = []
with open(path, 'r') as f:
for line in f.readlines():
line_list = line.strip().split("\t")
temp_list = []
for i in range(len(line_list) - 1):
temp_list.append(float(line_list[i]))
dataMat.append(temp_list)
labelMat.append(float(line_list[-1]) * 2 - 1) # 把结果变成 -1. +1 两种。
return dataMat, labelMat


def sigmoid(inx):
''' sigmoid 函数
'''
return 1.0 / (1 + np.exp(-inx))


def initialize_v(n, k):
'''初始化交叉项
input: n(int)特征的个数
k(int)FM模型的超参数
output: v(mat):交叉项的系数权重
'''
v = np.mat(np.zeros((n, k))) # np.mat变成matrix形式
for i in range(n):
for j in range(k):
v[i, j] = normalvariate(0 , 0.2)
return v


def getCost(predict, classLabels):
'''计算预测准确性
input: predict(list)预测值
classLabels(list)标签
output: error(float)计算损失函数的值
'''
m = len(predict)
error = 0.0
for i in range(m):
error -= np.log(sigmoid(predict[i] * classLabels[i]))
return error


def getPrediction(dataMatrix, w0, w, v):
'''得到预测值
input: dataMatrix(mat)特征
w(int)常数项权重
w0(mat)一次项权重
v(mat)交叉项权重
output: result(list)预测的结果
'''
m = dataMatrix.shape[0]
reslut = []
for x in range(m):
inter_1 = inter_1 = dataMatrix[x] * v
inter_2 = np.multiply(dataMatrix[x], dataMatrix[x]) * np.multiply(v, v)
interaction = np.sum(np.multiply(inter_1, inter_1) - inter_2) / 2.
y_hat = w0 + dataMatrix[x] * w + interaction
pre = sigmoid(y_hat[0, 0])
reslut.append(pre)
return reslut


def stocGradAscent(dataMatrix, classLabels, k, max_iter, alpha):
'''利用随机梯度下降法训练FM模型
input: dataMatrix(mat)特征
classLabels(mat)标签
k(int)v的维数
max_iter(int)最大迭代次数
alpha(float)学习率
output: w0(float),w(mat),v(mat):权重
'''
m, n = np.shape(dataMatrix)
# 1、参数初始化
w = np.zeros((n, 1)) # 这就是一次项w
w0 = 0 # 常数项
v = initialize_v(n, k) # 交叉项
# 开始进行迭代训练
for it in range(max_iter):
for x in range(m): # 开始优化,每个样本都要过一次
inter_1 = dataMatrix[x] * v
inter_2 = np.multiply(dataMatrix[x], dataMatrix[x]) * np.multiply(v, v)
# 完成交叉项的计算
interaction = np.sum(np.multiply(inter_1, inter_1) - inter_2) / 2.
# 计算出预测的输出,是一个常数
y_hat = w0 + dataMatrix[x] * w + interaction
# 计算损失
loss = sigmoid(y_hat[0, 0] * classLabels[x]) - 1

# 更新 常数项 w0
w0 = w0 - alpha * loss * classLabels[x]
# 更新 w 和 v
for i in range(n):
w[i, 0] = w[i, 0] - alpha * loss * classLabels[x] * dataMatrix[x, i]

# 更新v
for j in range(k):
v[i, j] = v[i, j] - alpha * loss * classLabels[x] * \
(dataMatrix[x, i] * inter_1[0, j] - v[i, j] * dataMatrix[x, i] * dataMatrix[x, i])

# 计算损失函数的值
if it % 1000 == 0:
print("\t------- iter: ", it, " , cost: ", getCost(getPrediction(np.mat(dataTrain), w0, w, v), classLabels))
# 把模型的参数返回
return w0, w, v


def getAccuracy(predict, classLabels):
'''计算预测准确性
input: predict(list)预测值
classLabels(list)标签
output: float(error) / allItem(float)错误率
'''
m = len(predict)
allItem = 0
error = 0
for i in range(m):
allItem += 1
if float(predict[i]) < 0.5 and classLabels[i] == 1.0:
error += 1
elif float(predict[i]) >= 0.5 and classLabels[i] == -1.0:
error += 1
else:
continue
return float(error) / allItem

if __name__ == '__main__':
print("---------- 1.load data ---------")
dataTrain, labelTrain = loadDataSet("data.txt")
print("---------- 2.learning ---------")
# 2、利用随机梯度训练FM模型
w0, w, v = stocGradAscent(np.mat(dataTrain), labelTrain, 3, 10000, 0.01)
predict_result = getPrediction(np.mat(dataTrain), w0, w, v) # 得到训练的准确性
print("----------training accuracy: %f" % (1 - getAccuracy(predict_result, labelTrain)))

———- 1.load data ———

———- 2.learning ———

——- iter: 0 , cost: 158.937545578

——- iter: 1000 , cost: 111.918235669

——- iter: 2000 , cost: 108.236920062

——- iter: 3000 , cost: 106.763275985

——- iter: 4000 , cost: 105.815190198

——- iter: 5000 , cost: 105.116731734

——- iter: 6000 , cost: 104.581618271

——- iter: 7000 , cost: 104.161136945

——- iter: 8000 , cost: 103.822237844

——- iter: 9000 , cost: 103.542542809

———-training accuracy: 0.990000

训练数据下载

测试数据下载

参考文献

[1] http://www.algo.uni-konstanz.de/members/rendle/pdf/Rendle2010FM.pdf

[2] http://www.cs.cmu.edu/~wcohen/10-605/2015-guest-lecture/FM.pdf

[3] http://www.algo.uni-konstanz.de/members/rendle/pdf/Rendle2010FM.pdf

[4] Koren Y, Bell R M, Volinsky C, et al. Matrix Factorization Techniques for Recommender Systems[J]. IEEE Computer, 2009, 42(8): 30-37.

[5] https://tracholar.github.io/machine-learning/2017/03/10/factorization-machine.html#fm与矩阵分解

[6] http://www.cnblogs.com/pinard/p/6370127.html

[7] https://tech.meituan.com/deep-understanding-of-ffm-principles-and-practices.html

[8] Mikolov T, Sutskever I, Chen K, et al. Distributed representations of words and phrases and their compositionality[C]. neural information processing systems, 2013: 3111-3119.

[9] Guo H, Tang R, Ye Y, et al. DeepFM: A Factorization-Machine based Neural Network for CTR Prediction[J]. arXiv preprint arXiv:1703.04247, 2017.

[10] Ling X, Deng W, Gu C, et al. Model Ensemble for Click Prediction in Bing Search Ads[C]//Proceedings of the 26th International Conference on World Wide Web Companion. International World Wide Web Conferences Steering Committee, 2017: 689-698.

[11] http://blog.csdn.net/google19890102/article/details/45532745