大模型在推荐系统中的应用:LLM 做特征工程、排序模型蒸馏、生成式推荐
关键词:LLM 推荐系统、特征工程、知识蒸馏、GenRec、排序模型
推荐系统遇到了什么瓶颈,LLM 能解决什么?
推荐系统是一个天花板极高的领域。传统推荐系统(协同过滤、矩阵分解、DeepFM、DCN 等)在过去十年里积累了极其成熟的工程体系,但到了 2025-2026 年,几个本质问题越来越突出:
- 冷启动:新用户、新商品只有 ID 特征,没有行为数据,协同过滤完全失效。淘宝 2024 年双十一期间,新注册用户的首单推荐转化率只有老用户的 1/8。
- 长尾内容:头部 20% 的商品占了 80% 的曝光,长尾内容曝光不足,但用户对长尾的个性化需求反而更高——你搜"复古机械键盘",传统模型优先给你推 Cherry 的 MX 系列,但你可能想找的是小众品牌。
- 语义理解浅:传统模型只能看到用户点击了哪些商品 ID,但看不到用户为什么点击——是"买了 iPhone 需要配件",还是"在对比竞品"。
- 跨域迁移难:用户在抖音刷了 30 分钟美食视频,这个兴趣偏好很难迁移到美团外卖场景,因为没有共享特征。
LLM 的入场不是要替代整个推荐系统,而是补上传统模型最大的短板:语义理解能力。这就引出了 LLM 在推荐系统中的三个层次应用。
第一层:特征工程增强——让推荐模型"看懂"商品
传统推荐模型的特征主要来自三类:用户 ID 类特征(性别、年龄、地域)、商品 ID 类特征(类目、品牌、价格区间)、行为序列特征(最近 30 天点击的商品 ID 序列)。这些特征的问题是:数字和 ID 是冷冰冰的,模型不知道这些 ID 背后代表什么。
原理:文本 → 语义特征向量
LLM 做特征工程的核心思路是:用 LLM 把非结构化文本转化为语义特征,然后作为 Embedding 或标签特征喂入传统推荐模型。
┌──────────────┐
│ 商品原始数据 │
│ (标题/描述/ │
│ 图片/属性) │
└──────┬───────┘
│
▼
┌──────────────┐
│ LLM 离线 Batch │
│ 提取语义特征 │
│ (GPT-4o-mini) │
└──────┬───────┘
│
┌──────────┼──────────┐
▼ ▼ ▼
┌──────────┐┌──────────┐┌──────────┐
│ 类目标签 ││使用场景 ││用户群体 │
│(召回用) ││(排序用) ││(兜底用) │
└──────────┘└──────────┘└──────────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────┐
│ 传统推荐模型 (DeepFM/DCN) │
│ ID特征 + 行为特征 + 语义特征 │
└──────────────────────────────────┘# 示例:用 LLM 提取商品语义特征
import openai
def extract_item_features(item_title, item_desc):
prompt = f"""请从以下商品信息中提取特征标签,以 JSON 格式输出:
商品标题:{item_title}
商品描述:{item_desc}
输出格式:
{{
"category_tags": ["标签1", "标签2", ...],
"use_case": "使用场景描述",
"target_user": "目标用户群体",
"style_attributes": ["风格1", "风格2", ...],
"price_segment": "高端/中端/性价比",
"related_brands": ["相关品牌"]
}}
"""
response = openai.chat.completions.create(
model="gpt-4o-mini", # 用轻量模型降成本,单次约 0.01 元
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"}
)
return response.choices[0].message.content
# 实际落地时,离线 batch 处理所有商品,结果存入 Redis/向量库
# 在线推理时直接查,不走 LLM关键点:LLM 提取的特征不是在线推理时实时生成,而是离线 batch 处理,作为训练阶段的补充特征。在线阶段直接查缓存,这样延迟和成本都可控。
真实落地数据
某电商公司(日活 5000 万)在 2024 年 Q4 做了这个方案:
| 指标 | 改动前 | 改动后 | 变化 |
|---|---|---|---|
| 商品池大小 | 2 亿 | 2 亿 | - |
| 离线 batch 耗时 | N/A | 48 小时(300 台 A100) | 每两周一次 |
| 存储成本 | N/A | 额外 200GB Redis | 可接受 |
| 召回阶段 CTR | 3.2% | 3.8% | +18.75% |
| 冷启动商品曝光 | 0.5 次/天 | 2.3 次/天 | +360% |
| 长尾商品转化率 | 0.8% | 1.5% | +87.5% |
踩坑记录
坑 1:LLM 对中文商品名理解有偏差。某次上线后,发现"儿童安全座椅"被 LLM 打上了"家居用品"标签,导致在母婴频道不显示。原因:LLM 的预训练数据中"座椅"和"家居"关联太强。解法:在 prompt 里加硬约束 "category_tags" 必须从预定义类目树中选择,不能自己编。
坑 2:LLM 输出的标签不稳定。同一个商品,同一批数据,跑两次 batch 出来的标签可能不一样(temperature=0 也不完全确定)。解法:每次跑两轮取交集,再人工抽检 1% 的样本覆盖。
第二层:排序模型蒸馏——用大模型"教"小模型
排序阶段是推荐系统的核心环节。粗排从几千个候选集里筛选出几百个,精排再从几百个里选出 Top-K 展示给用户。精排模型要求毫秒级响应和高精度,传统 DeepFM 或 DCN 模型虽然快,但受限于特征维度,精度上限有限。
原理:Teacher-Student 蒸馏
┌─────────────────────┐
│ Teacher (LLM) │
│ 用全部特征打分 │
│ 延迟 200-500ms │
└──────┬──────────────┘
│ soft labels
▼
┌──────────────────────────────────┐
│ Distillation Loss │
│ α * KL(teacher || student) + │
│ (1-α) * CE(student, label) │
└──────────────────────────────────┘
│
▼
┌─────────────────────┐
│ Student (DeepFM) │
│ 只用 ID 特征打分 │
│ 延迟 < 5ms │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ 线上部署 (毫秒级) │
└─────────────────────┘蒸馏的思路:把 LLM 作为 teacher 模型,对候选集打分,然后用打分结果训练 student 模型(轻量级的精排模型)。student 模型学到的是 LLM 的排序偏好,而不是直接复制 LLM 的输出。
# 排序模型蒸馏的伪代码
import numpy as np
from sklearn.metrics import ndcg_score
def distill_ranking_model(teacher_model, student_model, candidates, k=20):
"""
teacher: LLM 打分
student: 轻量排序模型
candidates: 候选商品列表
"""
# 1. teacher 打分(离线 batch 执行)
teacher_scores = []
for batch in chunk(candidates, size=32):
scores = teacher_model.score(batch) # 调用 LLM 打分
teacher_scores.extend(scores)
# 2. 计算 teacher 的排序列表(作为 ground truth)
ranked_indices = np.argsort(teacher_scores)[::-1]
ideal_ranking = candidates[ranked_indices]
# 3. 训练 student 模型,损失函数 = NDCG loss + 交叉熵
# student 模型的输入是传统特征(ID、类目、价格等)
# 输出是对每个候选的预测分数
def distillation_loss(student_logits, teacher_logits, labels):
# KL 散度:让 student 的分数分布接近 teacher
kl_loss = kl_divergence(
softmax(student_logits / temperature),
softmax(teacher_logits / temperature)
)
# 交叉熵:保留原始 label 信号
ce_loss = cross_entropy(student_logits, labels)
return alpha * kl_loss + (1 - alpha) * ce_loss
# 4. 训练完成后,student 模型部署到线上
# teacher 模型只在离线蒸馏阶段使用
student_model.train(distillation_loss)
return student_model实际效果与踩坑
真实数据:某短视频推荐团队(2024 年 Q3)用 GPT-4 做 teacher,蒸馏出 3 层 MLP 的 student 模型:
| 指标 | 基线 (DeepFM) | 蒸馏后 | 提升 |
|---|---|---|---|
| 精排 AUC | 0.742 | 0.773 | +4.2% |
| 线上 CTR | 4.1% | 4.5% | +9.8% |
| 单次推理延迟 | 2ms | 2ms | 持平 |
| 模型大小 | 50MB | 50MB | 持平 |
坑 3:Teacher 模型必须先做 Calibration。直接拿 LLM 的 logits 做 distillation label,如果 LLM 对正样本的打分普遍偏高(比如所有商品都打 0.8-0.9),student 学到的只是"都差不多",等于没学。解法:对 teacher 的分数做 temperature scaling——在 validation set 上调整 temperature 参数,使 teacher 的 ECE(Expected Calibration Error)降到 0.05 以下。
坑 4:蒸馏样本的构造。如果只拿 LLM 对候选集的打分做训练,等于让 student 学的是"LLM 认为哪些商品好",而不是"用户实际会点哪些"。解法:蒸馏损失函数里一定要保留交叉熵项(CE loss),让 student 也看到真实的用户点击信号。
第三层:生成式推荐(GenRec)——让 LLM 直接给推荐
GenRec 是 2025-2026 年最激进的方向:直接让 LLM 输出推荐结果,不走传统推荐链路。用户的行为序列作为 prompt 输入,LLM 输出推荐商品列表和理由。
架构对比
传统推荐流水线:
用户 → 召回(几万→几千) → 粗排(几千→几百) → 精排(几百→几十) → 重排 → 展示
↑
│
LLM 做特征增强或蒸馏
GenRec 流水线:
用户 → LLM 直接输出 Top-K 推荐
(一次推理,同时做了召回+排序)# 生成式推荐的简单实现
def genrec_recommend(user_profile, recent_actions, candidate_items):
prompt = f"""你是一个电商推荐助手。根据用户信息和最近行为,推荐 5 个商品。
用户画像:{user_profile}
最近 30 天行为(点击/购买/收藏):
{recent_actions}
候选商品列表(含标题和类目):
{candidate_items}
请按以下格式输出推荐结果:
1. [商品 ID] - 商品标题 - 推荐理由
2. ...(共 5 个)
注意:
- 推荐理由必须基于用户行为,不能虚构
- 优先推荐用户未点击过但相关度高的商品
- 包含 1-2 个长尾商品,避免全是热门商品
"""
response = openai.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}],
temperature=0.3
)
return parse_recommendations(response.choices[0].message.content)优劣势对比
| 维度 | 传统推荐 | GenRec | 说明 |
|---|---|---|---|
| 冷启动 | ❌ 需要大量行为数据 | ✅ 从描述就能推 | 新用户 1 条行为都没有也能推 |
| 长尾覆盖 | ❌ 头部集中 | ✅ 语义匹配 | 小众商品只要有描述就能被推 |
| 可解释性 | ❌ 黑盒 | ✅ 每次都有理由 | 但理由可能幻觉 |
| 延迟 | 2-5ms | 200-500ms | 差两个数量级 |
| 单次成本 | ¥0.00001 | ¥0.01-0.05 | 差 1000-5000 倍 |
| 一致性 | ✅ 确定 | ❌ 非确定性 | 同样的输入可能不同输出 |
| 吞吐量 | 10 万 QPS/台 | 50 QPS/台 | 完全不在一个量级 |
踩坑记录
坑 5:GenRec 的幻觉推荐。某次测试中,LLM 给用户推荐了"iPhone 17 保护壳",但 iPhone 17 还没发布。用户看到后投诉"虚假推荐"。原因:LLM 的知识截止日期是 2024 年,它会"编造"不存在的商品。解法:候选集必须用 candidate_items 参数硬约束,LLM 只能从候选列表里选,不能自己编。
坑 6:GenRec 的推荐结果不稳定。XA 用户今天搜了"机械键盘",GenRec 推荐了 3 个键盘;明天同样的行为,GenRec 推荐了 2 个键盘 + 1 个鼠标垫。原因:temperature=0 对 LLM 也不是严格确定。解法:对同一个用户,缓存 GenRec 的结果 24 小时,不重复生成。
大厂落地策略
2025-2026 年大厂的主流做法是:GenRec 不做主链路,只做兜底和增强。传统推荐链路正常跑,当遇到冷启动用户或长尾请求时,fallback 到 GenRec。
用户请求 → 传统推荐链路是否命中?
├── 是 → 用传统推荐结果(正常返回)
└── 否 → 触发 GenRec 兜底
├── 冷启动用户(行为 < 10 条)
└── 长尾商品覆盖(候选集里头部商品占比 > 80%)第三层补充:多模态推荐与时序感知
多模态推荐
LLM 不仅看文本,还能看图片。服装推荐时,LLM 可以同时理解商品图片的风格和用户历史购买的款式。
# 多模态特征提取
def multimodal_item_features(image_url, title, desc):
prompt = f"""分析这张商品图片和文本信息,提取风格特征。
图片:{image_url}
标题:{title}
描述:{desc}
请输出:
1. 风格标签(如:简约/复古/运动/商务)
2. 颜色方案(如:暖色调/冷色调/黑白灰)
3. 适用场景(如:通勤/约会/运动)
4. 与以下用户历史购买风格的相似度计算逻辑
"""
response = openai.chat.completions.create(
model="gpt-4o", # 多模态模型
messages=[{"role": "user", "content": prompt}],
)
return response.choices[0].message.content时序感知
用户兴趣随时间变化,LLM 需要理解行为序列的时间权重。最近 3 天的行为比 30 天前的重要得多。
# 时序加权的行为序列
def build_time_weighted_sequence(actions, decay_factor=0.9):
"""
actions: [(timestamp, item_id, action_type), ...]
decay_factor: 每天衰减 0.9
"""
today = datetime.now()
weighted = []
for ts, item_id, action_type in actions:
days_ago = (today - ts).days
weight = decay_factor ** days_ago
weighted.append((item_id, action_type, weight))
# 按权重降序,取前 50 条
weighted.sort(key=lambda x: x[2], reverse=True)
return weighted[:50]面试高频问题
Q1:LLM 做推荐的成本怎么控制?
答:分三层控制。第一层,离线 batch 做特征工程,用 GPT-4o-mini 级别模型,单次 0.01 元,两亿商品全量跑一次约 20 万,但每两周跑一次就够了。第二层,蒸馏只做一次,teacher 打分成本是一次性的。第三层,GenRec 只在冷启动用户上触发,覆盖率控制在 5% 以内,月均成本可控在 3 万以内。
Q2:GenRec 和传统推荐怎么选?
答:不要二选一。最务实的路径是:先做特征工程增强(离线 batch,ROI 最高),再考虑蒸馏,最后才上 GenRec。不要一上来就搞 GenRec,成本会让你怀疑人生。面试官问这个,重点听你讲 ROI 权衡和落地路径,不是听你吹 GenRec 多牛。
Q3:推荐系统的可解释性怎么落地?
答:LLM 生成的推荐理由不能有幻觉。需要做 Factuality Check——推荐理由中提到的商品属性必须与被推荐商品的实际属性一致。具体做法:生成推荐理由后,用另一个 LLM 做评判,检查理由中的每个断言是否能在商品属性中找到依据。如果发现幻觉,回退到简单的"为您推荐"模板。
总结:P7+/P8 该掌握的核心认知
- LLM 不自作推荐,而是作为特征增强模块嵌入传统推荐流水线,这是 ROI 最高的路径。2025 年趋势明确:全量 GenRec 的时机还没到。
- 蒸馏的关键在 teacher 的 Calibration,不在 student 的架构。花 80% 的精力把 teacher 的分数校准好,student 自然学得更好。
- 冷启动的终极解法不是 GenRec,而是多模态语义特征 + 跨域迁移。GenRec 只是兜底方案。
- 面试时不要只讲概念:能画出三层架构的时序图、能说出具体的成本数据和 CTR 提升幅度、能讲出真实踩过的坑,比背论文标题有用得多。
参考论文:P5(Google, 2022)、RecLLM(Google, 2023)、M6-Rec(阿里, 2023)、LLM for RecSys 综述(2024, ACM Computing Surveys)