一、数据预处理与特征工程
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, Dataset
class WideDeepDataset(Dataset):
def __init__(self, data, categorical_cols, continuous_cols, target_col):
self.categorical = data[categorical_cols].values
self.continuous = data[continuous_cols].values
self.target = data[target_col].values
def __len__(self):
return len(self.target)
def __getitem__(self, idx):
return torch.tensor(self.categorical[idx], dtype=torch.long), \
torch.tensor(self.continuous[idx], dtype=torch.float32), \
torch.tensor(self.target[idx], dtype=torch.float32)
关键点:
- 分离类别特征(如用户ID、物品ID)和连续特征(如价格、评分)
- 使用
Dataset
封装数据加载逻辑,支持批量处理
二、模型架构实现
class WideAndDeep(nn.Module):
def __init__(self, cat_dims, emb_dims, hidden_units, num_wide_features):
super().__init__()
# Wide部分:线性模型 + 交叉特征
self.wide = nn.Linear(num_wide_features, 1)
# Deep部分:Embedding层 + MLP
self.embeddings = nn.ModuleList([
nn.Embedding(cat_dim, emb_dim) for cat_dim, emb_dim in zip(cat_dims, emb_dims)
])
# 计算Embedding总维度
total_emb_dim = sum(emb_dims)
self.deep = nn.Sequential(
nn.Linear(total_emb_dim + continuous_cols.shape[1], hidden_units[0]),
nn.ReLU(),
nn.Linear(hidden_units[0], hidden_units[1]),
nn.ReLU(),
nn.Linear(hidden_units[1], 1)
)
def forward(self, categorical_feats, continuous_feats):
# Wide部分:直接线性组合
wide_out = self.wide(categorical_feats)
# Deep部分:Embedding + MLP
embedded_feats = [emb(feats) for emb, feats in zip(self.embeddings, categorical_feats)]
embedded_feats = torch.cat(embedded_feats, dim=1)
deep_input = torch.cat([embedded_feats, continuous_feats], dim=1)
deep_out = self.deep(deep_input)
# 联合输出
out = 0.5 * wide_out + 0.5 * deep_out # 权重可调节
return torch.sigmoid(out)
关键代码解读:
-
Wide部分:
- 使用线性层直接学习特征交叉(如
AND(gender=female,language=en)
) - 输入为经过One-Hot编码的交叉特征向量
- 使用线性层直接学习特征交叉(如
-
Deep部分:
nn.Embedding
层将离散特征映射为低维稠密向量- 多层全连接网络(MLP)学习高阶非线性关系
- 与Wide部分共享输入特征
-
联合训练:输出层加权融合(权重可调),通过Sigmoid激活得到CTR预测
三、训练流程实现
# 超参数设置
cat_dims = [1000, 500] # 各分类特征的类别数
emb_dims = [64, 32] # 各分类特征的Embedding维度
hidden_units = [128, 64] # MLP隐藏层单元数
num_wide_features = 10 # Wide部分的特征数量
# 初始化模型
model = WideAndDeep(cat_dims, emb_dims, hidden_units, num_wide_features)
# 损失函数与优化器
criterion = nn.BCELoss() # 二分类交叉熵
optimizer = torch.optim.AdamW(model.parameters(), lr=0.001, weight_decay=1e-5)
# 训练循环示例
for epoch in range(10):
for cat_feats, cont_feats, labels in DataLoader(dataset, batch_size=256):
preds = model(cat_feats, cont_feats)
loss = criterion(preds, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
关键点:
- 使用
BCELoss
处理二分类问题(点击率预测) - AdamW优化器结合权重衰减防止过拟合
- 特征输入需预处理为:
- 类别特征:整数编码(如
user_id=123
→索引) - 连续特征:标准化/归一化
- 类别特征:整数编码(如
四、核心设计解析
-
记忆与泛化的平衡:
- Wide部分通过人工设计的交叉特征(如
user_age * item_category
)直接记忆重要规则 - Deep部分通过Embedding自动学习特征表示,泛化新特征组合
- Wide部分通过人工设计的交叉特征(如
-
特征处理策略:
- 稀疏特征:使用Embedding层降维(如用户ID映射到64维向量)
- 稠密特征:直接输入线性层或归一化后处理
-
联合训练优势:
- 相比Ensemble,参数共享减少计算量
- 端到端训练避免误差累积
五、应用场景优化建议
-
特征工程:
- Wide部分加入高频交叉特征(如
gender * occupation
) - Deep部分对长尾类别进行分桶处理
- Wide部分加入高频交叉特征(如
-
正则化
# 在Deep部分添加Dropout
self.deep = nn.Sequential(
nn.Linear(...),
nn.ReLU(),
nn.Dropout(0.5),
...
)
-
线上服务:
- 使用
torchscript
导出模型 - 通过Redis缓存高频特征的Embedding
- 使用