大规模大语言模型推理深度解析 — Harshul Jain (Audible) 与 Tanmay Sah (独立AI研究员)

AAI Engineer
컴퓨터/소프트웨어경제 뉴스AI/미래기술

스크립트

00:00:00大家下午好。我叫哈沙尔·杰恩,这位是坦米沙。我们
00:00:21欢迎大家来参加这个关于大语言模型推理的两小时工作坊。
00:00:26本次工作坊的目标是从第一性原理出发理解这个领域,
00:00:33深入探讨并了解整个行业正在发生的事情。
00:00:39简单介绍一下我们的背景。我是 Audible 的高级软件工程师。
00:00:47过去五年里,我一直在构建机器学习与人工智能数据平台,同时
00:00:53业余时间我一直在编写这本关于大语言模型推理的开源手册。
00:00:59而谭迈是西安银行股份有限公司的高级量化建模师。
00:01:06他最近刚完成博士学位,并一直在积极从事智能体验证器和世界模型的研究所。
00:01:12大家简单举个手看看。有多少人对大语言模型推理完全是新手?
00:01:24好的,没问题。那么有多少人已经在生产环境中部署过这些模型?一直在
00:01:33进行调优,并处理生产环境的流量?好的,很好。所以本次
00:01:42工作坊面向初学者和中级水平。所有的
00:01:48幻灯片和练习都在代码仓库中,我很快就会分享给大家。以下是本次工作坊的简要
00:01:56议程。我们将从问题陈述开始。我们将尝试
00:02:01了解大语言模型推理的一些痛点。然后我们了解这些痛点产生的原因,并以此为基础建立基础知识。
00:02:08接下来,我们将深入探讨两种优化,即模型优化和
00:02:14服务优化。然后我们将开始了解不同的服务引擎,
00:02:19这些引擎可用于在生产环境中部署我们的大语言模型推理解决方案。我们还将展示一些基准测试
00:02:25以及关于选择哪个引擎的决策图表。
00:02:32好的。那么要理解痛点,首先我们需要知道什么叫做大语言模型推理。
00:02:39所以,可能我们很多人已经知道了。但是,是的,你让你的 AI 去做的任何事情,
00:02:47无论是生成视频、音频,分析任何文本,分析你的医疗报告,还是你的
00:02:55税务账单,所有这些都类似于大语言模型推理。而这个市场在今天大约有 230 亿美元。
00:03:02Semianalysis 最近分享说,如果你想用大语言模型对谷歌搜索查询进行建模,你需要承担大约 360 亿美元的利润消耗。
00:03:12为了保持搜索业务的盈利,查询成本必须低于 0.5 美分。
00:03:25另一方面,Business Insider 提到你的 AI 必须正确部署,每个人都必须开始审计和预算他们的 Token 使用量。
00:03:33这一切正在发生。为什么?因为你的硬件有限,计算昂贵,推理成本高昂。
00:03:43随着对 AI 使用需求的不断增长,这种推理成本正在不断上升。
00:03:50所以这个统计数据,虽然是 OpenAI 的一个旧统计数据,但它仍然是真实的。
00:04:03如果你看一下 GPT-3 的训练成本,大约是 460 万美元。这是一次性成本。
00:04:11但如果你看到推理成本,它一直以来都是一种经常性成本,因为它是一项运营成本,会随着进入的每一个用户、
00:04:19每一个 Token 以及在 AI 上发起的每一个会话而不断扩展。
00:04:36基本上只有两种方法来应对这种情况:一种方法是减少你的 Token 使用量。
00:04:47另一种方法是,你应该尝试为客户和自己优化推理解决方案,将其作为一项推理服务。
00:04:58因此,我们一直在看到越来越多的新解决方案不断涌现。
00:05:06所以我们的想法是,我们将尝试建立这些基础,帮助我们理解和评估接下来推出的任何新事物。
00:05:18所以,是的,为了开始,我们将做一个快速演示,这是一个简短的演示,关于推理周围的不同痛点。
00:05:28所以这是一个代码仓库。
00:05:31我的意思是,你可以拉取它,也可以在 GitHub 上打开它。
00:05:36它叫做大规模大语言模型推理。
00:05:39这里有一些背景信息,比如四个月前当我对外汇大语言模型推理一无所知时,我开始学习它。
00:05:46我发现很多资源都很分散。
00:05:49所以我们开始把它们放在同一个地方,以便造福人们。
00:05:55是的,所以让我实际退出这个幻灯片模式,可能进入这个扩展模式。
00:06:11是的,在这个仓库中,如果你看到一个 readme 文件,那里有一个指向幻灯片的链接。
00:06:27所以这个文件夹里有 PPTX,里面还有一个基准测试报告。
00:06:34你随时可以下载它,为了演示目的,我们有几个 Jupyter 笔记本。
00:06:43我们与 Molab 进行了合作,他们是 Google Colab 的替代品,他们基本上为你提供免费的 RTX 6000 GPU。
00:06:54所以这是一个 100 GB RAM 的 GPU,我们已经设置好了这些笔记本,以便于进行实验,并且所有的资产和一切都为你预设好了。
00:07:09所以我们将从一个简单的演示 A 开始。
00:07:16可能,可能,让我看一下。
00:07:31是的,所以当涉及到推理时,你需要对某个模型进行推理,对吧?
00:07:40所以,为了工作坊的目的,我们正在使用一个简单的 Mistral 7B 模型。
00:07:45这是一个小模型,大小约为 15 GB。
00:07:49所以我们打算把它加载到 GPU 中。
00:07:53所以,我们也会查看一些 GPU 统计数据。
00:07:58所以我们看到,我们在 6000 Blackwell 上运行。
00:08:01所以,你可能会想,我没有运行单元格,因为我不信任会议上的 Wi-Fi。
00:08:08所以,是的,我可能只会浏览我们之前运行过的一些结果。
00:08:18是的,所以我们有一个 102 GB 的 GPU。
00:08:22现在,我脑海中浮现的第一件事是,当我进行大语言模型推理时,我的内存消耗是什么样的?
00:08:31所以我加载了这个模型,发现这里大约有 15 GB。
00:08:37所以我大概还有 87.5 GB。
00:08:40现在,当我在这里进行推理时,我注意到传递的输入越多,需要的内存就越多。
00:08:52它在慢慢增加,但仍在增加。
00:08:56所以想象一下,如果你有 4,000、16,000 或 32,000 个 Token 左右的上下文长度。
00:09:03所以这个内存真的会增长得非常大,你实际上可能会遇到所有这些内存溢出的问题。
00:09:12所以这绝对是你的第一个问题,即你的内存随着 Token 的增加而增加。
00:09:18所以以一个简单可视化的形式,它看起来是这样的。
00:09:24你会看到的第二个问题是,你生成第一个 Token 的时间非常非常慢。
00:09:32我们用一个叫做 TTFT 的指标来衡量它。
00:09:35这是它的简写。
00:09:37当你试图将 TTFT 与输入大小进行衡量时,你会发现上下文越长,这个 TTFT 就会越慢。
00:09:52所以现在有两个问题。
00:09:54你的内存随着 Token 大小的增加而增加。
00:09:56你的 TTFT 随着 Token 大小的增加而增加。
00:09:59抱歉,不是 Token 大小,是上下文大小。
00:10:07然后第三个是吞吐量。
00:10:09吞吐量是指你每秒可以提供多少个 Token?
00:10:13然后你每秒可以为多少个用户服务?
00:10:16所以如果你在本地系统上采用非常非常朴素的实现,它将是非常串行的。
00:10:24所以如果你发送五个请求,这五个请求都将被串行处理,而不是并行处理。
00:10:31因此,如果你有多个用户,你的请求基本上需要更多的时间来完成。
00:10:40所以这就是这三个问题。
00:10:43还有第四个。
00:10:44我在这里没有描述它。
00:10:45随着我们的深入,我们可能会建立这种直觉。
00:10:48但让我们记住,这三个问题是:内存、TTFT 和吞吐量。
00:10:55好的。
00:10:56我将回到幻灯片。
00:11:02好的。
00:11:03完美。
00:11:04所以应该是这个。
00:11:17看得见吗?
00:11:18对。
00:11:19看得见。
00:11:20看得见。
00:11:21对。
00:11:22看得见。
00:11:23看得见。
00:11:24看得见。
00:11:25对。
00:11:26看得见。
00:11:27看得见。
00:11:28对。
00:11:29看得见。
00:11:30对。
00:11:31看得见。
00:11:32对。
00:11:33看得见。
00:11:34对。
00:11:35看得见。
00:11:36看得见。
00:11:37对。
00:11:38看得见。
00:11:39对。
00:11:40看得见。
00:11:41对。
00:11:42看得见。
00:11:43对。
00:11:44看得见。
00:11:45对。
00:11:46对。
00:11:47所以在那个仓库中,如果你看到一个工作坊文件夹,你会看到那个 README,然后 README
00:11:55包含了所有的链接、幻灯片和演示。
00:12:01这样行吗?
00:12:06好的。
00:12:07完美。
00:12:09好的。
00:12:10完美。
00:12:16好的。
00:12:17让我们开始学习基础知识。
00:12:19让我们开始了解这些痛点背后的原因。
00:12:25为此,我们必须看看这个推理管道。
00:12:32因此我们输入了一段文本。
00:12:35这段文本可以包含任意数量的词。
00:12:38你需要将这些词转换为标记。
00:12:41为了简单起见,你可以假设一个词等于一个标记。
00:12:46然后将它们转换为嵌入,再发送到Transformer中。
00:12:53比如有32层Transformer,但这是Mistral 7B特有的。
00:12:58不同的模型有不同类型的层数。
00:13:01然后你生成一个新的标记。
00:13:04而这个标记基本上会返回到输入中。
00:13:06接着你生成另一个标记,这个过程会一直持续下去。
00:13:09现在在整个管道中,你会发现大约95%的算力都被这些Transformer层占用了。
00:13:18因此,非常有必要看看Transformer层内部究竟发生了什么。
00:13:24在Transformer层内部,还会有更多的层。
00:13:28比如有一个归一化层。
00:13:30有一个注意力层。
00:13:32还有一个前馈神经网络等等。
00:13:35而注意力层是其中我认为非常非常著名的。
00:13:40也就是《Attention is all you need》那篇论文。
00:13:42我想这已经是家喻户晓了。
00:13:44因此,注意力机制是计算最密集的层。
00:13:48我们需要理解注意力层内部到底发生了什么。
00:13:53那么注意力机制是做什么的呢?
00:13:56如果你有一段输入文本,它需要计算每个标记相对于之前所有标记的注意力分数。
00:14:05为了做到这一点,它需要将每个标记投影到键、查询和值空间中。
00:14:14所以用更简单的话来理解就是这样的。
00:14:20如果你有10个标记,就需要10个不同的查询、键和值向量。
00:14:26如果有100个标记,就需要100个键和值向量。
00:14:31如果有1000个标记,就需要1000个键值向量。
00:14:35因此,随着输入规模的增加,键和值向量的数量也会增加。
00:14:45如果你计算每个标记的KV大小,对于Mistral 7B来说,结果大约是131字节的KV。
00:14:54这是因为你有两个向量,K和V。
00:14:59你必须将大小相乘。
00:15:02一个向量大约是128维。
00:15:04你必须将其乘以32个Transformer层。
00:15:08然后你还得乘以KV头数。
00:15:11对于Mistral 7B,它是KV头。
00:15:15它不是32,因为它使用了不同类型的注意力机制,我们稍后一定会讨论这一点。
00:15:23但是,是的,每个标记的KV大小大约是131。
00:15:30现在想象一下,如果你有4K的上下文,这个大小就会变成半个GB。
00:15:37如果你使用16K上下文,大小就会变成2.1GB。
00:15:42现在乘以用户数。
00:15:45假设你可以同时服务多个用户。
00:15:49在同一块GPU上,使用4K上下文和80个用户,可能会占用42GB。
00:15:58如果你的GPU只有比方说24GB,你的内存就已经耗尽了。
00:16:04所以你无法用这么大的上下文服务那么多用户。
00:16:11为了直观地展示这一点,来看看GPU内存。
00:16:14GPU内存中包含模型权重,这些是相当固定的。
00:16:19这些是预训练权重。
00:16:21还有一些开销也是固定的。
00:16:24虽然它会变,但变化不会太大。
00:16:29总体来说,你可以假设它是固定的。
00:16:32然后会剩下一些剩余内存。
00:16:35这部分剩余内存就是被你的KV内存(即键和值向量)所使用的。
00:16:41所以,假设你只有一个用户。
00:16:46你只能服务那些能够塞进剩下这整个80GB内存里的键值向量或标记数量。
00:17:00所以我们也可以通过一个简单的演示来展示这一点。
00:17:07好的,太好了。
00:17:08好的,太好了。
00:17:08让我看看我能不能实际运行一下。
00:17:14好的,太好了。
00:17:15让我看看我能不能实际运行一下。
00:17:15这到底在哪里?
00:17:15好的,太好了。
00:17:16让我看看我能不能实际运行一下。
00:17:21这到底在哪里?
00:17:22好的,太好了。
00:17:23好的,太好了。
00:17:24对。
00:17:25对。
00:17:25所以,你会看到——让我看看我能不能实际运行它。
00:17:31让我看看我能不能实际运行它。
00:17:33让我看看我能不能运行它。
00:17:34让我看看我能不能运行它。
00:17:35让我看看我能不能运行它。
00:17:36让我看看我能不能运行它。
00:17:37好的,太好了。
00:17:38对。
00:17:39所以,你会看到GPU已经连接上了。
00:17:43好的,太好了。
00:17:44对。
00:17:45所以,你会看到GPU已经连接上了。
00:17:56因此,在这里我们只是尝试根据数学原理以及我们建立的直觉来确认内存大小
00:18:13。
00:18:14所以模型内存是这样的,比方说如果你有70亿参数,并且正在使用
00:18:1916位精度。
00:18:20你的总内存算出来是14.6GB。
00:18:23你基本上可以通过数学计算来验证这一点。
00:18:27所以,如果完成所有这些计算,结果就是14.6GB。
00:18:33接下来是KV以及KV大小。
00:18:37所以,这个KV大小大约是每个标记131字节的KV。
00:18:42如果你进行这些计算并尝试将其可视化。
00:18:47哦,当然。
00:18:52等等。
00:18:53好的。
00:18:54然后让我们把这个可视化。
00:19:07好的,太好了。
00:19:10太好了。
00:19:11对。
00:19:12所以,这就是内存图表。
00:19:15你可以看到,随着上下文增加,内存也在不断增加。
00:19:21另一件需要意识到的是,随着用户增加,内存也会增加。
00:19:28所以,如果你想在单块GPU上服务160个用户,你只能支持
00:19:37更短的上下文长度。
00:19:40因此,你所能支持的上下文长度与通过在单个GPU上放置多个用户或并发用户所节省的成本之间
00:19:47始终存在权衡。
00:19:54所以你必须始终做出这种权衡。
00:19:57我们会在接下来的几张幻灯片中讨论这一点。
00:20:06能请你重复一遍吗?
00:20:10非常抱歉。
00:20:11我听不见你说话。
00:20:12你看到的是不是一个具有不同上下文长度的不同内存池,这样你就可以服务......
00:20:25对吗?
00:20:26好的。
00:20:27好的。
00:20:28好的。
00:20:29好的。
00:20:30很好。
00:20:31那么,让我退回一步。
00:20:32刚才那是关于内存的。
00:20:33我们需要理解为什么当我们增加上下文长度时,首字延迟(TTFT)会变慢。
00:20:53长度。
00:20:54为此,我们需要了解推理的两个阶段。
00:20:58这两个阶段就是预填充(Pre-fill)和解码(Decode)阶段。
00:21:01我想你们大家都看过很多相关的文章,但我们还是想解释一下。
00:21:06因此,当你发送大量输入标记时,你想要做的是,为所有标记构建我刚才提到的键和值向量。
00:21:20然后你需要计算每个标记相对于之前标记的注意力分数。
00:21:27你所做的所有这些运算,都是非常非常依赖矩阵运算的。
00:21:31这是一个计算密集度非常非常高的操作。
00:21:34我们都知道GPU非常适合处理高计算负载的工作负载。
00:21:41因此,我们称预填充阶段为计算受限型(Compute-bound)。
00:21:46并且它确实需要花一些时间来完成。
00:21:49所以,这个阶段完成所需要的时间,就是你的首字延迟(TTFT)。
00:21:55因此,如果你有更多的输入标记,你就必须生成更多的键值向量。
00:22:02你必须进行更多的注意力数学运算。
00:22:05正因如此,你的首字延迟(TTFT)会变得越来越慢。
00:22:12而一旦你生成了一个Token,你就需要不断重复这个过程来生成其他Token,按顺序一个接一个地进行。
00:22:20但在那个过程中,每次你都必须构建所有先前Token的Key和Value向量,这与预填充阶段是一样的。
00:22:30就像你在那里构建Key Value向量一样,这里也是如此。
00:22:34但在解码阶段,你只需要为新Token计算注意力矩阵数学运算。
00:22:40正因如此,它的计算量非常少,对计算的依赖较小。
00:22:45它也被称为内存受限型(Memory-bound)。
00:22:48我们马上就会看到为什么它被称为内存受限。
00:22:54因此,在经典的时间线中,你会看到预填充和解码阶段是这样的。
00:22:59所以,预填充所花费的时间,就是你的首字延迟(Time to First Token)。
00:23:03然后,每个解码步骤所花费的时间,基本上就是你的Token间延迟(Inter-token Latency)。
00:23:12所以,这是你需要关心的第四个指标。
00:23:18比如你的解码步骤花费了多少时间?
00:23:21好的。
00:23:22好的。
00:23:23很好。
00:23:24那么,为什么解码步骤或者说为什么解码需要花费时间?
00:23:34以及为什么它被称为内存受限操作?
00:23:38让我们试着理解这一点。
00:23:40为了理解这一点,我们需要从宏观层面来看看矩阵数学是如何在GPU上映射的。
00:23:47因此,GPU有两类内存。
00:23:50你有高带宽内存(HBM)。
00:23:52你还有共享内存(Shared Memory)。
00:23:55所以,高带宽内存容量更大,但带宽较低。
00:24:01所谓较低的带宽,我的意思是你可以以较低的速率从其中传输数据。
00:24:06与共享内存相比,共享内存容量较小,但它拥有非常非常高的带宽。
00:24:13这意味着你可以以非常快的速度向其导入和导出数据。
00:24:19因此,当你必须进行矩阵数学运算时,你必须从高带宽内存中分块挑选数据。
00:24:27你必须把它放入共享内存中。
00:24:30进行数学计算。
00:24:32将结果写回高带宽内存中。
00:24:38对于预填充阶段,当你要这样做时,你只需要进行一次这样的矩阵数学运算。
00:24:46但在解码阶段,你必须一次又一次地重复这个矩阵数学运算,因为你在按顺序生成每一个Token。
00:24:59所以,不管你的解码有多快,因为现在你只能以一定的速度将数据从高带宽内存传输到共享内存中。
00:25:14因为你受到了高带宽内存带宽速度的限制。
00:25:18因此,这决定了你的Token上限,即你实际上能以什么速率从解码步骤中生成Token。
00:25:32如果你在屋顶线模型图(Roofline Plot)中看这一点,左侧部分被称为内存受限。
00:25:42在数学上,它由算术强度(Arithmetic Intensity)决定。
00:25:47算术强度是指传输每字节数据所执行的浮点运算次数。
00:25:55因此,对于解码步骤,由于你要传输大量数据,比如所有先前Token的Key和Value向量、模型权重,
00:26:08但你进行的计算较少,因为你只为单个Token计算注意力数学。
00:26:13所以它的算术强度非常低。
00:26:18但对于预填充阶段,你传输数据是一次性的,但随后你会进行大量计算。
00:26:27因此,它的算术强度非常高。
00:26:30所以,现在从数学角度你明白了,为什么预填充的算术强度比解码高得多。
00:26:44好的。
00:26:46所以这就像是另一个小型演示。
00:26:51好的。
00:26:52每次我都必须...
00:26:53好的。
00:26:54好的。
00:26:55很好。
00:26:56我希望这个已经...
00:26:58所以,是的。
00:26:59再次强调,我们正在加载模型。
00:27:04现在,这就是预填充成本。
00:27:09所以,我们基本上在做的是获取输入文本,然后尝试生成这个预填充步骤以及它所花费的时间量。
00:27:22我们看到,随着我们增加输入Token的大小,预填充时间也在增加。
00:27:28所以你知道,这就是你的首字延迟增加的原因。
00:27:33然后就是你的解码时间。
00:27:36所以解码时间平均来说基本上是保持不变的。
00:27:41因此,如果像这样假设你忽略了冷启动,你的解码时间大致就在平均线左右。
00:27:50它仍然受到输入大小的影响。
00:27:57并不是说它是一个恒定时间。
00:28:00这是因为它仍然需要从内存中为所有先前的Token提取Key和Value向量。
00:28:07因此,在解码步骤中,你仍然会看到时间上有微小的增加。
00:28:15然后这就是经典的屋顶线图。
00:28:20好的。
00:28:22好的。
00:28:23演示。
00:28:24好的。
00:28:25好的。
00:28:26好的。
00:28:27所以,现在让我们试着理解吞吐量维度。
00:28:43你想了解你实际上可以为多少用户提供服务。
00:28:48我想我们看到了GPU内存的图表,其中看到,有一些内存是留给Key和Value向量增长的空闲内存。
00:28:56好的。
00:28:57所以,假设你只有一个用户。
00:29:01你可以支持的总KV大小是多少?
00:29:09它由你的上下文限制(Context Limit)决定。
00:29:11你能支持的最大用户数是:无论GPU可用内存是多少,除以每个用户的Key和Value大小。
00:29:25当你这样做时,计算结果就是并发用户数。
00:29:32现在,假设你的GPU是固定的,你的模型是固定的,所以你每个Token的KV大小也是固定的。
00:29:46这里只剩下两个维度,即上下文长度和你的并发用户数。
00:29:53如果你想为更多并发用户服务,你就必须缩短上下文长度。
00:29:57如果你缩短上下文长度,可能会影响你的质量。
00:30:02所以,这就是我们目前正在进行权衡的两个维度。
00:30:09那么,你真的能够服务最大数量的并发用户吗?
00:30:17在一个理想的世界里,可能不行,因为每个业务都有我们必须满足的延迟SLO(服务等级目标)。
00:30:27所以,如果你还记得我在解码步骤中说过的话,如果你有更多的输入,解码时间仍然会增加。
00:30:37如果你有更多的用户,它也会增加。
00:30:40因此,如果你有更高的批处理大小(Batch Size),你的Token间延迟最终也会受到影响。
00:30:51你的首字延迟也会受到影响。
00:30:53所以,现在你必须关心第三个维度,那就是延迟。
00:30:58因此,你拥有的三个维度是:质量、延迟和吞吐量。
00:31:04这就变成了一个权衡三角形,你必须在二者之间做出选择。
00:31:11因此,对于高级聊天应用程序,你绝对会优先考虑质量,并且优先考虑延迟。
00:31:21你不会希望你的用户等待,虽然不是无限期等待,但可能会有较大的延迟。
00:31:30你总是可以牺牲你能在GPU上支持的用户数量,并可能承担这部分成本,变得更加以客户为中心。
00:31:39所以,如果是诸如代理、抱歉,异步代理工作负载,你肯定会想要优先考虑质量和吞吐量。
00:31:53因为这些是长时间运行的任务。
00:31:56你会希望在保证非常非常高的质量的同时,尽可能多地服务并发任务。
00:32:06而且通常我们认为,如果GPU是非常昂贵的GPU,那可能不适合我们。
00:32:19但事实证明,它实际上可以为你提供百万Token最低的成本。
00:32:27但你真的必须信任你对最大用户数的计算,并且你真的必须正确做出这些估算。
00:32:40所以我们确实有,让我想想,好的,太好了。
00:32:55关于容量计算器,有一个Colab链接,因为我对它比较熟悉。
00:33:01我不得不从整个小部件库(Widget Library)中迁移出来,而且我没有时间。
00:33:16所以出于偷懒,我直接在那里选了Colab。
00:33:20向我的实验室道个歉。
00:33:23所以我的显存(V-RAM)连接好了。
00:33:34好的。
00:33:45所以可能是Wi-Fi的问题。
00:33:46好的,太好了。
00:34:02所以,我们在这里做的是,我们展示了一些带有显存、带宽、浮点运算能力和每小时成本的GPU。
00:34:11然后我们构建了这个简单的容量计算器。
00:34:19这只是一个KV可视化工具,当你增加Token数量时,你会看到KV大小在增加。
00:34:29当你增加用户数量时,你的容量大小会以快得多的速度增长。
00:34:37然后,在这个容量计算器中,让它运行。
00:34:47所以,我们这里有一个模型,是我们所选的一个70亿参数的模型。
00:34:56我们将精度设为FP16。
00:35:01现在,我们决定,我们决定GPU的方式是:你必须首先确定你最关心的那个维度,并将其固定下来。
00:35:15所以,对于高级聊天,我提到过延迟绝对是关键。
00:35:21然后对于异步工作负载,你想用单个GPU处理的最小批处理大小,这是第二个维度。
00:35:31所以,你得先把这些确定下来。
00:35:34因此,我将以高级聊天应用程序为例。
00:35:39所以我可以接受大约10毫秒的延迟。
00:35:44最小批处理大小,我不在乎。
00:35:46所以我觉得差不多两个也可以。
00:35:53好的。
00:35:54所以,在单个GPU上大概支持7个并发用户。
00:35:59我的上下文限制对我非常重要,因为我也很关注质量。
00:36:07所以,我确实看到了一些GPU。
00:36:10比如H100 80GB,大约是每小时8美元。
00:36:15但是,我的3090呢?
00:36:19是吗?
00:36:20对。
00:36:21所以大概是每小时 10 美元左右。
00:36:24因此,如果你用我们之前在数学部分分享的所有吞吐量公式去计算,就能得出每百万美元代币的成本。
00:36:34这可能会非常、非常……可能会更低。
00:36:37所以,你需要通过固定这些维度来做此类计算,并决定你的 GPU 选择,从而降低某种推理成本。
00:36:49所以,这至少是你为优化推理可以采取的第一步。
00:36:56好的。
00:37:01那么,下一张幻灯片。
00:37:04让我……
00:37:08好的,很好。
00:37:10那么现在,接下来要讲的是模型优化。
00:37:15所以我们现在基本上已经打好了基础,理解了一些痛点、这些痛点背后的原因,以及它们为什么会发生。
00:37:23我们如何才能解决 GPU 容量的问题。
00:37:29我们需要理解我们能做些什么,也就是我们还能对此做些什么。
00:37:33所以,这是关于模型优化的,我想邀请 Tanmay。
00:37:39他可以多谈谈这些模型优化,毕竟他在研究期间就做过这方面的工作。
00:37:47好的。
00:37:48好的。
00:37:49我可以控制。
00:37:50我可以控制。
00:37:54对。
00:37:55在这里。
00:37:56好的。
00:37:57大家好。
00:37:58麦克风测试。
00:37:59我终于有声音了吗?
00:38:00对。
00:38:01好的。
00:38:02那么,嗨。
00:38:03我是 Tanmay Shah。
00:38:04我是一名高级量化建模师,同时也是一名人工智能研究员。
00:38:08所以,我的重点是智能体验证,目前正在构建世界模型。
00:38:13那么对于这个话题,也就是模型优化。
00:38:16在我们开始模型优化之前,我创建了一个研究模板,以便我们更容易理解所有这些复杂的事情。
00:38:25所以,我们的模板很简单。
00:38:28第一步,我们将识别问题。
00:38:30第二步,我们将使用两个算法来解决问题。
00:38:34这些只是虚构的算法。
00:38:35所以,第一个算法叫做鸵鸟算法。
00:38:39每当我们看到问题时,就像鸵鸟一样,每当看到问题,鸵鸟就会把头埋进沙子里。
00:38:46所以,我们也会做同样的事情。
00:38:47每当我们遇到问题,我们就会直接忽略它。
00:38:51所以,这是我们应该遵循的一个重要算法。
00:38:55第二个是新创建的,叫做世界杯算法。
00:38:59例如,我们不知道谁会赢得这届国际足联世界杯。
00:39:03所以,主办方做的是,他们把 48 支球队分成了 12 个小组。
00:39:11然后是 32 强赛,所以 32 强赛目前正在进行中。
00:39:16然后是 16 强赛,接着是四分之一决赛、半决赛和决赛。
00:39:21所以他们所做的,就是把它分解成更小的问题,并将有用的结果推进到下一轮。
00:39:30因此,我们将使用相同的类比或相同的算法来理解这个模型优化以及所有这些事情。
00:39:38所以,是的,让我们开始吧。
00:39:41所以,我有一张 H100 GPU。
00:39:46我必须使用这个开源模型,也就是所谓的 GPT OSS 1200 亿参数模型。
00:39:54所以现在我想,他们是用 BF float 16 训练的,权重达到了 240 GB。
00:40:02我该怎么办?
00:40:04这就是我们面临的问题。
00:40:07首先,我们要处理的是 240 GB 的模型和 80 GB 的 H100 GPU。
00:40:16而且我必须把它只塞进一个 GPU 里,而不是多个 GPU。
00:40:21所以,我们能做什么?
00:40:22我想简单的步骤就是直接压缩它。
00:40:27但我们该如何压缩它呢?
00:40:29这是另一个挑战。
00:40:30所以,如果我们把 BF float 16 压缩为 FP8,那么它大约是 120 GB。
00:40:38但我们的 H100 GPU 仍然只有 80 GB。
00:40:42所以,我想他们做的是把它进一步压缩到 MXFP4。
00:40:49我想大小大约是 65 GB。
00:40:53所以,这是我们可以做的,压缩,但问题来了。
00:40:59并且我们会用到这个鸵鸟算法。
00:41:03我们假设将更大的模型压缩到更小的尺寸是没有损失的。
00:41:10所以第二点,在这个里面,好吧,是的。
00:41:16所以在这里,在这张幻灯片里,我们使用了 Mistral 7B。
00:41:20也就是 70 亿参数。
00:41:22所以这是一个小模型,70 亿参数。
00:41:25所以如果你把它乘以两个字节,它的权重大约是 14。
00:41:3114.5 GB,这可以轻松塞进 H100 甚至 A40 中。
00:41:38所以接下来,我们可以做的是,对于 Mistral 7B,我们不把它压缩到浮点数 16,而是应用不同的技术,比如 int8、int4 或 nf4。
00:41:53所以基本上,我们只需要使用鸵鸟算法,并坚信不会有任何质量损失之类的事情。
00:42:01但是,不知何故,我们还得通过在某些外部基准测试上进行某种测试,来数学上证明它是否真的可行。
00:42:10所以,这属于训练后量化之类的事情。
00:42:16人们也可以在微调期间这样做。
00:42:20人们也可以进行这种量化。
00:42:22这属于量化感知训练之类的事情。
00:42:25所以,让我们进入下一个问题。
00:42:31所以我们有这些巨大的矩阵。
00:42:37所以想象一下,1000 乘 1000 维度的矩阵 A,以及另一个 1000 乘 1000 的矩阵。
00:42:51所以如果你将这两个矩阵相乘,那么操作次数将是 1000 的三次方。
00:43:00这在计算方面是个问题。
00:43:06所以我们希望我们的矩阵乘法能够快速并且节省内存。
00:43:13所以我们该怎么做?
00:43:15我们有一个巨大的矩阵。
00:43:17好吧,我们拿这个来说,4096 乘 4096。
00:43:23为了解决加速运行和节省内存的问题,面对 4096 乘 4096 的矩阵,我们应该做些什么?
00:43:33所以,第一件事就是我们将使用我们的世界杯算法。
00:43:37我们可以决定一个随机数,直接垂直切分这个块。
00:43:42你选择什么并不重要。
00:43:45所以,假设我们有 4096 列。
00:43:51我们将把它分成每组 128 列。
00:43:57所以,垂直方向上就是 128、128、128、128、128。
00:44:03所以如果我们除以这个 4096,我们会得到 32 个块。
00:44:09那么,这样做会发生什么呢?
00:44:13所以,如果我们把这个垂直切分,我们就可以使用多个 GPU 来加速这个过程。
00:44:21所以这种事情被称为多头注意力机制。
00:44:26所以我们还能做什么?
00:44:29我们有一个大矩阵,就像我刚才提到的鸵鸟算法。
00:44:36所以我们主要的问题是尺寸大小。
00:44:39所以我们可以做的是,与其保留所有这 32 个垂直块,不如扔掉 31 个块。
00:44:49然后我们假设这一个块就已经足够应付所有的查询了。
00:44:57我们的损失几乎可以忽略不计。
00:45:00然后我们想出了这个算法。
00:45:04这个算法被称为多查询注意力机制。
00:45:08所以正如我们所见,现在我们处于两个极端。
00:45:12一个是多头注意力,我们把它拆成 32 个块,并使用不同的 GPU 或进行一些并行处理。
00:45:22与此同时,我们直接扔掉了 31 个块。
00:45:26并且我们把这叫做多查询注意力。
00:45:31所以在两个极端之间,我们应该找到一个折中的中间地带。
00:45:38我们可以说,与其扔掉所有 31 个,也许我们可以把一些块组合在一起。
00:45:49我们可以假设相似的块会关注相似类型的查询。
00:45:58所以这种技术属于分组查询注意力,现在非常流行。
00:46:04甚至在 Mistral 或其他模型中,这种分组查询注意力也都在起作用。
00:46:11所以现在,我们已经理解了我们有一个大矩阵。
00:46:16我们可以随心所欲地对其进行切分,并通过一些数学计算,
00:46:19证明这种损失几乎可以忽略不计。
00:46:23所以我们还能做什么?
00:46:25所以在那之后,在分组查询注意力之后,
00:46:34看,我们有一个大矩阵。
00:46:38一个是键,一个是值。
00:46:42让我们把那个矩阵压缩成一个潜在向量。
00:46:47然后想出一些算法,从潜在向量重建出我们的原始矩阵。
00:46:55所以这种策略就属于这个范畴。
00:46:59多头潜在注意力。
00:47:02但话又说回来,它在旋转位置编码(RoPE)上有一些问题,因为 RoPE 是位置相关的,而它是位置无关的。
00:47:10所以,人们也需要为键包含一些索引,以便能够进行映射。
00:47:16但话又说回来,主要的问题是,我们为什么要将所有这些大矩阵相乘。
00:47:25因为这就是注意力机制的工作原理,每个标记(token)都会关注每一个标记。
00:47:33所以,如果……我们不去关注所有之前的标记,而只关注对我们重要的那些重要标记,会怎么样呢?
00:47:42所以,这类领域正在不断演进。
00:47:46所以,这属于稀疏注意力的范畴,比如 DeepSeek 稀疏注意力。
00:47:51所以,是的。
00:47:53然后,是的,所以,好吧。
00:47:56接下来。
00:47:59对,接下来一个是 Flash Attention。
00:48:02在 Flash Attention 中,主要的问题在于,
00:48:09目前,目前,目前,不是目前,就是现在,几乎每个人都在使用 Flash Attention,但在 2022 年或 2023 年的时候。
00:48:20它的工作原理是这样的。
00:48:23它的工作方式是,这个 Q、K,也就是 Query 和 Key 矩阵,它们原本存储在 HBM 中。
00:48:34它会加载,它,首先,将其加载到我们的 Tensor Core 中,进行一些计算,然后将其写回 HBM。
00:48:47然后,这个过程会重复多次。
00:48:51所以在 Flash Attention 中,他们的做法是,不是去相乘整个矩阵,
00:48:59而是像分块算法那样,将大矩阵分割成小的 Tile(图块),
00:49:05并且只把这些小 Tile 放进 SRAM 中,以便快速进行矩阵乘法,
00:49:13同时跟踪这三个变量,以便计算在线 Softmax(online softmax)。
00:49:20所以,是的,这只是数学问题,如果我们有一个多头注意力机制,如果是 524 个 KV,
00:49:34那么这取决于我们想要多少分组,因此,如果我们不想用 32 个 KV 头,而只想用 8 个 KV 头,
00:49:47那么我们可以获得 4 倍的压缩,这就是多头潜在注意力(Multi-head Latent Attention)。
00:49:54这个公式因模型而异,取决于你的模型有多少层。
00:50:00所以在最初的 DeepSeek 论文中,我想他们使用了 128 维,128,1.something,我记不清确切的维度了,但根据这一点,
00:50:11他们使用了这个潜在向量,其中维度是 512,还有大约 64 维用于 RoPE 索引,
00:50:23然后他们表明它的压缩率比多头注意力机制高出 56 倍。
00:50:32好的。
00:50:33所以,是的,这就是权衡图,在这里我想我们还没有讨论线性注意力(linear attention)或 Mamba。
00:50:50主要的问题在于所有这些矩阵乘法,目前每个人都在使用注意力机制。
00:50:56假设在未来,如果我们不想使用注意力机制,不再按顺序生成 Token,而是使用扩散模型(diffusion models),
00:51:07在那里我们可以同时生成所有内容,那么所有这些算法也将随之改变。
00:51:14但在这里,我认为他们还有另外两种,一种是线性注意力,另一种是 Mamba。
00:51:19根据这张幻灯片,如果我们不进行任何压缩,那么 MHA 只是在并行化这个过程。
00:51:29所以没有质量损失,所以很好,然后是分组查询注意力(Grouped Query Attention),我想几乎每个模型都在使用 GQA 和 DSA 之类的东西。
00:51:42是的。
00:51:43我想我们在注意力机制记分卡中也提供了同样的内容,所以,我认为 MHA 的质量很好,吞吐量也还可以。
00:51:56至于分组查询注意力,这也取决于你的用例,虽然质量与多头注意力几乎相似,但用例也非常重要。
00:52:10是的,多查询注意力(Multi-query Attention)只是其中一个极端。
00:52:13我们,不知道为什么,但我们只是假设我们只需要一个块,然后所有的 Query 都会去关注那些更小的块。
00:52:24所以 MQA 的质量并不是那么好。
00:52:29至于多头潜在注意力,如果你尝试过这些 DeepSeek 模型,我认为它们在质量方面做得非常出色,此外还有滑动窗口。
00:52:44所以所有这些都是一些技术,比如调整窗口之类的,并且线性注意力不是将所有东西相乘,而是说先总结所有内容,然后在其中进行查找,至于 Mamba,这只是一种状态空间模型(state-space model),是的。
00:53:09所以对于模型优化,我们这里也有两个 Notebook。
00:53:28所以,我得转到这里。
00:53:35好的,至于量化,比如这个演示,这个已经运行过了吗?没有。
00:53:51让我运行一下这个。
00:53:54好的,我们正在加载模型,比如 Mistral 7B。
00:54:10所以这个是带有 FP16 基准的模型。
00:54:20等等。
00:54:21它运行了吗?
00:54:21等等。
00:54:22它运行了吗?
00:54:23好吧。
00:54:24所以,呃,两毫秒,它运行了。
00:54:27这个运行了吗?
00:54:28好吧。
00:54:29所以,是的,这次它正在以 FP16 精度获取该模型。
00:54:35好吧。
00:54:36所以,呃,两毫秒,它运行了。
00:54:40这个运行了吗?
00:54:41好吧。
00:54:42所以,是的,这次它正在以 FP16 精度获取该模型。
00:54:47Wi-Fi。
00:54:58这需要一些时间。
00:55:03好吧。
00:55:08是的,它,因为正在从 Hugging Face 下载权重。
00:55:14哈?
00:55:17对,所以 Colab 是在线运行的。
00:55:22是的。
00:55:23因为它需要通过 Hugging Face 发起网络请求并进行获取。
00:55:30我不知道,但是下载可能需要一些时间。
00:55:35好吧。
00:55:36好吧。
00:55:37好吧。
00:55:38好吧。
00:55:39好吧。
00:55:40所以在这里我们可以看到,在 FP16 精度下,内存大小大约是 15 GB 左右。
00:56:00我们正尝试使用 int8 进行 2 倍压缩,正如 Talmud 所讲的那样。
00:56:15好吧。
00:56:16所以我们确实看到,你的内存大小现在变成了 0.7、0.5 GB 左右。
00:56:21这意味着现在你有更多的内存空间留给 KV 缓存来增长。
00:56:27这意味着你要么可以支持更高的上下文限制,要么可以支持更多并发用户。
00:56:36所以,如果你使用 int4,基本上你是在进行 4 倍压缩。
00:56:41因此,通过 4 倍压缩,它会变得更低。
00:56:45我想大约在 3 到 4 GB 左右。
00:56:50对,4.5 GB。
00:56:51然后,是的。
00:56:52所以,这是,等等。
00:56:53所以这只是一个基础图表,这些都是理论数字。
00:57:09我们这里没有做任何吞吐量测试。
00:57:12但是,通常你会发现你的内存增加了。
00:57:15所以你也会拥有稍高的吞吐量。
00:57:21根据我们研究的一些基准测试,我们看到 int8 压缩。
00:57:26它的吞吐量确实较低。
00:57:32好吧。
00:57:33然后,还有一个关于注意力机制的演示。
00:57:42所以关于注意力,好的。
00:57:45我得运行这个。
00:57:58好吧。
00:57:59所以它已经运行了。
00:58:02哦。
00:58:03等等。
00:58:04为什么显示没有检测到 GPU?
00:58:09我应该说应该检测到 GPU 才对。
00:58:18哦。
00:58:19好吧。
00:58:29等等。
00:58:30等等。
00:58:30等等。
00:58:50这太令人意外了。
00:58:57我猜它不知出于什么原因无法检测到 GPU。
00:59:05我们这里明明有 GPU。
00:59:11好吧。
00:59:12没关系。
00:59:14是的。
00:59:14但是这里的基本思想更多是,当你试图通过使用不同的注意力机制来压缩计算时,
00:59:26比如从多头注意力转向分组查询注意力,然后再转向 MLA。
00:59:33你会开始看到一些优化效果。
00:59:38我想昨天晚上我们做了一些基准测试。
00:59:43我想纠正这部分。
00:59:45所以,不是 56 倍。
00:59:48而是 14 倍。
00:59:50基本上,演示中有一个计算错误,它没有乘以层数。
01:00:02是的。
01:00:03所以为此道歉。
01:00:04因此,与你的多头注意力相比,这个 MLA 节省了大约 14 倍的量。
01:00:15既然我们已经了解了痛点、基础知识以及优化的一方面(即模型优化),我们接下来想讨论你在服务(Serving)端可以做些什么。
01:00:31所以第一点我们看到,当你执行一个简单的解码步骤时,你正在拉取模型权重,然后为所有先前的 Token 重新计算 Key 和 Value 向量。
01:00:50尽管你已经为所有 Token 计算过这些向量了。
01:00:55所以这里绝对存在大量的计算浪费。
01:01:02如果你对其时间复杂度进行分析,结果会是 O(N 平方)。
01:01:07解决这个问题的方法是与内存进行经典的权衡。
01:01:12你可以针对 Token 维护这些向量的内存,并且可以引用该内存。
01:01:19所以,这个内存被称为 KV Cache。
01:01:24并且,整个流程看起来大致是这样的。
01:01:27然后,基于这个 KV Cache,确实有四种优化方案是真正可行的。
01:01:35第一个是关于 Paged Attention(分页注意力)。
01:01:42那么,现在的区别是什么,现在的问题是什么?
01:01:45所以,当你向GPU发送多个输入请求时,这些请求会组合成一个批次。
01:01:52每个请求都会被分配一块连续的内存存储空间。
01:01:58比方说,我举个例子,假设是2个KV。
01:02:05然而,你的请求实际上可能只需要1个KV。
01:02:11因此,就会产生大约50%的内存碎片。
01:02:19而这种内存碎片基本上会导致内存浪费。
01:02:24这意味着内存中本来有空间可以服务更多的请求,但由于你在寻找连续的内存块,导致无法处理这些请求。
01:02:36所以,这借鉴了操作系统的运作方式。
01:02:41比如,你维护一个逻辑内存,并且底层对应一个物理内存。
01:02:47因此,在逻辑内存中,每个Token的KV向量看起来依然是连续的。
01:02:59但它实际上会被映射到不同的物理地址。
01:03:06这确实在节省大量内存方面起到了很大作用。
01:03:11而这之所以能够实现,仅仅是因为他们将内存视为一组块(Blocks)。
01:03:18并且你会根据请求的需求动态分配这些块。
01:03:22随着新的Token不断涌入,它们会需要这种内存。
01:03:27另一个着力点是,当你把多个请求放在一个批次中发送时,
01:03:39GPU会处理这些请求。
01:03:42但在该批次中的所有请求都完成之前,它不会接受新的批次。
01:03:49所以,这个图表看起来很像分页注意力机制(PagedAttention)。
01:03:52但这里更多的是关于GPU何时可以用来处理下一个批次。
01:03:59因此会存在一段时间,GPU实际上处于完全空闲状态。
01:04:04而你希望解决这个问题。
01:04:08为此,当时的想法是:好吧,让我们采用连续批处理(Continuous Batching)。
01:04:17因此,连续批处理也对提升吞吐量大有帮助,因为现在你可以非常迅速地发送更多请求。
01:04:24确保GPU始终得到利用,时刻处于忙碌状态,而不是闲置着。
01:04:33这样你就能节省计算资源。
01:04:36第三个是前缀缓存(Prefix Caching)。
01:04:39大家还记得,KV缓存帮你在单个请求的各个Token之间节省了计算量。
01:04:46但是,如果你在多个请求中拥有相同的Token,又该怎么办呢?
01:04:53这部分该怎么节省开销呢?
01:04:55因此,由vLLM引入的前缀缓存正好解决了这个问题。
01:05:04接下来是第三个,不对,其实我们谈谈第四个。
01:05:10我们讨论过对模型进行量化,但你也可以对KV权重进行量化。
01:05:21这意味着现在你的键(Key)和值(Value)向量所需的空间更小了。
01:05:28也就是说,你可以在内存中存储更多的键值向量。
01:05:32这意味着你可以处理更多的Token。
01:05:34这意味着你可以支持更长的上下文限制。
01:05:37这意味着你可以提供更高的模型质量。
01:05:41所有这些特性在vLLM中都已经具备了。
01:05:51你真的不需要重复造轮子。
01:05:56你可以把vLLM部署到生产环境中,然后就能看到这种性能的增长。
01:06:03接下来,我们做了一个基准测试(Benchmark)。
01:06:08这个基准测试,让我看看我有没有准备好。
01:06:14在这里。
01:06:17这些是演示(Demos)。
01:06:22运行这个基准测试大约需要一个小时,因为你必须不断地停止和重启vLLM服务器,还要加载模型等等。
01:06:35所以测试确实花了很多时间,但我可以在这里直接向大家介绍我们在做什么。
01:06:41我们保持模型不变,使用的是Mistral 7B。
01:06:46然后我们有一组作为输入的测试问题。
01:06:53你可以把它们当作提示词(Prompts)。
01:06:56接着这里有几个辅助函数,比如检查服务器是否启动。
01:07:01这个服务器就是vLLM服务器。
01:07:04然后还有获取vLLM指标的辅助函数。
01:07:09我会讲一讲这些指标分别是什么。
01:07:14然后还有很多基准测试相关的内容。
01:07:17接着你需要测量KV内存的使用情况等。
01:07:22所以这些就是辅助函数。
01:07:24基准线(Baseline)非常简单。
01:07:26比如,我们有一个Hugging Face的基准线。
01:07:29这是最原始的方式,将文本发送给LLM,然后获取返回的响应。
01:07:36我们在这里看到了一些结果。
01:07:38我们发现Hugging Face的吞吐量大约是每秒51个Token。
01:07:44首字延迟(Time to First Token)大约是54。
01:07:46然后Token之间的延迟是19。
01:07:49这些测试全都是在H100上运行的。
01:07:56接下来,我们启动一个非常默认的vLLM服务器。
01:08:00因此,vLLM默认提供了分页注意力、连续批处理和KV缓存。
01:08:08所以这三项功能默认都是具备的。
01:08:13然后,当你尝试对比这些基准测试时,你会发现吞吐量提高了近15倍。
01:08:21你能够每秒处理更多的Token。
01:08:24然后,你的首字延迟(TTFT)也有所上升。
01:08:34接着,Token间的延迟有所下降。
01:08:38然后,你的KV缓存相对于用户数以及上下文的使用量肯定增加了。
01:08:43现在,当我们在其中应用前缀缓存时。
01:08:51通过前缀缓存,你会发现吞吐量有了进一步的提升。
01:08:57你的首字延迟(TTFT)减少了。
01:08:59你的Token间延迟大致相同。
01:09:02然后,KV缓存使用量相对于用户数来说有所下降。
01:09:09相对于上下文的使用量并没有下降。
01:09:12它大致保持不变。
01:09:14我认为这也是大致相同的。
01:09:16这其实算不上什么大问题。
01:09:19当你在其基础之上应用KV量化时。
01:09:25你会发现吞吐量几乎是相似的。
01:09:33你的首字延迟是相似的。
01:09:36你的Token延迟也是相似的。
01:09:39但随后,你的KV内存使用量确实下降了。
01:09:42这是因为你对键值空间进行了量化。
01:09:49然后还有一个推测解码(Speculative Decoding)的概念,Tanmay会对此进行讲解。
01:09:54所以,当你尝试对这些进行基准测试时,你也会发现KV内存的使用量略有减少。
01:10:06尽管结果大致相同。
01:10:08所以,总的来说,这些就是各项指标。
01:10:21也许我应该缩小画面。
01:10:25好吧。
01:10:26没有。
01:10:27缩小画面。
01:10:28没起作用。
01:10:29太棒了。
01:10:30所以,是的,这些就是vLLM的基准测试。
01:10:35顺便说一句,这是你们生产环境的默认选择。
01:10:38当我们讨论其他推理引擎时,我们也会分享这个决策树。
01:10:48所以,我们应该讨论一下,在其之上我们还能做哪些其他的推理优化。
01:10:58以及还有哪些其他的解决方案被提出来。
01:11:02因此,我想再次邀请Tanmay。
01:11:07他来为大家讲解其中一些优化方案。
01:11:11哦,抱歉。
01:11:12非常抱歉。
01:11:13我刚刚没打开幻灯片。
01:11:26刚才那个是?
01:11:27好吧。
01:11:28太棒了。
01:11:29完美。
01:11:30哪一个?
01:11:31推测解码。
01:11:32对。
01:11:33谢谢你,Harshal。
01:11:34对。
01:11:35所以,这些全都是推测解码。
01:11:40所有这些都是——怎么说呢——同一种苏打水的不同口味。
01:11:46所以,这项技术属于解码加速器(Decoding Accelerators)。
01:11:51首先,我们只讨论这种推测解码,但还有其他变体,比如自推测解码(Self-speculative)、Eagle、Medusa。
01:12:01我个人只比较喜欢其中一个,也就是Eagle算法。
01:12:05那么,让我们从推测解码开始讲起。
01:12:06好。
01:12:07好的。
01:12:08那么我们先来看看什么是推测解码。
01:12:09主要的问题在于,在Transformer架构中,所有这些Token都是按顺序一个接一个生成的。
01:12:26如果我们换个思路,使用一个较小的模型,让小模型先生成比方说四到五个Token,会怎么样呢?
01:12:37而这个教师模型,或者按照我们的世界杯算法来比喻,我们可以称之为裁判。
01:12:43所以裁判会决定它接受其中多少个Token。
01:12:48这个循环会一直持续下去。
01:12:51我们的设想是,在某些特定领域,这种方法会非常奏效。
01:12:59比如在编写代码时,几乎没有什么创造性可言。
01:13:05每段代码或语法都大同小异。
01:13:08所以,这种方法或许能派上用场。
01:13:10但根据我個人的測試,我發現這種推測解碼完全沒有用。
01:13:18不過,其他技術像是自我推測解碼,其中教師模型也有一個頭、輔助頭,它會做類似基礎模型或小模型正在做的事情。
01:13:35但是後來這個 EGLE 出現了,EGLE 1、2、3,我不知道有多少版本,但它的意思是,與其創建、與其生成 Token,不如訓練一個小模型,然後直接從其中一個主模型的層中取得特徵,這樣它生成的就不是 Token,而是這個特徵。
01:14:02所以比起其他這類技術,EGLE 更好。
01:14:10然後另一個是 MEDUSA,它的說法就是,直接平行生成所有這些 Token。
01:14:17好的,所以這裡,在這個幻燈片裡。
01:14:21對。
01:14:22下一張幻燈片。
01:14:24好的。
01:14:25好。
01:14:26好,對。
01:14:27好。
01:14:28現在我們來談談,現在我們要來談這個Prefix Caching(字首快取)。
01:14:34所以,我不知道大家是否正在使用這種靜態字首快取。
01:14:39但問題是,字首快取的主要問題在於,有時候我們打字會犯一些小錯誤。
01:14:47而這種標準的靜態字首快取基本上是,它取得提示詞,做一些雜湊。
01:14:53然後下次當使用者問類似的問題時,它會嘗試比對雜湊值。
01:14:58所以,如果雜湊值相同,那麼它就不會重新計算所有這些 K 和 V,而是直接從儲存中取得。
01:15:09但是你知道,有時候我們會犯錯,或者我們可能會改一個詞或一個字母之類的。
01:15:15那我們的快取未命中率就會非常高。
01:15:21所以這就是為什麼有這個,Radix Tree(基數樹)。
01:15:25因此,Radix Tree 正變得非常受歡迎,這也是因為代理(Agent)。
01:15:30所以我想幾乎每個人都在做 Agent,而且大部分的計算都發生在測試時、推論這類階段。
01:15:38在那個階段,我們不斷詢問相同的問題和提示詞。
01:15:42例如,你是一個專家軟體工程師乘以 200 次。
01:15:48這種迴圈在這類代理式的架構中不斷進行。
01:15:55在這種情況下,把類似的東西保留或儲存在 Radix Tree 中是很有必要的。
01:16:03所以 Radix Tree 只是字首樹的進階版本,如果某個節點沒有任何分支,我們就會將它摺疊。
01:16:17對於這種不斷重複相同事情的工作來說。
01:16:24這個 Radix Tree 幫助很大,而 sglang 就將這種演算法用於字首快取。
01:16:35好的,對,然後還有另一件事。
01:16:38一個是 TensorRT-LLM。
01:16:41這非常令人混淆。
01:16:42我剛開始的時候也是一頭霧水。
01:16:46什麼是 TensorRT-LLM?
01:16:49所以,是的,TensorRT 只是一種標準的 SDK。
01:16:55而 TensorRT-LLM 則只是一個推論引擎。
01:16:59就像 vLLM、sglang 一樣。
01:17:01但問題是它與 NVIDIA 有關。
01:17:05他們對每一個層和每一個問題都進行了最佳化。
01:17:10正如我在我們的世界盃演算法中所提到的,他們打破了一切,並在硬體層面上也最佳化了一切。
01:17:18所以,好的,下一個。
01:17:23對,所以在這個工作坊中,我們也做了一些基準測試,看看哪個最好。
01:17:33所以我們的設定大致是這樣的。
01:17:36因此我們做了兩種測試。
01:17:39第一種是沒有代理測試的,我們只是……
01:17:44我們使用了 ShareGPT 這個資料集,然後用 vLLM 和 sglang 來詢問這些問題。
01:17:57好的。
01:18:05對,好的。
01:18:07讓我放大一下。
01:18:12好的,太好了。
01:18:13好,對,在這個工作坊中,我們使用了 H100,我們的第一次測試是我們只是問……
01:18:23我們從 ShareGPT 中取出問題並放入 vLLM 和 sglang 中,我們發現實際上在誰更好方面沒有統計學上的差異。
01:18:34所以兩者都有幾乎相似的……
01:18:37因此,兩者在每秒請求數、首字延遲和延遲方面都滿足了類似的要求。
01:18:43不過我們唯一的差別是在代理分支(Agentic branching)期間看到的。
01:18:50所以我們做的是,我們問了類似的問題,像是你是世界上最好的軟體工程師。
01:18:59所以請解決城市交通擁堵之類的問題。
01:19:04然後,我們將其輸入到 LLM 中。
01:19:08LLM 生成了一些輸出。
01:19:10然後我們也進行了第二輪。
01:19:13所以,一旦這個 LLM 生成了輸出,那麼在第二輪中,我們特別提到……
01:19:22提供、審查提案並給出一到十的評分。
01:19:28所以這是我們做的兩輪,並且這個迴圈不斷重複。
01:19:35我们发现,对于这种一切都很标准的工作流,所有这些提示词和上下文工程就派上用场了。
01:19:47因此,如果我們做好適當的代理分支,那麼我認為這個 SGLang 要好上三到四倍。
01:19:54但話又說回來,這可能取決於不同的設定。
01:19:58如果你這樣做,你可能會得到不同的結果。
01:20:02好的。
01:20:03對。
01:20:04所以,我想……
01:20:06我們把它上傳到 GitHub 了嗎?
01:20:08對。
01:20:09好的。
01:20:10對。
01:20:11所以 PDF 也放進雲端硬碟裡了。
01:20:15它和幻燈片是同一個連結。
01:20:18這裡做個快速總結。
01:20:22所以對於標準的 API 工作負載吞吐量,你會發現 vLLM 和 SGLang 是一樣的。
01:20:31所以如果你沒有……
01:20:33如果你有標準的工作負載,絕對選擇 vLLM。
01:20:36它本來就是生產環境的預設選擇。
01:20:38但 Tanmay 剛才也提到,當你嘗試將其用於代理工作負載時,那正是 SGLang 真正發光發熱的地方。
01:20:50它會為你提供所有這些好處。
01:20:55所以,對。
01:20:58把 vLLM 當作預設選項。
01:21:00但如果你有代理工作負載,不妨考慮轉向 SGLang。
01:21:05如果你對 vLLM 的部分不太滿意的話。
01:21:09好的。
01:21:10讓我……
01:21:15等等。
01:21:18好的。
01:21:21然後就像是還有……
01:21:29針對 120B(1200億參數模型)進行的比較。
01:21:33像是針對 GPT OSS 120B。
01:21:36這是 PlayPy 準備的基準測試。
01:21:42這裡有一個部落格連結。
01:21:46哦,不錯。
01:21:48好的。
01:21:49對。
01:21:50所以他們做了類似的基準測試,並且把 TensorRT-LLM 也包含在其中。
01:21:57當然,你隨時可以查看這些基準測試,並試著了解哪一個基本上適合你的使用情境。
01:22:05正如我們提到的 TensorRT,他們試著從硬體端進行最佳化,以獲得尖峰硬體效能。
01:22:16然後,在你想部署你的引擎時,一旦你在 vLLM、SGLang、TensorRT 之間做出選擇之後,會有一些新興的引擎湧現出來。
01:22:29NVIDIA Dynamo 肯定是其中之一。
01:22:43所以它們也是為了代理會話路由而設計的。
01:22:49Hugging Face 也一直都在。
01:22:51這是一個簡單的概述。
01:22:53還有史丹佛最近提出的 MSTAR 引擎。
01:23:00NVIDIA Dynamo 則是用於多模態。
01:23:05所以你絕對可以去探索這些。
01:23:08當你嘗試基本上——為了做個快速總結,我們從基準開始。
01:23:15我們試著找出什麼模型能符合我們的使用情境。
01:23:22所以你可以選擇像是 DeepSeek。
01:23:27你可以選擇像是——不要選 Mistral 7B。
01:23:30我的意思是,它不太好。
01:23:32不過,對啦。
01:23:35所以你選擇你的模型,並且你希望擁有較小的記憶體,並嘗試把較大的模型塞進較小的記憶體中。
01:23:44這樣你就能節省 GPU 的成本。
01:23:47所以你可以進行所有這些量化(Quantization)。
01:23:51然後你可以在底層使用正確的服務引擎來套用所有這些服務最佳化。
01:23:58這樣就能真正提供你所期望的吞吐量。
01:24:07現在,回家後你大概可以做的一件事——因為我們無法在這裡瀏覽所有資料——絕對是閱讀一些來源資訊,像是不同的注意力機制、不同的這些引擎。
01:24:27試著閱讀網路上現有的各種基準測試。
01:24:34然後,還有很多深入的指南或它的下一階段,也就是學習一些 KV 快取逐出策略。
01:24:45因此,世界正朝著擁有一個獨立的 KV 快取工程領域發展。
01:24:50所以你會想了解那裡面正在發生什麼事。
01:24:52因此,KV 逐出、快取壓縮、混合記憶體。
01:24:57周圍有非常多的解決方案正在湧現。
01:25:01所以,始終試著堅持這些基礎、基本原理或第一性原理。
01:25:08並試著看哪種解決方案基本上解決了什麼問題,以及你的使用情境是否真的需要解決該問題。
01:25:17然後,還有分散式 LLM 推論,這完全是另一個重點。
01:25:26你可能也需要在那裡舉辦一個兩小時的工作坊,來瀏覽所有的內部原理並進行所有實機操作。
01:25:40對,這是我們正試著為紐約 AI 工程師會議(AI Engineer New York session)提議的內容,也就是更深入探討 LLM 推論的進階部分。
01:25:51所以這個工作坊更多是針對初學者和中級水準的。
01:25:55所以在这种形式下,我们也会收集大家的反馈以及兴趣。
01:26:02如果你觉得某些部分需要改进,也欢迎随时提出反馈。
01:26:09如果你希望在纽约展(New York Fair)上看到这个工作坊,请务必填写你的兴趣意向。
01:26:22哈?
01:26:24哦,怎么会这样?
01:26:27Boom。
01:26:32我来检查一下。
01:26:37好的。
01:26:38哈?
01:26:39对。
01:26:40链接没问题吧?
01:26:41对。
01:26:42不是二维码吗?
01:26:43好的。
01:26:44可能我忘记把这两个连起来了。
01:26:45好吧。
01:26:46行。
01:26:46对。
01:26:47所以,如果你能提供那个。
01:26:49好的。
01:26:50行。
01:26:51对。
01:26:52所以,如果你能提供那个。
01:26:53好的。
01:26:54对。
01:26:55好吧。
01:26:56行。
01:26:57对。
01:26:58所以,如果你能提供那个。
01:26:59让我...
01:27:00好吧。
01:27:00好吧。
01:27:01行。
01:27:02对。
01:27:02所以,如果你能提供那个。
01:27:03让我...
01:27:04好吧。
01:27:05那就没问题了。
01:27:06嗯,对,我想我们差不多要结束这个工作坊了。
01:27:13我确信在座各位一定有很多问题。
01:27:14所以,我们可以把这些问题留在会后交流。
01:27:15我们可以碰面,然后讨论这些问题。
01:27:16好的。
01:27:17当然。
01:27:18当然。
01:27:19谢谢大家。
01:27:20感谢参与。
01:27:21嗯,我觉得真的...
01:27:22呃,谢谢大家。
01:27:23呃,谢谢大家。
01:27:24感谢参与。
01:27:25谢谢大家。
01:27:26感谢参与。
01:27:27嗯,我觉得真的...
01:27:28哦,好的。
01:27:29嗯,好的。
01:27:30那就没问题了。
01:27:31嗯,对,我想我们差不多要结束这个工作坊了。
01:27:33嗯,对,我想我们差不多要结束这个工作坊了。
01:27:36我确信在座各位一定有很多问题。
01:27:39所以,我们可以把这些问题留在会后交流。
01:27:41我们可以碰面,然后,嗯,讨论这些问题。
01:27:42好,没问题。
01:27:43谢谢大家。
01:27:44嗯,我觉得这次非常有意义,大家也都来到了现场。
01:27:49非常感谢。
01:27:50好,谢谢。

핵심 요약

优化大语言模型推理需要通过量化、注意力机制改进以及vLLM和SGLang等服务引擎来平衡内存、延迟和吞吐量之间的权衡。

하이라이트

  • 大语言模型推理市场规模目前达到230亿美元,并且随着每个用户、每个Token的使用而产生经常性运营成本。

  • Mistral 7B模型在加载时占用约15GB内存,而随着上下文长度增加,内存消耗与首字延迟(TTFT)会显著上升。

  • 解码阶段由于需要不断从高带宽内存传输模型权重和KV缓存,受限于内存带宽,表现为内存受限型操作。

  • vLLM引擎凭借分页注意力、连续批处理和前缀缓存等技术,将吞吐量较原始Hugging Face实现提高了近15倍。

  • 多头潜在注意力(MLA)通过将KV矩阵压缩为潜在向量,比标准多头注意力机制显著节省了内存开销。

타임라인

大语言模型推理的痛点与基本指标

  • 大语言模型推理是一项随着用户和Token增加而不断扩展的经常性运营成本。
  • 内存消耗随着上下文长度的增加而线性增长,容易导致内存溢出错误。
  • 首字延迟(TTFT)随着输入标记数量的增加而变慢。

工作坊从第一性原理出发,剖析了大语言模型在生产环境中部署时面临的成本压力与性能瓶颈。通过Mistral 7B模型的演示展示了内存占用、首字延迟以及串行处理带来的低吞吐量问题,为后续的优化方案奠定了基础。

推理管道、注意力机制与计算瓶颈

  • 推理过程分为计算受限的预填充阶段和内存受限的解码阶段。
  • 解码阶段因为频繁从高带宽内存读取键值向量而受到内存带宽的限制。
  • 延迟、质量和吞吐量构成了推理优化的权衡三角形。

深入解析了Transformer层内部注意力机制的数学原理,解释了键和值向量如何随上下文增加而占用显存。通过 roofline 模型图对比了预填充与解码阶段在算术强度上的根本差异,并介绍了容量计算器的使用方法。

模型优化技术与量化策略

  • 量化技术如int8和int4可以将模型权重压缩数倍,从而腾出显存空间给KV缓存。
  • 分组查询注意力(GQA)和多头潜在注意力(MLA)有效减少了KV缓存的内存占用。
  • Flash Attention通过将矩阵分块放入SRAM中,减少了高带宽内存与计算核心之间的数据传输开销。

探讨了如何在单块GPU上运行超大模型。通过模型量化和修改注意力机制结构,如从多头注意力转向分组查询注意力或多头潜在注意力,能够在几乎不损失模型质量的前提下大幅降低计算和内存成本。

服务引擎优化与基准测试对比

  • vLLM通过分页注意力、连续批处理和前缀缓存默认提供了生产环境的高性能支持。
  • SGLang利用基数树(Radix Tree)优化前缀快取,在代理工作负载中表现出更高的效率。
  • 基准测试表明,使用专用的推理引擎能够将吞吐量提升高达15倍。

对比了vLLM、SGLang和TensorRT-LLM等主流推理引擎的性能表现。标准API工作负载下vLLM是默认首选,而涉及复杂代理分支的工作流则更适合采用SGLang,最后展望了KV快取逐出与分散式推理的未来方向。

커뮤니티 글

아직 글이 없습니다. 이 영상에 대한 첫 번째 글을 작성해 보세요!

이 영상에 대해 글쓰기