共计 2382 个字符,预计需要花费 6 分钟才能阅读完成。
什么是成对比较数据
在推荐系统、体育赛事排名等场景中,我们经常遇到这样的数据:用户对物品 A 和 B 表现出明确的偏好(如 A >B),但缺乏绝对评分。这类数据称为成对比较数据(pairwise comparison),它的核心特点是:

- 只记录相对偏好关系
- 不依赖绝对评分尺度
- 天然适合排名学习任务
Bradley-Terry 模型数学原理
基本概率公式
Bradley-Terry 模型假设每个物品 $i$ 有一个潜在强度参数 $\theta_i$,物品 $i$ 战胜物品 $j$ 的概率表示为:
$$ P(i > j) = \frac{e^{\theta_i}}{e^{\theta_i} + e^{\theta_j}} $$
损失函数推导
给定观测到的对战结果集合 $D={(i,j)}$,通过极大似然估计求解参数。对数似然函数为:
$$ \mathcal{L}(\theta) = \sum_{(i,j) \in D} \left[\theta_i – \log(e^{\theta_i} + e^{\theta_j}) \right] $$
对应的损失函数(负对数似然)为:
$$ \mathcal{J}(\theta) = -\mathcal{L}(\theta) $$
Python 实现详解
数据预处理
首先需要将原始比较数据转换为配对矩阵。假设有 N 个物品,构建 N×N 的矩阵 $C$,其中 $C_{ij}$ 表示 i 战胜 j 的次数:
import torch
def build_pair_matrix(raw_data, num_items):
"""
raw_data: List of (winner_idx, loser_idx) tuples
num_items: Total number of distinct items
"""
C = torch.zeros((num_items, num_items))
for winner, loser in raw_data:
C[winner, loser] += 1
return C
损失函数实现
关键点:使用 log-sum-exp 避免数值溢出
class BradleyTerryLoss(torch.nn.Module):
def __init__(self, reg_lambda=0.01):
super().__init__()
self.reg_lambda = reg_lambda
def forward(self, theta, C):
"""
theta: (N,) tensor of strength parameters
C: (N,N) count matrix where C_ij = # times i beat j
"""
N = theta.shape[0]
# Pairwise differences
theta_diff = theta.view(N,1) - theta.view(1,N) # (N,N)
# Log likelihood terms
log_terms = torch.log1p(torch.exp(-theta_diff)) # stable log(1 + exp(-x))
# Apply mask for observed pairs
valid_pairs = (C > 0)
loss = -torch.sum(C[valid_pairs] * (theta_diff[valid_pairs] - log_terms[valid_pairs]))
# L2 regularization
reg_loss = self.reg_lambda * torch.sum(theta**2)
return loss + reg_loss
完整训练示例
# Hyperparameters
learning_rate = 0.1
num_epochs = 100
# Initialize parameters
theta = torch.randn(num_items, requires_grad=True)
# Optimizer
optimizer = torch.optim.Adam([theta], lr=learning_rate)
loss_fn = BradleyTerryLoss(reg_lambda=0.1)
# Training loop
for epoch in range(num_epochs):
optimizer.zero_grad()
loss = loss_fn(theta, C)
loss.backward()
optimizer.step()
if epoch % 10 == 0:
print(f"Epoch {epoch}, loss={loss.item():.4f}")
生产环境优化建议
超参数调优策略
- 学习率与正则化系数的平衡:
- 高学习率 (>0.1) 需要配合更强的正则化
- 使用验证集监控排名指标(如 NDCG)
- 早停策略:当验证损失连续 3 个 epoch 不下降时终止训练
分布式训练技巧
- 按物品 ID 哈希分片比较数据
- 参数服务器架构:
- 中心节点维护 theta 参数
- 工作节点计算梯度分片
常见问题解决方案
数值不稳定性
症状:出现 NaN 或极大 loss 值
解决方法:
1. 对 theta 初始化使用较小的值(如 N(0,0.1))
2. 实现时始终使用 log-sum-exp 形式
3. 添加梯度裁剪(gradient clipping)
稀疏数据处理
当物品数量很大时(>1M):
– 使用稀疏矩阵存储 C
– 采用负采样技术
– 实现分 batch 训练
延伸思考
开放性问题
- 如何将 Bradley-Terry 模型扩展到考虑平局(tie)的情况?
- 当比较数据包含上下文信息(如用户特征)时,应该如何扩展模型?
推荐阅读
- Sparse Pairwise Ranking (ICML 2021)
- Scalable Bradley-Terry (NeurIPS 2019)
实战心得
在电商推荐系统中实施 Bradley-Terry 模型时,我们发现物品冷启动是个关键挑战。通过引入先验强度(如新品使用类目平均分),显著提升了前几周的推荐效果。另一个意外收获是:模型学到的 theta 参数可以直接作为物品质量指标,用于库存管理等其他业务场景。
