ARTICLE · 1148586
AI infra和常见训练工具概览
上一篇我们聊了 SFT、PPO、GRPO 和 OPD,花了不少篇幅解释 loss 为什么这么写,以及它到底在给模型什么更新建议。
但公式写完,离模型真的训练起来,其实还隔着很多事情。
比如 GRPO,让模型对同一道题写几份答案,打个分,算出组相对优势,再反传更新。听起来好像一个循环就够了。但是实际项目里还要用 vLLM、verl、Ray、FSDP、Megatron 这么多乱七八糟的东西。
这篇就来初步地、入门地(就像我一样)看一下这些内容。我们从模型生成一条回答开始,一直看到它拿着这条回答更新参数。中间碰到哪个问题,就介绍解决它的办法。
有些地方会算一点显存,不过数学比上一篇少很多,同时也不需要先学会 CUDA。但是我们默认你知道模型会根据前文生成下一个 token,也知道训练时要做反向传播,还知道Attention机制的大概原理(也就是关于token的QKV矩阵计算),其他概念碰到了文中再说。
从一个循环认识 AI Infra
先写一段产生训练数据集再到训练过程的伪代码:
for prompts in dataset:
responses = actor.generate(prompts)
rewards = score(responses)
advantages = compute_advantages(rewards)
loss = compute_policy_loss(responses, advantages)
loss.backward()
optimizer.step()
这里省略了很多具体的实现,只是把上一篇的算法翻译成可能存在的程序步骤,但是整体上看,这段伪代码基本包括了基础的rl组件。
如果模型很小,数据很少,代码块中这些事情确实可以在一个进程里完成。但换成一个大语言模型,问题马上就来了。
第一行生成回答时,有的问题写两百个 token 就结束,有的要写两千个。我们要等最长的那条吗?还在生成的回答要保存多少中间状态?显存够不够?
等到 backward(),显存里又要多放激活值、梯度和优化器状态。一张卡不够,分到多张卡上,怎么分?训练完的参数又怎样送回负责生成的那一边?
AI Infra,AI 基础设施,基本上研究的就包括这些问题:怎样组织计算、管理内存、拆分模型、传输数据,让算法能够在给定的机器上跑起来,并且跑得划算一点。
这个范围很大,集群、存储、网络、监控也都算。本文先集中在推理和强化学习训练,我们刚才那段循环就够展开不少内容了。
先看 generate()。
一条回答中的 Prefill 和 Decode
假设输入是“请解释一下什么是强化学习”,模型接下来要生成“强化学习是一种……”。
人看起来,这就是接着写。对模型来说,要先处理完整的输入,才能给出第一个输出 token;之后再把刚生成的 token 接回去,继续预测下一个。
处理输入这一段提示词叫 prefill,后面逐步生成新的token叫 decode。
Prefill:处理整段 prompt → 得到第一个输出 token
Decode :处理刚生成的 token → 得到下一个 token
Decode :继续处理新 token → 再得到一个 token
……
我们平时看到的自回归,说的是生成时:后一个 token 依赖前一个已经选出来的 token。输入 prompt 已经完整给定,里面各个位置的计算可以利用 GPU 并行完成,因果关系由 attention mask 限制(比如环境如系统返回的token不被利用)。
长输入prompt的 prefill 往往能组织出规模比较大的矩阵运算,Decode则每条序列每一步只增加一个 token。所以在较小 batch 等常见情形下,读取模型权重和历史状态的成本会很突出,经常受显存带宽限制。Batch、上下文长度、模型结构和硬件变了,瓶颈也会变,先按这个常见情况理解。
这也解释了聊天产品里两种不同的慢:有时半天不出第一个字,开始输出后倒很流畅;有时第一个字来得很快,后面却一个一个往外挤。
前一种要看 TTFT,Time To First Token,首 token 延迟,即prefill并计算出第一个token的时间,它还包含排队等时间。后一种看 TPOT,Time Per Output Token,后续decode输出 token 的平均耗时。除这两个之外服务端还关心 throughput,吞吐量,比如每秒总共生成多少输出 token(一个用户等得快,和一台机器总体生成得多,评价的是两件事。我们后面会看到,它们有时还需要做取舍)。
现在有个基础的问题:每生成一个 token,前面的内容是要重新算一遍所以这么慢吗?
KV Cache 省下来的计算与增加的显存占用
还记得 attention 里的 Q、K、V 吧?Query、Key、Value。
当前 token 需要用自己的 Q,去和之前各位置的 K 计算相关性,再对 V 加权。对于普通的因果 Transformer,在模型参数不变时,给前缀后面接一个新 token,不会改变前面已经算好的 K 和 V。
既然没变,那就存下来,下次接着用。这些保存的历史 K、V 就是 KV Cache。
注意,缓存的是每一层需要的中间张量。它和模型参数是两份东西,也不是模型把这段话永久学会了。换一个不共享前缀的请求,要计算另一份 KV;继续生成,当前请求的 KV 还会变长。
于是一个特别常见的困惑就出现了:模型权重明明能装进显存,为什么多开几个请求就 OOM (Out Of Memory内存不够)了?
因为你只算了模型,没算这些正在生成的回答。
我们粗略算一下。对于每层 KV 头数和维度相同的普通全注意力模型,一条长度为 的序列,其 KV Cache 大约占:
前面的 2 表示 K 和 V 各一份; 是层数, 是 KV 头数, 是每个头的维度, 是每个元素的字节数。有些模型采用 GQA,Grouped-Query Attention,让多个 Query 头共享一组 K、V 头。这里算的是 KV 占用,不能直接拿 Query 头数代进去。
假设有一个模型,32 层、8 个 KV 头、每个头 128 维,用 BF16 存 KV,每个元素 2 字节。那么每增加一个 token,大约需要:
也就是 128 KiB。长度达到 8192 个 token,一条序列的 KV 就大约 1 GiB。20 条这么长的序列,不共享缓存时就是约 20 GiB,还没加权重、计算工作区和其他开销。
这里只是在假设的模型上算账。采用滑动窗口、MLA、KV 量化或其他结构,账单会变化。先把最基本的关系看清楚:同时生成的序列越多、上下文越长,KV 通常就越占显存。 vLLM 的 PagedAttention 论文[1]讨论的正是这种动态增长的缓存,以及它对服务吞吐的影响。
因此高效推理至少要处理两件事:GPU 这一轮一起算哪些请求,以及这些请求的 KV 放在哪里。
Continuous Batching 怎样安排不同长度的请求
先说一起算谁。
最简单的办法,是把几个请求凑成一个 batch,一起开始,等所有请求结束,再处理下一批。这是这里用来比较的静态 batching。
假设 A 生成 20 个 token,B 生成 200 个,C 生成 2000 个。A、B 早就结束了,C 还要继续。如果这一批的位置不能及时拿来服务新请求,后面的请求就只能排队。
Continuous batching 连续批处理,会在生成的迭代之间重新安排参与计算的请求。
某一轮:A、B、C 继续生成
A 结束:下一轮移出 A,安排新请求 D
B 结束:再安排新请求 E
D 要先做自己的 prefill,才有东西可 decode。真实调度还要检查 token 预算、KV 空间和请求上限,不是有个空位就一定能立刻塞进去。但这个变化已经很有用了:一批请求不必一起开始、一起结束。
这就是 vLLM 这类推理引擎要做的工作之一。我们给它请求,它维护等待与运行队列,选择这一轮做哪些计算,再调用底层模型执行。vLLM 官方介绍[2]把 continuous batching 与 KV 管理都列为核心能力。
不过新请求进来以后,显存得先有地方放它。
PagedAttention 怎样管理 KV Cache
假设我们不知道一条回答最终多长,最省心的分配方式就是按最大长度预留一大段连续显存。
问题也很明显:预计最多写两千个 token,实际只写了两百个,剩下的预留就浪费了。请求不断进来、结束、释放空间,空闲区域也可能散落在不同位置。
PagedAttention 借用了操作系统分页内存的想法,把 KV Cache 按固定大小的 block 管理。一个请求逻辑上连续的前缀,可以对应显存中不同位置的 block,中间用 block table 记录映射。
请求 A 的第 0 个 KV 块 → 物理块 7
请求 A 的第 1 个 KV 块 → 物理块 2
请求 A 的第 2 个 KV 块 → 物理块 19
模型仍然按正确的 token 顺序计算 attention,只是保存 KV 的位置不必连续。生成变长了,继续分配块;不再需要的块,就回收到可用空间里。支持分页布局的 attention 计算负责正确找到这些数据。
这样可以减少按最大长度预留造成的浪费,也更方便管理请求的增长、释放和共享。vLLM 最初的技术介绍[3]对这个设计有比较直观的说明。
它没有让一份 KV 凭空变小。刚才那条约 1 GiB 的缓存,换成分页管理,并不会自动只剩 100 MiB;省下来的主要是分配浪费和能够共享的部分,块内尾部等开销也仍然存在。
再把两件事放在一起看:continuous batching 让运行中的请求不断变化,PagedAttention 让这些请求增长和退出时,KV 可以更灵活地分配。一个负责安排计算,一个负责配合这种安排管理缓存。
还有个名字也带 Attention,它和分页又是什么关系?
FlashAttention 和 Prefix Caching 又分别省了什么
聊 vLLM 经常还会遇到 FlashAttention。名字都有 Attention,确实容易混。
FlashAttention 关心一次 attention 计算怎样少在 GPU 的显存和片上高速存储之间搬数据。它通过分块和在线 softmax 等办法,避免把完整的 attention 中间矩阵写回显存,减少 IO 和中间存储。FlashAttention 原论文[4]里的 IO-aware,就是这个意思。
PagedAttention 主要处理多条序列的 KV 怎样分块保存和访问;FlashAttention 主要优化 attention 运算的数据访问与计算过程。两者可以配合,现代 vLLM 也会接入不同的 attention backend,具体走哪套实现要看模型和硬件。
另一个常见名字是 prefix caching,前缀缓存。
比如我们给同一道题采样 8 份回答。这 8 份回答的 prompt 一样,开头那段 KV 能不能复用?在模型、token 前缀和相关条件一致、缓存命中的情况下,可以。至于怎么命中,就是按token序列做哈希。
它主要省掉相同前缀的 prefill。后面的回答已经各写各的,不能因为 prompt 相同,就把整条回答的计算也共享掉。文本意思差不多也不够,前缀 token 得符合缓存复用条件。vLLM 前缀缓存文档[5]也专门区分了对 prefill 和 decode 的影响。
如果一条新请求带着很长的 prompt,prefill 本身又可能影响正在 decode 的请求。Chunked prefill 会把长输入拆成几段,按调度预算与 decode 一起安排,减少长 prefill 一次占用太久带来的影响。vLLM 调优文档[6]介绍了这种安排及其延迟取舍。
这些优化省的东西各不相同。有的是少算重复前缀,有的是减少搬数据,有的是让请求少等,有的是减少显存浪费。
我们现在能把一批问题交给 vLLM,让它组织生成了。接下来给回答打分,然后更新模型。终于轮到开头那个 backward()。
训练显存与 FSDP 等并行方式
生成时不需要对参数求梯度,过去的 K、V 留着复用就好。训练时,反向传播还要利用前向计算中的中间结果,这些结果通常叫激活值;参数有梯度,Adam 等优化器还有自己的状态。
所以“一个模型推理能放进单卡”,并不意味着同一张卡也能直接全参数训练它。
以 BF16 权重为例,7B,也就是约 70 亿个参数,只算一份权重就约 14 GB,注意这是十进制字节估算。训练时再加上前面那些东西,实际占用会多很多,还取决于优化器、精度和激活重计算等设置。
显存不够以后,我们会想起多卡。但先别急着把“多卡”理解成几张卡自动合成一张大卡。
最直接的数据并行,Data Parallel,是每张卡都有模型副本,各自吃不同的数据,再同步梯度。它能分担数据量,普通的副本式数据并行却没有解决每张卡都得放下一份模型和训练状态的问题。PyTorch DDP 教程[7]介绍了这种方式。
FSDP,Fully Sharded Data Parallel,会把参数、梯度、优化器状态分片保存到不同的卡。以常见的全分片方式理解:平时每张卡只保存自己负责的一部分;计算某个参数组时,再通过通信临时收集需要的完整参数,算完后按策略释放,梯度也分片归还。PyTorch FSDP 教程[8]可以对照着看这个过程。
注意这个“临时收集”。FSDP 并不是所有时候每张卡的占用都严格等于总量除以卡数,计算峰值、通信缓冲和激活值仍然要算进去。
Tensor Parallel,TP,张量并行,则会拆开一层里的张量运算。比如一个大的线性层,让多张卡各算其中一部分,再通过通信得到所需结果。vLLM 推理时也可以使用 TP。
还有 Pipeline Parallel,PP,流水线并行,把不同层放到不同设备或阶段,数据按顺序经过它们。Megatron-LM 及 Megatron Core 提供了组合这些并行方式的训练能力。Megatron Core 官方说明[9]里可以继续找这些模块。
把这几种切法放在一起看:
这些方法可以组合。代价也跟着来了:卡之间要传参数、梯度或激活值。通信慢了,多加卡未必能按比例提速。
记住这一点,后面 actor 从训练切到生成时,为什么还要折腾权重布局,就容易理解了。
从生成回答到收集 Rollout
普通 SFT 可以把已经准备好的示范答案送进训练。而我们这里讨论的在线 RL,要先让当前策略自己写答案,再根据这些答案学习。
这一步收集策略实际行为的过程,就叫 rollout。单轮问答里,可以先把它理解成生成回答;如果模型还要调用工具、接收环境反馈、继续行动,rollout 就会包括更长的交互过程。
所以 vLLM 除了给用户提供聊天服务,也能负责训练过程中的 rollout。它生成的 token,一会儿会进入训练 batch。
为了少带几种模型,下面用 GRPO 举例:一道题生成多份回答,分别打分,用组内相对分数构造优势,再更新 actor。这个配置不需要单独训练一个 value critic。verl 的 GRPO 文档[10]对各项配置有解释。
但“没有 critic”不等于“没有评分”。数学题可以用规则或验证器判断结果,代码题可以运行测试,也可以用 reward model 给回答打分。Reward 负责评价实际答案,critic 负责估计未来回报,上一篇提过的区别在这里仍然成立。
还有两种概率,很容易在代码里看着看着就混了。
Old policy 是这一批数据对应的旧策略,常在 ratio 的分母里出现;reference policy 是用于 KL 约束的参考策略,通常保持固定。Old 会随着新一轮采样而变化,reference 不必跟着变化。
固定同一道题和同一个回答前缀,用 、 分别记当前策略与旧策略对已选 token 的 logprob,ratio 就可以写成:
更新过程中,前面那个 logprob 要用当前参数重新算;这批数据的 old logprob 要固定下来。Reference logprob 则是另一份计算,是否需要它取决于我们有没有启用相应的约束。
一个逻辑角色也不一定就要在显存里常驻一套模型。Old logprob 可以保存成张量,未必得一直保留完整的 old model。verl 的典型同步流程会在更新前,用训练侧 actor 再算一遍 logprob,把它固定为这轮的 old logprob;有些配置也可以直接采用 rollout 返回的概率。两种引擎的计算可能存在差异,具体还要看相应的校正配置。这里先分清这些数各自干什么,别把它们互相替代。
现在问题变成:生成、评分、计算概率、算优势、更新参数,这么多步骤到底谁来组织?
verl 怎样组织强化学习训练
verl 是面向大语言模型的强化学习训练框架,也是 HybridFlow 工作的开源实现。它可以把 FSDP、Megatron 等训练后端,与 vLLM、SGLang 等 rollout 后端接在同一套 RL 流程里。verl 官方仓库[11]列出了这些集成。
我们刚才担心的那段循环,在这里可以分成两层来看。
外面一层决定:先拿 prompt,生成回答,再交给谁评分,什么时候计算 advantage,什么时候开始更新。这层是控制流。
里面一层完成实际计算:一个模型的前向、反向、优化器更新,具体怎样在多张 GPU 上执行。这层由相应的计算后端和 worker 完成。
verl 的 HybridFlow 设计把这两层分开,让控制层能描述完整的数据流,底下各组 worker 用适合自己的分布式方式执行。这样改 GRPO 的优势估计时,可以沿用已有的分布式训练实现;rollout 换成另一套引擎,也可以通过相应接口衔接。HybridFlow 论文[12]讨论了这种组织方式。
这里就可以介绍 Ray 了。
你可以先把一个 Ray actor 理解为运行在某个进程里的、有自己状态的对象。我们调用它的方法,它去执行任务。对 verl 的典型 Ray 训练流程来说,Ray 帮忙组织远程 worker、资源放置和任务调用;实际的矩阵乘、梯度同步等计算通信,还要靠 PyTorch、NCCL 等相应后端。NCCL 是 NVIDIA GPU 之间常用的通信库。Ray actor 文档[13]解释了这个对象模型。
顺便说一句,这里的 Ray actor 是一种编程对象,和 RL 里负责生成动作的 actor 不是同一个概念。一个项目里这俩词会同时出现,读代码时得看上下文。
vLLM 自己也有 scheduler。它安排的是推理执行时哪些请求、哪些 token 进入当前计算;verl 控制层安排的是一次 RL 训练里生成、评分、更新的依赖关系;Ray 则在进程和资源层面支持这些任务运行。
这些职责会在实现上交织,先这样区分就能看懂大部分关系。也别把 Ray 当成使用 vLLM 的前提,单机 vLLM 可以走 multiprocessing 等执行方式。
同一个 actor 在训练与生成之间切换
既然生成用 vLLM,训练用 FSDP,那分别放一份模型,训练完把权重传过去,不就好了?
可以。这是把训练和 rollout 放到不同设备上的一种思路。但 GPU 有限时,还会希望同一组设备轮流做这两件事。
麻烦在于,适合训练和推理的权重分片方式可能不同。比如训练侧用 FSDP 分片,推理侧用 TP 分片;即使逻辑上是同一组参数,也不代表每张卡上保存的是同一部分,更不代表底层张量格式能直接照搬。
训练阶段还要把显存留给激活值、梯度和优化器状态,生成阶段又想尽量多留一些空间给 KV。两个引擎都按自己的需求把显存占满,当然放不下。
所以,共用设备时要安排模型状态的驻留和释放,必要时做 offload,也就是把暂时不用的数据挪到 CPU 等地方。新参数送进 rollout 引擎时,还可能需要重分片、转换布局和同步。
HybridFlow 论文[12]里的 3D-HybridEngine 就专门处理 actor 在训练与生成之间切换时的重分片和内存问题。具体怎么切,要看使用的后端与设备映射。vLLM 的 sleep / wake-up 等能力也可以用于释放推理引擎暂时不用的显存,但它本身并不替我们完成整个 RL 训练流程。vLLM Sleep Mode 文档[14]介绍了这些接口。
这里还有一个比显存更容易忽略的问题。
权重更新以后,旧权重算出来的 KV 不能直接当作新模型的 KV 继续用。
即使 prompt 的文字完全一样,生成 K、V 的模型已经变了,它们一般也会变。负责切换的实现必须把这种缓存失效处理好,verl 的 vLLM rollout 实现[15]就会在更新权重后清理 KV 缓存。刚才讲 prefix caching 时说的“模型和前缀一致”,模型一致就是这个意思。
至于把 rollout 和训练分到不同 GPU 上,也不是没有代价。它能让资源更独立,有机会重叠执行,但需要传输权重、平衡两边的速度;如果训练侧继续更新,而 rollout 还在用较旧参数生成,数据就会出现策略滞后,还要由算法处理。
这篇先按同步流程看。同步就是这一批生成和更新按轮次衔接,不代表模型只能用一张卡,也不代表这轮里的各项计算全都不能并行。
跟着一批 GRPO 数据完整走一次
前面的名字有点多,我们拿具体的数走一遍。
假设一次取 8 道题,每道题生成 4 份答案,就会得到 32 条回答。先限定为单轮数学问答,用规则验证最后的答案,正确得 1,错误得 0。
先生成。 控制层把题目交给 rollout workers,vLLM 按当前这轮的 actor 权重生成回答。32 条序列长短不同,由引擎安排批处理和 KV 分配。
我们拿回来的不只是字符串。后面训练还要用 token ID,知道哪些 token 属于 prompt、哪些是有效 response,哪些只是为了凑形状补的 padding;还得知道哪 4 条回答来自同一道题。
再评分、算概率。 验证器检查答案;系统取得更新前的 old logprob,需要 KL 约束时还会计算 reference logprob。这些工作要满足依赖关系,有些计算可以并行,并不必严格按这两句话的顺序串行。
假设第一道题的四份答案得分是:
回答 A:1
回答 B:0
回答 C:1
回答 D:0
接着算 advantage。 均值是 0.5。按 verl 这项实现采用的样本标准差,标准差约为 0.577;用“分数减均值,再除以标准差”,忽略防止除零的小量,四份回答的优势大致是:
A:+0.87 B:-0.87
C:+0.87 D:-0.87
这里采用的是 verl 的 GRPO 优势计算[16]。如果改用总体标准差,这个例子会得到正负 1;这里只是说明更新倾向,具体数值要和实现对上。
计算这组均值、标准差本身不需要大模型。要紧的是同一道题的四份回答得放回同一组,不能把 32 份答案一锅算了;mask 也得对,别把 padding 当作模型的真实动作。
然后更新。 把 response、old logprob、advantage 等组成训练数据,actor 用当前参数前向,计算新的 logprob,构造带 ratio 和 clip 等项的 loss,再反向传播、更新参数。
还记得 prefill 那一节说的,已知前缀可以并行处理吗?回答生成完以后,训练看到的是已经给定的整条 token 序列,所以重新计算这些位置的 logprob,不必再逐个采样一次。因果 mask 保留条件依赖,前向与反向可以按训练方式组织。
但不能直接拿刚才 rollout 的 KV 当成完整训练激活来反传。普通推理缓存并没有保留训练所需的整套计算图和中间状态,训练前向还得按需要重新做。
最后同步权重。 更新完成,把 actor 的新参数交给 rollout 引擎,按后端要求完成同步和缓存处理,下一轮再生成新回答。
verl 的编程指南[17]里能找到对应的生成、logprob、评分和更新接口。沿着这些接口看,刚才这一轮做过的事情就能一一对上。
Batch Size 为什么配置里到处都是
如果现在去看一份 verl 配置,还会碰到一个问题:怎么有这么多个 batch size?
同样是 batch,它们限制的计算范围不同。
我们刚才的 8,是一次取多少个 prompt;4,是每个 prompt 采样多少份回答;两者相乘,才是这轮的 32 条轨迹。
这 32 条轨迹未必一次性拿来更新。可以分成 mini-batch;每个 mini-batch 如果一次前反向放不下,再切成 micro-batch,通过梯度累积完成一次更新。分布式情况下还得区分全局和每张卡的大小,具体字段要看对应配置。
而 vLLM 调度器的 token budget,限制的是一轮引擎执行里安排多少 token 计算。它也不等于训练里的 mini-batch,更不能把整个请求已占用的 KV 总量直接当作这个预算。
这些词长得像,直接凭名字改,很容易改错地方。
比如把每道题的采样数从 4 改成 8,除了生成量增加,GRPO 用来比较的组也变了。减小训练 micro-batch,则通常是为了降低单次计算的显存峰值;在其他条件和梯度累积保持等价时,它和“每轮少训练一半数据”不是同一回事。
我们看配置时可以先做这个动作:在每个 batch size 旁边写下它到底数的是 prompt、response 还是 token,数的是全局还是单卡,限制的是生成还是更新。很多看似复杂的参数,就会先清楚一半。
第一次动手可以先看什么
如果想把上面的内容跑出一点感觉,我觉得可以先做推理实验,再看完整 RL 训练。
用一个机器能装下的小模型(记得改小vllm的启动显存占用,实操问自己的ai,这篇很多细节操作就不多赘述了),先固定输入和输出长度,比较单请求与多请求并发。记录首 token 延迟、后续输出耗时和总输出吞吐,再改变输入长度、输出长度,看看结果怎样变化。对比前先预热,固定采样条件,记录模型提前结束造成的实际输出长度差异。
这时你已经有几个具体的问题可以问了:输入变长主要影响哪一段?并发增加以后,总吞吐是不是提高了,单个请求等得是不是更久了?到什么程度开始出现排队、缓存不足或重计算?
轮到 verl,再沿着一次 step 看各阶段耗时:生成、评分、计算 logprob、更新、同步权重。整轮慢,不一定是 backward 慢;回答长度差得很大,同步流程可能在等少数几条还没生成完的回答(这里大家应该对第一篇文章的single-rollout有更好的理解了);验证器执行慢,也可能让训练侧等着。
另外,显存占用高也不能直接证明 GPU 算得很忙。引擎可能预留了缓存空间,里面未必都有有效 token;GPU utilization 高,也只说明设备在忙,要结合 token 吞吐、有效序列长度和通信情况,才能判断这些忙碌有没有产出。
总结
上一篇我们追着分布和梯度,想知道模型为什么应该这么学。这篇沿着同一批回答,看到这些更新建议怎样变成机器上的实际工作。
读到这里,再去看一个 RL 项目的配置,希望你已经能先问出“这一项是在解决哪一步的问题”。搞清楚这个,比一次背下所有框架的名字有用。
感谢看到这里。之后会根据各个模块写具体的简单小实验文章,希望即使不是ai领域的朋友也可以更深入地了解这个领域。
参考资料
[1] vLLM 的 PagedAttention 论文
https://arxiv.org/abs/2309.06180
[2] vLLM 官方介绍
https://docs.vllm.ai/en/latest/
[3] vLLM 最初的技术介绍
https://vllm.ai/blog/2023-06-20-vllm
[4] FlashAttention 原论文
https://arxiv.org/abs/2205.14135
[5] vLLM 前缀缓存文档
https://docs.vllm.ai/en/latest/features/automatic_prefix_caching/
[6] vLLM 调优文档
https://docs.vllm.ai/en/latest/configuration/optimization/
[7] PyTorch DDP 教程
https://docs.pytorch.org/tutorials/intermediate/ddp_tutorial.html
[8] PyTorch FSDP 教程
https://docs.pytorch.org/tutorials/intermediate/FSDP_tutorial.html
[9] Megatron Core 官方说明
https://github.com/NVIDIA/Megatron-LM
[10] verl 的 GRPO 文档
https://verl.readthedocs.io/en/latest/algo/grpo.html
[11] verl 官方仓库
https://github.com/verl-project/verl
[12] HybridFlow 论文
https://arxiv.org/abs/2409.19256
[13] Ray actor 文档
https://docs.ray.io/en/latest/ray-core/actors.html
[14] vLLM Sleep Mode 文档
https://docs.vllm.ai/en/latest/features/sleep_mode/
[15] vLLM rollout 实现
https://github.com/verl-project/verl/blob/main/verl/workers/rollout/vllm_rollout/vllm_rollout.py
[16] verl 的 GRPO 优势计算
https://github.com/verl-project/verl/blob/main/verl/trainer/ppo/core_algos.py
[17] 编程指南
https://verl.readthedocs.io/en/latest/hybrid_flow.html