FM到FFM——自动特征交叉
概述
在推荐系统中,特征交叉是一种常见的特征工程手段,它可以帮助模型捕捉特征之间的相互作用关系。然而,手动进行特征交叉可能会导致特征维度的爆炸,同时也无法保证交叉特征的有效性。因此,自动特征交叉的方法应运而生,其中最具代表性的算法是因子分解机(Factorization Machines, FM)和场感知因子分解机(Field-aware Factorization Machines, FFM)。
[关键词:因子分解机、场感知因子分解机、特征交叉、推荐系统、数学推导、Python实现]
目录
因子分解机(FM)
FM原理
因子分解机(FM)是一种能够自动进行特征交叉的模型,它通过引入隐向量的方式,实现了特征之间的二阶交叉。FM模型的数学表达式为:
y ^ ( x ) = w 0 + ∑ i = 1 n w i x i + ∑ i = 1 n ∑ j = i + 1 n ⟨ v i , v j ⟩ x i x j \hat{y}(x) = w_0 + \sum_{i=1}^{n}w_ix_i + \sum_{i=1}^{n}\sum_{j=i+1}^{n}\langle v_i, v_j \rangle x_ix_j y^(x)=w0+i=1∑nwixi+i=1∑nj=i+1∑n⟨vi,vj⟩xixj
其中,(w_0)是全局偏置项,(w_i)是第(i)个特征的权重,(v_i)是第(i)个特征的隐向量,(\langle v_i, v_j \rangle)表示隐向量的内积,(x_i)和(x_j)是输入特征向量中的第(i)个和第(j)个特征值。
数学推导
FM模型的损失函数通常采用平方损失函数,其数学表达式为:
L ( y , y ^ ) = 1 2 ( y − y ^ ) 2 L(y, \hat{y}) = \frac{1}{2}(y - \hat{y})^2 L(y,y^)=21(y−y^)2
为了优化损失函数,我们采用梯度下降算法进行迭代更新。对于
样本(i),损失函数关于权重(w_i)、隐向量(v_i)和全局偏置项(w_0)的梯度分别为:
∂ L ∂ w i = − ( y − y ^ ) x i \frac{\partial L}{\partial w_i} = -(y - \hat{y})x_i ∂wi∂L=−(y−y^)xi
∂ L ∂ v i = − ( y − y ^ ) x i ( ∑ j = 1 n v j x j − v i x i ) \frac{\partial L}{\partial v_i} = -(y - \hat{y})x_i\left(\sum_{j=1}^{n}v_jx_j - v_ix_i\right) ∂vi∂L=−(y−y^)xi(j=1∑nvjxj−vixi)
∂ L ∂ w 0 = − ( y − y ^ ) \frac{\partial L}{\partial w_0} = -(y - \hat{y}) ∂w0∂L=−(y−y^)
其中,(y)表示真实标签,(\hat{y})表示预测值。
根据梯度下降的原理,我们可以对权重(w_i)、隐向量(v_i)和全局偏置项(w_0)进行迭代更新:
w i ← w i − α ∂ L ∂ w i w_i \leftarrow w_i - \alpha \frac{\partial L}{\partial w_i} wi←wi−α∂wi∂L
v i ← v i − α ∂ L ∂ v i v_i \leftarrow v_i - \alpha \frac{\partial L}{\partial v_i} vi←vi−α∂vi∂L
w 0 ← w 0 − α ∂ L ∂ w 0 w_0 \leftarrow w_0 - \alpha \frac{\partial L}{\partial w_0} w0←w0−α∂w0∂L
其中,(\alpha)是学习率,用于控制参数更新的步长。
Python实现
接下来,我们使用Python实现FM模型,并通过注释解释代码的每个部分。
import numpy as np
class FM:
def __init__(self, n_features, n_factors, lr, epochs):
self.n_features = n_features
self.n_factors = n_factors
self.lr = lr
self.epochs = epochs
self.w0 = 0
self.w = np.zeros(n_features)
self.v = np.random.normal(0, 0.1, (n_features, n_factors))
def predict(self, X):
linear_terms = np.dot(X, self.w)
interactions = 0.5 * np.sum(np.dot(X, self.v) ** 2 - np.dot(X ** 2, self.v ** 2), axis=1)
return self.w0 + linear_terms + interactions
def fit(self, X, y):
for epoch in range(self.epochs):
y_hat = self.predict(X)
loss = 0.5 * np.mean((y - y_hat) ** 2)
print("Epoch: {}, Loss: {:.4f}".format(epoch + 1, loss))
# 更新参数
self.w0 -= self.lr * np.mean(y_hat - y)
self.w -= self.lr * np.dot(y_hat - y, X)
for i in range(self.n_features):
for f in range(self.n_factors):
self.v[i, f] -= self.lr * np.mean((y_hat - y) * X[:, i] * (np.dot(X, self.v[:, f]) - self.v[i, f] * X[:, i]))
场感知因子分解机(FFM)
FFM原理
场感知因子分解机(FFM)是FM模型的扩展,它引入了“场”的概念,使得模型能够区分不同场的特征交叉。在FFM模型
中,每个特征都会有多个隐向量,每个隐向量对应一个场。FFM模型的数学表达式为:
y ^ ( x ) = w 0 + ∑ i = 1 n w i x i + ∑ i = 1 n ∑ j = i + 1 n ⟨ v i , f j , v j , f i ⟩ x i x j \hat{y}(x) = w_0 + \sum_{i=1}^{n}w_ix_i + \sum_{i=1}^{n}\sum_{j=i+1}^{n}\langle v_{i,f_j}, v_{j,f_i} \rangle x_ix_j y^(x)=w0+i=1∑nwixi+i=1∑nj=i+1∑n⟨vi,fj,vj,fi⟩xixj
其中,(w_0)是全局偏置项,(w_i)是第(i)个特征的权重,(v_{i,f_j})是第(i)个特征在第(j)个特征场的隐向量,(\langle v_{i,f_j}, v_{j,f_i} \rangle)表示隐向量的内积,(x_i)和(x_j)是输入特征向量中的第(i)个和第(j)个特征值,(f_i)和(f_j)分别表示第(i)个特征和第(j)个特征所属的场。
数学推导
FFM模型的损失函数与FM模型相同,通常采用平方损失函数。对于样本(i),损失函数关于权重(w_i)、隐向量(v_{i,f_j})和全局偏置项(w_0)的梯度分别为:
∂ L ∂ w i = − ( y − y ^ ) x i \frac{\partial L}{\partial w_i} = -(y - \hat{y})x_i ∂wi∂L=−(y−y^)xi
∂ L ∂ v i , f j = − ( y − y ^ ) x i ( ∑ j = 1 n v j , f i x j − v i , f j x i ) \frac{\partial L}{\partial v_{i,f_j}} = -(y - \hat{y})x_i\left(\sum_{j=1}^{n}v_{j,f_i}x_j - v_{i,f_j}x_i\right) ∂vi,fj∂L=−(y−y^)xi(j=1∑nvj,fixj−vi,fjxi)
∂ L ∂ w 0 = − ( y − y ^ ) \frac{\partial L}{\partial w_0} = -(y - \hat{y}) ∂w0∂L=−(y−y^)
其中,(y)表示真实标签,(\hat{y})表示预测值。
根据梯度下降的原理,我们可以对权重(w_i)、隐向量(v_{i,f_j})和全局偏置项(w_0)进行迭代更新:
w i ← w i − α ∂ L ∂ w i w_i \leftarrow w_i - \alpha \frac{\partial L}{\partial w_i} wi←wi−α∂wi∂L
v i , f j ← v i , f j − α ∂ L ∂ v i , f j v_{i,f_j} \leftarrow v_{i,f_j} - \alpha \frac{\partial L}{\partial v_{i,f_j}} vi,fj←vi,fj−α∂vi,fj∂L
w 0 ← w 0 − α ∂ L ∂ w 0 w_0 \leftarrow w_0 - \alpha \frac{\partial L}{\partial w_0} w0←w0−α∂w0∂L
其中,(\alpha)是学习率,用于控制参数更新的步长。
Python实现
接下来,我们使用Python实现FFM模型,并通过注释解释代码的每个部分。
import numpy as np
class FFM:
def __init__(self, n_features, n_fields, n_factors, lr, epochs):
self.n_features = n_features
self.n_fields = n_fields
self.n_factors = n_factors
self.lr = lr
self.epochs = epochs
self.w0 =
0
self.w = np.zeros(n_features)
self.v = np.random.normal(0, 0.1, (n_features, n_fields, n_factors))
def predict(self, X):
linear_terms = np.dot(X, self.w)
interactions = 0
for i in range(self.n_features):
for j in range(i + 1, self.n_features):
field_i = self.field_indices[i]
field_j = self.field_indices[j]
interactions += np.dot(self.v[i, field_j], self.v[j, field_i]) * X[:, i] * X[:, j]
interactions = 0.5 * interactions
return self.w0 + linear_terms + interactions
def fit(self, X, y, field_indices):
self.field_indices = field_indices
for epoch in range(self.epochs):
y_hat = self.predict(X)
loss = 0.5 * np.mean((y - y_hat) ** 2)
print("Epoch: {}, Loss: {:.4f}".format(epoch + 1, loss))
# 更新参数
self.w0 -= self.lr * np.mean(y_hat - y)
self.w -= self.lr * np.dot(y_hat - y, X)
for i in range(self.n_features):
for f in range(self.n_fields):
for k in range(self.n_factors):
field_i = self.field_indices[i]
self.v[i, f, k] -= self.lr * np.mean((y_hat - y) * X[:, i] * (np.dot(X[:, field_i], self.v[:, f, k]) - self.v[i, f, k] * X[:, i]))
总结
本文详细介绍了因子分解机(FM)和场感知因子分解机(FFM)的原理、数学推导以及Python实现。FM和FFM是推荐系统中常用的自动特征交叉模型,能够有效捕捉特征之间的相互作用关系,从而提升模型的预测性能。
FM模型通过引入隐向量实现了特征之间的二阶交叉,而FFM模型在FM的基础上引入了“场”的概念,使得模型能够区分不同场的特征交叉。这两种模型在推荐系统、点击率预估等任务中都有广泛的应用。
需要注意的是,虽然FM和FFM能够自动进行特征交叉,但它们仅考虑了特征之间的二阶交叉关系,对于更高阶的特征交叉可能无法很好地建模。此外,随着特征数量和场的数量的增加,FFM模型的参数量会显著增加,可能导致计算复杂度较高。
参考文献
- Rendle, S. (2010). Factorization machines. In Proceedings of the 10th IEEE International Conference on Data Mining (ICDM), pp. 995-1000.
- Juan, Y., Zhuang, W., Chin, W. S., & Lin, C. J. (2016). Field-aware factorization machines for CTR prediction. In Proceedings of the 10th ACM Conference on Recommender Systems (RecSys), pp. 43-50.