为什么 DeepSeek 大规模部署很便宜,本地很贵

作者 | Sean Goedecke
译者 | 王强
策划 | Tina

为什么 DeepSeek-V3 据说在大规模服务时快速且便宜,但本地运行时却太慢且昂贵?为什么有些 AI 模型响应很慢,但一旦开始运行就变得很快?

AI 推理服务提供商经常讨论吞吐量和延迟之间的一个基本权衡:对于任何给定的模型,你要么以高吞吐量和高延迟提供服务,要么以低吞吐量和低延迟提供服务。实际上,有些模型天生是 GPU 效率比较低的,以至于在实践中它们必须以高延迟提供服务,才能有可行的吞吐量(例如 DeepSeek-V3)。

这种权衡来自于推理服务提供商为模型选择的 批处理大小:不是在单个请求内进行批处理推理,而是跨数十或数百个并发用户请求进行批处理。基于 Transformer 的大型语言模型的一个奇特特性是,同时计算一批补全几乎和计算单个补全一样快。为什么会这样?

什么是批处理推理?

GPU 擅长执行大型矩阵乘法(GEMMs,或“通用矩阵乘法”)。假设你有一个 token,你想通过模型传递(即通过乘以其所有权重,其他架构细节不谈)。你将其表示为一个与模型维度(或隐藏大小)相匹配的向量(即 1x 其大权重矩阵的宽度),然后将其乘过去。那就是 1 个 GEMM。但如果你想一次通过一批十个 token,也仍然只是一个 GEMM,因为你可以将 token 堆叠成一个矩阵(10x 模型维度)。这比执行十个稍小的 GEMM 要快得多。因此,推理服务器的实现可能看起来是这样的:

  1. 一个请求带着提示词进来
  2. 该提示被预填充(通过注意力传递,我们稍后将看到如何将它也做批处理),形成一个 KV 缓存和一个 token 大小的矩阵(1x 模型大小),最终成为预测的 token
  3. 那个 token 大小的矩阵进入一个队列
  4. GPU 服务器从该队列中拉取一些批次(例如 128 个),将它们堆叠成一个 128x 模型大小的矩阵,并通过前馈模型权重进行乘法运算
  5. 最终结果被分割成 128 个单独的 token
  6. 针对原始请求的那个 token 被流回给用户
  7. 假设那个 token 不是序列结束 token,返回步骤 2 以继续生成响应中的下一个 token

请注意,服务器会决定拉取多大的批次大小。这是吞吐量和延迟之间的权衡。如果你不进行批处理,只是逐个处理 token,那么没有用户会在队列中等待(上述步骤 3),所以延迟很低(假设你有足够的 GPU)。然而,如果你进行大量批处理,延迟会很高,因为用户将等待到批次大小填满,但吞吐量会高得多,因为 GPU 的使用效率更高。

为什么 GPU 在一次乘以大型矩阵时比多次乘以小型矩阵更快?有两个原因。首先,向 GPU 发出每个命令都涉及一些开销,而一个大乘法可以用单个命令启动。其次,每个新的 GPU 命令都要从内存中获取权重,这对于大型权重来说可能很昂贵。如果你运行很多小 GEMMs,你最终可能会花费大部分时间在内存中进出权重,而不是进行计算。

为什么有些模型针对大批次大小进行优化?

通常,推理服务器会有一个“收集窗口”,用户请求送进这个窗口并被排队。聊天服务器通常的延迟目标是 5-10 毫秒,但非常高的批次后端可能会达到 200 毫秒。如果一个新请求在窗口开始时进来,它可能需要等待整个窗口的持续时间后才能被处理。当窗口关闭时,所有排队的请求都被批处理(即所有 1x 模型大小的矩阵被连接成一个单一的 128x 模型大小的矩阵),然后该批次通过管道发送。像这样运行一个批次有时被称为一个“tick”。

正如上述解释所建议的,你可以在任何批次大小下运行任何模型。批处理过程本身并没有什么限制会排除某些类型的模型。然而,我们有可能构建一个模型,使其 GPU 效率如此低下,以至于实际上它需要批处理才能满足实用要求。

为什么专家混合机制需要更高的批次大小

例如,考虑一个专家混合模型(如 DeepSeek-V3 或据说是原始的 GPT-4 所使用的机制)。你可以让它拥有数百个“专家”来获得一个强大的模型。这些“专家”是独立的前馈权重块,路由层从中选择一个子集用于每个 token。但这样的模型真的对 GPU 效率很差。原因在于:GPU 想要执行少量的大型矩阵乘法,但如果有很多专家,你会被迫做很多小型乘法。除非你以整个批次进行推理,否则这意味着吞吐量很低。

我们考虑一下 5 毫秒和 200 毫秒的“收集窗口”对于大型专家混合模型的表现如何。假设你在那个 5 毫秒窗口中接收到十个用户请求。如果你有很多专家,一些专家可能最终只对一个或两个 token 运行(即每个专家的批次大小将远低于你在窗口中接收到的总请求集)

为什么大型管道需要大的批次以避免管道气泡

对于大型模型来说,保持 GPU 始终活跃可能是一个挑战。大型模型通常有很多 transformer 层:即组成前馈网络的数百个权重矩阵。在这里进行快速推理的唯一方法是管道化这些层,让一个 GPU 处理前十个层,另一个处理接下来的十个层,依此类推。否则,你根本无法将所有权重放入单个 GPU 的内存中,这样你会花费大量时间在内存中交换权重,最终会变得非常慢。在推理过程中,每个 token(通常位于每个包含几十个 token 的“微批次”中)会顺序通过 GPU 管道。

你的管道效率取决于你拥有的层数和你的收集窗口大小。当你在“tick”期间处理窗口中的 token 时,你会在开始时有一些空闲的 GPU(因为后层的 GPU 还没有输入可以操作),在结束时会有更多的空闲 GPU(当队列中没有更多的 token 时,早期层的 GPU 将不得不等待下一个“tick”)。这些空闲期有时被称为“预热”和“排水”。如果你有很多小窗口,你将比拥有较少大窗口时花费更多的 GPU 时间在预热和排水上。通过选择你的窗口大小,你就能直接在吞吐量和延迟之间进行权衡。

如果你有很多层,你的收集窗口非常短,有时最终处理的 token 数量可能少于层数。这被称为“管道气泡”——实际上,“排水”阶段比平时更早开始。你不能消除预热和排水(由于下面讨论的原因,推理必须以顺序“tick”操作),但可以让收集窗口足够长来消除管道气泡。管道气泡可能对模型吞吐量造成极大的影响,因此推理提供商总是设置足够宽的窗口以避免它们。这为具有许多层的模型增加了明显的延迟。

你不能保持队列满载吗?

为什么推理提供商不能通过保持 GPU 队列满载来完全消除预热和排水?换句话说,你不能完全摆脱 tick,而只是让很多 token 微批次持续流动下去?当然,每个用户的推理必须是顺序的(因为你不能在当前 token 完成之前开始生成下一个 token),但大型推理提供商应该有足够的并发流量来保持队列满载。

我承认我在理论上看不出为什么这是不可能的。据我所知,实际障碍是如何批量处理注意力步骤:如果你想批量处理注意力 GEMMs,它们需要都是相同的形状(即序列中有相同数量的先前 token)。所以你不得不同时运行相同形状的组,而不能只维护一个队列。在这方面至少有一些公开的研究(https://arxiv.org/abs/2403.02310),但如果有更多我没见过的更聪明的技巧来做这件事,我不会感到惊讶。

另一个想法:如果你需要 tick 来处理注意力步骤,为什么不只使用基于 tick 的注意力推理系统,以及更高效的连续系统用于 FFN?据我了解,原因是内存开销:

  1. 由于 FFN 需要注意力输出,你需要在内存中有一个位置来停放它,同时等待其在 FFN 队列中的插槽,这将很快变得过于昂贵。
  2. 现代推理栈能够将注意力和 FFN 步骤合并成几个大 GEMMs 放在一个“操作”中。如果你在不同的 GPU 上进行这些操作,你必须运行不同的操作并来回移动权重。
总   结
  • GPU 在大型 GEMMs 上最高效,因此将许多 token 堆叠到一个矩阵乘法中,可以比逐个处理它们获得更高的 token 吞吐量。
  • 在解码过程中,注意力只能为同一步骤的 token 批量处理,迫使调度器以短“tick”运行。你将多少 token 打包到一个“tick”中(即你等待收集 token 的时间),就是你的批次大小。这些 token 来自不同的用户。你不能从同一个用户批量处理 token,因为你需要之前的 token 来生成下一个 token,所以批处理需要不同用户组成的高流量。
  • 更大的批次增加了延迟,因为用户 token 可能需要等待多达 200 毫秒,直到批次足够大才能开始运行,但它们通过允许 FFN 步骤中更大的(因此更有效的)GEMMs 来提高吞吐量。
  • 具有许多层(例如长管道)的模型需要更大的批次以避免管道气泡(确保每个 tick 包含的批次多于管道步骤)。
  • 混合专家模型需要以高延迟提供服务以提升效率:每个专家只看到路由到它的 token,所以你需要的是更大的全局批次以让每个专家都忙碌起来。
  • 推理提供方选择一个批次大小 / 窗口,以清除流水线泡沫并让专家饱和工作。高批次大小可以为你提供更多吞吐量,但代价是更高的延迟,因为 token 需要等待填充。
  • 一些模型(如 DeepSeek)是具有许多层的专家混合模型,因此需要大批次大小和高延迟,否则吞吐量会急剧下降。这就是为什么说 DeepSeek 很难在个人场景中使用:因为只有一个用户一次运行一个推理,它的运行效率 / 吞吐量非常低。
  • OpenAI 和 Anthropic 的模型响应迅速这一事实表明,要么:他们的模型具有更高效的架构(非 MoE,更少的层),或者 OpenAI/Anthropic 有一些非常巧妙的推理服务技巧,或者他们支付了高昂的费用,购买了比他们所需的量更多的 GPU。

编辑:这篇文章发表在 Hacker News 上,并附有一堆评论。我有点希望我给这篇文章起了另一个名字——它实际上讲的不是在自己的计算机上运行模型,而讲的是为个人使用场景运行模型,假设你拥有所有 GPU(即批次 / 吞吐量权衡)。

  1. 人们观察到的 transformer 的一个常见优势是,它们可以在单个用户请求内批量预填充。当你给它们一个长提示时,它们可以一次性处理该提示,因为注意力机制的工作原理就是这样。以前的递归模型必须逐 token 进行,这要慢得多(因为它涉及更多的 GEMMs)。这与我在这篇文章中谈论的批处理无关。我谈论的是如何在预填充完成后,有效地跨许多不同的用户请求来批量推理。
  2. 只要你只批处理具有相同数量 token 序列的注意力操作(例如每个序列预测第四个 token 可以一起批量处理),这也可以实现。否则,KV 缓存矩阵的大小是不一样的,因此你不能轻松地将它们组合成一个批次。
  3. 技术上,它不是一个被生成的 token,而是“logits”(所有可能 token 的概率分布)。为了保持简单性,我在这里和以后会说“token”。
  4. 注意,在实践中,现代推理栈将使用“连续批处理”,一旦批次满就发送,而不是等待固定时间窗口的全部时间。然而,推理仍是批量进行的,核心的吞吐量和延迟之间的权衡是相同的。

如果你喜欢这篇文章,请考虑订阅我的新文章的电子邮件更新(https://buttondown.com/seangoedecke)。

原文链接:

https://www.seangoedecke.com/inference-batching-and-deepseek/

声明:本文由 InfoQ 翻译,未经许可禁止转载。

InfoQ 老友!请留步!极客邦 1 号客服上线工作啦!

后续我将通过微信视频号,以视频的形式持续更新技术话题、未来发展趋势、创业经验、商业踩坑教训等精彩内容,和大家一同成长,开启知识交流之旅

欢迎扫码关注我的微信视频号~


今日荐文

李飞飞曝创业招人标准!总结AI 大牛学生经验,告诫博士们不要做堆算力项目

Altman嘲讽小扎挖走的都不是顶尖人才!OpenAI高管再营业曝内幕:ChatGPT爆红后,我火速升职了!

跳槽实现财富自由!小扎千万年薪快要“掏空”OpenAI核心人才,还高调“晒”挖人成绩单:各栈大牛,近70%是华人

华为盘古大模型开源,推理方案、基础代码全公开!

老黄亲自挖来两名清华天才;字节 Seed 机器人业务招一号位;清华北大浙大中科大校友跳槽去Meta | AI周报

你也「在看」吗?👇


(文:AI前线)

发表评论