共计 2551 个字符,预计需要花费 7 分钟才能阅读完成。
推荐系统中的表征学习痛点
在推荐系统领域,用户和物品的表征学习(Representation Learning)一直是核心任务。传统协同过滤(Collaborative Filtering)方法面临两大主要挑战:

-
数据稀疏性(Data Sparsity):用户 - 物品交互矩阵通常非常稀疏,大多数用户只与极少数物品有过交互。例如在电商场景中,一个用户可能只点击过不到 0.1% 的商品。
-
冷启动问题(Cold Start):新用户或新物品由于缺乏历史交互数据,难以获得有效的表征。这个问题在内容推荐、新闻推荐等场景尤为突出。
CMC 对比学习算法原理
对比学习(Contrastive Learning)通过构建正负样本对(Positive/Negative Pairs)来学习表征,其核心思想是 ” 拉近正样本,推远负样本 ”。CMC(Contrastive Multiview Coding)是其中一种经典方法,相比传统 BPR(Bayesian Personalized Ranking)和 NCF(Neural Collaborative Filtering)有以下优势:
- 更鲁棒的表征:通过多视角对比(如用户点击序列和用户画像)增强模型泛化能力
- 更高效的负采样:可利用批次内其他样本作为隐式负样本(in-batch negatives)
- 数学表达简洁:损失函数形式为
$$\mathcal{L}{CMC} = -\log\frac{\exp(s$$}/\tau)}{\sum_{k=1}^N \exp(s_{i,k}/\tau)
其中 $\tau$ 是温度系数,控制分布平滑度
PyTorch 完整实现
1. 数据加载与负采样
# 重要性采样负采样器(Importance Sampling Negative Sampler)class ImportanceSampler:
def __init__(self, item_popularity, num_negatives=4):
self.pop_dist = np.power(item_popularity, 0.75) # 平滑处理
self.pop_dist /= np.sum(self.pop_dist)
self.num_neg = num_negatives
def sample(self, positive_items):
negatives = []
for _ in range(self.num_neg):
neg = np.random.choice(len(self.pop_dist),
size=len(positive_items),
p=self.pop_dist)
negatives.append(neg)
return np.stack(negatives, axis=1) # [batch_size, num_neg]
2. 双塔模型结构
class CMCModel(nn.Module):
def __init__(self, user_dim, item_dim, hidden_size=64, temp=0.1):
super().__init__()
self.user_tower = nn.Sequential(nn.Linear(user_dim, hidden_size),
nn.ReLU(),
nn.Linear(hidden_size, hidden_size)
)
self.item_tower = nn.Sequential(nn.Linear(item_dim, hidden_size),
nn.ReLU(),
nn.Linear(hidden_size, hidden_size)
)
self.tau = temp # 温度系数 τ
def forward(self, user_feat, item_feat):
user_emb = F.normalize(self.user_tower(user_feat), dim=1)
item_emb = F.normalize(self.item_tower(item_feat), dim=1)
logits = user_emb @ item_emb.t() / self.tau
return logits
3. 梯度累积实现
accum_steps = 4 # 累积 4 个 batch 的梯度
optimizer.zero_grad()
for i, (users, items) in enumerate(train_loader):
logits = model(users, items)
loss = criterion(logits)
loss = loss / accum_steps # 损失归一化
loss.backward()
if (i+1) % accum_steps == 0:
optimizer.step()
optimizer.zero_grad()
性能优化实战
Batch Size 影响实验
| Batch Size | InfoNCE Loss | AUC 提升 |
|---|---|---|
| 256 | 1.32 | +8.2% |
| 512 | 1.18 | +10.5% |
| 1024 | 1.05 | +12.1% |
结论:增大 batch size 能提供更多隐式负样本,但需平衡显存消耗
混合精度训练要点
- 使用
torch.cuda.amp自动管理精度 - 对 embedding 层保持 FP32 精度
- 梯度缩放(Gradient Scaling)防止下溢
scaler = torch.cuda.amp.GradScaler()
with torch.cuda.amp.autocast():
logits = model(users, items)
loss = criterion(logits)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
生产环境注意事项
特征归一化
- 用户 / 物品特征需做 Z -score 标准化
- embedding 层输出必须 L2 归一化
- 实验发现:未归一化特征会使 AUC 下降 3 -5%
Embedding 缓存策略
- 离线预计算全量物品 embedding
- 用户 embedding 实时计算
- 使用 FAISS 建立 ANN 索引
- 缓存热点用户 embedding(TTL=15min)
开放性问题
如何将 CMC 与图神经网络(Graph Neural Networks)结合来提升跨域推荐效果?现有两种思路:
- 在 GNN 的消息传递过程中加入对比损失
- 使用 CMC 学习节点初始表征后再输入 GNN
期待与各位同行探讨更优方案!
正文完
