BERT4Rec实战:如何用双向Transformer提升推荐系统效果(附代码示例)

发布时间:2026/7/8 20:32:13

BERT4Rec实战:如何用双向Transformer提升推荐系统效果(附代码示例) BERT4Rec实战如何用双向Transformer重塑推荐系统推荐系统早已从简单的协同过滤进化到能够捕捉用户行为序列中复杂模式的深度学习模型。在这个进化过程中BERT4Rec的出现标志着一个重要转折点——它首次将自然语言处理领域的双向Transformer架构成功引入推荐领域。与单向模型相比这种架构能同时捕捉用户历史行为的前后依赖关系显著提升了推荐质量。1. 为什么BERT4Rec是推荐系统的新标杆传统序列推荐模型如GRU4Rec或SASRec采用单向结构只能从左到右处理用户行为序列。这种设计存在明显局限当用户浏览商品A后购买商品B时单向模型无法利用后续行为B来优化对A的理解。BERT4Rec通过双向注意力机制解决了这一问题。核心优势对比特性GRU4RecSASRecBERT4Rec序列建模方向单向单向双向并行计算能力低中高长序列捕捉能力中等较强极强上下文依赖建模局部局部全局训练效率较低中等较高实际业务中这种差异会直接转化为效果提升。在某电商平台的A/B测试中BERT4Rec相比GRU4Rec将点击率提高了18.7%转化率提升12.3%。这种提升主要来自三个方面更全面的序列理解双向注意力能同时考虑用户历史行为和未来意图更强的特征交互Self-Attention机制自动学习物品间的复杂关系更高的训练效率Transformer架构支持完全并行化计算提示虽然BERT4Rec效果出众但它对数据量和计算资源的要求也更高。建议在用户行为记录超过100万条的场景中使用。2. 从零搭建BERT4Rec模型的关键步骤2.1 数据准备与预处理BERT4Rec需要严格的序列数据处理流程。以下是一个典型的Python预处理示例import pandas as pd from sklearn.preprocessing import LabelEncoder # 读取原始行为日志 df pd.read_csv(user_behavior.csv) df[timestamp] pd.to_datetime(df[timestamp]) # 按用户分组并生成行为序列 user_sequences df.sort_values([user_id,timestamp])\ .groupby(user_id)[item_id]\ .apply(list).reset_index() # 物品ID编码 item_encoder LabelEncoder() all_items df[item_id].unique() item_encoder.fit(all_items) # 序列填充与截断 max_len 50 # 根据业务场景调整 padded_sequences [] for seq in user_sequences[item_id]: encoded_seq item_encoder.transform(seq) # 截断过长的序列 if len(encoded_seq) max_len: encoded_seq encoded_seq[-max_len:] # 填充短序列 padded_seq np.pad(encoded_seq, (max_len-len(encoded_seq), 0), constant, constant_values0) padded_sequences.append(padded_seq)关键注意事项序列长度需要根据业务特点确定电商推荐通常50-100视频推荐可能更长建议保留时间戳信息可用于后续的时间衰减加权对新物品要预留足够的ID空间2.2 模型架构实现以下是PyTorch实现的BERT4Rec核心组件import torch import torch.nn as nn from transformers import BertConfig, BertModel class BERT4Rec(nn.Module): def __init__(self, item_size, hidden_size256, num_layers4, num_heads4): super().__init__() self.item_embeddings nn.Embedding(item_size, hidden_size) self.position_embeddings nn.Embedding(100, hidden_size) # 假设最大长度100 config BertConfig( vocab_size1, # 不使用原始BERT的词表 hidden_sizehidden_size, num_hidden_layersnum_layers, num_attention_headsnum_heads, intermediate_sizehidden_size*4, max_position_embeddings100 ) self.transformer BertModel(config) def forward(self, input_ids, attention_maskNone): seq_length input_ids.size(1) position_ids torch.arange(seq_length, dtypetorch.long, deviceinput_ids.device) position_ids position_ids.unsqueeze(0).expand_as(input_ids) item_emb self.item_embeddings(input_ids) pos_emb self.position_embeddings(position_ids) embeddings item_emb pos_emb outputs self.transformer( inputs_embedsembeddings, attention_maskattention_mask ) return outputs.last_hidden_state模型设计要点使用HuggingFace的BertModel作为基础架构自定义物品嵌入层和位置嵌入层通过attention_mask实现变长序列处理隐藏层大小需要与业务规模匹配通常256-5123. 训练技巧与优化策略3.1 掩码语言模型训练BERT4Rec采用类似BERT的掩码训练策略但有以下改进def mask_sequence(sequences, mask_prob0.2): 随机掩码序列中的部分物品 masked_sequences sequences.clone() labels torch.full_like(sequences, -100) # 忽略未掩码位置 # 为每个序列随机选择掩码位置 prob_matrix torch.full_like(sequences, mask_prob, dtypetorch.float) masked_indices torch.bernoulli(prob_matrix).bool() # 80%的概率用[MASK]替换10%随机物品10%保持原样 mask_token item_size - 1 # 假设最后一个ID是[MASK] random_items torch.randint(0, item_size-1, sequences.shape) replace_mask torch.rand(sequences.shape) 0.8 random_mask torch.rand(sequences.shape) 0.5 masked_sequences[masked_indices replace_mask] mask_token masked_sequences[masked_indices ~replace_mask random_mask] \ random_items[masked_indices ~replace_mask random_mask] labels[masked_indices] sequences[masked_indices] return masked_sequences, labels训练循环关键代码model BERT4Rec(item_sizelen(item_encoder.classes_)) optimizer torch.optim.AdamW(model.parameters(), lr1e-4) for epoch in range(10): for batch in train_loader: inputs, masks batch masked_inputs, labels mask_sequence(inputs) outputs model(masked_inputs, attention_maskmasks) logits nn.Linear(outputs.size(-1), item_size)(outputs) loss nn.CrossEntropyLoss(ignore_index-100)( logits.view(-1, item_size), labels.view(-1) ) optimizer.zero_grad() loss.backward() optimizer.step()3.2 业务场景调优技巧冷启动处理方案对新用户混合使用热门物品和与其人口统计特征相似用户喜欢的物品对新物品利用内容特征如品类、价格区间初始化嵌入表示时间衰减加权# 在计算loss时加入时间衰减权重 time_decay torch.exp(-time_intervals * decay_rate) # time_intervals是距当前时间的天数 loss (nn.CrossEntropyLoss(reductionnone)(...) * time_decay).mean()多目标优化# 同时优化点击率和转化率 ctr_loss nn.BCEWithLogitsLoss()(ctr_logits, ctr_labels) cvr_loss nn.BCEWithLogitsLoss()(cvr_logits, cvr_labels) total_loss 0.7 * ctr_loss 0.3 * cvr_loss 0.1 * mask_loss4. 生产环境部署与性能优化4.1 实时推荐架构用户请求 → API网关 → 特征服务 → 召回模块(BERT4Rec) → 排序模块 → 结果过滤 → 返回推荐 ↑ ↑ ↑ 用户画像 实时行为日志 模型服务关键组件模型服务化使用TorchScript导出模型通过gRPC提供服务特征缓存用户最近行为序列缓存更新频率1-5分钟异步更新用户行为通过消息队列异步更新特征存储4.2 性能优化技巧索引优化# 使用FAISS进行近似最近邻搜索 import faiss item_embeddings model.item_embeddings.weight.detach().numpy() index faiss.IndexFlatIP(item_embeddings.shape[1]) index.add(item_embeddings) # 查询时 _, topk_indices index.search(user_embedding, k100)量化加速# 模型量化 quantized_model torch.quantization.quantize_dynamic( model, {torch.nn.Linear}, dtypetorch.qint8 )缓存策略热门用户预计算推荐结果物品相似度矩阵每日预计算用户短期兴趣最近10个行为实时更新在实际部署中这些优化能将推理延迟从200ms降至50ms以下QPS提升3-5倍。某视频平台采用这种架构后成功将BERT4Rec服务扩展到每天处理10亿请求。

相关新闻