[音乐]
大家好,今天过得怎么样?哇!好吧,今天是推理之夜。来吧,你们得兴奋起来。好的,我其实不知道如何用图片来表现“推理”(inference),所以我做了万圣节主题。所以,用我三岁的孩子会说的话来说,让它变得更“ spooky ”(诡异/吓人)了。嗯,这是我的妻子在这儿。好的,那么为什么是推理?谁在乎推理呢?嗯,我想我们在 YC(Y Combinator)做了很多关于推理的投资。我们对此研究非常多。嗯,我以前经营一家 AI 公司,我们会进行训练,也会做大量的推理工作,而推理总是瓶颈所在。要在大规模下做好推理而不让销售成本(COGS)让你破产是非常困难的,这是一个被称为 COGS 的术语,一些软件行业的人可能不知道这个词的意思。嗯,所以在过去的十年里,它产生了 2,200 亿美元的市值。
而且我们有两家非常酷的公司——抱歉,今晚大概有三四家很酷的公司会发言。这里的第八位,实际上,我们一起有研究负责人 Dan Fu(注:原文为 Foo/Fu)。那么,为什么推理很重要?这真的有什么关系吗?嗯,这个公……我喜欢从训练 FLOPs(浮点运算次数)与推理 FLOPs 的比较来思考它,而唯一一家真正发布所有数据的公司是 DeepSeek。所以,只是为了思考我们应该在多大程度上关心训练 FLOPs 与推理 FLOPs 的比例……DeepSeek 报告称,训练 DeepSeek V3 大约需要 280 万个 H1/H800 GPU 小时,他们每天使用约 43,000 个 H800 GPU 小时,而且这是旧数据,对吧?这不是最新的数据。所以可能远不止这些数字。但即使按那些数字计算,对于该权重文件每年而言,推理的 FLOPs 也比训练多约 6 倍。
如果你相信权重文件的使用寿命,我不知道,比如说 2 年,那么它的重要性应该是训练 FLOPs 的 12 倍。随着更多使用该权重文件的用户上线,随着 AI 的传播,它实际上变得越来越重要。所以我在写论文等东西时经常这样想,但是当你进行不同模型的选择(如在 NeRF 论文中)时,你应该尽量减少训练 FLOPs,是的,但你应该关心推理 FLOPs 的程度是它的 12 倍。但通常,这就像 Chinchilla 论文一样,在大多数 NeRF 论文的 X 轴上,你只会看到训练 FLOPs 作为 ISO(标准化)基准。我们应该真正开始关心标准化的推理 FLOPs。
对于那些做过 SAS(软件即服务)的人来说,他们不知道 COGS 是什么意思,但如果你是一个模型服务器提供商,这真的很重要。这些是一些昨天出来的数字,有些人对他们的利润率可以这么好感到震惊,但这意味着 DeepSeek 的利润率为 80%,这是因为他们推理方面进行了大量创新。嗯,首先,为什么它如此复杂?这就像 model.forward 一样简单吗?其实没那么难。嗯,实际上真的非常困难。所以对于在场非 LLM(大型语言模型)领域的人,基本上发生的事情是你收到一些提示词、一组 token 或 token ID,然后你必须调用预填充(prefill),并在初始提示词上运行它。
如果你已经缓存了其中一部分,你应该不要再次运行预填充,如果之前已经做过的话。而且更好的是,如果在 DRAM 中它是“热”的(常用的),或者在某个 Redis 缓存、某处的 SSD 上。所以确保做一些类似 LRU(最近最少使用)的缓存机制之类的操作。这已经是节省成本的一个巨大机会了。在那之后,你将进入这个解码循环(decode loop)。如果我们稍后定义这些术语的话……比如模型 FLOPs 利用率,即你真正用于训练和预填充的 GPU 比例。在训练期间,你几乎总是在做……预填充本质上是一样的事情。你的 MFU 可能是 50%。所以如果黄仁勋向你保证有 2 万亿 FP8 FLOPs,你是得不到的。甚至差得远呢。也许你只能得到一半。如果你能得到那一半就已经很棒了。
而在解码阶段情况更糟,可能只有 5% 左右或类似数字。嗯,所以你要非常小心这些东西。因此,那些 Dan Fu 和其他人不断试图优化的术语和 KPI(关键绩效指标)是首 token 时间。即通过预填充所需的时间。然后是每输出 token 的时间或每个解码步骤的时间,以产生下一个 token。还有吞吐量,即对于每 GPU 小时你想要非常好的吞吐量,你实际上产生了多少 token?以及 good put(有效产出),即你在某些服务级别下生产了多少。为了举例说明,这些是我认为昨天刚出来的 DC 数据。
每 GPU 的吞吐量(不包括缓存命中)在预填充时为每秒 60,000 个 token。包括缓存命中在内,他们的输入缓存命中率达到了 96%,这非常好。但这会根据你的流量而变化。如果你在做智能体(agents),你将拥有非常好的输入缓存,但如果你有像我妈妈那样的人……她经常开始与 ChatGPT 进行全新的聊天,这些聊天与过去毫无关系,比如问:“天气怎么样?这是什么?那是什么?”这种情况下没有 KV 缓存。你必须一次又一次地反复运行预填充。嗯,所以情况会糟糕得多。但是当有缓存命中时,每 GPU 的吞吐量是每秒 150 万个 token,这非常好。
然后是首 token 时间大约为 400 毫秒,P99(第 99 百分位)的首 token 时间约为 1 秒。然后是每 GPU 在解码上的吞吐量,为每秒 14,000 个 token。你可以看到解码与预填充的吞吐量对比:从 150 万到 14,000。所以你会发现解码确实是瓶颈,这也是许多芯片创新涌现以试图加速它的原因。嗯,然后这实际上是人们注意到的最有趣的一点,即 KV 缓存保留策略(KV cache retention policy)。他们有 PB 级别的用户数据用于他们的 KV 缓存,并保留 72 小时,这就是他们实现如此高缓存命中率的方式。
这里还有一些你会看到的术语,即算术强度(arithmetic intensity)。所以当我从 DRAM 转移到 SRAM 时……如果我传输,比如 4 字节的数据,我能在这 4 字节上执行多少 FLOPs 或计算量?如果我做的是 RNN(循环神经网络)或者扩散模型,这些都很高,因为我传输了一张图像……然后我必须一次又一次地对同一个网络调用相同的权重文件。我不需要回到 DRAM,除非
这是一个非常大的图像,我不得不进行平铺或类似的操作。因此,对于大语言模型(LLMs)来说,其算术强度相当低。这是Volo在介绍扩散大语言模型时提出的论点。此外,我的实验室的Herman最近也发表了一个名为“Next Concept Model”的新模型,它的算术强度要高得多。另外两个概念是模型带宽利用率和模型FLOP利用率(MFU)。
当你购买H100时,他们声称你能获得2万亿次FP8 FLOP运算。但在测试时你实际能得到多少呢?通常这远低于该数值,而MFU就是指你实际获得的数量。模型带宽则是指峰值内存带宽是多少?假设你有1TB的带宽,那么在测试时平均实际能获得多少呢?
这里还有一个“屋顶线分析”(Roofline analysis)的概念,即理论上可能达到的最佳性能。这取决于芯片的峰值FLOP每秒与内存字节每秒乘以FLOP之间的最小值。因此,人们提出了许多创新举措。我将这些归类为三大类。虽然有些人可能会争论分类的归属,但我认为首先是模型创新。
例如,你可以做一种称为“混合专家”(MoE)的操作:如果你有K个专家,那么本质上可以通过1/K来分摊减少测试时所有专家的FLOP运算量。还有状态空间模型(SSMs),你不需要在每个层都进行完整的注意力机制计算,也许只需要每隔五层或第十层进行一次。
这样可以大大减少测试时所需的推理量。还有多头潜在注意力(multi-headed latent attention),我实际上是在序列域中进行压缩以减少FLOP运算量。此外还有量化,我们都见过低至2字节的模型,以及蒸馏到更小的模型中。
一旦我们获得了一个庞大且强大的大模型,而我们不一定需要直接服务它时,如果只需要较小的能力,我们可以将其蒸馏为更小的模型。这些都属于模型创新。其次是内核和运行时创新,如Radix Attention、Flash Attention和Page Attention等。
其中许多理念源自经典的操作系统和数据库理论。如果你学过入门级数据库课程,就会知道如何让生产环境中的数据库变得极快:你是使用缓存?还是前缀树(Prefix trees)或Radix树?这就是Radix Attention的基本思路。
TriDao提出的GPU平铺思想被称为Flash Attention,这是一个巨大的创新。而Page Attention与Radix Attention互斥,其基本理念是虚拟内存分页和缓冲区池的概念。你可以将数据页式加载到DRAM中,从而优化内存访问效率。
最后是框架(Harness)创新。当然最简单的是路由机制。我们在Focal时做过这个工作,并非所有请求都应该发送给那个庞大复杂的模型。我们可以采取更聪明的策略,例如将任务瀑布式地传递给更大的模型或工具调用至大模型。
这将节省大量的FLOP运算量。还有投机解码(Speculative Decoding),我们有一个经过验证的草稿模型。而我最喜欢的是递归语言模型,即永远不要将完整的上下文放入KV缓存中。
让大语言模型自行决定它需要什么以及何时需要。通过这些想法,你可以大幅减少KV缓存的使用量。最后一个是压缩(Compaction),这是每个框架的必备部分。我的实验室里的Sabri和Dan Simran提出了一种非常酷的创新,称为“ cartridges ”(卡带)。
[清嗓子并咳嗽] >> 如果你想了解更多,我认为关于此话题最好的两个讲座是:Percy在CS336课程第10讲中关于从头构建LLM的精彩讲座。还有Ryner Pope和Dwarkesh的演讲,我觉得非常酷且出色。
对我来说足够了。你们拥有世界上绝对的专家。首先是Dan Fu,他负责Together AI的研究工作,出自我在Hazy的实验室。其次是Jeffrey Morgan,Alama的创始人兼CEO。还有Joe Fiaty,Luminol的创始人兼CEO。
然后是Lang Shang,我想他快到了。他说自己迟到了,希望他能赶到。他是SG Lang论文的核心开发者及第二作者。好了,请帮我欢迎Dan Fu。 >> [掌声]
很好。好吧。谢谢大家非常感谢大家邀请我,也感谢Francois对推理过程所做的精彩概述。今天我将更详细地讨论令牌的生命周期,即一个推理请求的完整生命周期。
我会带你走过整个流程:当你发送请求进入API,到达数据中心,一直深入到内核的根源,了解如何在GPU上实际执行,以及如何将令牌返回。这个演讲是我之前版本精简后的内容。
原来说有六件事可以让我们做得更快,我划掉了一半,我们将讨论其中三件。有些Francois之前稍微提到过。那么,为什么我们现在关心推理呢?我们正处于一个非常令人兴奋的推理时刻。
开源AI从未如此壮大。这张图表有点旧,但在几个月前的某个时候,Together每月处理的令牌数量达到了40万亿。有趣的是,就在我们公布这个数字的同时,Google也公布了Gemini处理的流量数据。
作为一家小型初创公司,我们要处理相当于Google总流量的八分之一,这相当酷。到今年早些时候,通过OpenRouter传输的令牌中有一半以上来自开源模型。营销团队提醒我,就在几周前的某一天,我们的处理量甚至超过了OpenAI。
这些开源模型也是推动这一趋势的因素之一,因为它们便宜得多。你可以获得几乎相同的质量,在许多情况下甚至更好或完全相同的质量,但成本每年可降低多达10倍。
因此,我们正处于一个非常有趣的领域:开源模型正在迅速改进,每天都有大量新动态发生。在今天的演讲中,我将简要介绍它们是如何被服务的,以及如何获得更多令牌。
Together花费大量时间思考如何为世界创造更多令牌,并为所有想要它们的人提供服务。简单介绍一下Together:我们是一家AI原生云服务商。就像AWS你可以租用CPU并获得一些计算资源一样,你也可以设置SQL数据库、存储和REST API以及其他所有服务。
你需要运行一个Web服务。Together公司感兴趣的是探讨:构建一家AI原生企业需要完成哪些事项?这当然包括预训练、微调,以及我们将要讨论的推理。此外,还需要沙盒环境供你的智能体尝试各种操作。Together正在构建云基础设施以支持所有这些需求,并特别专注于推理领域。
在Together,我们拥有一支强大的团队,约有80名研究人员和众多博士毕业生。过去我们在多个领域有所建树,而现在我们专注于推理以及提升GPU的性能。Francois曾简要提及过这一点,但我们思考的一个核心问题是:工作负载究竟呈现何种形态?不同用户的工作负载有何差异?
因此,我们会花大量时间思考:如果你运行的是代码生成工作负载,你的缓存命中率是多少?你发送请求与收到响应之间的时间间隔(即轮次间延迟)有多长?例如,当你要求Claude Code、OpenCode或Cursor构建某个功能时,你需要等待多久才能再次与其交互?这种响应速度需要达到何种标准?
如果生成的代码量很大,那么生成过程的速度要求有多快?在满足特定服务等级协议(SLA)的前提下——例如响应速度以及整体任务完成的时效性——我们将致力于优化这些环节。那么,当请求实际到达时会发生什么?假设你向某个REST API发送了一个请求,它会经过互联网的标准环节,如Cloudflare和DNS等。
最终,该请求会抵达一系列数据中心。假设你拥有数千张GPU卡以及Francois提到的KV缓存(键值缓存)。首先进行的是基本的请求级路由。稍后我会详细解释所有步骤:你需要确定将请求发送给哪个GPU副本,然后将高级的人类文本转换为模型能够处理的二进制数据(0和1)。
接着进入大规模批处理循环,其中包括预填充(prefill)和解码(decode)阶段。最后输出令牌(tokens),再将其翻译回人类可读的文本发送给用户。首先是请求和路由环节。思考这一过程的方式是:你拥有数千张GPU,全球各地的请求不断涌入。这既涉及负载均衡,也需考虑KV缓存等因素。
如果你熟悉传统的数据库或生产级Web服务领域,你可能会说:“好的,哪台计算机承载了过多流量?我通过轮询将其发送到其他地方即可。”但在语言模型时代,我们需要考虑其他因素:如果你反复将相同的请求发送给同一张GPU卡,实际上会获得更好的性能表现。
因为如果之前已经处理过某些代码片段(例如整个代码库的第一个请求),系统会解析所有内容并理清逻辑。而在后续请求中,由于这些工作已经完成,无需重复处理。因此,路由具有一定的艺术性:你不仅要平衡数据中心和GPU之间的负载,还要决定将请求发送到何处,以便KV缓存已经驻留。
接下来是标记化(tokenization)阶段。这部分看似简单,实则包含大量复杂操作。这是因为在使用这些模型时,你的请求往往伴随着许多我们称之为“工具调用”的内容。所谓工具调用,大致意思是:“嘿,我希望模型执行某项操作。顺便说一下,你可以搜索、使用grep在我的文件系统中查找,或者运行命令来构建应用并测试一切,甚至可以在互联网上搜索。”
模型本身只是一组抽象的数学组合,由矩阵乘法和注意力模块构成。它实际上不具备搜索互联网或执行其他操作的能力。因此,当你发送请求时,会告知模型:“嘿,这里有10项你可以执行的任务。”随后模型需要决定:“我现在想调用互联网搜索功能”或“我想在此处进行搜索”。
在实现过程中,需要确保大量的正确性。我认为Jeff稍后会从用户侧角度讨论如何管理这一点,但在标记化阶段,确保这些内容正确无误且运行良好存在相当多的复杂性。接下来是模型的核心调度循环。所有请求不断流入,它们会等待批处理在一起:要么等待空闲的GPU或计算周期,要么等待前一个请求完成。
你在调度器上做出的选择将决定总吞吐量:你是希望在同一批GPU上服务更多用户,还是追求极快的响应速度?例如,如果你使用过Google搜索或Gemini,你会发现首次回复总是非常快。但随后进行聊天时速度可能会变慢。这可能是由于调度策略不同,甚至后端使用了不同的模型所致。
现在我们进入两个被称为预填充(prefill)和解码(decode)的阶段。你可以将预填充视为提示处理:当你传入整个代码库时,需要确切了解其中包含的内容、具体的询问意图以及理解所有相关部分。这是一个计算密集型操作,对GPU的压力很大,Francois提到的MFU(模型浮点运算利用率)会非常高。
算术强度也会很高。另一方面,第二个阶段是模型一次生成一个或少数几个令牌以返回给用户的一种方式:每生成一个字,都需要将整个模型的权重从GPU内存加载到计算单元中。这就是解码阶段。这是一个显存密集型操作,与预填充属于完全不同的类型。因此,我们花费大量时间思考如何服务这两种不同的工作负载。
以不同的方式处理不同负载?我们如何分离它们、将它们解耦?是否有不同的并行策略或内核,或者任何我们可以用来使这部分更高效的东西?最后,当你得到一堆由零和一组成的数据时,这些标记 ID 随后会被翻译回文本并流式传输给你。这部分相对简单。当然,围绕何时释放你的 KV 缓存、保留它们多久、以及尝试让那些标记驻留多长时间,存在一些复杂性。
总之,我们认为大约有六种方法可以用来加速事情。所以,按任意顺序排列,首先是内核。“内核”只是 GPU 程序的一个花哨说法。最终,当你运行模型时,所有这些都必须实际在 GPU 上运行,因此你可以花大量时间优化它们,编写不同的注意力内核、MOE(混合专家)内核和 Mega 内核,以使这一切更快。
其次是解耦选择,因为预填充和解码是截然不同的负载,所以你想将它们放在不同的 GPU 上。如何做出这些选择会影响用户体验和 GPU 总吞吐量等许多方面。第三是并行性,例如 Chimney K3 是一个拥有三万亿参数的模型,采用 FP4(每参数半字节)格式,总大小为 1.5 TB。
你的最佳 B200 或 B300 GPU 仅容纳 280 GB 内存,这意味着你有一个大模型,必须将其跨 GPU 拆分。如何将这些 GPU 进行拆分?这是一个并行性问题。第四是路由,即一旦请求进入数据中心,你将其发送到哪些部分?那里有很多选择需要做出。
最后两项几乎更像是模型层优化。首先是投机解码(Speculative Decoding)。基本思想是你有一个非常大的模型,但你并不需要它来回答诸如“一加一等于几”这样的问题。因此,你总是同时运行一个小模型,试图抢先大模型。如果你猜对了,你就可以免费获得这些标记。
最后是量化(Quantization),我稍后会谈到这一点:面对这些大模型,你实际上需要存储多少标记?你需要用多少位来存储这些权重?为了节省时间,今天我将重点讨论内核、并行性和量化,并对此进行更详细的阐述。
首先是内核。内核发挥作用的地方在于计算的核心部分——预填充和解码阶段。这里是数学运算实际运行的地方,包括矩阵乘法、注意力机制以及其他所有部分。那么在优化内核时需要考虑哪些因素呢?
当你考虑模型时,有些部分的优化相对容易。而有许多其他部分则更难优化。例如,你模型中的随机层归一化(Layer Norms)和 RMS 归一化、以及此处彼处的随机 Softmax。每个单独的操作都具有非常低的算术强度(Arithmetic Intensity)。
因此,如果你为它编写一个单独的内核,你在这些内核上的浮点运算利用率(FLOP utilization)会非常低。这历来是为什么在训练期间你只能获得 50% 的端到端浮点利用率,而在解码期间仅为 5% 的原因之一。
多年来我们一直采用的方法是:查看模型的前向传播过程,找出每一个可以优化的地方,然后让一支非常有才华的内核工程师团队去优化它。长期以来,这一直是行业内每个人的工作模式,即让一群聪明的人为此付出巨大努力。
最近在 Together AI,我们一直在思考如何利用智能体(Agents)来自动优化其中的很大一部分内容。这是一个名为 Together Compiler 的内部项目,我们将在未来几周更多地讨论它。我认为到发布时我们可能会给它起一个稍微不同的名字,因为我前几天有一个惊人的发现:Together Compiler 实际上并不是编译器。
基本思想是:你有一个目标模型、一些目标硬件以及特定的流量负载,然后你将使用智能体去优化这个内层循环。这里有两个关键思想:首先,你希望这个循环由真实流量驱动,因为不同的工作负载(如编码与聊天)实际上会以非常不同方式激活内核。
我们发现,进行完整的端到端研究循环非常重要:即实际查看在生产环境中运行的内容以据此迭代。因此,我们开始构建的几乎是一个推理递归自我改进循环(RSI loop)的雏形。好了,这就是关于内核的部分。
接下来我要讲的是如何优化你的并行方案(Parallelism Scheme)。基本思想是:你拥有所有这些权重,它们被拆分了,因为太大而无法放入单个 GPU 中。如果你可以将模型跨多个 GPU 拆分,你就能获得更多的计算能力、更多的带宽以及所有我们喜欢用来执行这些操作的东西。
有一些基本的并行方案已经存在很久了。首先是张量并行(Tensor Parallelism),即你只需将矩阵乘法拆分,然后在最后执行基本的 Map-Reduce 式归约。其次是流水线并行(Pipeline Parallelism),这个想法是将你的许多层逐层切断,将不同的完整层放置到 GPU 上。
数据并行(Data Parallelism)是另一个想法,即你将不同的序列发送到不同的 GPU 上,但保持模型的任何部分相同。这里的关键一直在于:你的 GPU 有多少内存?你能多快地编写内核以支持特定的并行方案?以及有哪些资源可用?
最近变得非常流行的一件事是我们投入了大量精力,这也是 François 之前提到的 DeepSeek V3(每小时 38 美元)背后的核心驱动力之一,那就是利用更大规模的扩展域。这意味着什么?如果你熟悉情况,或者你最近尝试购买 GPU,你应该听说过这些 Blackwell 芯片和 GB(Grace-Blackwell)芯片。
它们之间最大的区别在于,对于这些 Blackwell 芯片以及之前所有的 Nvidia 芯片,你只能让八个 GPU 真正以高带宽方式相互通信。这基本上意味着……
如果你将模型拆分成八份,尝试拆分更多比如十六份时,你会进入带宽更低的通道,导致服务变得极其缓慢。最近英伟达开始销售能够以更高带宽连接72个GPU的机架组件。因此,你可以采取的一项措施是将模型中的所有专家分别放置在不同的GPU上。这意味着你可以在相同的GPU上服务更高数量的请求,其基本原因也回到了算术强度的概念:如果每个GPU只运行一个专家并服务大量令牌,这看起来就像是大矩阵乘法操作,而这是GPU擅长的领域。
人们几年前就了解了这些概念,你可以回溯两三年找到关于专家并行的演示。最近我们解决了如何让它们更可靠的问题。虽然这些GB机架非常棒,但节点或GPU可能会在不同时间出现故障。如果你有一个依赖72个GPU的单一模型副本,而其中一个失效且没有适当的恢复机制和可靠性措施,那么单个GPU的故障可能导致72个GPU的计算能力全部丧失。因此我们花费大量时间研究如何让系统对硬件故障具有弹性和韧性,能够优雅地继续运行。
最后要讨论的是量化技术。这在某种意义上很简单,因为有数百甚至数千篇关于量化的论文,你只需选择自己最喜欢的算法并使用即可。但在另一种意义上它相当复杂,因为它直接关系到最终用户的体验质量。模型通常使用BF16进行训练,即每个参数16位、2字节。拥有3万亿参数的模型将占用6TB空间,但机器学习的计算本质上是模糊的而非精确的。我们早就知道可以使用算法将16位压缩至8位或4位,同时保持合理的模型质量。
我们非常关注如何确保正确的质量和用户体验,从系统层面确定最适合硬件的量化方式以获取最高吞吐量。同时考虑到人们正利用这些技术构建运行重要业务的公司,我们在Together构建了测量质量红队测试循环。在将任何模型发送给用户之前,我们会进行内部深度试用以捕获工具调用等问题(例如Claude代码助手无法搜索互联网),并检测模型是否因量化而变笨或基准测试得分下降。
我们运行着一个快速循环的Flappy Bird编码测试,如果量化出错导致游戏无法正常运行,我们就必须排查并修复问题。关键在于严格的评估循环:基准测试、代理式评估以及大量的内部试用,而不仅仅是算法优化。我们很幸运能与许多开源模型提供商深入合作,例如协助MiniMax服务其M3及即将发布的M3.1模型。
MiniMax在将模型量化为NVFP4格式时遇到了一些困难,但我们对此很感兴趣因为从内核和服务角度来看这是Nvidia GPU上理想的量化方式。左侧淡紫色代表原始的FP8模型,中间的热粉色是MiniMax使用自身配方量化的尝试。而右侧橙色代表我们提供的配方,我们能够匹配原始模型的性能并服务更多令牌,帮助他们实现从训练到盈利服务的闭环。
这次成功的案例展示了我们如何利用量化专业知识帮助核心模型开发者,让他们获得收入以继续训练下一代大家喜爱和了解的模型。以上就是演示的主要内容,回顾一下:我简要介绍了请求在系统中的流动过程(包括路由和数据中心选择)、令牌化、工具调用等细节。
调用以及所有你期望正确的组件,包括核心的排队与批处理、任务调度工作。预填充和解码阶段通常在具有不同内核和并行方案的独立 GPU 上进行。最终,当你完成所有这些步骤并证明 GPU 的价值后,你会获得令牌,用于进行编码、聊天以及我们所熟知和喜爱的一切。
总之,如果你觉得这些内容有趣,想使用开源模型,或者你的首席财务官正施压说我们在云代码上花费太多钱,也许该尝试一些开源模型了。其中一个是销售二维码;另一个则指向我们的招聘页面。再次感谢大家的聆听和陪伴,掌声响起。
接下来我们有几分钟问答时间。嘿 Dan,谢谢你的演讲,我有两个问题。关于 NVFP4 量化,你是如何衡量其质量的?是看 GSM8K、困惑度还是 MMLU 相对于 MXFP FP8 的表现?另一个问题是关于互联技术,它如何使 Grace Blackwell 系列芯片以网格形式连接超过八个 GPU?
关于第一个问题,这里有三个公开的基准测试。有些已经饱和了,比如 GSM8K 超过90%。关键在于如果指标波动一点或更多,就说明出错了,模型在实际应用中会挣扎。我们发布标准基准测试,并查看 KL 散度以观察激活值的变化。当然,我们还评估端到端的智能体管道,建立了数百个测试用例来确保模型正常运作。
此外,我们还有像 Flappy Bird 这样的智能体评估循环,将其放入 Together Compiler 项目中让模型优化自身内核。如果模型无法再优化内核,我们就介入检查。关于第二个问题 NVLink,我不是硬件专家,最近玩过的最接近硬件的是买了 Hugging Face 的微缩模型。
实际上看机架,每个装有四个 GPU 的托盘都有粗大的电缆连接到大交换机,所有托盘以此方式互联。数据通过这些厚电缆进入交换机进行全归约等操作。我不太清楚从 8 GPU 到 72 GPU 技术的具体跳跃,但我喜欢并正在使用它。
如果我想问电池续航与 iPhone 15 相比如何?你有手机,不知道面试测试了什么。是的,我们做公开基准测试、智能体循环等一切中间环节。好的,谢谢。我的问题是关于那个图表,你找到了更好的量化配方。区别是什么?你是从头训练还是在引入量化后运行多个预训练 epoch?还是说粉色部分没有进行任何微调,只是直接转换数字而不做训练?
是的,有很多量化配方要求在模型完成后进行训练。我们不喜欢使用它们,除非你是拥有原始完整数据集的模型提供商。否则你会导致模型遗忘事物。这是一种完全无需训练的方法,结合了公开存在的一些内容,如校准数据和几个月前发布的 Scale Search 论文。
这是一种非常谨慎地组合不同部分的方法。有一百种无需训练的量化方法,关键在于仔细选择使用哪些以及如何严谨地评估它们。谢谢。好的,我们得继续了。最后一个问题:你对哪种模型类型感到兴奋?我知道有很多可谈的,如 SSMs、DLLMs 以及其他可能大幅简化解码侧生活的架构。你是否还对某些类型抱有希望?
有趣的是,我在博士期间研究过 SSMs 等新技术时,总有人说没人会用这个这是死胡同 Transformer 将统治世界。几年后这些技术都被用于大型模型中。例如 Kimmy K3 中的 KDAs,这是一种 SSM 线性注意力风格的层,被用于最佳开源模型之一。稀疏注意力也被使用,扩散语言模型 DLLMs 也应用于所有推测解码中。这些技术都在被广泛采用。
被使用过。嗯,我的学生们最近对循环模型非常感兴趣。所以这里的想法是,在这个基本的计算图中,你将令牌通过相同的层多次传回,其思想是在不增加权重的情况下提高训练浮点运算次数。嗯,我们当时想:“哦,这真是个奇怪的想法,没人会用它。”结果发现至少OpenAI和Anthropic已经在使用了。嗯,所以是的,这是一个令人兴奋的时刻,如果你现在提出一个关于架构的好想法并经过繁琐的工作证明它是有效的,它很快就会出现在GPT-6或GPT-7中。反馈时间和影响时间现在都非常快。>> 谢谢,丹。 >> 酷。 >> [掌声]
大家好,我是杰夫。Ollama的联合创始人兼首席执行官,稍微退一步看,我想谈谈所有开源模型令牌并不都是一样的。以及如何在一个可能有众多推理提供商的世界中思考这个问题——我认为我们在弗朗索瓦的演示文稿中看到过一份清单,列出了当今存在的顶级推理公司的数量。在可预见的未来,可能会有数百甚至数千个潜在的推理提供商。如果你是一个构建者并试图在开源模型之上构建应用程序,如何衡量这些推理提供商的质量呢?
稍微介绍一下背景。我和Ollama刚刚和大家一起吃过晚饭。Ollama是一个开源项目,最初于2023年发布,用于在本地运行开源模型。回到2023年,像Llama这样最受欢迎的开源模型通常是比较小的模型,你可以在单个H100、笔记本电脑或小型服务器上运行它们。该项目的核心目标始终是提供与OpenAI和Anthropic同等水平的开发者体验,但针对的是开源模型。
自那以后它发展迅速,而且随着模型变得越来越大,特别是在今年年初Kimmy、Jill M和MiniMax推出了这些5000亿到1万亿参数的庞大模型时,我们发布了Ollama Cloud,旨在在云端为开发者提供与本地相同的体验。今年这一服务迅速爆发,基本上成为美国及欧洲最大甚至最大的独立云服务之一,用于访问开源模型。
这一切之所以重要,是因为我们花费大量时间思考如何跨多种模型和提供商提供卓越的开发者体验。因此,如果我们考虑云服务的架构——与传统的推理提供商不同——我们重点关注几件事:获取最新模型、确保速度极快以及提供可靠的体验。这些内部运作机制很多都在丹刚才提到的路由器中实现。
我们这样做有几个原因。我认为可靠性和早期访问模型可能最重要,因为不同的提供商在提供不同模型方面各有所长。但最终我们拥有与许多使用Ollama的客户非常相似的架构。大量财富500强企业部署了Ollama。通过与那些因成本、控制或其他原因从闭源模型转向开源模型的团队交流,我们发现他们都面临着类似的问题。
因此我想今晚花些时间谈谈这个问题。路由的主要原因是在理想情况下,通过结合多个提供商可以获得所有世界的最佳体验。“推理提供商”的定义相当宽泛。即使你是推理提供商,如果你在不同硬件、不同区域有不同部署且使用不同的推理引擎(例如),这也可能成为一个问题。
当你采用这种架构并考虑将多个推理提供商整合在一起时,面临哪些挑战?总结来说最大的问题是并非所有人都以相同的方式提供模型。事实上这是好事,因为不同的团队在不同的性能优化方面表现出色。有些人运行在特定类型的硬件上,我认为有一家新的推理提供商专门在全球范围内运行Mac mini。
最终客户关心几个不同的维度:成本、智能水平、速度以及地区和隐私问题。跨多个提供商进行路由的挑战显然是一个坏苹果会毁掉整筐果。如果单个提供商在四个标准中的某一项表现不佳,客户体验可能会变得平庸。
这可能有多种原因:模型精度可能有问题。你可能遇到一个提供商对模型过度量化、在故障硬件上运行或使用不同版本的权重。工具调用是一个令人担忧的大领域,正如丹所讨论的那样。有效进行工具调用非常困难。虽然看起来很简单(就像解析JSON和字符串),但有很多地方可能出错,比如从工具调用失败中恢复、为不同的工具和参数强制执行特定的模式以及确保解析在分词器级别正确发生。
不同提供商可能采用不同的采样技术,推理引擎的复杂程度以及可编辑性也非常深。无论是自定义内核还是KV缓存精度,许多这些变化只能在非常定制化的情况下看到,例如阅读超长上下文时。甚至会发生一些愚蠢的事情,比如模型被错误命名。你以为访问的是一个模型,实际上是另一个不同的模型,但这很难知道。它们都是友好的助手试图帮助你。
因此服务堆栈中有很多事情可能出错,如果你尝试跨不同区域和提供商为大量客户服务,这确实会产生影响。
这确实很难追踪,所以我在这次演讲前花了一些时间思考我们以及行业是如何处理这个问题的。事实证明这并不是一个新问题,关于“这个问题究竟有多大”已经进行了大量研究。令人惊讶的是,我们常讨论开源模型与闭源模型的差距,即开源模型落后多少。但实际上,对于仅使用开源模型的服务而言,最差的提供商与最好的提供商之间的差距,实际上比闭源和开源模型之间的差距还要大。这是一个非常严重的问题。
例如,与 Together 这样优秀的提供商合作(他们是我们的好朋友和伙伴),其体验可能与由不同提供商提供的端点服务大相径庭。由于我们来自 OpenAI 和 Anthropic 主导的世界,它们极大地激发了对 token 的需求。在这些世界中,无论通过何种方式访问模型,你都期望获得一致的体验。但对于开源模型来说情况绝非如此,这甚至可能比闭源和开源模型之间的性能差异更严重。
好消息是这并不是新问题,已有大量出色研究。2024 年有一篇论文测试了 31 个不同的 Llama 端点,发现某些模型的表现甚至更接近 GPT-3.5,而其他声称完全相同的 Llama 模型则表现不同。因此我深入研究了这一问题:如果使用开源模型,如何确保获得良好的质量体验?在介绍具体技术之前需指出,对于编码智能体和代理工作负载而言,这一问题会显著恶化。
这是因为它们属于长上下文或长视野的工作负载,通常涉及连续多次调用。如果模型提供不准确的响应,误差会不断累积。例如一个可能需要 15 步的适度编码任务,如果模型输出有 10% 的概率偏离预期设计,最终错误率可能高达 79%。如果整体错误率达到 80%,显然会毁掉用户体验。随着代理工作负载变得越来越复杂,这个问题只会愈发严重。
那么如何监控和审视推理端点呢?显然你不能像 Ollama 尝试做的那样阅读所有 token,尤其是在每 GPU 处理 160 万 token 的预处理情况下。首先可以运行评估(evals),这是一种蛮力方法,可能也是最昂贵的方式。例如 Artificial Analysis 定期运行基准测试并对不同提供商进行排名。以 Kimi K2 为例,大多数提供商表现良好,少数稍差,但整体情况不错。
这种方法的挑战在于它只能捕捉到量化不良等基础问题,因为本质上是在测试模型最初的基准表现。而且运行这些评估非常昂贵且耗时漫长,无法持续进行。去年年底出现的另一种方法是 Moonshot 团队发布的 Gemini K2 工具验证器,他们对不同提供商在使用 Gemini 模型时的工具调用表现感到不满。
该工具发布了一个记分牌,显示哪些提供商做得最好。这引发了一场确保工具调用准确性的竞赛,Gemini 模型随后也采取了类似措施。如果你正在构建模型,这是确保人们正确运行它的绝佳方式:提供测试工具并让他们进行测试。另一种更有趣的方法是我在阅读论文时发现的,我们正考虑将其整合到 Ollama 的路由和模型聚合中。
这是一种分布测试,基本思路是比较参考权重与提供商 API 的输出,然后观察两者之间的分布差异以了解差距大小。这很好且通常很便宜,不像基准测试那样昂贵,每次测试可能只需一美元。其难点在于虽然它无法捕捉量化不良等复杂问题,也不像综合评估套件那样能指出具体错误原因(后者需要运行参考模型并产生额外费用),但它提供了另一种视角。
另一种方法是比较对数概率(log probs)。这是一种查看不同 token ID 对未来 token 的概率分布的方法。这很好且通常调用成本很低,你只需获取下一个 token 的对数概率并与之前的基线进行比较。通过测量提供商的漂移情况,你可以判断事物是否发生了变化,例如他们是否部署到了其他地方或升级了推理引擎。
这种方法非常便宜,甚至比前一种测试更廉价,基本上只需单次 token 请求。论文中的例子发现不同端点存在大量变化。他们能够识别出服务堆栈的静默更改,这很有趣:每个人的服务堆栈都在演变、打补丁并部署新版本。
SG Lang。嗯,所以这是一种随时间监控的好方法,以确保客户获得一致的体验。如今许多 API 不再支持 Log Probs(对数概率),这是一个缺点。而且你实际上只是在检测与过去的漂移,当然你也可以将其与另一个参考模型进行比较。但在这种方法中,你实际上只能检测到相对于过去的漂移。所以,如果你从一个没有正确服务的起点开始,这就不是特别有用。
许多流行的路由器还做另一件事,这也是我们目前所处的阶段:在不保留任何数据的情况下,能否仅仅通过内省来查看有多少工具调用正在传递、其中多少是不正确的、是否发生了工具调用错误,以及请求的形状随时间如何变化?不同的提供商可能提供不同长度的输入和输出——抱歉,是不同长度的输出——这可以很好地指示是否存在常见问题。
我曾见过像 DLM 或 DeepSeek 这样的服务出现运行问题,模型陷入无限循环。因此,你可以通过查看请求的形状来提前发现这些问题,甚至无需查看数据本身。然后你可以保持一个动态评分,并最终密切关注并逐请求地监控它。Open Router 也做了类似的事情,例如他们正在采取的 Exacto 模型方法。
退一步看,我们知道我们总是反向思考如何获得像 OpenAI 和 Anthropic 那样使用开放模型时的体验。你要知道,它们运行在不同的硬件上,比如 TPU。我想去年 Anthropic 曾出现过一次故障,TPU 内核中的一个微小错误可能导致了质量方面的全面停机。因此他们对服务质量有着极高的重视。但在开放模型生态系统中,我们该如何达到这一标准呢?
我认为一种方式是实验室可以分享更庞大的堆栈来运行他们的模型。通常人们会将权重发布到互联网上,例如 Hugging Face,或者上传到 Llama 平台,然后说“这里有一些示例代码”。但这实际上并不是他们在自己的推理堆栈中使用的服务。通常是由另一个应用 AI 团队生成这些代码,以便广大社区使用。
DeepSeek 昨天推出了 DeepInfer,这是他们自己的推理堆栈。现在你可以获取 DeepSeek 的权重,并希望在你的 GPU 上以每小时每张卡 38 美元的成本运行它。这很棒的地方在于,你可以使用一致的基本堆栈。所以如果你是一个新崛起的推理提供商,你可以设置它并确信能以高质量提供服务。
许多实验室正在对模型进行量化。他们与专业人士如 Dan 的团队合作,在发布前就对模型进行量化,这样他们在 API 上服务的模型与你获取并自行部署的开放模型是一致的。这与最初的情况非常不同,最初的量化往往是在模型发布后由那些没有训练数据的人完成的,或者最终是为了将其适配到 MacBook 等设备上。
现在实验室发布自己量化的模型版本变得越来越普遍。你可以通过更低级别的 API 访问模型。我们要意识到的一点是,有些工具调用实际上可以提取到框架中,通过在提供商之间一致地执行这一点来获得大量准确性,而不是将其委托给提供商。推理、提取和思维过程也是如此。
我认为初创公司在监控 LLM 质量方面有很大的机会。如果考虑到 LLM 是应用程序关键循环的一部分,这非常重要。我不知道现在是否已有此类服务存在,但这确实是一个很有趣的想法。我最喜欢的一个观点来自一篇论文,它谈到每个现代 Nvidia GPU 实际上都配备了一个可信执行环境,可以证明正在运行的权重和提供服务模型的软件。
这可以作为获取指纹的一种方式,告诉你模型是否以正确的方式被服务。我认为这相当先进,但我认为这是一个非常有趣的想法。嗯,是的,这就是关于如何思考当推理无处不在且开放模型被到处服务时,如何真正衡量质量并确保从这些新兴的出色开放模型中获得最佳 token 的内容。谢谢你的邀请。
(掌声) 嘿 Jeff,感谢这次演讲。我的问题是关于预填充准入的间隔标准以保障质量。你如何决定在工具调用和大型上下文、运行上下文之间进行选择,特别是针对跨越长会话的多轮代理工作负载?哪种更好呢?我假设是较长上下文的预填充准入间隔。
嗯,我不确定自己是否有最好的背景来回答这个问题。你是在问关于准入的问题吗? 是的,是的,是的。比如在大约 48 或 64 个解码步骤之后进行解码的交错准入,你是采取每个 API 端点工具调用的准入方式呢?还是从运行上下文中获取预填充准入。所以你的演示文稿中有一张幻灯片涵盖了这一点。
哦,是的。这只是说你可以发送一个单 token 的提示词,然后测量另一端输出的对数概率,并用基线进行真正衡量。所以你可以随时间这样做。这并不是特定于特定的上下文长度。不过我认为你可以做的是使用一致地越来越长的提示词进行此操作,然后开始测量随着提示变长对数概率的偏差是多少。
因为我们看到许多提供商首先会将模型的上下文窗口限制住,这是一个愚蠢的功能性因素需要查看。但当你特别超过 200,000 token 的上下文范围时,能够测试下一个 token 的准确性是否与预期一致。
与参考模型提供商或之前返回的结果进行比较。我认为这是这里的一般方法。我对每篇论文的具体内容不太熟悉,但我认为这是一种相当优雅的方法,例如不需要运行整个评估。好的,谢谢。那你觉得量化器只是通过爬山法来优化评估指标吗?比如
如果现在有越来越多的开源测试用例来确保模型质量,而它们只是针对这些进行测试训练,我们并没有真正保留模型的原有质量。那我们要如何避免这种情况呢?是的,这是一个很好的问题。我听过很多关于这方面的讨论,特别是在推理提供商争夺最低令牌价格的庞大市场中
你被激励去匹配某种流量模式或特定用例。我认为我们与客户的合作中,他们经常来找我们说:“嘿,我们有这个特定的用例。”我们可以与他们共同设计一套可以持续运行的测试或评估指标。这对于单个提供商来说很难作弊,因为这些内容并不公开
而是特定于该客户的数据或流量模式。因此,这是一种方法,因为提供商无法访问这些定制内容,你可以抓住特定客户未能达到预期性能表现的提供商。这很好,因为通常我们听到关于质量问题的反馈都源于此
比如某位客户提出了一些意见。这在某种程度上形成了一个闭环。谢谢。我有个快速问题,在这里,杰夫。哦,嗨。嗯,你知道的,当我第一次尝试 Llama 时,它是一个本地模型。然后我们逐渐远离了本地模型。而今天随着 Flint
以及 Richard Socher 推出的 Recursive,我们又回到了本地模型。你如何看待 Llama 的未来?你认为这会对 Llama 产生什么影响吗?是的,你知道的,随着许多本地模型的发布
也有多种方式在本地运行模型。不同的推理引擎,比如 MLX 框架现在驱动着 Apple Silicon 上的 Llama。还有 Calibri,我不知道你是否见过它。它在本地运行 Qwen 3.5 MoE
并从磁盘提供服务,这非常酷。是的,有这么多方法。我认为我们在这里讨论的所有问题也适用于本地推理模型服务。它可以是桌面端,也可以是桌边设备,比如 DJX Spark
如果你拥有新的 M5 芯片组,我们今天就拿到了我们的 M5 Ultra。或者对于许多拥有 GPU 的企业来说
数据中心的服务也是一个问题。这不仅仅是提供商的问题,也是你用来服务模型的技术栈的问题。你需要能够可靠地测试它。所以我认为有两点。首先,关于本地模型
这款新模型非常棒。我们非常兴奋成为 Recursive 的启动合作伙伴之一,以及 Flint 模型的合作伙伴。但我确实认为
无论是云服务提供商为你提供令牌,还是你自己运行的推理引擎每周更新一次模型来提供令牌
这是一个共同的问题。开源模型的现实情况是它真的是“狂野西部”,这是一件非常了不起的事情,因为这些模型可以被定制、更改和量化。我认为几乎每个模型都有大约 20 种不同的量化版本
你可以直接从 Llama 或 Hugging Face 上获取并下载。真正的挑战在于如何对这些模型进行基准测试?但如果你要将其实时提供给客户,你需要比基准测试更轻量的测试。所以我认为这在本地同样是一个重要问题
太棒了,谢谢杰夫。关于 Ollama 的未来,我认为人们可能会混合使用本地和云端模型。我们看到这款新模型在编码方面表现非常出色。因此,我认为
现在你可以运行的任务数量中, frankly(坦白说),许多最好的编码问题或最难的编码任务此前只能通过云端模型解决
这些模型今年变得非常流行。现在,随着这些超强大的本地模型的问世,它们可以在 MacBook 上运行
我们将看到路由问题从单纯的云端提供商之间竞争转变为本地和云端的混合路由。总之,太棒了。非常感谢大家。谢谢。掌声。好的,是的
我叫 Joe 是 Luminal 公司的联合创始人兼首席执行官。我们是一家编译器公司这有点不寻常,但通过这场演讲
你会明白原因所在。我们将编译器视为堆栈中非常关键的一部分它定义了 GPU 浮点运算或 GPU 小时与令牌之间的转换函数。你的编译器越好
就越能决定你是亏损、盈利还是大赚。因此,我们开发了一款基于一种非常奇特的技术“等量饱和”(equality saturation)的编译器
这是一种相对较新的技术。我认为我们是最大的工业应用案例,我们认为这是将模型高效编译到各种不同类型硬件的正确方法
因此我们真的认为 Nvidia 价值数万亿美元的主要原因之一在于 CUDA 是一个非常受支持的软件生态系统。将模型映射到复杂硬件上是一个非常有难度的问题
我认为目前仍未完全解决。我们的解决方案是利用更好的方法,即进行大量的编译时搜索。这使得我们基本上不需要维护许多复杂的重写规则或启发式算法
而普通编译器通常需要维护这些。这也让我们无需维护一个庞大的内核库,从而避免每次有新模型出现时都要不断修改它。因此我们能够构建一个非常小的共享编译器核心
为数据中心中不同类型的推理 ASIC 建立后端,并在 Nvidia 和 AMD 的 GPU、TPU、Trainium 以及大量其他推理 ASIC 上运行大量的推理任务
在任何给定时间点,我相信我们每月处理的令牌数量约为 80 到 90 万亿。虽然我们现在只有八个人,但总有一天我们会达到那个目标的
关于编译器思维方式的独特方法,与典型的基于 MLIR 的编译堆栈相比,以及为什么这种方法在大语言模型、扩散模型和生物模型等至少能带来收入的应用中效果显著。特别是我们如何使用 Egglog 引擎来驱动我们的等价饱和(equality saturation),以及这等价饱和究竟意味着什么。然后是如何构建中间表示(IR)以针对多种不同的硬件,并充分利用所有这些硬件资源。接着是扩展搜索空间的方法。
我们审视现有的所有神经网络和深度学习应用,发现它们并不复杂。本质上,它们是稠密线性代数运算,且涉及的稠密线性代数操作种类并不多。我们将模型简化为大约 14 到 15 种基本原语操作,如加法、乘法、求和等。事实证明,可以将整个 PyTorch 的操作集分解为这个非常简单的操作集合,而不会丢失任何信息。
虽然我们的系统不是图灵完备的,但我喜欢称其为“PyTorch 完备”。这意味着只要你的模型可以用 PyTorch 表示,我们就能将其摄入并编译到所有支持的后端。例如矩阵乘法(matmul),我们的系统中甚至没有专门的 matmul 操作。我们将 matmul 分解为广播乘法和随后的求和归约运算。
这极大地简化了 PyTorch 中新模型的复杂性瓶颈,尽管 PyTorch 拥有约 2000 种操作。新模型通常很复杂,硬件也实现了大量不同的操作。因此,进行复杂的映射时,你不希望采用全对全的方式,而是在中间设置一个瓶颈。我们将所有 PyTorch 操作映射到这个瓶颈上,然后再将其映射回硬件操作。这种类似 RISC 的基本原语操作集正是我们的瓶颈所在。
编译器领域有一个术语叫“超级优化”(super optimization),源自 90 年代,除了在编译器研究领域外并未真正普及。其基本概念是:尝试搜索给定程序的所有可能形式,并使用某种成本函数来确定最佳方案。这样编译器工程师就不需要预先知道如何将特定程序降低(lower)到硬件上,因为可能的降低方式呈指数级增长。
因此,许多公司雇佣编译器团队,其人数随着目标应用数量的增加呈指数级增长。最终导致拥有 500 或 1000 人的编译器团队,即便如此,编译器的效果仍然很不完美。一旦尝试扩展支持的模型数量、架构类型、后端类型以及数据类型,就会出现巨大的乘数爆炸效应。
真正要做的是编写非常小的原子重写规则来指定不同的等价性,然后让搜索过程自行决定何时应用这些规则。这就是“等价饱和”技术让我们能够做到的:编写小型的等式重写规则,构建搜索空间,然后遍历该空间。
我们使用计算图作为 IR 结构(有向无环图 DAG)。你可以想象加法、乘法操作及其依赖关系。我们使用一种称为 E-graph(等价图)的概念。普通的重写规则会对图形进行模式匹配,然后将匹配部分替换为规则的右侧内容,这实际上破坏了原始信息。应用该规则后必须确保它是正确的选择,因为无法回头。
而 E-graph 允许我们应用规则并将右侧信息添加到图中,同时保留方程的两边:即变换前和变换后的状态。我们基本上以所有可能的顺序应用所有重写规则,直到无法再添加更多信息为止,此时 E-graph 达到最大扩展状态,称为“饱和”。
一旦饱和,我们提取大量代表程序的具体图。将这些图全部编译为硬件上的可执行文件并在硬件上运行,测量其耗时并尝试最小化该时间。这是一个非常直接简单的过程。
这张图直观地展示了 E-graph 的样子。左侧有一个代表方程 A * 2 / 2 的图,顶部节点是表达式的根。从根发出的箭头分别指向除法的两侧:A * 2 和 2。
我们应用不同的规则来扩展图,例如指出 A * 2 等价于将 A 左移一位。继续应用这些规则,最终得到右侧完全饱和的图。虚线框包含了我们最初作为根的除法运算以及 A 本身。
通过这种机制,我们发现 a * 2 / 2 等价于简单的 a。这是一种无需预先知道启发式重写规则即可简化表达式的方法。我们的重写规则非常直观简单,采用 Lisp 语法编写,原因是我们使用的引擎由人们用这种语言开发。
我们的编译器内部机制其实很像 Lisp。因此,我们必须用 Lisp 编写所有的规则,尽管它们看起来大致如此。首先看到的规则是基本的交换律,即 x + y = y + x。值得注意的是,这在浮点数运算中并不成立。虽然结合律和交换率在涉及 IEEE 浮点数时相当棘手,但那是另一个话题了。
接着,底部展示了一种称为“常量折叠”的技术:如果在编译时你知道 a + b 的值,你可以直接相加并从程序中移除整个加法操作。既然可以在编译时完成,运行时就不需要执行它了。我们之前讨论了核心 IR 集,这个非常简化的原始 IR 集在我们的所有后端中是共享的。
但我们希望编译到各种不同的后端,比如 TPUs、训练加速器(training ins)和 GPU。那么如何指定每个芯片实现的操作呢?硬件通常会提供一组已实现的低级操作,这些操作的层级可能不同。在 GPU 上,你可以想象张量核心 MMA(矩阵乘法累加)是其中一个可指定的操作。
它从 SRAM 读取输入并将输出写入 SRAM。因此,我们可以很好地表征我们要针对的新硬件后端,然后指定这些操作在编译器已理解的操作中等同于什么。我们可以编写重写规则来进行这种等价转换,这将传播到编译器中已指定的其他等式中。
引入新的硬件后端是一项相当低效的工作,即使它与我们要支持的现有硬件后端看起来截然不同。因此,这避免了通常需要庞大的编译器团队和大量复杂重写的需求。而且,硬件本质上是分层的:你可能在编程单个线程、一个 warp(束)、或本质上等同于完整 CUDA 核心/SM 的线程块。
或者你可能在编程整个 GPU,甚至使用操作来编程整个节点。因此,我们希望显式地表示这一点。我们的 IR 本质上是多级的。我们可以明确建模所有这些不同的层级,这让我们能够引入来自社区或供应商库中在这些不同级别实现的操作。
等式饱和技术让我们在一种合理的方式下桥接所有这些不同的语义,指定什么等于什么,然后让引擎进行交换并执行所有搜索。这就是当你替换掉大量不同已实现操作时的样子。例如,你可以有一个 cuBLAS 主机操作,它基本上是从 CPU 调用的并执行完整的矩阵乘法。
cuBLAS 是由 Nvidia 编写的 matmul(矩阵乘法)库,它实现了非常优秀的矩阵乘法。你还可以有线程块级别的操作,如 CUTLASS(另一个 Nvidia 库)、CuBLAS DX(也是 Nvidia 的库)或来自 Hazy Research 的 ThunderKit。这些是线程块级别的操作,意味着它们在给定时间实际上编程一个 GPU 核心。
我们的编译器因为能够推理这些不同层级的操作,我们可以将低级操作的子图结合起来形成一个巨大的超级内核。因此,在任何给定时刻,前向传播都可以由普通内核和超级内核共同实现。这很好,因为虽然每个人都喜欢谈论超级内核,但它们并不总是最优解决方案,尤其是在处理非常大的模型时。
最后的问题是:一旦定义了搜索空间,你如何实际进行搜索?也就是说,你怎么知道程序 A 比程序 B 好?这本质上是一个需要成本模型的问题。你需要一个适应度函数来评估程序,判断哪个更昂贵。对于复杂硬件(如现代 GPU)拥有良好的成本模型极其困难。
这需要许多博士级别的工作,以便在不实际运行的情况下估算 GPU 程序的运行时间。因此,作为一家小公司,我们不可能为我们想要针对的每种硬件类型都建立准确的成本模型。本着“苦涩教训”的精神,最好的成本模型就是没有成本模型;我们应该直接在我们要运行的真实硬件上运行这些程序并查看耗时。
这是一种看似愚蠢但非常有效的解决方案。我们在搜索空间中可能有不同的移动方式,作为程序员我们可能认为它们没有益处,但当它们在 GPU 上实际运行时,最终变得非常有益。因此,我们实际上不必运行搜索空间中的所有东西。
有一些技术可以修剪搜索空间:通过对给定子程序进行屋顶线评估,你可以提前剪枝,因为你知道在最坏情况下它比已经测量的等效程序要慢。我们确实进行了一些早期剪枝,但总体上我们尝试在真实硬件上测量一切。
这是一个小视频,展示了生成内核时的样子。从这个视频中你能得到的信息不多,但看起来很有趣。当然,还要做一个必要的广告:我们完成了编译器公司变现这一罕见的壮举。在坟墓中有一长串死去的编译器公司,因为它们未能弄清楚如何从编译器赚钱。我们终于实现了盈利的闭环。
各种异构硬件。我们每月处理约80万亿个token,数百万张图像。作为类似通用编译器的编译器之一,我们的优势在于不仅能运行最受欢迎的模型,还能运行各种奇特的模型。因此我们运行蛋白质折叠模型、生物模型、图像模型等所有这类东西。我们的增长非常迅速,目前团队只有八人,但情况不会一直如此,我们正在积极招聘。我们利用奇特的编译器技术将异构硬件商品化。我们认为计算的未来是商品化的,欢迎访问lumina.com。(掌声)
嘿,我很欣赏你的风险导向方法。我想知道你们在多大程度上适应了机器学习工作负载?例如你提到IEEE 754中的结合律,但机器学习中并不关心这个。你们甚至可以在硬件层面处理不同的序列。这些特性有多少被内置到你们的成本模型或语言建模中?结合律和交换律确实很重要,因为它们影响数值精度。
如果你进行求约简运算,树状求和与纯顺序求和会给出不同的答案。关于模型质量的问题,如果数值精度出错,确实会影响输出token的质量。因此我们要明确如何建模数值精度。虽然某些重写规则仍有优势,但通常是因为硬件对输入A和输入B实现了不同的功能。例如Nvidia张量核可能只从SRAM读取输入A,而从其他内存空间如Tensor Memory读取输入B。
我不太明白为什么不能直接在硬件上运行,因为如果涉及整个模型这似乎不可能;如果只是针对特定内核,我不知道如何建模资源争用等问题。这是第一部分。第二部分是你显著扩大了搜索空间,对吧?是的,甚至像TRT这样的编译器即使使用启发式算法也非常慢。既然搜索空间扩大了一个数量级,它是如何扩展的呢?
首先回答第一个问题:运行完整模型前向传播仍然相当快。取决于你对慢的定义,我们可以以不到10毫秒的速度运行一次前向传播并进行分析迭代。因此每秒可以进行上千次分析运行,这仍然算合理快速。对于第二个问题:是的搜索空间确实变得很大,我们使用标准的搜索算法来遍历它比如遗传算法。
我认为这里有很多创新空间可以使用类似MuZero的学习引导搜索,但到目前为止我们不需要这样做。最终只搜索了约1%的空间,人们对此感到惊讶是因为我们从不针对全局最优解而是寻找接近99%质量的全局最优的局部最优解,这更容易找到。在编译时间方面我们做出了明确权衡:推理客户并不关心编译时间只要运行速度更快。
因为只需编译一次然后服务于数百万GPU,如果获得1%、5%或10%的性能提升,即使编译一天也无所谓。我们通常让用户设置时间预算或者我们自己设定。你可以编译一小时或十小时,最终会达到收益递减点但这种方法行之有效且扩展性良好。谢谢我认为这种分析很有道理你们是否也使用实际的推理配置文件来引导搜索还是纯粹的形式化搜索?
我们尽量使用接近真实输入的数据。由于我们在云端运行,我们知道客户关心的确切形状因此对于张量形状我们大致知道运行时输入的内容以及数据分布等。不需要检查正确性因为除非重写规则错误否则我们会得到正确答案只要尝试表示行业相关的张量形状就能获得相当准确的时间估算。
你好感谢分享另一个编译器需要比较不同模型架构到不同硬件,我觉得调试很难特别是识别降低操作健康问题的层。Luminos如何解决这个问题?通用调试总是很困难我们使用PyTorch提供的工具标记高级操作并跟踪元数据。我们还用标准Profiler找出瓶颈和问题Egglog让我们指出输出程序错误并提供连接输入输出的重写链从而快速锁定无效规则。
我实际上很喜欢这个解决方案因为通常不支持的操作会被优雅地回退到CPU执行但没有免费的午餐,扩展操作通常会增加延迟而我们通常在编译器层面融合操作。你们通常看到什么样的延迟影响?我们绝对进行融合扩展
编译器在搜索各种形式的降维操作时,最终会得到相当融合的操作。我们通常采用屋顶线启发式方法作为准则:如果一个内核在DRAM之间移动的数据量比另一个内核多出一个数量级,那通常是不好的。因此我们最终得到高度融合的操作,只是在中间层会将其展开,然后搜索过程需要找到这些模式并重新将它们压缩回去。
关于语言非图灵完备的评论很有趣,这意味着它不受里奇定理等理论的约束。因此我们可以推理程序的行为而无需运行它。我们在堆栈的哪里利用这一点呢?这并非图灵完备是一个早期的认识,因为90年代超级优化失败的原因之一就是试图将其应用于C这样的通用图灵完备语言。
我们编译的是稠密线性代数程序,这是一个非常狭窄的空间。我们可以解析地精确计算不同内存空间之间的字节移动量,从而在不运行模型的情况下推导性能屋顶线。这允许我们进行大量的早期剪枝,相对于整个搜索空间的大小,不需要运行很多分析迭代。
解决方案空间中是否包含量化、推测解码和并行性等优化?通常不包含,我们主要处理前向传播。你指定一个前向图并编译它,端到端地在前向传播范围内完成。我们运行在vLLM和SGLang等推理引擎之上,利用它们提供的推测解码等大解决方案。
我们的观点是主要的增益在于处理复杂的并行拓扑和前向传播的复杂性,而将其他创新留给vLLM和SGLang。因此我们不搜索那一层。接下来是SGlang的演讲,Leon Shaw介绍了Agent工作负载的故事始于2023年底。
当时人们猜测Agent工作负载的样子,设想有工具调用和平行的子代理共享大上下文。由于内存有限且前缀重用效率低,我们引入了Radix Attention(基数注意力)。所有代理的上下文在引擎内存系统中像树一样管理,称为Astra内部的Radix Tree。
我们共享系统提示和认证等前缀,但子代理执行不同任务时后缀可能被驱逐。这是一种基于Radix Tree的内存管理方式,核心设计和调度都围绕此展开。当需要内存时,驱逐策略会移除未使用的叶子节点,并配有多种调度算法以适应工作负载。
提示词或智能体以交错方式发送给引擎。因此,如果能对提示词进行重排序,就可以按照感知缓存的顺序将它们批处理,从而复用上下文A,而不是交错使用A和B,否则可能会面临缓存驱逐的风险。这就是Islam成为推理引擎的故事。原始论文的想法是在内存管理系统内部构建一棵红黑树。
到了2024年年中,我们打造了一个通用且通用的大语言模型推理引擎。我们在2024年年中发布了0.2版本,当时支持Llama 8B和Llama 405B的在线及离线服务。你可能会注意到,对于所有的批处理操作、内存管理以及驱逐匹配,你都需要对所有等待的请求进行排序并确定优先级。
我们知道GPU的推理速度越来越快,但与此同时CPU的效率并没有改变。因此我们可以发现,如果使用原生的调度策略,就需要为批次一做准备、运行批次一、获取所有数据,然后再为批次二做准备。当GPU较慢时这没问题,通常可接受的GPU空闲时间在10%以内;但当GPU越来越快时,由CPU瓶颈导致的GPU气泡变得明显。
针对这种情况,我们设计了带有重叠调度的系统,这意味着我们将设备到主机的同步延迟到下一轮执行,以确保关键路径上没有阻塞操作。同时我们提前进行准备工作。因为GPU正在运行,一旦执行延迟操作,就不会有任何东西阻塞下一个批次的启动。因此我们在批次一运行时就启动批次二。
我们仍在使用这一机制,许多新引擎包括昨天DeepSeek发布的DeepInfra也使用了零生命周期重叠调度来支持其内部推理引擎。在Hopper时代,与原生调度相比吞吐量仅提升1.5至1.1倍;但在Blackwell时代,在GB设置下有时甚至能达到两倍的提升。
智能体和缓存的故事仍在继续,因为GPU内存总是有限的。当我们进行长上下文服务时,用户可能在几分钟、几小时甚至几天内发送提示词或下一轮响应。你必须找到地方存储他们之前的前缀,这就是为什么我们引入了高级缓存技术,这也是许多引擎中使用的标准技术。
我们将之前提到的寄存器与GPU内存、主机内存以及我们称为L1、L2和L3缓存的内部存储连接起来。每个令牌可以存在于L1、L2或L3缓存中,这些都可以通过特定策略进行管理,以提高命中率以及从L2到L1或从L3到L2的加载速度。
在我们的实验中,这可以将TRFT降低56%。如今传统的Transformer对于长上下文和高效部署已不再适用,因此关于Transformer架构的创新设计将越来越多。例如滑动窗口机制,许多开源甚至闭源模型都使用滑动窗口来服务其模型;还有当前的循环状态如Mamba状态、KDH状态,它们将信息以令牌为单位放入固定形状的状态中。
当时你可能认为这很简单只需在不同类型的内存池中添加不同层来实现混合内存池、混合存储和混合计算。但这并不简单,因为必须考虑如何构建缓存共享策略。例如若想实现位置10(从1开始计数)的缓存命中,必须匹配完整注意力池的全部KV令牌。
此外如果滑动窗口大小为4,则必须包含从10反向到7的至少四个令牌。如果保留的令牌只达到8,则不会命中缓存因为它是新的需要为滑动窗口进行完整注意力计算。若想从位置10开始共享前缀,必须在位置10拥有完全匹配的Mamba状态和KDS状态以共享前缀。
如果我们单独管理所有内存池,那么在如何实现高效缓存共享方面将面临巨大挑战。想象一下当你的缓存命中率从95%提升到99%,你减少的成本是五倍而不只是百分之五,因为你为所有推理工作负载减少了四倍的计算量。
此前我们使用不同的基数树(Radix Tree),它们是独立实现的,未考虑这些底层内存池。这带来了许多兼容性和灵活性问题。此外当你尝试保护历史记录、锁定前缀节点时,还需要在整个层之间、滑动窗口层以及循环层之间进行大量协调。
每个组件似乎都必须遵循特定协议以协调满足外部策略,这显然不可行。这就是为什么我们引入了统一的基数树。在新设计的统一基数树中,我们不将各个组件视为独立部分,而是使用基于完整令牌的语义令牌思想树。
基于完整 token 的概念很有趣,因为无论架构如何变化,总会存在完整的 token。至少在当下是这样。因此,我们为完整 token 以及其他基于内部维护组件的 token 构建了语义 token 思想中的基数树(Radix Tree)。统一的基数树将作为每个组件的控制器和协调器,以确保实现最佳的缓存匹配效率。为此,每个组件都设计为适配统一基数树,拥有各自的规则、匹配淘汰和内存管理策略。
这将所有边缘分析推理工作负载中的极端情况整合在一起,以避免任何内存泄漏或浪费。此外,幻灯片上未展示但您可能注意到的一点是:我们有三种重型状态,如完整 token、窗口 token 和循环状态(如 A-State)。您可以想象,上下文在不同用户、不同工作负载甚至不同时区之间是变化的。例如,用户 A 仅使用约 4K 的上下文;用户 B 可能使用 128k 的上下文;而另一位用户始终使用百万级 token 的上下文。
在这种情况下,他们都需要完整 token 池来提供其上下文支持。然而,滑动窗口出于某种目的,仅为工作负载提供最末尾的 128k token。这就产生了比例差异,因为您不能按固定比率预留 token 池。如果您必须设计一个引擎来适配所有不同工作负载上下文的不同比例,那么您可能需要使用多种不同类型的资源。
但如果只有一个引擎、有限资源或希望服务大量不同工作负载,就必须有一种统一的方式来管理所有底层内存池。因此,除了统一的基数树外,我们还提出了统一的内存池来服务于不同的工作负载。此前,我们只是按预计算的固定比例和大小预先分配所有工作负载的资源;而现在,我们仅分配一堆物理内存。
对于每种类型的内存池(如 4K、窗口 KW),我们不再设置限制,而是将它们视为最小单位字节的组合。它们分别从物理内存的左侧和右侧根据使用情况增长。当上下文变得很长时,我们可能会为完整 token 池分配更多内存;而当上下文较短时,则主要为循环状态和窗口 KW 分配更多内存。
然而,对于大多数内核和后端的实现而言,所有操作必须在物理内存中连续进行。如果在之前进行内存分配或淘汰,GPU 的物理内存中将出现空洞(即空闲可用的区域)。这些空洞无法被重用,因为一旦触及两个不同内存池的边界,就会出现问题。
为此,我们引入了一项名为“紧凑化”(Compaction)的操作。我们将物理内存空间中的所有内存碎片重新组织,通过低延迟的内存复制内核将它们打包紧凑。然后,我们重映射所有虚拟 token ID(实际上是内存页 ID)。在此过程中,虚拟页 ID 本身无需更改,我们仅改变底层的映射关系。
这为整个服务工作负载带来了更大的灵活性。此外,从推理阶段开始,我们还引入了在训练循环内的 Miles 推理机制。XLA 和 Miles 主要由 Red Hat Arc 贡献,其中 XLA 负责 rollout(策略执行),是 Miles RL(强化学习)训练的完美后端。Miles 支持完全异步执行,保持生成和训练的同步推进。
实际上,我涉及的内容超出了论文范围。我们从 Red Hat 的因果注意力机制起步,逐步发展为开源推理引擎、Miles,甚至 XLA Diffusion。我们做了许多其他工作来提升当时的开源 AI 基础设施。在最后一页,我们将在未来几个月举办 XLA Summit,届时会有许多特色演讲嘉宾出席并参与。您可以扫描二维码进行注册。
以上就是我的分享以及问答环节。(掌声) 好的,非常感谢大家。