推荐算法之Factorization Machine
1.模型原理
因子分解机模型(Factorization Machine)出现之前的传统的处理方法是人工特征工程加上线性模型(如逻辑回归Logistic Regression)。为了提高模型效果,关键技术是找到到用户点击行为背后隐含的特征组合。如男性、大学生用户往往会点击游戏类广告,因此 “男性且是大学生且是游戏类” 的特征组合就是一个关键特征。但这本质仍是线性模型,其假设函数表示成内积形式一般为:
y
l
i
n
e
a
r
=
σ
(
<
w
⃗
,
x
⃗
>
)
y_{linear} = \sigma(<\vec{w},\vec{x}>)
ylinear=σ(<w,x>)
其中 x ⃗ \vec{x} x 为特征向量, w ⃗ \vec{w} w为权重向量, σ \sigma σ为sigmoid函数。
人工进行特征组合通常会存在诸多困难,如特征爆炸、特征难以被识别、组合特征难以设计等。为了让模型自动地考虑特征之间的二阶组合信息,线性模型推广为二阶多项式
2
d
−
P
o
l
y
n
o
m
i
a
l
2d−Polynomial
2d−Polynomial模型:
y
p
o
l
y
=
σ
(
<
w
⃗
,
x
⃗
>
+
∑
i
=
1
n
∑
j
=
1
n
w
i
j
⋅
x
i
⋅
y
i
)
y_{poly}=\sigma\big(<\vec{w},\vec{x}>+\sum_{i=1}^n\sum_{j=1}^nw_{ij}\cdot x_i \cdot y_i\big)
ypoly=σ(<w,x>+i=1∑nj=1∑nwij⋅xi⋅yi)
就是对特征两两相乘(组合)构成新特征(离散化之后其实就是“且”操作),并对每个新特征分配独立的权重,通过机器学习来自动得到这些权重。将其写成矩阵形式为:
y
p
o
l
y
=
σ
(
w
⃗
T
⋅
x
⃗
+
x
⃗
T
⋅
W
(
2
)
⋅
x
⃗
)
y_{poly}=\sigma(\vec{w}^T \cdot \vec{x}+ \vec{x}^T \cdot W^{(2)} \cdot \vec{x})
ypoly=σ(wT⋅x+xT⋅W(2)⋅x)
其中
W
(
2
)
W^{(2)}
W(2)为二阶特征组合的权重矩阵,是对称矩阵。而这个矩阵参数非常多,为
O
(
n
2
)
O (n^2)
O(n2) 。为了降低该矩阵的维度,可以将其因子分解(Factorization)为两个低维(比如
n
∗
k
n∗k
n∗k)矩阵的相乘。则此时W矩阵的参数就大幅降低,为
O
(
n
k
)
O(nk)
O(nk)。公式如下:
W
(
2
)
=
W
T
⋅
W
W^{(2)} = W^T \cdot W
W(2)=WT⋅W
FM的矩阵形式公式如下:
y
F
M
=
σ
(
w
⃗
T
⋅
x
⃗
+
x
⃗
T
⋅
W
T
⋅
W
⋅
x
⃗
)
y_{FM} = \sigma\big(\vec{w}^T \cdot \vec{x}+ \vec{x}^T \cdot W^T \cdot W \cdot \vec{x}\big)
yFM=σ(wT⋅x+xT⋅WT⋅W⋅x)
内积的形式:
y
F
M
=
σ
(
<
w
⃗
,
x
⃗
>
+
<
W
⋅
x
⃗
,
W
⋅
x
⃗
>
)
y_{FM} = \sigma(<\vec{w} , \vec{x}>+<{W} \cdot \vec{x},{W} \cdot \vec{x}>)
yFM=σ(<w,x>+<W⋅x,W⋅x>)
可以将上式进一步改写成求和式的形式:
y
F
M
=
σ
(
<
w
⃗
,
x
⃗
>
+
∑
i
=
1
n
∑
j
=
1
n
<
x
i
⋅
v
j
⃗
,
x
j
⋅
v
j
⃗
>
)
y_{FM} = \sigma \big(<\vec{w} ,\vec{x}> +\sum_{i=1}^n\sum_{j=1}^n<x_i \cdot \vec{v_j},x_j \cdot \vec{v_j}>\big)
yFM=σ(<w,x>+i=1∑nj=1∑n<xi⋅vj,xj⋅vj>)
其中向量
v
i
⃗
\vec{v_i}
vi是矩阵W 的第i列。为了去除重复项与特征平方项,上式可以进一步改写成更为常见的FM公式:
y
F
M
=
σ
(
<
w
⃗
,
x
⃗
>
+
∑
i
=
1
n
∑
j
=
1
n
<
v
j
⃗
,
v
j
⃗
>
x
i
⋅
x
j
)
y_{FM} = \sigma \Big(<\vec{w} ,\vec{x}> +\sum_{i=1}^n\sum_{j=1}^n< \vec{v_j} ,\vec{v_j}>x_i \cdot x_j \Big)
yFM=σ(<w,x>+i=1∑nj=1∑n<vj,vj>xi⋅xj)
对比二阶多项式模型,FM模型中特征两两相乘(组合)的权重是相互不独立的,它是一种参数较少但表达力强的模型。
本质上,FM引入隐向量,与矩阵分解用隐向量代表用户和物品的做法异曲同工。隐向量的引用使FM能更好地解决数据稀疏性的问题。
在工程方面,FM同样可以使用梯度下降法进行学习,不失灵活性和实时性,较容易实现线上服务。
2. 延伸
2.1 对比MLP+Embedding
FM公式的矩阵内积为 < w ⃗ , x ⃗ > + < W ⋅ x ⃗ , W ⋅ x ⃗ > <\vec{w} , \vec{x}>+<{W} \cdot \vec{x},{W} \cdot \vec{x}> <w,x>+<W⋅x,W⋅x>,发现 W ⋅ x ⃗ W \cdot \vec{x} W⋅x部分就是将离散系数特征通过矩阵乘法降维成一个低维稠密向量。这个过程对神经网络来说就叫做embedding。
2.2 领域信息Field
对不同领域的离散特征分别进行嵌入,之后再进行二阶特征的向量内积。
3. FM的Tensorflow实现
class FM(Model):
def __init__(self, input_dim=None, output_dim=1, factor_order=10, init_path=None, opt_algo='gd', learning_rate=1e-2,
l2_w=0, l2_v=0, random_seed=None):
Model.__init__(self)
# 一次、二次交叉、偏置项
init_vars = [('w', [input_dim, output_dim], 'xavier', dtype),
('v', [input_dim, factor_order], 'xavier', dtype),
('b', [output_dim], 'zero', dtype)]
self.graph = tf.Graph()
with self.graph.as_default():
if random_seed is not None:
tf.set_random_seed(random_seed)
self.X = tf.sparse_placeholder(dtype)
self.y = tf.placeholder(dtype)
self.vars = init_var_map(init_vars, init_path)
w = self.vars['w']
v = self.vars['v']
b = self.vars['b']
# [(x1+x2+x3)^2 - (x1^2+x2^2+x3^2)]/2
# 先计算所有的交叉项,再减去平方项(自己和自己相乘)
X_square = tf.SparseTensor(self.X.indices, tf.square(self.X.values), tf.to_int64(tf.shape(self.X)))
xv = tf.square(tf.sparse_tensor_dense_matmul(self.X, v))
p = 0.5 * tf.reshape(
tf.reduce_sum(xv - tf.sparse_tensor_dense_matmul(X_square, tf.square(v)), 1),
[-1, output_dim])
xw = tf.sparse_tensor_dense_matmul(self.X, w)
logits = tf.reshape(xw + b + p, [-1])
self.y_prob = tf.sigmoid(logits)
self.loss = tf.reduce_mean(
tf.nn.sigmoid_cross_entropy_with_logits(logits=logits, labels=self.y)) + \
l2_w * tf.nn.l2_loss(xw) + \
l2_v * tf.nn.l2_loss(xv)
self.optimizer = get_optimizer(opt_algo, learning_rate, self.loss)
#GPU设定
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
self.sess = tf.Session(config=config)
# 图中所有variable初始化
tf.global_variables_initializer().run(session=self.sess)
3. 参考
- 王喆,深度学习推荐系统
- 寒小阳博客