直播与短视频

直播与短视频知识分享

短视频推荐算法揭秘:双塔模型、用户行为建模与冷启动策略实战

短视频推荐算法揭秘:双塔模型、用户行为建模与冷启动策略实战

# 短视频推荐算法揭秘:双塔模型、用户行为建模与冷启动策略实战

## 摘要

抖音、快手、视频号的推荐算法决定了内容的生死。本文揭秘工业级推荐系统的核心技术,附带PyTorch实现双塔模型代码。

## 一、工业推荐系统架构

```

用户请求

┌────────────────┐

│ 召回层(Recall) │ ← 从百万候选中选千级

│ 双塔模型 / 多路召回 │

└────────┬───────────────┘

↓ 千级候选

┌────────────────┐

│ 粗排层(Pre-Ranking) │ ← 快速打分,筛选百级

│ 轻量模型(LR/小DNN) │

└────────┬───────────────┘

↓ 百级候选

┌────────────────┐

│ 精排层(Ranking) │ ← 多目标打分,选几十条

│ 多目标DNN(点击/点赞/完播)│

└────────┬───────────────┘

↓ 几十条

┌────────────────┐

│ 重排层(Re-Ranking) │ ← 多样性、去重、策略调整

│ MMR / DPP / 业务规则 │

└────────┬───────────────┘

↓ 几至几十条

推送给用户

```

## 二、双塔模型(Two-Tower Model)

### 2.1 原理

双塔模型将用户和物品分别编码为向量,通过向量相似度(内积/余弦)预测匹配度:

```

用户塔(User Tower) 物品塔(Item Tower)

输入:用户特征 ─→ DNN ─→ 用户向量u ∈ R^d

输入:物品特征 ─→ DNN ─→ 物品向量v ∈ R^d

预测分数 = u · v / (||u|| ||v||) [余弦相似度]

```

**优势**:物品向量可预计算,线上只需计算用户向量与候选物品向量的内积。

### 2.2 PyTorch实现

```python

import torch

import torch.nn as nn

import torch.nn.functional as F

class TwoTowerModel(nn.Module):

def __init__(self, user_feat_dim: int, item_feat_dim: int, hidden_dims: list[int], embed_dim: int):

super().__init__()

# 用户塔

user_layers = []

prev_dim = user_feat_dim

for h in hidden_dims:

user_layers.extend([nn.Linear(prev_dim, h), nn.BatchNorm1d(h), nn.ReLU()])

prev_dim = h

user_layers.append(nn.Linear(prev_dim, embed_dim))

self.user_tower = nn.Sequential(*user_layers)

# 物品塔

item_layers = []

prev_dim = item_feat_dim

for h in hidden_dims:

item_layers.extend([nn.Linear(prev_dim, h), nn.BatchNorm1d(h), nn.ReLU()])

prev_dim = h

item_layers.append(nn.Linear(prev_dim, embed_dim))

self.item_tower = nn.Sequential(*item_layers)

def forward(self, user_feats: torch.Tensor, item_feats: torch.Tensor):

user_emb = self.user_tower(user_feats)

item_emb = self.item_tower(item_feats)

# L2归一化

user_emb = F.normalize(user_emb, dim=-1)

item_emb = F.normalize(item_emb, dim=-1)

# 余弦相似度

logits = torch.sum(user_emb * item_emb, dim=-1)

return logits

def encode_items(self, item_feats: torch.Tensor) -> torch.Tensor:

"""预计算物品向量(离线)"""

with torch.no_grad():

item_emb = self.item_tower(item_feats)

item_emb = F.normalize(item_emb, dim=-1)

return item_emb

def encode_user(self, user_feats: torch.Tensor) -> torch.Tensor:

"""计算用户向量(在线)"""

with torch.no_grad():

user_emb = self.user_tower(user_feats)

user_emb = F.normalize(user_emb, dim=-1)

return user_emb

```

### 2.3 负采样策略

```python

def negative_sampling(batch_users, batch_pos_items, all_items, num_neg=4):

"""每个正样本配4个负样本"""

batch_size = batch_users.size(0)

neg_items = []

for i in range(batch_size):

# 简单负采样:随机采样非正样本

neg = torch.randint(0, len(all_items), (num_neg,))

while neg == batch_pos_items[i]: # 避免采样到正样本

neg = torch.randint(0, len(all_items), (num_neg,))

neg_items.append(neg)

return torch.stack(neg_items, dim=0)

```

## 三、用户行为序列建模

### 3.1 SIM(Sparse Interest Model)

短视频场景用户行为序列极长(可能数千步),SIM通过两阶段处理:

```

阶段1(离线):长序列兴趣提取

用户行为序列(最长5000步)

→ 用搜索单元(Search Unit)提取与目标物品相关的历史行为

→ 输出:百级浓缩行为序列

阶段2(在线):短期+长期兴趣融合

短期兴趣:最近50步行为 → GRU编码

长期兴趣:SIM提取的百级行为 → Attention编码

→ 拼接 → 多目标预测

```

### 3.2 MIMN(Multi-Channel User Interest Memory Network)

```python

class MIMN(nn.Module):

"""多通道用户兴趣记忆网络"""

def __init__(self, memory_size=128, memory_dim=64):

super().__init__()

# 记忆单元(类NTM记忆网络)

self.memory = nn.Parameter(torch.randn(memory_size, memory_dim))

self.write_gate = nn.GRUCell(memory_dim, memory_dim)

self.read_attention = nn.MultiheadAttention(memory_dim, num_heads=4)

def forward(self, behavior_seq: torch.Tensor) -> torch.Tensor:

batch_size, seq_len, feat_dim = behavior_seq.shape

memory = self.memory.unsqueeze(0).expand(batch_size, -1, -1)

for t in range(seq_len):

# 读操作:用当前行为查询记忆

query = behavior_seq[:, t:t+1, :] # [B, 1, D]

attended, _ = self.read_attention(query, memory, memory)

# 写操作:更新记忆

memory = self.write_gate(attended.squeeze(1), memory)

memory = F.normalize(memory, dim=-1)

# 返回压缩后的用户兴趣表示

user_interest = memory.mean(dim=1) # [B, D]

return user_interest

```

## 四、多目标精排

### 4.1 为什么需要多目标

短视频平台需要同时优化多个指标:

| 目标 | 说明 | 权重示例 |

|------|------|----------|

| 点击率(CTR) | 用户是否点击 | 0.3 |

| 点赞率 | 用户是否点赞 | 0.2 |

| 评论率 | 用户是否评论 | 0.15 |

| 完播率 | 视频是否看完 | 0.25 |

| 分享率 | 用户是否分享 | 0.1 |

### 4.2 MMoE(Multi-gate Mixture-of-Experts)

```python

class MMoE(nn.Module):

"""多目标专家混合模型"""

def __init__(self, input_dim: int, num_experts: int, expert_dim: int, num_tasks: int):

super().__init__()

# 共享专家

self.experts = nn.ModuleList([

nn.Sequential(

nn.Linear(input_dim, expert_dim),

nn.ReLU(),

nn.Linear(expert_dim, expert_dim),

nn.ReLU(),

) for _ in range(num_experts)

])

# 每个任务一个门控网络

self.gates = nn.ModuleList([

nn.Linear(input_dim, num_experts) for _ in range(num_tasks)

])

# 每个任务一个塔

self.task_towers = nn.ModuleList([

nn.Sequential(

nn.Linear(expert_dim, 64),

nn.ReLU(),

nn.Linear(64, 1),

nn.Sigmoid(),

) for _ in range(num_tasks)

])

def forward(self, x: torch.Tensor) -> list[torch.Tensor]:

# 专家输出

expert_outputs = [expert(x) for expert in self.experts]

expert_outputs = torch.stack(expert_outputs, dim=1) # [B, num_experts, D]

task_outputs = []

for gate, tower in zip(self.gates, self.task_towers):

# 门控权重

gate_weights = F.softmax(gate(x), dim=-1).unsqueeze(-1) # [B, num_experts, 1]

# 加权专家输出

task_input = (gate_weights * expert_outputs).sum(dim=1) # [B, D]

task_output = tower(task_input)

task_outputs.append(task_output)

return task_outputs # [CTR, 点赞率, 评论率, 完播率, 分享率]

```

## 五、冷启动策略

### 5.1 新用户冷启动

| 策略 | 方法 | 效果 |

|------|------|------|

| 兴趣标签引导 | 注册时选择兴趣标签 | 解决50% |

| 热门内容兜底 | 无行为时推热门 | 解决30% |

| 探索策略(ε-greedy) | 10%流量推未知内容 | 解决15% |

| 社交关系引入 | 通讯录/关注列表 | 解决5% |

### 5.2 新内容冷启动

```python

def cold_start_boost(new_item_score: float, item_age_hours: float) -> float:

"""新内容冷启动加分(时间衰减)"""

# 新内容在24小时内获得2倍曝光机会

if item_age_hours < 24:

boost = 2.0 * (1 - item_age_hours / 24)

return new_item_score * (1 + boost)

return new_item_score

class ExplorationPolicy:

"""Bandit-based探索策略"""

def __init__(self, num_items: int):

self.counts = torch.zeros(num_items) # 每个物品的展示次数

self.values = torch.zeros(num_items) # 每个物品的平均回报

def select(self, user_emb: torch.Tensor, item_embs: torch.Tensor) -> int:

"""UCB算法选择物品"""

# UCB = 预期回报 + 探索奖励

ucb_scores = self.values + torch.sqrt(2 * torch.log(self.counts.sum()) / (self.counts + 1e-5))

return ucb_scores.argmax().item()

def update(self, item_id: int, reward: float):

"""更新Bandit统计"""

self.counts[item_id] += 1

n = self.counts[item_id]

old_value = self.values[item_id]

self.values[item_id] = old_value + (reward - old_value) / n

```

## 六、A/B测试与评估

### 6.1 核心指标

```

线上A/B测试指标

├── 留存指标

│ ├── 次日留存率

│ ├── 7日留存率

│ └── 30日留存率

├── 互动指标

│ ├── 人均播放时长

│ ├── 点赞率

│ ├── 评论率

│ └── 分享率

└── 系统指标

├── 召回率(Recall@100)

├── 精排AUC

└── 多样性指标(ILAD)

```

### 6.2 离线评估代码

```python

def evaluate_recall(model, test_loader, item_embs, k=100):

"""评估召回率@K"""

model.eval()

total_recall = 0

total_samples = 0

with torch.no_grad():

for user_feats, pos_items in test_loader:

user_emb = model.encode_user(user_feats) # [B, D]

# 计算所有物品的分数

scores = torch.matmul(user_emb, item_embs.T) # [B, num_items]

_, topk = torch.topk(scores, k, dim=-1) # [B, K]

for i, pos in enumerate(pos_items):

if pos in topk[i]:

total_recall += 1

total_samples += 1

return total_recall / total_samples

```

## 总结

短视频推荐算法是一个多阶段漏斗系统:双塔召回从百万候选选千级,多目标精排选几十至百条,重排保证多样性。行为序列建模(SIM/MIMN)是长期兴趣捕捉的关键,冷启动策略决定新内容和新用户的生存空间。完整系统需离线训练和线上serving协同设计。

---

*本文由北科信息日采集系统自动生成,发布日期:2026-05-05*