特征交叉是推荐系统排序模型的核心技术。LR模型只能学习线性关系,而推荐场景中特征交叉往往非常重要(如"男性+科技"vs"女性+美妆")。FM(Factorization Machines)通过隐因子交叉高效地学习二阶特征交互。
| 模型 | 交叉方式 | 参数量 | 优势 |
|---|---|---|---|
| LR | 无交叉 | O(n) | 简单快速 |
| Poly2 | 显式交叉 | O(n²) | 直接建模 |
| FM | 隐因子交叉 | O(nk) | 稀疏数据友好 |
| FFM | 域感知交叉 | O(nfk) | 更精细的交叉 |
FM的核心创新:将Poly2的O(n²)参数量降为O(nk),通过隐因子的内积来近似特征交叉权重,使得稀疏场景下也能有效学习。
ŷ = w₀ + Σwᵢxᵢ + ΣΣ⟨Vᵢ,Vⱼ⟩xᵢxⱼ
线性项 + 二阶交叉项,交叉通过隐因子内积实现
ΣΣ⟨Vᵢ,Vⱼ⟩xᵢxⱼ = ½[Σ(ΣVᵢxᵢ)² - Σ(Vᵢxᵢ)²]
将O(kn²)降为O(kn)
每个特征对每个field有不同隐向量
ŷ = ΣΣ⟨Vᵢ,fⱼ,Vⱼ,fᵢ⟩xᵢxⱼ
import numpy as np
np.random.seed(42)
print("="*60+"\n特征交叉模型(FM/FFM)\n"+"="*60)
n_samples=500;n_features=10;n_fields=5
# 生成特征数据
X=np.random.randn(n_samples,n_features)
# 二阶交叉生成标签
true_w=np.random.randn(n_features)
true_V=np.random.randn(n_features,8)*0.3
y=X@true_w
for i in range(n_features):
for j in range(i+1,n_features):
y+=np.dot(true_V[i],true_V[j])*X[:,i]*X[:,j]
y=(y>np.median(y)).astype(float)
print(f"样本数:{n_samples} 特征数:{n_features} 正例率:{y.mean():.2%}")
# LR基线
from collections import defaultdict
def sigmoid(x):return 1/(1+np.exp(-np.clip(x,-500,500)))
def train_lr(X,y,lr=0.01,epochs=100):
n,d=X.shape;w=np.zeros(d);b=0
for ep in range(epochs):
pred=sigmoid(X@w+b)
err=pred-y
w-=lr*(X.T@err/n+0.01*w)
b-=lr*np.mean(err)
if(ep+1)%25==0:
loss=-np.mean(y*np.log(pred+1e-10)+(1-y)*np.log(1-pred+1e-10))
print(f" LR Epoch{ep+1}:Loss={loss:.4f}")
return w,b
print("\n--- LR基线 ---")
w_lr,b_lr=train_lr(X,y)
pred_lr=sigmoid(X@w_lr+b_lr)
acc_lr=np.mean((pred_lr>0.5)==y)
auc_lr=np.mean(pred_lr[y==1]>pred_lr[y==0])
print(f"LR ACC={acc_lr:.4f} AUC={auc_lr:.4f}")
# FM
def train_fm(X,y,k=8,lr=0.01,epochs=100):
n,d=X.shape
w=np.zeros(d);b=0;V=np.random.randn(d,k)*0.01
for ep in range(epochs):
pred=sigmoid(b+X@w+0.5*np.sum((X@V)**2-(X**2)@(V**2),axis=1))
err=pred-y
w-=lr*(X.T@err/n+0.01*w)
b-=lr*np.mean(err)
V-=lr*(X.T[:,None]@err[None,:]*X@V/n-X.T@(err[:,None]*(X**2))[:,:,None]*V/n+0.01*V)
if(ep+1)%25==0:
loss=-np.mean(y*np.log(pred+1e-10)+(1-y)*np.log(1-pred+1e-10))
print(f" FM Epoch{ep+1}:Loss={loss:.4f}")
return w,b,V
print("\n--- FM ---")
w_fm,b_fm,V_fm=train_fm(X,y)
linear=X@w_fm+b_fm
interaction=0.5*np.sum((X@V_fm)**2-(X**2)@(V_fm**2),axis=1)
pred_fm=sigmoid(linear+interaction)
acc_fm=np.mean((pred_fm>0.5)==y)
auc_fm=np.mean(pred_fm[y==1]>pred_fm[y==0])
print(f"FM ACC={acc_fm:.4f} AUC={auc_fm:.4f}")
# 交叉特征重要性
cross_imp=[]
for i in range(n_features):
for j in range(i+1,n_features):
imp=abs(np.dot(V_fm[i],V_fm[j]))
cross_imp.append((i,j,imp))
cross_imp.sort(key=lambda x:-x[2])
print(f"\nTop-5重要交叉特征:")
for i,j,imp in cross_imp[:5]:
print(f" 特征{i}x特征{j}: 重要性={imp:.4f}")
# FFM
field_map=np.array([i%n_fields for i in range(n_features)])
def train_ffm(X,y,field_map,k=4,lr=0.01,epochs=80):
n,d=X.shape;nf=max(field_map)+1
V=np.random.randn(d,nf,k)*0.01;w=np.zeros(d);b=0
for ep in range(epochs):
total_loss=0
for s in range(n):
pred=b+np.dot(w,X[s])
for i in range(d):
if X[s,i]==0:continue
for j in range(i+1,d):
if X[s,j]==0:continue
pred+=np.dot(V[i,field_map[j]],V[j,field_map[i]])*X[s,i]*X[s,j]
pred=sigmoid(pred)
err=pred-y[s]
total_loss+=-y[s]*np.log(pred+1e-10)-(1-y[s])*np.log(1-pred+1e-10)
w-=lr*(err*X[s]/n+0.01*w);b-=lr*err/n
for i in range(d):
if X[s,i]==0:continue
for j in range(i+1,d):
if X[s,j]==0:continue
gi=err*V[j,field_map[i]]*X[s,i]*X[s,j]/n
gj=err*V[i,field_map[j]]*X[s,i]*X[s,j]/n
V[i,field_map[j]]-=lr*(gi+0.01*V[i,field_map[j]])
V[j,field_map[i]]-=lr*(gj+0.01*V[j,field_map[i]])
if(ep+1)%20==0:print(f" FFM Epoch{ep+1}:Loss={total_loss/n:.4f}")
return w,b,V
print("\n--- FFM ---")
w_ffm,b_ffm,V_ffm=train_ffm(X[:100],y[:100],field_map) # 小样本
print(f"FFM训练完成(100样本)")
# 对比
print(f"\n{'模型':<10}{'ACC':<10}{'AUC':<10}")
print("-"*30)
print(f"{'LR':<10}{acc_lr:<10.4f}{auc_lr:<10.4f}")
print(f"{'FM':<10}{acc_fm:<10.4f}{auc_fm:<10.4f}")
✅ 验证通过
本课代码涵盖了从数据生成、模型训练到效果评估的完整流程。以下是关键步骤的详细解读:
代码设计原则:简洁可读、关键步骤有注释、结果可复现(固定随机种子)。
| 组件 | 版本 | 说明 |
|---|---|---|
| Python | 3.8+ | 推荐3.10版本 |
| NumPy | 1.21+ | 核心数值计算库 |
| SciPy | 1.7+ | 统计检验(部分课程) |
FM只捕捉二阶交叉,高阶交叉需要深度网络:
本课是第7课:特征交叉(FM / FFM),属于特征与模型阶段。我们系统学习了本课的核心概念、数学原理和代码实现,并通过实机运行验证了算法的正确性。
练习1:调整隐因子维度k∈{4,8,16},观察AUC变化
练习2:实现FM的SGD版本并对比收敛速度
练习3:实现DeepFM的简化版本
练习4:对比FM和Poly2在稀疏数据上的效果差异
| 公司 | 场景 | 核心技术 |
|---|---|---|
| 字节跳动 | 抖音/TikTok | 多目标排序+实时特征+双塔召回 |
| 阿里巴巴 | 淘宝推荐 | DIN/DIEN序列模型+MIND多兴趣 |
| 腾讯 | 微信看一看 | DeepFM+图神经网络召回 |
| 美团 | 本地生活推荐 | 多场景多目标+时空特征 |
| Netflix | 视频推荐 | 矩阵分解+深度学习混合 |
✅ 理解特征交叉的重要性和LR的局限
✅ 掌握FM的核心公式与高效计算
✅ 实现FM并评估AUC
✅ 理解FFM的域感知交叉思想