Skip to content

ML设计案例

学习ML系统设计的最佳方式是通过实战案例。本文件深入讲解七个完整的设计:推荐系统、搜索排序、广告点击预测、欺诈检测、内容审核、对话式AI和大规模图像搜索。

  • 每个案例都遵循一致的框架:
    1. 问题定义:我们要构建什么?用户是谁?约束条件是什么?
    2. 数据:我们有哪些数据?如何收集?如何标注?
    3. 特征:模型需要哪些特征?
    4. 模型:什么架构和训练方法?
    5. 推理服务:如何部署和提供模型服务?
    6. 评估:如何衡量成功?
    7. 迭代:随着时间推移我们会做哪些改进?

1. 推荐系统(例如YouTube、Netflix、Spotify)

问题定义

  • 目标:向用户展示他们可能喜欢的内容,最大化参与度(观看时长、收听次数、点击次数)。
  • 规模:10亿+用户,1亿+内容,每秒1万+推荐请求。
  • 延迟:完整推荐流水线<200ms。
  • 关键挑战:候选项空间极其庞大(1亿个内容)。无法在实时情况下对所有内容为所有用户打分。

架构:两阶段流水线

推荐流水线:从1亿个内容缩窄到1000个候选生成,排序到100个,重排到最终展示的20个

1亿个内容 → 候选生成(快速、粗粒度) → 1000个候选
          → 排序(慢速、精确) → 100个排序结果
          → 重排(业务规则) → 向用户展示20个

候选生成

  • 目标:将1亿个内容缩减到约1000个候选。必须快速(<50ms)。
  • 双塔模型:将用户和内容编码到同一嵌入空间中。用户嵌入捕获偏好;内容嵌入捕获内容特征。得分 = 用户与内容嵌入的点积。
  • 训练:在(用户, 正例, 负例)三元组上进行对比学习。正例 = 用户参与过的内容。负例 = 随机内容 + 难负例(用户未参与的热门内容)。
  • 推理服务:预计算所有内容嵌入。请求时:计算用户嵌入,通过ANN搜索(向量数据库中的HNSW)找到1000个最近的内容嵌入。

排序

  • 目标:精确为1000个候选打分。可以花费约100ms。
  • 模型:深度神经网络(MLP或Transformer),利用丰富特征:用户特征(人口统计、历史、上下文)、内容特征(内容本身、热度、新鲜度)以及交叉特征(用户-内容交互历史、上下文相关性)。
  • 输出:预测的参与概率(点击、观看50%以上、点赞、分享)。多个目标可组合:\(\text{score} = w_1 \cdot P(\text{click}) + w_2 \cdot P(\text{watch}) + w_3 \cdot P(\text{like})\)

重排

  • 应用业务规则:多样性(不展示同一创作者的5个视频)、新鲜度(提升新内容)、安全(过滤被标记的内容)和个性化探索(展示一些用户可能发现的低排位内容)。

封底估算

  • 内容嵌入索引:1亿个内容 × 256维 × float16 = 50 GB。HNSW索引增加约2倍开销 → 约100 GB。可放入一台128 GB RAM的单机,或分片到4台×32 GB的机器上。

  • 用户嵌入计算:每个用户约5ms(在用户特征上运行小型MLP)。在10K QPS下,需要约50个模型副本来处理负载。

  • ANN搜索:使用HNSW从1亿向量中检索top-1000约需2ms。在10K QPS下,每个索引副本处理约500 QPS → 需要20个副本。

  • 排序模型:1000个候选 × 每个候选约0.1ms = 每个请求100ms。在10K QPS下,需要每秒1000 GPU-秒 → 仅排序就需要约10张A10G GPU。

  • 总基础设施:约20个嵌入索引副本 + 约50个用户嵌入服务器 + 约10张排序GPU + 缓存 + 负载均衡器。成本:按云价格约\(5万-\)10万/月。

冷启动

  • 新用户(无历史数据):使用人口统计特征、设备/位置上下文以及基于热度的推荐。5-10次交互后,切换到个性化模型。

  • 新内容(无参与数据):使用基于内容的特征(标题、描述、缩略图嵌入)。分配探索预算:向一部分用户展示新内容以快速收集参与数据。在推广期后仍无参与的内容被降级。

  • 冷启动是一个系统问题:特征存储必须优雅地处理缺失特征(返回默认值而非错误)。模型必须在训练时接受缺失特征(在用户历史特征上使用dropout来模拟新用户)。

评估

  • 离线:在留出集上的NDCG(归一化折损累积增益)、recall@K、precision@K。
  • 在线:A/B测试衡量观看时长、DAU、留存率。进行长期A/B测试(数周)以捕捉短期测试遗漏的对用户留存的影响。

2. 搜索排序(例如Google、Bing)

问题定义

  • 目标:给定用户查询,从数十亿文档的语料库中返回最相关的结果。
  • 延迟:总计<500ms(100ms检索 + 200ms排序 + 100ms渲染 + 开销)。

架构:查询理解 → 检索 → 排序

查询理解

  • 在检索之前,处理原始查询以改善结果:

  • 拼写纠正:"reccomendation systm" → "recommendation system"。使用编辑距离模型或基于搜索日志中(错误, 正确)词对训练的seq2seq模型。

  • 查询扩展:添加相关词以提高召回。"Python ML" → "Python machine learning scikit-learn pytorch"。使用同义词词典、词嵌入或LLM来生成扩展。

  • 意图分类:确定用户想要什么。"买Nike鞋"是交易型(显示产品页面)。"反向传播如何工作"是信息型(显示文章)。"facebook.com"是导航型(直接跳转到该网站)。不同意图应触发不同的检索策略和结果布局。

  • 实体识别:从查询中提取实体。"时代广场附近最好的餐厅" → 位置:"时代广场",实体类型:"餐厅"。路由到位置感知的搜索流水线。

检索

  • BM25(传统方法):使用倒排索引的词匹配检索。快速,对关键词查询有效。无语义理解("狗粮"不匹配"犬类营养")。

  • 稠密检索:将查询和文档编码为嵌入(使用双编码器如DPR或ColBERT)。通过ANN搜索检索。捕获语义相似性("狗粮"匹配"犬类营养")。比BM25慢,但对自然语言查询效果更好。

  • 混合检索:结合BM25和稠密检索。BM25找到精确关键词匹配;稠密检索找到语义匹配。合并并去重。取两者之长。

排序

  • 学习排序(Learning to Rank):模型为每个(查询, 文档)对打分。三种方法:

    • 逐点法(Pointwise):独立预测每个文档的相关性分数。简单但忽略相对排序。
    • 逐对法(Pairwise):预测两个文档中哪个更相关。LambdaMART(梯度提升树)是经典方法。
    • 列表法(Listwise):针对列表级指标(NDCG)直接优化整个排序列表。更复杂但效果最好。
  • 交叉编码器(Cross-encoder):一个Transformer,以[query, document]作为输入,输出相关性分数。比双编码器(独立编码查询和文档)更准确,因为它捕获细粒度交互。但对整个语料库来说太慢——仅用于对检索出的前100-1000个候选进行重排序。

特征

  • 查询特征:查询长度、语言、意图分类(导航、信息、交易)。
  • 文档特征:PageRank、新鲜度、内容质量评分、域名权威度。
  • 查询-文档特征:BM25分数、嵌入相似度、精确匹配数量、历史日志中该(查询, 文档)对的点击率。

3. 广告点击预测

问题定义

  • 目标:预测用户点击广告的概率。这决定了在实时竞价中出价多少。
  • 规模:每秒10万+场拍卖,每场需在10ms内完成预测。
  • 收入影响:点击预测准确度提升0.1%可转化为数百万美元的额外收入。

架构

  • 特征工程是广告系统的核心。特征包括:

    • 用户特征:人口统计、浏览历史、购买历史、设备、位置、时间。
    • 广告特征:创意(图片/文字)、广告主、类别、历史CTR、出价金额。
    • 上下文特征:页面内容、广告位置、设备类型、连接速度。
    • 交叉特征:用户类别×广告类别交互、用户地区×广告活动交互。
  • 模型:历史上使用逻辑回归(简单、快速、可解释)。现代系统使用深度学习:一个DLRM(深度学习推荐模型),对类别特征使用嵌入表,对稠密特征使用MLP。

  • 校准:预测概率必须准确(如果模型说P(点击)=0.05,那么5%的此类展示应该实际被点击)。校准至关重要,因为预测概率直接决定出价金额。

  • 探索-利用:总是展示预测最优的广告长期来看是次优的(你永远不会发现一个新广告可能更好)。Thompson采样或\(\epsilon\)-贪婪探索确保一部分展示分给不太确定的广告以收集数据。

实时竞价

  • 当用户加载页面时,广告拍卖在<100ms内完成:
    1. 发布商向多个广告交易平台发送竞价请求(用户信息、页面上下文)。
    2. 每个广告主的竞价服务器为其广告预测CTR。
    3. 出价 = CTR × 每次点击的价值。出价更高者赢得拍卖。
    4. 获胜广告被展示;如果被点击,广告主付费。

4. 欺诈检测

问题定义

  • 目标:实时检测欺诈交易(信用卡欺诈、账户盗用、虚假评论)。
  • 延迟:<100ms(交易必须在支付处理完成前获得批准或被标记)。
  • 关键挑战:极端类别不平衡(0.1%欺诈率)。假正例阻止合法用户;假负例损失金钱。

架构

欺诈检测流水线:交易→实时特征流水线→ML模型→决策引擎→批准/审核/阻止,人工审核将标签反馈用于重训练

特征

  • 交易特征:金额、币种、商户类别、时间、是否国际交易。
  • 用户特征:账户年龄、平均交易金额、近期交易数量、设备指纹。
  • 速度特征(实时,来自流处理流水线):过去5分钟内的交易次数、过去1小时内不同商户数量、与上一笔交易的地理距离。
  • 图特征:该商户是否与已知欺诈团伙关联?该设备是否与已被标记的账户共享?

模型

  • 梯度提升树(XGBoost、LightGBM)是表格数据欺诈检测的标准。它们处理混合特征类型,可解释(特征重要性),训练快速。

  • 处理不平衡:欠采样多数类、过采样少数类(SMOTE),或在损失函数中使用类别权重。Focal Loss(第8章)降低容易分类的负样本权重。

  • 代价矩阵:假正例(阻止合法交易)有代价(用户挫败感、丢失销售)。假负例(遗漏欺诈)有不同的代价(财务损失)。决策阈值应最小化总期望成本,而非最大化准确率。

人机协同

  • 不确定的预测(模型置信度在0.3到0.7之间)发送给人工审核员。审核员的决定成为重训练的标签。这创建了一个反馈循环:模型随时间改进,因为它看到更多标注的欺诈案例。

5. 内容审核

问题定义

  • 目标:自动检测并从平台中移除有害内容(仇恨言论、暴力、虚假信息、CSAM)。
  • 规模:每天数十亿条帖子(文本、图片、视频)。
  • 挑战:依赖上下文(讽刺、幽默、文化差异)。必须在表达自由和安全之间取得平衡。

架构

  • 多模态分类:为文本、图片和视频分别设立模型,通过融合层组合其信号。

  • 文本审核:微调的语言模型将文本分类到各种类别(骚扰、仇恨言论、虚假信息、垃圾信息)。多语言模型处理100+种语言。

  • 图片审核:视觉模型检测:露骨内容(裸露、暴力)、图片中的文字(OCR + 文本分类器)以及已知有害内容(与已知CSAM数据库进行哈希匹配)。

  • 视频审核:按固定间隔采样帧,对每帧运行图片分类器,结合音频转录(ASR → 文本分类器)。

  • 策略即代码:审核策略以结构化规则定义,将模型输出映射到操作:

if text_model.hate_speech_score > 0.9:
    action = "remove"
elif text_model.hate_speech_score > 0.7:
    action = "human_review"
else:
    action = "allow"
  • 策略频繁变化(新法规、演进的规范)。将策略与模型分离,确保可以在不重训练的情况下部署变更。

主动审核 vs 被动审核

  • 主动审核(发布前):在内容上线前运行分类器。高置信度违规被自动阻止。防止有害内容被看到,但增加发布延迟并存在假正例风险(阻止合法内容)。

  • 被动审核(发布后):内容立即上线。用户可举报违规内容。举报触发分类器 + 人工审核。对发布者延迟更低,但有害内容在被检测前可见。

  • 大多数平台两者都用:对高严重性类别使用主动审核(CSAM:零容忍,发布前阻止),对微妙类别使用被动审核(虚假信息:需要人工判断,举报后审核)。

哈希匹配

  • 对于已知有害内容(CSAM、恐怖主义宣传),使用感知哈希:计算图片/视频的哈希值,该哈希对微小修改(裁剪、调整大小、压缩)具有鲁棒性。与已知有害内容数据库(NCMEC的哈希数据库、GIFCT共享哈希数据库)进行比较。匹配 → 无需分类器即可立即移除。

  • PhotoDNA(Microsoft)是CSAM检测的标准感知哈希。在许多司法管辖区,这是一种法律义务,而不仅仅是技术选择。

封底估算

  • 规模:每天10亿条帖子 = 约12K条帖子/秒。每条帖子需要:文本分类(约5ms)、图像分类(约20ms)、哈希匹配(约1ms)。在12K QPS下:需要约60个文本分类器、约240个图像分类器和约12个哈希匹配器(加上冗余)。

  • 人工审核:如果2%的帖子被标记为需要审核 = 每天2000万条。按每个审核员每天100条计算,需要20万审核员(这就是为什么自动化的准确度很重要:假正例每降低0.1%就节省每天100万条审核量)。

  • 延迟预算:主动审核必须在发布流水线内完成(约500ms)。文本(5ms)+ 图像(20ms)+ 哈希(1ms)+ 开销 = 完全在预算内。视频是例外:即使从10分钟视频中每秒采样1帧,也需要600次分类器调用 → 异步处理。

升级工作流

  • 自动移除 → 对申诉进行人工审核 → 专家审核(法律、文化专家)→ 对模糊案例的政策团队。每一级处理更少的案例但具有更多的细微判断。

  • 反馈给模型:人工审核决定是重训练中最高质量的标签。模型和审核员之间的分歧被优先用于主动学习——它们代表模型处理最差的案例。


6. 对话式AI(基于RAG的聊天机器人)

问题定义

  • 目标:一个使用公司产品文档回答用户问题的聊天机器人。
  • 需求:准确(不产生幻觉)、引用来源、处理追问,并保持在产品领域内。

RAG架构:嵌入查询,在向量数据库中搜索相关片段,重排序,与原始查询一起输入LLM生成有依据的响应

架构:检索增强生成(RAG)

用户查询 → 查询嵌入 → 向量搜索(文档) → Top-K片段
用户查询 + 检索到的片段 → LLM → 响应(带引用)

组件

  • 文档摄入:将文档分块并嵌入。分块策略非常关键:

    • 固定大小分块:每N个token(如500)切一块,M个token重叠(如50)。简单、块大小可预测,但可能在句子或段落中间切断,丢失上下文。

    • 语义分块:在段落或章节边界处切分。每个块是一个连贯的信息单元。大小可变(有些块100个token,有些800个),要求检索系统能处理可变长度。

    • 递归分块:尝试在段落边界切分。如果段落太长,在句子边界切分。如果句子太长,在固定大小处切分。在连贯性和大小一致性之间取得最佳平衡。

    • 嵌入:使用文本编码器(如E5、BGE、Cohere embed)将每个块嵌入。存储在向量数据库中。

  • 检索:嵌入用户查询,在向量数据库中搜索\(k\)个最相似的片段(通常\(k=5\)\(10\))。可选地使用交叉编码器重排序以获得更高精度。

  • 生成:构造包含检索到片段作为上下文的提示词:

System: 你是一个有帮助的助手。只基于提供的上下文回答。
如果答案不在上下文中,说"我不知道"。

上下文:
[片段1]
[片段2]
...

用户:{问题}
  • 护栏:防止LLM回答超出产品领域的问题、生成有害内容或与检索到的上下文矛盾。实现方式:输入过滤(拒绝离题查询)、输出过滤(检查响应是否符合检索到的上下文)和宪法式提示(指示模型拒绝某些请求)。

  • 对话记忆:维护最近\(n\)轮对话。将其包含在提示词中,使模型理解追问("价格呢?"→ 需要之前关于哪个产品的上下文)。

查询重写

  • 用户经常提出模糊的追问:"价格呢?"(什么的定价?)。查询重写利用对话历史生成独立查询:

    • 输入:对话历史 + "价格呢?"
    • 重写后:"产品X的企业版定价是多少?"
  • 这个重写后的查询被用于嵌入和在向量数据库中搜索。没有重写,检索会搜索"定价"而没有上下文,返回不相关的片段。

  • 查询重写可以通过小型LLM调用(约50ms)或微调的seq2seq模型(约5ms)完成。

封底估算

  • 文档语料库:1万页,平均每页2000 token = 2000万token。以500 token/块、50重叠 = 约4.4万个块。
  • 嵌入索引:4.4万个块 × 768维 × float16 = 约65 MB。轻松放入内存。即使是1000万个块也约15 GB。
  • 延迟分解:查询嵌入(5ms)+ 向量搜索(2ms)+ 交叉编码器重排序(前50个约20ms)+ LLM生成(500-2000ms)= 总计约600-2100ms。LLM占主导地位。使用流式输出减少感知延迟。
  • 成本:以\(3/100万token(Claude/GPT-4 API)计算,每天1000次查询 × 每次约2K token = 约\)6/天。在大规模下(每天100万次查询),在2张A10G GPU上自托管7B模型(约$50/天),成本降低100倍。

评估

  • 检索质量:Recall@K(top-K片段是否包含答案?)、MRR(平均倒数排名)。
  • 生成质量:事实准确度(响应是否与检索到的上下文匹配?)、有据性(响应是否引用了正确的片段?)、答案相关性。
  • 端到端:用户满意度(赞/踩)、升级到人工客服的比率。

7. 大规模图像搜索

问题定义

  • 目标:给定一张图片,从10亿+图片的语料库中找到视觉相似的图片。
  • 应用:反向图像搜索、商品搜索(拍照→匹配商品)、重复检测。
  • 延迟:<500ms,包含网络往返。

架构

查询图片 → 嵌入模型(ViT/CLIP)→ 512维向量 → ANN搜索 → Top-K结果

嵌入提取

  • 模型:预训练的视觉编码器(ViT、CLIP的图像编码器、DINOv2)。如果需要,在特定领域(时尚、电商、医学影像)上微调。

  • 训练:对比学习(第10章)。正例对 = 同一图像的不同视图(或图像+匹配文本)。负例对 = 随机图像。模型学习为相似图像生成相似嵌入,为不同图像生成不同嵌入。

索引

  • 离线:嵌入所有10亿张图片并构建ANN索引。对于HNSW(文件03),构建索引需要数小时,索引存储在内存中(10亿 × 512维 × float16 + 图开销,约128 GB)。

  • 分片:将索引拆分到多台机器。每台持有一个分片。查询时,并行搜索所有分片并合并top-K结果。

  • 增量更新:新图片(上传、新产品)必须添加到索引中。HNSW支持增量插入而无需重建。向量数据库(Milvus、Pinecone)原生处理此问题。

推理服务

  • 嵌入服务:运行ViT模型的GPU服务器。延迟:每张图片约20ms。批量处理多个查询以提高吞吐量。

  • 搜索服务:ANN索引服务器。延迟:在10亿向量上搜索top-100约10ms(使用HNSW)。

  • 缓存:缓存热门查询的结果。对于重复检测,缓存最近上传图片的嵌入,在搜索完整索引之前先与缓存比较。

评估

  • Precision@K:top-K结果是否真正相似?
  • Recall@K:在语料库中所有真正相似的图片中,有多少在top-K中?
  • 平均准确率均值(mAP):PR曲线下面积。
  • 人工评估:对于主观相似性,人工评分员判断检索到的图片是否相关。

面试框架

  • 当你遇到系统设计问题时,遵循以下框架:

  • 明确需求(2-3分钟):询问规模、延迟、一致性要求和边界情况。"有多少用户?可接受的延迟是多少?故障时如何处理?"

  • 高层设计(5-7分钟):画出主要组件及其交互。从正常流程开始。使用文件01-03中的模式。

  • 深入探讨(15-20分钟):选择最有趣/最具挑战性的组件,详细设计。这是你展示深度的地方。对于ML系统,深入探讨通常是:模型架构、特征流水线或推理架构。

  • 评估和监控(3-5分钟):如何衡量成功?什么可能出错?如何检测和响应问题?

  • 迭代(2-3分钟):如果有更多时间/资源,你会改进什么?这表明你理解权衡并能排定优先级。

  • 面试官看重什么:结构化思维(不急于跳入解决方案)、权衡意识(每个选择都有代价)、实践知识(你实际构建过系统)和沟通能力(你能清晰地解释你的设计吗?)。