<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Ray on 方糖猫Blog</title>
    <link>https://ftmao.com/blog/tags/ray/</link>
    <description>AI 视频、AI Agent 与工程实践笔记</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <lastBuildDate>Fri, 13 Feb 2026 10:00:00 +0800</lastBuildDate><atom:link href="https://ftmao.com/blog/tags/ray/index.xml" rel="self" type="application/rss+xml" /><item>
      <title>Netflix 的 LLM 后训练框架：从「跑通脚本」到「管好四根支柱」</title>
      <link>https://ftmao.com/blog/posts/netflix-llm-post-training/</link>
      <pubDate>Fri, 13 Feb 2026 10:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/netflix-llm-post-training/</guid>
      <description>&lt;h1 id=&#34;netflix-的-llm-后训练框架从跑通脚本到管好四根支柱&#34;&gt;Netflix 的 LLM 后训练框架：从「跑通脚本」到「管好四根支柱」&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href=&#34;https://netflixtechblog.com/scaling-llm-post-training-at-netflix-0046f8790194&#34;&gt;Scaling LLM Post-Training at Netflix&lt;/a&gt;
目标读者：做过或想做 LLM 微调/强化学习的工程师，尤其是正在考虑「自建训练管线还是买现成框架」的人。
阅读目标：搞清楚两件事——后训练在 Netflix 规模下到底难在哪；以及一个内部框架靠什么证明自己比「组装开源组件」更有价值。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;先拆一个直觉后训练不就是拿个开源模型-finetune-一下吗&#34;&gt;〇、先拆一个直觉：后训练不就是「拿个开源模型 finetune 一下」吗&lt;/h2&gt;
&lt;p&gt;直觉版本的后训练长这样：准备点私有数据，从 Hugging Face 拉一个开源权重，写个 batch 循环迭代。实验规模下确实就几行代码。原文的核心论点是：&lt;strong&gt;从「跑通脚本」到「生产级后训练」之间隔着一整条工程鸿沟&lt;/strong&gt;——数据管线、多机 GPU 集群的分布式状态、训练与推理交错的工作流，每一个都是深渊。&lt;/p&gt;
&lt;p&gt;Netflix 的场景是推荐、个性化和搜索，需要让通用基座模型理解自家片库和用户交互历史。这种场景下，post-training 很快就不再是建模问题，而是分布式系统问题。AI Platform 团队建这个框架的目标很直白：&lt;strong&gt;把基础设施复杂度藏起来，让模型开发者专注模型本身，而不是分布式管道工。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;一三个环节每个都有自己的坑&#34;&gt;一、三个环节，每个都有自己的坑&lt;/h2&gt;
&lt;p&gt;原文把后训练旅程拆成三步，每一步都有一批「纸面上没有」的坑。&lt;/p&gt;
&lt;h3 id=&#34;数据难的不是读取是精确控制&#34;&gt;数据：难的不是读取，是精确控制&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Loss masking 是命门。&lt;/strong&gt; HF chat template 只负责把对话序列化成文本，不告诉你哪些 token 该进 loss。必须显式做 loss mask，只让 assistant 的 token 参与优化——否则模型会去学习 prompt 和非目标文本，质量直接掉。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;变长序列是隐形浪费。&lt;/strong&gt; batch 内 padding 浪费算力，FSDP 各 worker 形状不齐会带来 GPU 同步开销。更省的做法是 &lt;strong&gt;sequence packing&lt;/strong&gt;：把多个样本塞进定长序列，再用 document mask 阻止跨样本注意力。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;模型加载不了只是开始&#34;&gt;模型：加载不了只是开始&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;模型放不进单卡时，需要 FSDP/TP 分片，并且要&lt;strong&gt;把权重分片直接加载到 device mesh 上&lt;/strong&gt;，避免在任何单设备上物化完整模型。&lt;/li&gt;
&lt;li&gt;加载完还要让它「可训练」：全量微调还是 LoRA，activation checkpointing、编译、精度设置——RL 场景下 rollout 和 policy 的精度必须对齐，这是容易翻车的细节。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;大词表（＞128k）的内存陷阱&lt;/strong&gt;：logits 形状是 batch × seq_len × vocab，峰值内存会炸。常见缓解：投影前先丢掉被忽略的 token，沿序列维度分块算 logits 和 loss。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;训练生产训练不是一个-for-循环&#34;&gt;训练：生产训练不是一个 for 循环&lt;/h3&gt;
&lt;p&gt;要支持从 SFT 的单次前反向传播，到 on-policy RL 那种「rollout 生成、reward/reference 推理、policy 更新交错进行」的工作流。Netflix 用 Ray 的 actor 模型做编排，把建模逻辑和硬件解耦。另外两样是标配：实验追踪（loss 这类质量指标 + MFU 这类效率指标）和标准化 checkpoint 带来的容错恢复。&lt;/p&gt;
&lt;h2 id=&#34;二框架总览四根支柱&#34;&gt;二、框架总览：四根支柱&lt;/h2&gt;
&lt;p&gt;整个技术栈从底到顶是：Mako（Netflix 内部 ML 计算平台，在 AWS 上供给 GPU）→ 开源三件套（PyTorch、Ray、vLLM，基本开箱即用）→ Post-Training Framework（以库的形式提供 SFT、DPO、RL、知识蒸馏等标准化 recipe，用户用配置文件选配方、插任务组件）。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    subgraph APP[&amp;#34;使用者: 模型开发者&amp;#34;]
        CFG[&amp;#34;配置文件&amp;lt;br/&amp;gt;选 recipe 插组件&amp;#34;]
    end
    subgraph FW[&amp;#34;Post-Training Framework&amp;#34;]
        R1[&amp;#34;SFT&amp;#34;] --- R2[&amp;#34;DPO&amp;#34;] --- R3[&amp;#34;RL&amp;#34;] --- R4[&amp;#34;蒸馏&amp;#34;]
    end
    subgraph OSS[&amp;#34;开源基础组件&amp;#34;]
        PT[&amp;#34;PyTorch&amp;#34;] --- RAY[&amp;#34;Ray&amp;#34;] --- VLLM[&amp;#34;vLLM&amp;#34;]
    end
    subgraph INFRA[&amp;#34;基础设施&amp;#34;]
        MAKO[&amp;#34;Mako 内部 ML 计算平台&amp;lt;br/&amp;gt;在 AWS 上供给 GPU&amp;#34;]
    end
    CFG --&amp;gt; FW
    FW --&amp;gt; OSS
    OSS --&amp;gt; MAKO
    style FW fill:#fff3cd&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;框架的能力沿四个维度组织，对应 ML 系统的三支柱加一个新增支柱：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Data&lt;/strong&gt;：SFT、奖励建模、RL 各自的数据集抽象；超本地存储的数据集从云和磁盘做高吞吐流式读取；&lt;strong&gt;异步 on-the-fly packing&lt;/strong&gt;，把 CPU 密集的打包和 GPU 计算重叠，减少空闲。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt;：支持现代架构（Qwen3、Gemma3）和 MoE 变体（Qwen3 MoE、GPT-OSS）；LoRA 直接集成进模型定义；高层 sharding API，开发者不用写底层分布式代码就能跨 device mesh 分片。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compute&lt;/strong&gt;：统一作业提交接口，单机到几百卡一个入口；自定义架构和 LoRA 下依然准确的 MFU 监控；完整 checkpoint（参数、优化器、dataloader、data mixer 状态），中断后精确恢复。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Workflow&lt;/strong&gt;：这是 RL 逼出来的第四支柱——支持超越「单个训练循环」的多阶段执行模式。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;三教训一从-sft-到-rl架构要换心&#34;&gt;三、教训一：从 SFT 到 RL，架构要换心&lt;/h2&gt;
&lt;p&gt;框架最初围绕 SFT 设计：数据流相对静态、单一训练循环、SPMD 执行模型（每个 GPU worker 跑同一个 step 函数，处理不同数据分片，靠 PyTorch 分布式原语同步）。这个假设在 2025 年失效了——DeepSeek-R1 和 GRPO 这类高效 on-policy RL 普及之后，SFT 从终点变成了入场券。&lt;/p&gt;
&lt;p&gt;两者差异的本质在学习信号：&lt;strong&gt;SFT 的信号稠密而即时&lt;/strong&gt;（每个 token 位置都有全词表 logits 和可微 loss），长得就像 pre-training；&lt;strong&gt;on-policy RL 的信号稀疏而延迟&lt;/strong&gt;（episode 结束才有一个标量 reward），且训练数据依赖当前 policy 生成。单个子阶段（policy 更新、rollout、reference 推理、reward 打分）各自都能 SPMD 化，但端到端算法需要显式协调：prompt、采样轨迹、reward、advantage 这些产物在阶段间不停交接，生命周期要有人管。&lt;/p&gt;
&lt;p&gt;架构上的变化：原来的 driver 是「瘦」的，只负责拉起 N 个相同的 Ray actor，扩容就是加 worker；RL 要求把系统拆成不同角色（Policy、Rollout Worker、Reward Model、Reference Model），driver 进化成&lt;strong&gt;主动控制器&lt;/strong&gt;，编码整个控制面——何时生成 rollout、怎么组 batch 和打分、何时触发优化、跨阶段怎么调度集群资源。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    subgraph SFT[&amp;#34;SFT 架构: 瘦 driver + SPMD&amp;#34;]
        D1[&amp;#34;瘦 driver&amp;lt;br/&amp;gt;只负责拉起 worker&amp;#34;] --&amp;gt; A1[&amp;#34;actor 1&amp;lt;br/&amp;gt;完整训练循环&amp;#34;]
        D1 --&amp;gt; A2[&amp;#34;actor 2&amp;lt;br/&amp;gt;完整训练循环&amp;#34;]
        D1 --&amp;gt; A3[&amp;#34;actor N&amp;lt;br/&amp;gt;完整训练循环&amp;#34;]
    end
    subgraph RL[&amp;#34;RL 架构: 主动控制器 + 多角色&amp;#34;]
        C[&amp;#34;主动控制器&amp;lt;br/&amp;gt;编码控制面&amp;lt;br/&amp;gt;何时 rollout 何时优化&amp;#34;] --&amp;gt; P[&amp;#34;Policy 更新&amp;#34;]
        C --&amp;gt; R[&amp;#34;Rollout Worker&amp;#34;]
        C --&amp;gt; RM[&amp;#34;Reward Model&amp;#34;]
        C --&amp;gt; REF[&amp;#34;Reference Model&amp;#34;]
        R --&amp;gt;|&amp;#34;轨迹&amp;#34;| RM
        RM --&amp;gt;|&amp;#34;reward 和 advantage&amp;#34;| P
    end
    SFT ==&amp;gt;|&amp;#34;2025 年 DeepSeek-R1 与 GRPO 普及&amp;lt;br/&amp;gt;SFT 从终点变成入场券&amp;#34;| RL
    style RL fill:#fff3cd&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;落地方式是集成开源库 &lt;strong&gt;Verl&lt;/strong&gt; 的核心设施来管理 Ray actor 生命周期和 GPU 资源分配，自己只做「建模表面积」——Data/Model/Compute 抽象和内部优化。最终是混合设计：用户在 SFT 和 RL 之间切换不用换心智模型。这个取舍值得画重点：&lt;strong&gt;编排不自己造轮子，抽象层才是护城河。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;四教训二全面押注-hugging-face但模型实现自己来&#34;&gt;四、教训二：全面押注 Hugging Face，但模型实现自己来&lt;/h2&gt;
&lt;p&gt;HF Hub 事实上已是开源权重、tokenizer、配置的默认分发渠道，框架选择贴着生态走而不是另立内部标准：checkpoint 一律按 HF 格式存取，避免「围墙花园」摩擦。&lt;/p&gt;
&lt;p&gt;Tokenizer 的故事最有教训价值。早期直接绑定底层分词库（SentencePiece、tiktoken）追求控制力，结果造成&lt;strong&gt;静默的训练-推理 skew&lt;/strong&gt;：推理栈（vLLM）默认用 HF AutoTokenizer，归一化、特殊 token、chat template 的细微差异会产生不同的 token 边界——这类 mismatch 之后会以「莫名其妙的质量回退」形式冒出来，极难排查。修法：&lt;strong&gt;AutoTokenizer 成为唯一事实源&lt;/strong&gt;，上面加一层薄薄的兼容层（&lt;code&gt;BaseHFModelTokenizer&lt;/code&gt;）处理后训练特有需求（padding token、生成标记注入、语义 ID），同时保证字节级分词路径和生产一致。&lt;/p&gt;
&lt;p&gt;模型实现则相反：&lt;strong&gt;不直接用 transformers 的模型类训练&lt;/strong&gt;，而是维护自己优化的统一模型定义，能读写 HF checkpoint。这一层换来 FlexAttention、分块交叉熵、统一 MFU 核算、统一 LoRA 扩展，跨架构不用重实现；统一的模块命名还让程序化定位和替换组件（Attention、MLP、输出头）成为可能。&lt;/p&gt;
&lt;p&gt;代价很直接：支持一个新模型家族，要写 HF 参考实现到内部定义的桥。Netflix 的解法是 &lt;strong&gt;AI coding agent + logit verifier&lt;/strong&gt;：agent 自动生成大部分转换代码，验收标准是给定随机输入、内部模型的 logits 必须在容差内匹配 HF——因为验收可以机械检查，agent 能自主迭代到正确为止，大幅缩短新架构的支持周期。当前只支持显式适配过的架构（和 vLLM、SGLang、torchtitan 同款取舍），计划中的 HF fallback 后端会允许直接跑原生 transformers 模型做快速探索，代价是部分优化不生效。&lt;/p&gt;
&lt;h2 id=&#34;五教训三差异化价值投在深水区&#34;&gt;五、教训三：差异化价值投在深水区&lt;/h2&gt;
&lt;p&gt;自建框架值不值，取决于有没有超出「组装开源件」的价值。Netflix 的答案：开源换速度，重投入换两个方向——&lt;strong&gt;贴合自家负载特征的性能&lt;/strong&gt;和&lt;strong&gt;Netflix 特有的业务需求&lt;/strong&gt;。两个具体例子：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;On-the-fly sequence packing。&lt;/strong&gt; FSDP 训练里长尾序列制造 straggler，快的 worker 在同步点干等。标准的离线 bin-packing 有效，但在他们的数据规模下预处理延迟可观、数据难保鲜。解法是从存储流式读样本、在内存里动态打包，打包异步跑、和 GPU 计算重叠。结果：最倾斜的数据集上有效 token 吞吐提升最高 &lt;strong&gt;4.7 倍&lt;/strong&gt;（A100 和 H200 上均验证）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;词表大小对齐 64。&lt;/strong&gt; 业务常加自定义 token 和语义 ID，某些词表大小会让 LM head 从高度优化的 cuBLAS kernel 掉到慢得多的 CUTLASS 路径，该层耗时翻三倍。框架自动把词表 pad 到 64 的倍数，开发者不需要知道这层硬件玄学。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;第二个方向是支持「非标准」transformer 用例：比如基于用户交互事件序列（而非自然语言）训练的模型，需要定制的 RL 循环、对接高度定制的推理引擎、优化业务定义的指标——这些要求定制环境、奖励计算和编排模式，但性能、追踪、容错的底层保障必须一样。&lt;/p&gt;
&lt;h2 id=&#34;工程实践联想&#34;&gt;工程实践联想&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;「跑通」和「生产化」之间的鸿沟，大部分都是状态管理。&lt;/strong&gt; Loss mask、checkpoint 完整性、训练-推理一致性——每一项单看都是小细节，合起来决定系统可不可靠。这和我们写业务系统一样：demo 一周，上线半年，差的全在边界条件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;训练-推理 skew 是数据系统里「双写不一致」的 LLM 版本。&lt;/strong&gt; 两份各自「正确」的实现（训练 tokenizer vs 推理 tokenizer）因为微小差异产生静默错误。通用解法也通用：指定唯一事实源，其他都向它对齐，用兼容性测试兜底——logit verifier 本质就是契约测试。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI coding agent 最好用的地方是「验收可机械检查」的场景。&lt;/strong&gt; HF 到内部模型定义的桥接，验收是 logits 数值匹配，agent 可以放心自主迭代。反过来说，给 agent 派活之前先问：这活的验收标准能不能写成断言？写不出断言的活，agent 做完了你还得人工审。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编排层买、抽象层造，这个取舍可以抄。&lt;/strong&gt; Verl 管 actor 生命周期，自己只做 Data/Model/Compute/Workflow 抽象。通用版：消息队列买、存储买，贴合业务的领域模型自己造——护城河在建模，不在管道。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能优化要贴着自家负载的真实分布做。&lt;/strong&gt; 4.7 倍不是来自通用调优，是来自「我们的序列长度方差极端」这个具体事实；词表对齐 64 同理。先测量自己的负载长什么样，再决定优化什么——别人的 benchmark 不构成你的优化清单。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;本篇小结&#34;&gt;本篇小结&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;定位&lt;/strong&gt;：后训练在 Netflix 规模下是工程问题。框架目标是让模型开发者不碰分布式管道。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;三个坑位&lt;/strong&gt;：数据（loss mask、packing）、模型（分片加载、大词表 logits 内存）、训练（不只是 for 循环，要编排和容错）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;四根支柱&lt;/strong&gt;：Data、Model、Compute、Workflow——第四根是 RL 逼出来的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;架构演进&lt;/strong&gt;：SFT 的「瘦 driver + SPMD」演进为 RL 的「主动控制器 + 多角色」，编排集成 Verl，自己保住抽象层。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生态策略&lt;/strong&gt;：checkpoint 和 tokenizer 全面贴 HF（AutoTokenizer 是唯一事实源，治训练-推理 skew）；模型实现自己维护换优化空间，桥接新架构用 AI agent + logit verifier 自动化。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;差异化投入&lt;/strong&gt;：on-the-fly packing 最高 4.7 倍吞吐；词表对齐 64 避开 kernel 回退；支持交互序列这类非自然语言 transformer。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;一句话&lt;/strong&gt;：训练框架的价值不在堆了多少组件，在于把分布式系统的坑变成了配置项。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://netflixtechblog.com/scaling-llm-post-training-at-netflix-0046f8790194&#34;&gt;Scaling LLM Post-Training at Netflix&lt;/a&gt;（原文，Baolin Li、Lingyi Liu、Binh Tang、Shaojing Li）&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/volcengine/verl&#34;&gt;Verl&lt;/a&gt;（字节跳动开源的 RL 后训练库，Netflix 用其核心设施做 actor 生命周期与 GPU 资源管理）&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://arxiv.org/abs/2501.12948&#34;&gt;DeepSeek-R1&lt;/a&gt;（GRPO 等高效 on-policy RL 方法的代表作，原文称其为「SFT 变成入场券」的转折点）&lt;/li&gt;
&lt;li&gt;Torchtune、Torchtitan（原文致谢的两个 PyTorch 原生训练参考实现）&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://thinkingmachines.ai/tinker/&#34;&gt;Thinking Machines Tinker&lt;/a&gt;（原文对比的外部微调服务）&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
  </channel>
</rss>
