为什么 DeepSeek-V3 据说在大规模服务时快速且便宜,但本地运行时却太慢且昂贵?为什么有些 AI 模型响应很慢,但一旦开始运行就变得很快?
AI 推理服务提供商经常讨论吞吐量和延迟之间的一个基本权衡:对于任何给定的模型,你要么以高吞吐量和高延迟提供服务,要么以低吞吐量和低延迟提供服务。实际上,有些模型天生是 GPU 效率比较低的,以至于在实践中它们必须以高延迟提供服务,才能有可行的吞吐量(例如 DeepSeek-V3)。
这种权衡来自于推理服务提供商为模型选择的 批处理大小:不是在单个请求内进行批处理推理,而是跨数十或数百个并发用户请求进行批处理。基于 Transformer 的大型语言模型的一个奇特特性是,同时计算一批补全几乎和计算单个补全一样快。为什么会这样?
1 什么是批处理推理?
GPU 擅长执行大型矩阵乘法(GEMMs,或“通用矩阵乘法”)。假设你有一个 token,你想通过模型传递(即通过乘以其所有权重,其他架构细节不谈)。你将其表示为一个与模型维度(或隐藏大小)相匹配的向量(即 1x 其大权重矩阵的宽度),然后将其乘过去。那就是 1 个 GEMM。但如果你想一次通过一批十个 token,也仍然只是一个 GEMM,因为你可以将 token 堆叠成一个矩阵(10x 模型维度)。这比执行十个稍小的 GEMM 要快得多。因此,推理服务器的实现可能看起来是这样的:
请注意,服务器会决定拉取多大的批次大小。这是吞吐量和延迟之间的权衡。如果你不进行批处理,只是逐个处理 token,那么没有用户会在队列中等待(上述步骤 3),所以延迟很低(假设你有足够的 GPU)。然而,如果你进行大量批处理,延迟会很高,因为用户将等待到批次大小填满,但吞吐量会高得多,因为 GPU 的使用效率更高。
为什么 GPU 在一次乘以大型矩阵时比多次乘以小型矩阵更快?有两个原因。首先,向 GPU 发出每个命令都涉及一些开销,而一个大乘法可以用单个命令启动。其次,每个新的 GPU 命令都要从内存中获取权重,这对于大型权重来说可能很昂贵。如果你运行很多小 GEMMs,你最终可能会花费大部分时间在内存中进出权重,而不是进行计算。
2 为什么有些模型针对大批次大小进行优化?
通常,推理服务器会有一个“收集窗口”,用户请求送进这个窗口并被排队。聊天服务器通常的延迟目标是 5-10 毫秒,但非常高的批次后端可能会达到 200 毫秒。如果一个新请求在窗口开始时进来,它可能需要等待整个窗口的持续时间后才能被处理。当窗口关闭时,所有排队的请求都被批处理(即所有 1x 模型大小的矩阵被连接成一个单一的 128x 模型大小的矩阵),然后该批次通过管道发送。像这样运行一个批次有时被称为一个“tick”。
正如上述解释所建议的,你可以在任何批次大小下运行任何模型。批处理过程本身并没有什么限制会排除某些类型的模型。然而,我们有可能构建一个模型,使其 GPU 效率如此低下,以至于实际上它需要批处理才能满足实用要求。
3 为什么专家混合机制需要更高的批次大小
例如,考虑一个专家混合模型(如 DeepSeek-V3 或据说是原始的 GPT-4 所使用的机制)。你可以让它拥有数百个“专家”来获得一个强大的模型。这些“专家”是独立的前馈权重块,路由层从中选择一个子集用于每个 token。但这样的模型真的对 GPU 效率很差。原因在于:GPU 想要执行少量的大型矩阵乘法,但如果有很多专家,你会被迫做很多小型乘法。除非你以整个批次进行推理,否则这意味着吞吐量很低。
我们考虑一下 5 毫秒和 200 毫秒的“收集窗口”对于大型专家混合模型的表现如何。假设你在那个 5 毫秒窗口中接收到十个用户请求。如果你有很多专家,一些专家可能最终只对一个或两个 token 运行(即每个专家的批次大小将远低于你在窗口中接收到的总请求集)
4 为什么大型管道需要大的批次以避免管道气泡
对于大型模型来说,保持 GPU 始终活跃可能是一个挑战。大型模型通常有很多 transformer 层:即组成前馈网络的数百个权重矩阵。在这里进行快速推理的唯一方法是管道化这些层,让一个 GPU 处理前十个层,另一个处理接下来的十个层,依此类推。否则,你根本无法将所有权重放入单个 GPU 的内存中,这样你会花费大量时间在内存中交换权重,最终会变得非常慢。在推理过程中,每个 token(通常位于每个包含几十个 token 的“微批次”中)会顺序通过 GPU 管道。
你的管道效率取决于你拥有的层数和你的收集窗口大小。当你在“tick”期间处理窗口中的 token 时,你会在开始时有一些空闲的 GPU(因为后层的 GPU 还没有输入可以操作),在结束时会有更多的空闲 GPU(当队列中没有更多的 token 时,早期层的 GPU 将不得不等待下一个“tick”)。这些空闲期有时被称为“预热”和“排水”。如果你有很多小窗口,你将比拥有较少大窗口时花费更多的 GPU 时间在预热和排水上。通过选择你的窗口大小,你就能直接在吞吐量和延迟之间进行权衡。
如果你有很多层,你的收集窗口非常短,有时最终处理的 token 数量可能少于层数。这被称为“管道气泡”——实际上,“排水”阶段比平时更早开始。你不能消除预热和排水(由于下面讨论的原因,推理必须以顺序“tick”操作),但可以让收集窗口足够长来消除管道气泡。管道气泡可能对模型吞吐量造成极大的影响,因此推理提供商总是设置足够宽的窗口以避免它们。这为具有许多层的模型增加了明显的延迟。
5 你不能保持队列满载吗?
为什么推理提供商不能通过保持 GPU 队列满载来完全消除预热和排水?换句话说,你不能完全摆脱 tick,而只是让很多 token 微批次持续流动下去?当然,每个用户的推理必须是顺序的(因为你不能在当前 token 完成之前开始生成下一个 token),但大型推理提供商应该有足够的并发流量来保持队列满载。
我承认我在理论上看不出为什么这是不可能的。据我所知,实际障碍是如何批量处理注意力步骤:如果你想批量处理注意力 GEMMs,它们需要都是相同的形状(即序列中有相同数量的先前 token)。所以你不得不同时运行相同形状的组,而不能只维护一个队列。在这方面至少有一些公开的研究(https://arxiv.org/abs/2403.02310),但如果有更多我没见过的更聪明的技巧来做这件事,我不会感到惊讶。
另一个想法:如果你需要 tick 来处理注意力步骤,为什么不只使用基于 tick 的注意力推理系统,以及更高效的连续系统用于 FFN?据我了解,原因是内存开销:
6 总结
编辑:这篇文章发表在 Hacker News 上,并附有一堆评论。我有点希望我给这篇文章起了另一个名字——它实际上讲的不是在自己的计算机上运行模型,而讲的是为个人使用场景运行模型,假设你拥有所有 GPU(即批次 / 吞吐量权衡)。
如果你喜欢这篇文章,请考虑订阅我的新文章的电子邮件更新(https://buttondown.com/seangoedecke)。
原文链接:
https://www.seangoedecke.com/inference-batching-and-deepseek/
文章来自公众号“InfoQ”,作者“Sean Goedecke”
【开源免费】graphrag是微软推出的RAG项目,与传统的通过 RAG 方法使用向量相似性作为搜索技术不同,GraphRAG是使用知识图谱在推理复杂信息时大幅提高问答性能。
项目地址:https://github.com/microsoft/graphrag
【开源免费】Dify是最早一批实现RAG,Agent,模型管理等一站式AI开发的工具平台,并且项目方一直持续维护。其中在任务编排方面相对领先对手,可以帮助研发实现像字节扣子那样的功能。
项目地址:https://github.com/langgenius/dify
【开源免费】RAGFlow是和Dify类似的开源项目,该项目在大文件解析方面做的更出色,拓展编排方面相对弱一些。
项目地址:https://github.com/infiniflow/ragflow/tree/main
【开源免费】phidata是一个可以实现将数据转化成向量存储,并通过AI实现RAG功能的项目
项目地址:https://github.com/phidatahq/phidata
【开源免费】TaskingAI 是一个提供RAG,Agent,大模型管理等AI项目开发的工具平台,比LangChain更强大的中间件AI平台工具。
项目地址:https://github.com/TaskingAI/TaskingAI
【开源免费】LangGPT 是一个通过结构化和模板化的方法,编写高质量的AI提示词的开源项目。它可以让任何非专业的用户轻松创建高水平的提示词,进而高质量的帮助用户通过AI解决问题。
项目地址:https://github.com/langgptai/LangGPT/blob/main/README_zh.md
在线使用:https://kimi.moonshot.cn/kimiplus/conpg00t7lagbbsfqkq0