一、概念
Wide&Deep模型是Google于2016年提出的混合推荐模型,来自论文《Wide & Deep Learning for Recommender Systems》,通过结合线性模型(Wide部分)与深度神经网络(Deep部分),同时利用记忆能力(捕捉历史行为特征)和泛化能力(挖掘潜在特征关联),用于解决推荐系统中精排阶段的CTR预估等问题。由于结合了线性模型和深度神经网络的优点,模型天然地能够同时处理显式特征和隐式特征。
提出这个模型的背景是:传统推荐领域使用的线性模型可以很好地挖掘组合特征,但泛化能力不足。例如,它可以记住买了“苹果手机”的用户大概率会购买“苹果手机壳”,但它无法从这个组合中抽象出买“手机”的用户大概率会购买“手机壳”这个概念。因此,如果这时候出现了新的“华为手机”和“华为手机壳”,线性模型无法通过泛化能力将二者关联起来。另一方面,神经网络具有很好的泛化能力,但是记忆能力不足,很难学习低纬度的表示,从而造成过度泛化。例如,它会给买了“苹果手机”的用户同时推荐“苹果手机壳”和“华为手机壳”。因此,结合线性模型和神经网络的Wide&Deep成为了更优的解决方案。
二、原理
1、Wide部分(记忆能力)
Wide部分是一个线性模型,主要用于记忆历史数据中的显式特征组合。它擅长处理稀疏特征,例如用户和商品的交互记录。通过手动设计的特征交叉(feature cross),Wide部分可以直接学习这些特征组合的权重。Wide部分的核心就是记录历史数据中高频出现的组合(类似共现矩阵,但比共现矩阵更高阶,是统计层面的贡献规律/关系的可学习特征化表示),这往往通过人工来设计有业务意义的特征组合。以电商平台为例,当用户浏览“手机”商品时,Wide部分会激活相关的交叉特征,例如“手机类目 * 手机壳”以及“手机品牌 * 快充头”,从而生成相关的共现关系特征。
2、Deep部分(泛化能力)
Deep部分是一个深度神经网络,主要用于学习隐式特征和复杂的特征交互(如用户画像、应用类别)。它通过嵌入层(embedding layer)将稀疏特征转化为稠密向量,并通过多层神经网络捕获非线性关系和高阶特征交互。
3、联合训练机制
两个模块共享损失函数进行端到端训练,最终预测结果为:
其中,表示交叉特征,
是神经网络最后一层的激活值。
三、python实现
假设有一个简单的推荐场景,用户和商品的交互数据已经被处理成稀疏特征和稠密特征。这里的优化器使用了Adam,实际的工程应用可以分别给Wide和Deep部分设计对应的优化器。例如,比较常用的策略是在Wide部分使用FTRL优化器来处理稀疏的特征交叉,Deep部分则使用Adam或者AdamW。
import pandas as pd
import numpy as np
# 生成10000条用户-电影推荐数据
np.random.seed(42)
n_samples = 10000
data = {
# 用户特征
"user_id": np.arange(n_samples),
"user_age": np.random.randint(12, 70, n_samples),
"user_gender": np.random.choice([0,1], n_samples), # 0-女,1-男
"user_history_clicks": np.random.poisson(5, n_samples), # 历史点击次数
# 物品特征
"movie_id": np.random.randint(1000, 2000, n_samples),
"movie_genre": np.random.choice(["动作", "喜剧", "爱情", "科幻"], n_samples),
"movie_rating": np.round(np.random.uniform(3.0, 5.0, n_samples), 1),
# 上下文特征
"hour": np.random.randint(0, 24, n_samples),
"device": np.random.choice(["手机", "PC", "平板"], n_samples),
# 标签
"click": np.random.choice([0,1], n_samples, p=[0.7, 0.3]) # 30%点击率
}
df = pd.DataFrame(data)
from sklearn.preprocessing import OneHotEncoder, StandardScaler
# 分类特征编码
cat_features = ["movie_genre", "device"]
encoder = OneHotEncoder()
encoded_features = encoder.fit_transform(df[cat_features]).toarray()
# 数值特征标准化
num_features = ["user_age", "user_history_clicks", "movie_rating"]
scaler = StandardScaler()
scaled_features = scaler.fit_transform(df[num_features])
# 构造Wide部分交叉特征(示例:性别×电影类型)
df["gender_genre"] = df["user_gender"].astype(str) + "_" + df["movie_genre"]
wide_encoder = OneHotEncoder()
wide_features = wide_encoder.fit_transform(df[["gender_genre"]]).toarray()
# 合并特征
import torch
X_wide = torch.FloatTensor(wide_features)
X_deep = torch.FloatTensor(np.hstack([encoded_features, scaled_features]))
y = torch.FloatTensor(df["click"].values)
import torch.nn as nn
import torch.optim as optim
class WideAndDeep(nn.Module):
def __init__(self, wide_dim, deep_dim):
super().__init__()
# Wide部分(线性模型)
self.wide = nn.Linear(wide_dim, 1)
# Deep部分(3层神经网络)
self.deep = nn.Sequential(
nn.Linear(deep_dim, 64),
nn.ReLU(),
nn.Linear(64, 32),
nn.ReLU(),
nn.Linear(32, 1)
)
# 最终输出层
self.sigmoid = nn.Sigmoid()
def forward(self, x_wide, x_deep):
wide_out = self.wide(x_wide)
deep_out = self.deep(x_deep)
combined = wide_out + deep_out
return self.sigmoid(combined.squeeze())
# 初始化模型
model = WideAndDeep(wide_dim=X_wide.shape[1], deep_dim=X_deep.shape[1])
from torch.utils.data import Dataset, DataLoader
# 自定义数据集
class RecDataset(Dataset):
def __init__(self, x_wide, x_deep, y):
self.x_wide = x_wide
self.x_deep = x_deep
self.y = y
def __len__(self):
return len(self.y)
def __getitem__(self, idx):
return self.x_wide[idx], self.x_deep[idx], self.y[idx]
# 数据加载器
dataset = RecDataset(X_wide, X_deep, y)
train_loader = DataLoader(dataset, batch_size=64, shuffle=True)
# 训练配置
criterion = nn.BCELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 训练循环
for epoch in range(10):
for x_wide, x_deep, labels in train_loader:
optimizer.zero_grad()
outputs = model(x_wide, x_deep)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")
# 评估
with torch.no_grad():
preds = model(X_wide, X_deep)
accuracy = ((preds > 0.5).float() == y).float().mean()
print(f"测试准确率:{accuracy.item():.3f}")
四、总结
特性 | Wide部分 | Deep部分 |
---|---|---|
处理方式 | 记录明确的商品组合规律 | 发现潜在的跨品类关联 |
响应速度 | 即时生效(如节日促销) | 需要数据累积(新品推荐) |
应用场景 | 啤酒和尿布等经典组合 | 小众商品的长尾推荐 |