深度解析:AI模型 DeepSeek 为何大规模部署成本低廉,本地运行却步履维艰?

DeepSeek-V3在规模化部署中因高效批处理而经济,本地运行却因单用户批处理不足导致性能和成本劣势。理解批量推理是关键。

原文标题:为什么 DeepSeek 大规模部署很便宜,本地很贵

原文作者:AI前线

冷月清谈:

DeepSeek-V3 模型在实际应用中展现出一个有趣的悖论:它在大规模服务时既快速又经济,但在本地运行时却显得缓慢且昂贵。文章揭示了这背后的核心原因,即人工智能推理服务中吞吐量与延迟之间的基本权衡,以及推理服务提供商选择的“批处理大小”策略

批处理推理是实现高效率的关键。GPU擅长执行大型矩阵乘法(GEMMs),而将数十或数百个并发用户请求打包成一个批次,可以把多个小型计算合并为一个大型计算,从而显著提高GPU的利用效率。虽然这种方式会增加用户等待时间(延迟),因为它需要等待批次填满,但却能大幅提升GPU的整体处理能力(吞吐量)。

文章进一步解释了为什么某些模型,如DeepSeek-V3,尤其需要更大的批处理大小才能满足实用性要求。这主要归因于两类模型特性:

1. **专家混合机制(MoE)模型:** 这类模型拥有大量“专家”权重块,路由层会为每个token选择一个子集。如果没有足够的批次,每个专家可能只会处理极少量的token,导致GPU利用率低下。更大的全局批次才能使所有专家保持忙碌。
2. **大型管道模型:** 拥有众多Transformer层的模型需要通过管道化在多个GPU之间分担计算。如果批处理过小,会导致大量的“管道气泡”(即GPU在等待数据输入或输出时的空闲时间),严重影响吞吐量。只有足够大的批次才能有效消除这些气泡。

最终,推理提供商会根据模型的特性,选择一个批次大小来平衡吞吐量和延迟,清除管道泡沫并确保专家饱和工作。对于像DeepSeek这样兼具多层和MoE特性的模型,它天生就需要大批次和高延迟来维持可观的吞吐量,这正是它在单用户(本地)场景下表现低效、成本高昂的原因。

怜星夜思:

1、文章里提到了AI大模型推理的吞吐量和延迟权衡。除了DeepSeek,大家觉得日常生活中还有哪些AI应用,它们的响应速度或者使用体验也可能受到这种参数权衡的影响呢?有没有让你特别有感触的例子?
2、文章最后提到了“连续批处理”技术可能消除队列满载、提升效率。如果这项技术未来能大规模普及,大家觉得会对普通用户使用AI,或者AI应用开发带来哪些具体的改变或者新的可能性?会不会有更多模型能跑在本地了?
3、对于我们这些没有海量GPU资源的小开发者或者个人用户来说,如果想部署一个大模型(比如尝试微调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 号客服上线工作啦!

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

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


今日荐文

图片

你也「在看」吗?👇


除了量化、剪枝和蒸馏这些模型压缩技术,还可以从运行时层面考虑。比如选择一些本身就对小批次或者单用户推理更友好的轻量级架构,而不是MoE这种天生需要大批次的模型。另外,可以尝试分层推理(Layer-wise Inference)或者Offloading到CPU,就是把模型层拆开,一部分在GPU,一部分在CPU,或者把不太重要的计算放到CPU上,虽然肯定会增加延迟,但关键是能在资源有限的情况下跑起来。现在也有很多针对边缘设备的特定优化推理框架,可以研究一下。

我说个不那么明显的:现在很多云端图片/视频处理AI,比如智能抠图、风格转化、AI超分啥的。你上传一张照片可能秒出结果,但如果你批量上传上百张或者处理一个超高清视频,处理速度就会明显慢下来,甚至需要排队。这个很可能就是后台为了达到高吞吐,把大家的请求都拉去大批次处理了,单个请求的延迟就上去了。毕竟对服务商来说,让GPU一直满负荷运算才是最划算的。

如果连续批处理真的能普及,那绝对是AI推理领域的一大步!最直观的可能就是模型响应会更快,体验更流畅。对于开发者来说,或许可以在保证一定响应速度的前提下,使用更大的模型,或者在云端部署更多元、更便宜的模型。至于本地跑模型,我觉得可能性会更大,因为它可以更高效地利用有限的本地资源,减少等待时间。想象一下,我们能在自己的PC上更流畅地跑起Midjourney或者ChatGPT这种级别的模型,那该多酷啊!

连续批处理(Continuous Batching)确实是当前LLM部署优化的一大热点。它的核心优势在于能够显著提升GPU利用率,因为它打破了固定批次大小的限制,根据实际请求动态调度,减少了管道气泡和碎片化。这对低延迟、高吞吐量的场景至关重要。如果成功落地,未来我们可能会看到云端AI服务成本进一步下降,同时更多模型支持Streaming输出,用户感受的“流畅度”会大幅提升。对于本地部署,它意味着即便是一个用户也能以更接近服务器的效率运行模型,显著提升本地大模型应用的实用性,让我们用自己的电脑玩更多高级AI成为可能。

土办法嘛… 我觉得最“土”也是最简单粗暴的,就是**“换卡”!哈哈开玩笑的。认真点说,如果真的只有一块卡,但模型又特别大,那就只能分批加载,或者干脆租用云GPU**了,按需使用更划算。再不然,就是把模型量化得惨不忍睹,或者只用它的embedding层在本地跑,推理部分还是甩给API,曲线救国嘛!别想太多,小作坊就别跟大厂比吞吐量了,能跑起来就是胜利!

楼上问的这个问题特别好!我们没大厂那么壕,但又想玩大模型,确实得找土办法。我个人觉得最直接的就是模型量化(Quantization),把原来float16或者bfloat16的模型变成int8甚至int4,这样能极大减少显存占用和计算量。虽然精度可能有点损失,但好多场景下能接受。另外就是剪枝(Pruning)知识蒸馏(Knowledge Distillation),把大模型压缩成小模型,牺牲一点性能换取效率,这样至少能跑起来,哪怕慢点也行啊!

最有感触的,我觉得是智能客服和语音助手。有些AI客服几乎是秒回,但遇到复杂问题或者你讲话快一点,它就需要停顿一下才能给你反馈。还有手机上的语音助手,偶尔也会有明显的延迟。这应该就是为了保持低延迟,牺牲了部分吞吐量,或者说,它的批处理规模没法做到像DeepSeek那种后端服务那么大,所以资源利用率没那么极致吧。用户体验上就是,偶尔卡一下,感觉就不那么"智能"了。

比如在线翻译软件,有时候翻译长文本反应就慢,但短句秒出。我总觉得是不是长文本能凑成大批次,短句每次一两句,达不到批次效率,所以反而要等一下?感觉就像饭馆里,点大桌菜出菜快,单点个小菜反而慢一样。