多兴趣推荐:用多个向量捕获用户多样化偏好
FreeGuideOnline
最新
2026-06-23
loss = -log( exp(v_hat · e_i) / ( exp(v_hat · e_i) + Σ_{j in 负样本} exp(v_hat · e_j) ) )
这样训练使**最相关的兴趣向量**被推向正物品,并保证其他兴趣不会干扰。
---
## 3. 在线服务:如何利用多个兴趣召回?
### 3.1 多向量索引与召回
用户侧存储 K 个兴趣向量后,推荐流程变为:
- **离线/近线阶段**:提取用户多兴趣向量,将 K 个向量分别写入向量检索库(如 Faiss)。
- **在线请求阶段**:对于用户的 K 个向量,分别执行 K 次最近邻检索,得到 K 批候选物品集合。
- **合并与去重**:合并这 K 批结果,去除重复物品,送入精排层。
### 3.2 计算开销控制
K 通常取 3~5 个,因此检索开销是单一的 K 倍。可通过以下方式优化:
- **流量分层**:活跃用户使用全量 K,低活用户降为 1~2。
- **混合召回**:多兴趣通道与热门通道、协同过滤通道合并,控制总量。
- **稀疏向量**:对每个兴趣向量存储少量高分物品,避免全库检索。
### 3.3 上下文匹配问题
在许多场景中,用户当前意图明确(例如搜索了“插座”)。可以借助**目标商品/上下文**来动态选择最相关的兴趣向量进行召回,而不是无差别的K路召回。实践方法有:
- **目标物品导向选择**:用目标物品的嵌入与 K 个兴趣向量计算内积,选最大者。
- **序列模型转向**:用 Transformer 学习一个基于历史+当前需求的动态向量,取代静态多兴趣。
---
## 4. 实战:构建你的多兴趣召回模型
### 4.1 数据准备
需要用户行为序列数据,格式如:`user_id, item_id, timestamp`,并按时间排序。至少需要百万级交互量,负采样时随机抽取未交互的物品。
### 4.2 模型实现(PyTorch 伪代码)
```python
class MultiInterestNetwork(nn.Module):
def __init__(self, item_emb_dim, interest_num, route_iter=3):
super().__init__()
self.item_emb = nn.Embedding(num_items, item_emb_dim)
self.C = item_emb_dim # 胶囊维度
self.K = interest_num
self.route_iter = route_iter
def forward(self, hist_items):
# hist_items: (batch, seq_len)
seq_emb = self.item_emb(hist_items) # (B, N, C)
# 初始化路由权重
B, N, C = seq_emb.shape
logits = torch.zeros(B, N, self.K).to(seq_emb.device)
for t in range(self.route_iter):
route_weights = F.softmax(logits, dim=-1) # (B, N, K)
# 计算胶囊输入
# route_weights 转置并乘物品嵌入
# (B,K,N) @ (B,N,C) -> (B,K,C)
weighted_input = torch.bmm(route_weights.transpose(1,2), seq_emb)
# squash
v = self.squash(weighted_input) # (B, K, C)
if t < self.route_iter - 1:
# 更新 logits: 物品与胶囊的点积
# v: (B,K,C) -> (B,1,K,C) 或重复广播
# seq_emb: (B,N,C) -> (B,N,1,C)
logits += torch.matmul(seq_emb, v.transpose(1,2)) # (B,N,K)
# v 即为 K 个兴趣向量
return v
def squash(self, s):
s_norm = torch.norm(s, dim=-1, keepdim=True)
return (s_norm**2 / (1 + s_norm**2)) * (s / s_norm)