乐于分享
好东西不私藏

《Designing AI Chip Software and Hardware》中文整理版05- 大模型系统不是一颗芯片

《Designing AI Chip Software and Hardware》中文整理版05- 大模型系统不是一颗芯片

大家好,我是拓驰猎头CEO。基于20年AI硬科技猎头经验及百人规模猎头团队,我们在传统猎头模式的基础上,还发展了股东合伙人模式、OPC业务合伙人模式、SOHO猎头模式等创新性多元化业务模式,欢迎大家交流探讨。我的公众号也会持续分享行业信息及专业洞察,欢迎关注。

———————————————————————————–

原文标题:Designing AI Chip Software and Hardware 

原作者:Bjarke Hammersholt Roune

授权状态:原文公开免费阅读,但未见明确的开放许可或中文翻译发布授权。本文为非商业学习交流翻译。中文分篇标题为译者拟题;本文为第 5 / 9 部分。

AI 助手的并行化

本章给出一些关于如何并行化 AI 助手(也就是 LLM)的大图景想法。这一节已经有点复杂了,而且我可以向你保证,要以高性能真正实现这些想法,会比这一节让它看起来的样子复杂得多。

如果你只是想轻松读一读,也许可以直接跳到 co-design 章节——那一章比本节以及下一章关于 AI 芯片网络的内容读起来轻松得多。

说清楚一点,“并行化(parallelization)”指的是把一个 LLM 的计算分布到多颗芯片上,和/或分布到一颗芯片内部的多个核心上。所以,如果你有两颗芯片,理想情况下事情应该快两倍。这就是并行化。并行化也是为什么单颗芯片性能本身并不重要,尽管很多——甚至大多数?——AI 芯片广告都完全围绕这个并不相关的指标展开。真正重要的是:你的系统能够以什么价格交付什么能力,而不是某一颗单独芯片能交付什么能力。

对于一家 AI 芯片初创公司来说,准确理解并行化的影响至关重要。在我看来,这是一个非常棘手、也非常反直觉的主题。下面是关于 AI 并行性的三个事实:

  1. 网络带宽(network bandwidth)是层内并行化(within-layer parallelization)的限制因素。这就是为什么 AI 推理需要快速网络。层间并行化(between-layers parallelization)并不需要接近这么多的网络带宽。
  2. 如果你把网络带宽翻倍,那么你也许就能做两倍的层内并行化,而这最多可以把 token 延迟减半。所以,如果你的 AI 芯片有高 token 延迟问题——比如你的芯片由于使用大型脉动阵列而需要高 batch,那么你可以通过增加网络带宽来改善它。因此,大型脉动阵列会推动更快网络的需求。
  3. 层间并行化,也就是流水线化(pipelining),有助于降低权重存储需求,但它无助于降低 KV 缓存存储需求。相比之下,层内并行化确实有助于降低 KV 缓存存储需求,而这同样部分受网络带宽限制。所以,如果你把网络带宽翻倍,并且你已经做了足够多的并行化、让权重存储变得可以忽略,那么你也许可以大致把每颗芯片的 HBM 存储需求减半。因此,网络带宽和你需要多少 HBM 是相关的。

如果你使用专家混合模型(Mixture-of-Experts,MoE)——这很常见——那么所有这些都会变得更复杂。MoE 有点像一种层内并行,但它使用网络的方式不同。

还有一个事实是:如果你把芯片速度翻倍,那么你也需要一个快两倍的网络来跟上。这既显然也直观,但必须记住。

在这一章里,我会解释,除其他事情外,为什么上面这 3 个事实成立。它们成立的原因并不显然。我个人觉得这 3 个事实非常令人惊讶。如果你之前并不知道这 3 个事实,而你又参与制造或购买 AI 芯片,那么我建议你认真阅读这一节。无论你做什么涉及并行化的计算,我都建议你反复检查——两遍、三遍、四遍——确认你真的正确理解了这个主题。否则你的数学计算将毫无意义。很多非常聪明的人都在这里犯过错——你不会是第一个。例如,早期 TPU 网络就是因为错误理解并行化而被严重过度配置了。

如果不理解并行性,你就无法确定 HBM 的容量配置、网络带宽,也无法考虑 token 延迟。因此,在设计 AI 芯片时,深入讨论这个问题并不是可选项。这就是为什么这一节会比较长,并且会进入相当多细节。

并行化概念

有各种各样和并行化相关的概念。这些概念让我们能够理解不同并行化方法的优缺点。下面有点像一个清单,但如果没有这些概念,并行化的效果就不会清楚。所以,清单如下:

吞吐量(tokens per second) 这是并行化最显然的收益。LLM 的吞吐量可以用每秒 token 数(tokens per second)来衡量。如果一颗芯片每秒可以生成 1000 个 token,那么两颗芯片每秒可以生成 2000 个 token。

首 token 延迟(seconds to first token) 首 token 延迟(first token latency)是指 AI 在开始考虑你的请求之后,生成第一个 token 所需要的时间。

首 token 延迟可能很高,因为 AI 可能需要从磁盘检索数据,以便记住这个对话是关于什么的;它可能需要在你看不见的情况下默默对自己说很久的话,也就是“思考”;它还可能需要阅读许多网站和其他数据,以弄清楚自己想如何回应。AI 在大致知道自己想说什么之前,不能开始对你说话。所以,当你在 AI “思考”且还没说任何内容时等待的那段时间,就是首 token 延迟。

首 token 延迟不是一个并行化特有的概念,但它很容易和 token 延迟混淆,而 token 延迟是并行化中的一个重要概念。所以我在这里单独定义它,只是为了说明:这不是“token 延迟”。

Token 延迟(ms per token) Token 延迟(token latency)是指在稳态下生成一个 token 所需要的时间,也就是延迟。不要把它和首 token 延迟混淆,因为第一个 token 可能比其他 token 花更长时间。所以,如果你的 AI 开始回应你,然后你看到词一个接一个很慢地出现,比如每秒一个 token,那就是高 token 延迟。

Token 延迟和吞吐量之间的关系,并不像看起来那么紧密。如果吞吐量是每秒 1000 个 token,那么我是不是可以期待 1000 个 token 的回复在 1 秒内完成?不,完全不是这么回事。原因是,吞吐量是跨对话统计的。所以,如果同时有 1000 个对话进行,有 1000 个不同客户在等待 AI 完成回答,那么他们每个人可能每秒只收到 1 个 token。即使你个人没有那么快地收到 token,这对 AI 来说仍然是每秒 1000 个 token。

好吧,这听起来合理。但如果我让所有其他用户都停止使用这个 AI,只剩我一个人,那么如果它的吞吐量标称为每秒 1000 个 token,我就能拿到每秒 1000 个 token,对吧?这听起来有道理,但仍然是错的。AI 也许只有在这些 token 都来自相互独立的对话时,才能每秒生成 1000 个 token。而你只提供了一个对话,所以在这种情况下,你很可能无法在单个对话内收到全部吞吐量。事实上,你的 token 延迟可能完全不变。

如果 token 延迟是 1 ms,也就是 1/1000 秒,那吞吐量肯定就是每秒 1000 个 token,对吧?也不完全对。如果 token 延迟是 1 ms,那么吞吐量至少是每秒 1000 个 token,但它也可能更高。无论 AI 每 1 ms 生成 1 个 token,还是每 1 ms 生成 2 个 token(来自无关对话),token 延迟仍然都是 1 ms。在后一种情况下,吞吐量是每秒 2000 个 token,而不是每秒 1000 个 token。

这里的结论是:延迟和吞吐量分别来看都是简单概念,但要习惯“二者一般没有简单关系”这个想法,可能会很难。并不是说更高吞吐量一定会带来更低延迟,也不是说更低延迟一定会带来更高吞吐量。事实上,情况往往正好相反。

我可能刚刚给了你一种之前没有过的挫败感:当你看到一些材料只谈吞吐量和 token 延迟中的一个、却不谈另一个时,你会感到挫败。这会让那些数字变得没有意义,因为如果你不了解两者,就不可能知道一个系统是否既经济(吞吐量)又有用(token 延迟)。可悲的是,几乎所有材料都只谈其中一个,而不谈另一个。所以,欢迎来到这种挫败感里,现在你知道了。不客气。

可变性(Variability) 总体吞吐量和延迟都是平均值。所以,某个 token 可能比另一个 token 花费多得多或少得多的时间。一般来说,在做并行时,可变性非常不受欢迎,但它并不总是能够避免。专家混合模型(Mixture-of-Experts)就是造成可变性的一个主要原因。

存储(Storage) 存储看起来也许和并行性没有那么紧密的关系,但事实证明,在 AI Transformer 并行性中,它是一个非常重要的概念,因为并行化可能会显著增加或降低每颗芯片的存储需求,具体取决于并行化是怎么做的。在本章后面的章节中,我们会详细讨论并行化对昂贵 HBM 内存存储需求的影响。

局部需求与全局需求(Local vs global requirements) 在并行化中,一个数字到底是局部的,也就是每颗芯片的,还是全局的,也就是总体求和的,会造成很大差异。例如,一个 1 TB 模型需要 1 TB 昂贵 HBM 来存储。如果你的芯片只有 100 GB 昂贵 HBM 存储,那么你就不能运行这个模型,对吧?不对,因为这混淆了局部容量(每颗芯片)和全局容量(总体求和)。局部的每芯片数字是 100 GB 昂贵 HBM 容量;但如果你有 20 颗芯片,那么全局昂贵 HBM 容量就是:

20 * 100 GB = 2 TB

所以,只从存储权重的角度看,你应该可以运行这个 1 TB 模型。不过,这需要权重本身能够分布在 20 颗芯片上,也就是它们需要被并行化。这里的重点是,一定要注意一个数字到底是局部数字还是全局数字。

单颗芯片吞吐量 vs 总体吞吐量(Single chip throughput vs total throughput) 另一个类似的重点是单颗芯片性能和总体系统性能之间的差别。如果一家 AI 芯片初创公司做出的第一颗芯片单芯片性能只有 Nvidia 顶级 GPU 的一半,那这家公司就完蛋了吗?嗯,倒也不一定。如果它们的芯片价格也不到一半,或者功耗不到一半,那么情况也许还不错。单颗芯片性能只有在它影响整个系统性能和成本时才重要。不要迷恋单颗芯片数字。

芯片数量(Chip count) 芯片数量就是系统中使用多少颗芯片。芯片数量本身并不是坏事,尤其是如果每颗芯片都很便宜的话。不过,较高芯片数量通常会增加网络复杂性、可靠性问题和部署复杂度。很多并行化方法会通过增加芯片数量来换取更高吞吐量、更低每芯片存储需求,或者更低 token 延迟。

网络带宽(Network bandwidth) 网络带宽是并行化中的关键限制之一,尤其是层内并行化。很多人一开始会低估这一点。芯片之间交换中间激活、部分结果或其他数据时,网络可能直接进入性能关键路径。如果网络不够快,那么即使每颗芯片本身很快,整个系统也会被网络拖住。

每颗芯片上的昂贵 HBM(Expensive HBM per chip) 在 AI 芯片系统里,我们关心的不只是总共有多少 HBM,还关心每颗芯片需要多少昂贵 HBM。不同并行化方式会以不同方式改变每颗芯片需要存储的权重和 KV 缓存。例如,层内并行有助于降低每颗芯片上的 KV 缓存需求,而层间流水线并行主要有助于降低每颗芯片上的权重存储需求。

最小安装规模(Minimum installation size) 我们刚刚讨论过一个例子:当一个 1 TB 模型无法放在单颗芯片上时,可以把它放在 20 颗芯片上。这意味着我们必须始终拥有 20 颗芯片,否则就无法运行这个模型。所以最小安装规模就是 20 颗芯片,或者根据权重以外其他内容对昂贵 HBM 的需求,可能在 10 到 20 颗之间。即使 20 颗芯片产生的吞吐量远远超过你实际需要,你仍然需要 20 颗芯片。所以,大的最小安装规模很不方便。

可靠性(Reliability) 计算机有时会故障。线缆会断开,风扇会停止转动,芯片会熔化,很多事情都可能发生。如果你的 AI 并行化方案涉及 1024 颗芯片协同工作,那么只要其中一颗芯片故障,整个 AI 可能就无法运行,而其他 1023 颗芯片的能力都会被浪费,直到那颗故障芯片被定位、修复或替换。所以,在这种情况下,并行化会把故障率放大 1000 倍。

在早期 TPU 中,我们确实遇到过这个问题:TPU 可以以 1024 颗芯片的 pod 形式一起工作,但连接它们的线缆有时会故障,导致整个 pod 无法使用,直到数据中心的人类员工走过去,找到并更换故障线缆。单根线缆很少故障,但在任意一个 pod 中,需要成千上万根线缆来连接所有这些芯片,所以其中一根故障就并不罕见。我们无法改变线缆故障率,所以 Sameer Kumar 改变了并行化软件,让它即使在少数线缆故障、不可用或缺失的情况下仍然能良好运行。考虑到 TPU pod 使用的是环面网络拓扑(torus network topology),这相当棘手。这个解决方案的代价是把 torus 的有效网络带宽减半,但 TPUv3 的 torus 网络带宽本来就被过度配置了——这是一个例子,说明过度配置,即使是无意的过度配置,也会带来灵活性,使你能够解决芯片设计时没有预测到的问题。

故障域(Failure domain) 一个组件的故障域(failure domain)指的是:如果这个组件故障,系统中会停止工作的那一部分。因此,如果一个由 1024 颗 TPU 组成的 pod 在单根线缆断裂时就会停止工作,那么那根线缆的故障域就包含全部 1024 颗芯片。所以,并行化往往会增加故障率的方式,就是增加故障域的大小。如果两颗芯片以某种方式协作,那么其中一颗坏掉,就有可能影响另一颗的工作。最小安装规模往往也会成为最小故障域,因为如果需要 20 颗芯片,那么只要其中任何一颗坏掉,系统就无法运行。

使用复制进行并行化

并行化一个 LLM 最简单的方式,是拥有多个彼此独立的实例,每个实例负责处理 AI 助手参与的一部分对话。这是在软件中实现起来最简单的一种并行化,但即便如此,它也不是完全简单的。例如,如果分配给系统某一个实例的所有客户同时开始和 AI 说话,那么可能就需要做负载均衡(load balance),也就是快速把其中一些对话从系统的一个实例移动到另一个实例。

下面是系统中有 D 个重复实例时的影响:

复制 D 倍(Duplication by D)

  • 吞吐量(Throughput):乘以 D
  • 延迟(Latency):没有影响
  • 每颗芯片上的昂贵 HBM(Expensive HBM per chip):没有影响
  • 可靠性(Reliability):提高。如果一个实例故障,其他实例仍然可用
  • 网络带宽(Network bandwidth):变化不大,但可能需要能够快速移动 KV 缓存
  • 脉动阵列利用率(Systolic array utilization):不变,仍然可以达到 100%

如果你的对话所对应的 KV 缓存必须从一个实例移动到另一个实例,因为持有你的 KV 缓存的那个实例过载了,那么复制可能会对首 token 延迟(first token latency)和可变性(variability)产生负面影响。

复制是我们将要讨论的各种并行形式中,唯一一种能够改善可靠性的形式。

使用流水线进行解码的层间并行

如果你的模型中有例如 32 层,那么你可以把这些层分布到 32 颗芯片上。为了理解这是怎么工作的,下面是一个对 Transformer 结构的极度简化视角:

图示说明:每个 Transformer 层由一个注意力(attention)子层和一个 FF 子层组成。每一层被放在自己的芯片上,不同芯片之间可以通过某种网络交换向量。

这里,每个 Transformer 层由一个注意力和一个 FF 子层组成。每一层都被放在自己的芯片上,而芯片之间可以通过某种网络交换向量。第 1 层的输出是一批向量,也就是激活(activations),它们会作为第 2 层的输入。所以,第 2 层必须等第 1 层完成之后才能开始。这对并行化来说是一个挑战,因为这意味着当芯片 2 正在处理第 2 层时,芯片 1 和芯片 3 会处于空闲状态。这个问题的解决方案是流水线化(pipelining)。它具体如何工作,取决于我们是在做训练、预填充,还是解码。本节讨论解码,下一节讨论预填充。

对于解码来说,目标是让第 3 层判断下一个 token 会是什么;如果使用推测解码(speculative decode),那就是判断接下来几个 token 会是什么。假设我们有 3 个对话 batch,也就是 3 组对话 X1X2 和 X3,需要处理它们,也就是对它们做回应 / 生成输出 token。

下面是在使用流水线进行解码时发生的事件序列:

  1. 芯片 1 处理 X1
  2. 芯片 1 处理 X2,芯片 2 处理 X1
  3. 芯片 1 处理 X3,芯片 2 处理 X2,芯片 3 处理 X1
  4. 芯片 1 处理 X1,芯片 2 处理 X3,芯片 3 处理 X2
  5. 芯片 1 处理 X2,芯片 2 处理 X1,芯片 3 处理 X3
  6. 芯片 1 处理 X3,芯片 2 处理 X2,芯片 3 处理 X1

你可以看到,最终所有芯片都会同时保持忙碌。这是因为我们有三组独立对话供这些层处理,所以芯片 1 可以为一组对话处理第 1 层,同时芯片 2 为另一组对话处理第 2 层,以此类推。然后,这些对话会沿着各层形成一个环形流动。这个环在最后一层闭合,也就是图中的第 3 层。在最后一层中,下一个 token 被决定出来,然后被传回第一层,也就是第 1 层,让它重新开始处理下一个 token。像这样让独立元素在不同阶段同时被处理的并行化方案,就叫作流水线化(pipelining)。

所以,如果有 32 层,我们就会跨 32 颗芯片进行并行化。假设我们只有 4 颗芯片。我们仍然可以做流水线,只是把每颗芯片上放 8 层,从而创建一个运行在 4 颗芯片上的 4 阶段流水线。一般来说,一个 S 阶段流水线涉及 S 颗芯片,并且在所有层处理时间相同的假设下,会把吞吐量乘以 S

以这种方式跨 S 颗芯片做流水线,最明显的效果是把吞吐量乘以 S。这种流水线并不会降低 token 延迟,因为每个单独的对话 / token 在任意时刻都只由一颗芯片处理。事实上,相比于只使用 1 颗芯片,现在我们必须在每个阶段之间花时间在芯片之间传输数据,这会在一定程度上增加延迟并降低吞吐量。

回忆一下,使用复制进行并行化可以直接提高吞吐量,而不会增加延迟,这比我们在这里看到的流水线更好。那么,为什么还要使用流水线呢?流水线有一个很强的好处:它能降低权重存储需求,因此我们不需要购买那么多昂贵的 HBM。某一颗芯片 / 某一个阶段只需要存储属于它自己那一层或那几层的权重。所以,如果每颗芯片上只有一层,那么这颗芯片只需要存储这一层的权重。如果有 32 层,那么使用流水线可以把每颗芯片的权重存储需求降低 32 倍。复制并没有这个重要好处。

那么 KV 缓存存储需求呢?结果是,流水线对降低 KV 缓存的存储需求没有帮助。这里的原因并不显然。乍看起来,它似乎应该有帮助。每一层都有自己的 key,所以和权重一样,负责某一层的芯片不就只需要存储那一层的 key 吗?是的,事实确实如此。那么,如果有 32 层,我们是不是也像权重一样,把每颗芯片上的 KV 缓存存储需求降低了 32 倍?不是,事情并不会这样展开。

回忆一下,每一层都在处理不同 batch 的对话。所以由于流水线,在任何给定时刻,我们有 32 倍数量的 batch 正在被处理。某一个对话会沿着这个环走一圈,然后再次回到同一层,以便生成下一个 token,除非刚才那个 token 已经是最后一个了。所以,你需要把所有对话都保留在 KV 缓存中,即使那些当前正在被其他层处理的对话也要保留。因此,是的,我们把“每颗芯片、每个对话”的存储需求降低了 32 倍;但为了让所有芯片保持忙碌,也就是获得高利用率,我们同时需要在流水线中处理 32 倍数量的对话。这两个 32 倍相互抵消,所以最终流水线对 KV 缓存存储没有任何改善。

让我们更仔细地看看芯片之间的网络传输问题。每颗芯片处理完一批对话之后,都必须把自己的输出传给环中的下一颗芯片;在最后一层,这个环会通过一个 token 回到第一层。任何芯片都必须等收到下一批数据之后,才能开始处理下一批。这意味着,在网络把数据从一颗芯片传到另一颗芯片的时候,所有芯片都是空闲的;而每当流水线前进到下一个阶段时,这件事都会发生。

这个等待时间可能长,也可能短,取决于一颗芯片处理一层需要多长时间,以及网络传输该层输出数据需要多长时间。一个解决方案是接受这种等待,并确保网络足够快,让等待时间很短。这样,脉动阵列利用率总是会低于 100%,因为我们在传输期间让它们闲置;但如果你的网络真的很快,它可能不会比 100% 低太多。

另一种可以实现 100% 脉动阵列利用率的方案,是把流水线阶段数量翻倍,以隐藏传输时间。它的工作方式是:我们向流水线中引入两倍数量的对话 batch。一半对话正在由芯片处理,而另一半对话的数据正在通过网络传输。在流水线的每一步,这两半会交换位置。假设网络足够快,能够在处理完整一层所需的时间内完成数据传输——它应该做到这一点——那么在下一颗芯片处理完自己的层之前,传输就已经完成了。这意味着,只要任意芯片完成一层处理,它就可以立即开始处理已经传输到它那里的输入。所以,没有任何芯片需要等待网络传输,前提是网络传输可以并发进行,而且不会干扰脉动阵列。

这样确实会导致网络利用率低于 100%,除非网络带宽和脉动阵列吞吐量被完美平衡。我不会这么做——最好让网络稍微过度配置一点。

这种修改后的流水线方法,我们可以称为完整流水线(full pipelining)。如果使用 S 颗芯片,它可以确保我们得到完整的 S 倍吞吐加速。它也会让 token 延迟翻倍。为什么会这样?因为某一个对话现在必须经过两倍数量的步骤,而所有步骤都花费和一层处理相同的时间,所以生成一个 token 会花费两倍时间。KV 缓存需求也翻倍了,因为我们把需要存储 KV 缓存的对话数量翻倍了。权重存储不变,因为它不依赖于流水线阶段数量。

我们实现的一个更微妙但非常重要的事情是:现在网络不需要像之前那样快得多,因为我们不再等待它。它只需要能够在执行一层所需的、相当长的一段时间内完成传输即可。如果这是真的,那么它就完全不会造成减速。因此,在使用完整流水线时,层与层之间的网络连接不需要像人们可能想象的那么快。

总结一下,层间流水线的好处是显著降低权重存储需求。它的代价有两种:如果使用部分流水线(partial pipelining),代价是需要非常快的网络,并且吞吐量会小幅下降;如果使用完整流水线,代价是延迟和 KV 缓存存储需求翻倍,但网络需求会更低。

延迟翻倍有多糟?不同应用有不同的延迟需求。有时候它完全无关紧要,因为客户只需要某个时候拿到答案,明天拿到也可以。对于一个另一端有人在等待的 AI 助手,token 延迟应该较低;但如果它已经快到比如每秒 100 个 token,那么每秒 50 个 token 也没有差太多——这已经比人类阅读速度更快了。它已经足够快了。不过,如果你使用的是一个推理模型(reasoning model),它会默默对自己说话,也就是“思考”,因此需要生成许多用户看不到的 token,那么即使速度已经很快,token 延迟也可能更值得关注,所以你可能会,也可能不会,更倾向于避免完整流水线。

KV 缓存存储翻倍有多糟?这取决于每颗芯片上的 KV 缓存有多大,以及每颗芯片有多少昂贵 HBM 存储容量。如果你在合理处理自己的 KV 缓存,比如对很久以前的历史,也就是过去 1000 个 token 之前的历史,使用检索(retrieval),那么翻倍可能完全不重要。

注意,你也可以只每隔 4 层做一次流水线,而不是每一层都做流水线。这意味着,相比于每一层之间都必须通过网络传输同样数量的数据,你现在需要等 4 倍时间之后才需要传输同样数量的数据。因此,相比于每层都做流水线,这会把带宽需求降低 4 倍。它也会降低 KV 缓存存储需求的增加,因为你现在是在每隔 4 层之间多一个网络传输流水线阶段,而不是在每一层之间都有。

解码部分流水线,按 S 倍(Decode partial pipeline by S)

  • 吞吐量(Throughput):乘以略小于 S 的倍数,具体小多少取决于网络带宽和延迟
  • Token 延迟(Token latency):增加,具体取决于网络带宽和延迟
  • 每颗芯片上的昂贵 HBM(Expensive HBM per chip):权重存储除以 S,KV 缓存存储不变
  • 可靠性(Reliability):变差。故障域乘以 S
  • 网络带宽(Network bandwidth):极其重要,位于关键路径上
  • 脉动阵列利用率(Systolic array utilization):降低,永远无法达到 100%

解码完整流水线,按 S 倍(Decode full pipeline by S)

  • 吞吐量(Throughput):乘以 S,假设网络带宽合理
  • Token 延迟(Token latency):翻倍;如果网络带宽不够合理,则超过翻倍
  • 每颗芯片上的昂贵 HBM(Expensive HBM per chip):权重存储除以 S,KV 缓存存储翻倍
  • 可靠性(Reliability):变差。故障域乘以 S
  • 网络带宽(Network bandwidth):只要足够快,就没有影响
  • 脉动阵列利用率(Systolic array utilization):不变,仍然可以达到 100%

以上讨论的是解码。下一节讨论预填充的流水线化。

使用流水线进行预填充的层间并行

相比解码,流水线化(pipelining)对预填充(prefill)更有吸引力。原因是,对于预填充和训练来说,为了做流水线,并不需要引入额外的对话 batch。稳态 token 延迟仍然会增加,但通过这种方式,S 路并行化下的 KV 缓存存储需求会除以 S,这和解码不同。

对于一个非常大的文档,整个系统中的所有芯片都有可能都在读取同一个文档,或者同一个对话。只有当当前文档或对话已经被完全处理,也就是“读完”之后,才需要把一个新文档或新对话引入系统。

在解码期间,第一颗芯片会为一批对话处理第 1 层,把输出发送给下一颗芯片,然后继续为另一批对话处理第 1 层,以此类推。预填充的模式和解码类似,但我们看的不是不同对话的 batch,而是同一个文档 / 对话中的片段或子集。

例如,第一颗芯片可能会为某个文档的前 256 个 token 处理第 1 层,把输出发送给下一颗芯片,然后继续为这个文档后面的 256 个 token 处理第 1 层,以此类推。因为我们只是在看同一个文档,所以完整流水线化(full pipelining)不会显著增加 KV 缓存存储需求,甚至取决于你的实现方式,也可能完全不增加。此外,预填充本来对 KV 缓存的需求就不高。

预填充部分流水线,按 S 倍(Prefill partial pipeline by S)

  • 吞吐量(Throughput):乘以略小于 S 的倍数,具体小多少取决于网络带宽和延迟。
  • Token 延迟(Token latency):增加,具体取决于网络带宽和延迟。
  • 每颗芯片上的昂贵 HBM(Expensive HBM per chip):权重和 KV 缓存存储需求除以 S
  • 可靠性(Reliability):变差。故障域乘以 S
  • 网络带宽(Network bandwidth):极其重要,位于关键路径上。
  • 脉动阵列利用率(Systolic array utilization):降低,永远无法达到 100%。

预填充完整流水线,按 S 倍(Prefill full pipeline by S)

  • 吞吐量(Throughput):乘以 S
  • Token 延迟(Token latency):翻倍。
  • 每颗芯片上的昂贵 HBM(Expensive HBM per chip):权重和 KV 缓存存储需求除以 S
  • 可靠性(Reliability):故障域乘以 S
  • 网络带宽(Network bandwidth):只要足够快,就没有影响。
  • 脉动阵列利用率(Systolic array utilization):不变,仍然可以达到 100%。

我不会太多解释这套东西在训练中如何工作,因为这个主题更复杂。不过下一段的硬核读者说明框里会有一点相关信息。

给硬核读者看的流水线训练细节

在训练中,一开始会有和预填充期间一样的数据前向流动,随后会有一个方向相反的梯度流动。数据流的方向会在最后一层发生转向,然后从那里一路反向穿过所有层,直到最终回到第一层。

梯度是一种关于前向传播过程中发生了什么的反馈:每一层都会被它后面的那一层告知:“你刚才告诉我的是 X,但我其实更希望你说 Y。” 每一层都会从这种反馈中学习,这就是训练期间发生的事情。

每颗芯片会对每段文本,也就是 mini-batch 或 microbatch,处理两次:一次是前向,之后还有一次是反向。训练需要存储前向信息,也就是激活(activations),以便之后同一段文本的梯度绕回来时使用。

如果有 L 个前向层,那么梯度要回到第一层,总共需要穿过 2(L-1) 层,也就是前向 + 反向。由于我们使用的是一个同时有许多文本在飞行中的流水线,所以第一颗芯片需要存储 2(L-1) 份前向数据,也就是激活。

通过适当优化,比如 checkpointing,这个存储量是可管理的;另一个原因是,训练期间的序列长度通常不会像推理时可能那么大。你可能会用 4k 或 16k token 的序列长度训练,而不是 1m token。也许在训练最后会有一次非常短暂的长序列长度训练运行,但那种运行的做法不同,而且不必很快。例如,你也许会把激活存到 SSD 上;如果是普通训练,这会慢到不可接受。

层内并行化

在关于流水线化的章节中,我们假设每一层运行在单颗芯片上,或者几层运行在同一颗芯片上。也可以把单独一层并行化到多颗芯片上,这就是本节的主题。和层间流水线化相比,这种方式更有吸引力,原因包括它可以同时降低 KV 缓存需求和 token 延迟。这里的一个巨大代价是:它需要比流水线化高得多的网络带宽。

你可能还记得,一个 Transformer 层由两个子层组成:注意力(Attention)和前馈网络(Feed-Forward,FF)。这两者的并行化方式不同,所以我们分别讨论。

注意力相当容易并行化。如果有 128 个注意力头(attention heads),那么你可以让每颗芯片负责一个 head,这样就能跨 128 颗芯片并行化。之所以可行,是因为注意力头彼此独立运行,除了最后需要做一次求和。[^4]

不过,你很可能会使用 GQA,这会稍微改变情况。如果 GQA 把两个注意力头放进同一组,那么你也许更愿意把这两个注意力头放在同一颗芯片上,因为这样这颗芯片在解码期间会有更多 query 向量。否则,GQA 就无法获得我们在解码章节中声称的那些收益,而这些收益很重要。

所以,通过这种方式,你可能只能获得 8 倍并行,或者你有多少个 GQA 组,就获得多少倍并行。但你很可能希望在层内并行化中远远超过 8 颗芯片,所以我们还需要其他方法来进一步并行化。

还有其他方法可以在层内并行化注意力。一个想法是把 KV 缓存拆分到 2 颗或多颗芯片上。对两个子集分别做注意力之后,其结果可以以很低成本在芯片之间合并,所以这种方法很好用。对于长上下文注意力,通过这种方式,你大概可以得到 4 到 8 倍,甚至更多的并行。因此,总共我们可以达到 64 倍,也就是 8 个 GQA 组乘以 8 路 KV 缓存拆分,这已经相当不错了。如果你愿意,还可以继续往上推,尤其是在做预填充时,因为预填充可能还可以拆分 query。不过这里提到的技术同时适用于解码和预填充。

FF 的工作方式不同,但也很容易并行化。FF 主要由两个大型矩阵乘法组成,它会显著增大隐藏的中间向量宽度,通常是扩大 4 倍。你可以这样并行化:让每颗芯片负责更宽的中间向量宽度中的一个子集。

如果你有一个 256 × 256 的脉动阵列,并且模型向量宽度是 16k,那么理论上你可以一路把并行度推到:

16k * 4 / 256 = 256

也就是 256 颗芯片,同时仍然获得 100% 的脉动阵列利用率。由此你可以看到,虽然 FF 需要极大的计算能力,但它也非常适合并行化,而且是脉动阵列最好的朋友。对于 FF 来说,预填充和解码没有区别,两者同样有效。

注意力和 FF 最后都需要一次跨芯片求和,把并行芯片之间的结果合并起来。跨芯片求和叫作 all-reduce。我不会在这里描述 all-reduce 算法,因为它们非常复杂。我曾为 TPU 做过最初的 all-reduce 软件。不过,下一章关于网络拓扑的内容会讲一些相关细节。

当你使用层内并行化,把并行芯片数量翻倍时,层内网络的每芯片带宽需求会略多于翻倍。因此,你的网络带宽可能会限制这种并行化。如果你想知道“略多于翻倍”是什么意思,可以看下面的硬核读者说明框。

给硬核读者看的“略多于翻倍”网络带宽需求量化

为了做分布式求和,也就是 all-reduce,如果有 P 颗芯片参与,数据会被分成相等的 1/P 个 chunk。每颗芯片负责其中一个 chunk,因此需要从所有其他芯片接收这个 chunk。然后它会把这些 chunk 求和,并把求和后的结果发送回所有芯片。这是一个简化视角,但数学关系成立。

这里有两次传输,所以如果每颗芯片上的数据大小是 X,那么每颗芯片传输的数据总量是 2X。不过,你不需要把自己的 chunk 发给自己,所以实际数据量是:

2X * (1 - 1/P)

其中 1 - 1/P 这个因子,就是“略多于翻倍”里“略多”的来源。

如果你从 2 路并行变成 4 路并行,那么 1 - 1/P 这个乘子会从 50% 变成 75%,这很显著。如果你从 64 路并行变成 128 路并行,那么这个因子会从 98% 变成 99%,这就不显著了。所以这个因子主要在跨较少芯片并行时重要。1 - 1/P 对于 P=1 尤其显著,因为此时根本不需要带宽,因为没有并行化。

由此你可能会观察到,网络传输量 2X * (1 - 1/P) 在 P 很大时大约等于 2X;你还可能进一步观察到,2X 并不依赖于 P。所以无论 P 变得多大,我们每颗芯片、每一步都永远不需要传输超过 2X 的数据。那么,为什么把 P 翻倍会要求每颗芯片网络带宽也翻倍呢?

这是因为步骤发生的速率翻倍了,所以你需要在同样时间内完成两倍数量的分布式求和。这是 AI 芯片巨大带宽需求的一个重要驱动因素,所以我觉得值得稍微详细讲一下。

假设我们把网络平衡到这样一种程度:它可以在一颗芯片单独计算一个 batch 所需的时间内传输 2X字节。那么,如果使用P路层内并行化,我们现在需要P倍带宽,才能仍然在计算一个 batch 所需的时间内传输2X字节,因为现在使用了P颗芯片,计算速度也快了P倍。实际数学关系还要更复杂一点,因为还要考虑1 - 1/P这个因子。所以简化结论是:把P 翻倍,需要略多于 2 倍的网络带宽。

每芯片层间带宽需求对层内并行化是中性的

如果我们按 P 倍做层内并行化,那么在同样时间内,我们现在会处理 P 倍数量的 batch。这是不是意味着层间网络需求也会增加 P 倍?不一定。

如果你让同一层内的 P 颗芯片中的每颗芯片只把 1/P 的数据向前发送到下一层,那么这会让速度提高 P 倍,正好抵消了事情现在发生得 P 倍快这一事实。所以,如果你这样做,从每颗芯片的带宽角度看,它是中性的。

由于这里每颗芯片的层内带宽需求会增加 P 倍,而每颗芯片的层间带宽是中性的,所以你可以看到,在高度层内并行化时,你需要的层内带宽要远远大于层间带宽。对于高度层内并行化的 AI Transformer 模型推理来说,这是高网络带宽需求的重要驱动因素——而对于大模型来说,你需要这种层内并行化来获得低 token 延迟。

这也意味着,你也许可以把层间传输放到一个慢得多的网络上。另一个有助于这一点的因素是,让同一组芯片运行多层;这样会按比例降低层间网络需求。

至于跨层流水线化,现在我们有一个选择:当我们需要为层内并行化做网络传输时,可以选择让芯片空闲,直到网络传输完成。另一种选择是做一个 2 阶段流水线:把正在飞行中的对话数量翻倍,其中一半正在某一层中被处理,而另一半正在通过网络传输,也就是执行 all-reduce。每经过流水线的一个阶段,这两半就交换位置。

和前面一样,2 倍流水线化会让 KV 缓存存储需求和 token 延迟按相同倍数增加,也就是 2 倍;但它会降低网络带宽需求,这非常重要,而且只要网络能在执行一层所需的时间内完成传输,它也能确保我们完全不需要等待网络。

请注意,在注意力和 FF 之间存在一个通信步骤,所以对于完整流水线来说,整体 Transformer 层中的阶段数是 4 个,而不是 2 个。我建议修改模型,让注意力和 FF 独立、并行地执行,这样在一个注意力层内部就不存在二者之间的依赖。这会把 token 延迟和阶段数减半,因此也会降低 KV 缓存需求。代价是收敛后的模型准确率会有轻微下降。不过,你能下载到的公开 Transformer 模型通常不是这样做的,所以如果你要使用那些模型,就必须处理串行方式。这需要训练一个新模型,无法轻易改装进一个已经训练好的模型里。

假设你在层内和层间都做完整流水线。那么每一层的输出会有两次连续的网络传输:一次是层内传输,一次是层间传输。假设网络足够快,你可以把这两个阶段合并成一个阶段,从而减少阶段数量,并按比例降低 token 延迟和 KV 缓存存储。

如果你把层间并行化和层内并行化结合起来,权重存储的收益会相乘,所以这非常显著。这就是你如何实现跨大量芯片,比如 1024 颗芯片的并行化,并且即使对于巨大模型,也能把每颗芯片上的权重存储需求降到很小。当然,所需芯片数量也会相乘。故障域也会相乘。

完整流水线收益示例

假设你已经做了 8 路层内并行化,但没有做完整流水线,并且你观察到额外 25% 的时间都在等待网络。你担心这个开销,所以不想继续提高并行度。你也不想使用完整流水线,因为那样 token 延迟会翻倍。

你发现这里的思维错误了吗?如果网络开销是额外 25%,那么如果你做完整流水线,就可以在完全不等待网络的情况下把并行度再提高 4 倍,也就是总共 32 路。你确实让 token 延迟翻倍了,但现在你又把它除以 4,所以总体上你把 token 延迟减半了,而不是翻倍。而且还比这更好,因为你不再等待网络,所以还可以扣掉原来的 25% 开销。

如果你正在设计一颗 AI 芯片,你也许应该考虑运行一个模拟,模拟你将如何跨芯片并行化,而不只是用电子表格里的数学计算。理想情况下,如果你的时间表和仿真资源允许,这应该是某种完整实现运行在模拟器中;当然这未必可行。

我给出这个建议的原因是:在某种并行化方案下,网络带宽需求和其他需求会如何变化,这件事既复杂又反直觉。如果你算错了,你可能会造出一颗无法按你预期方式使用的芯片。我不太愿意承认这一点,但在我职业生涯早期,我曾经错误推导过这部分数学,而且错了相当长一段令人尴尬的时间。当时身边没有人能解释,所以我只能自己搞明白。TPUv2 的网络带宽是由别人更早设定的,那个人也算错了,导致网络被过度配置。TPUv2 上很多东西都被过度配置了。也许你确信自己算对了,因为你的工程师比我更擅长可靠地使用数学。这当然可能,虽然我确实有计算机代数博士学位。做一次模拟可以大幅降低你从根本上搞错某件事的风险。

即使模拟也抓不住所有问题

当 TPUv3 抵达 Google 总部时,网络出了一个问题。问题是 TPUv3 芯片无法以允许两颗 TPUv3 芯片之间通信的方式驱动线缆。考虑到大规模扩展是 TPU 的卖点之一,你可以想象这是个问题。

结果发现,芯片上的线缆通信组件没有按预期工作。团队甚至已经做过联网芯片的模拟,但他们抽象掉了通过线缆通信的一些物理细节。固件团队和硬件团队进行了一场英勇而紧张、持续一周的合作,我没有参与,也不能为此邀功;他们最终发现了一种通过软件操作芯片的方法,使得 TPUv3 最终拥有完全可工作的网络,而不需要重新做芯片,因此 TPUv3 的发布没有被推迟。即使是详细模拟也不是保证,不过它们确实有助于抓住许多问题。

给 AI 芯片硬核读者看的便宜网络

注意力 / FF 末尾的求和是一个 all-reduce,而 all-reduce 可以分解为两个独立操作:reduce-scatter 和 all-gather。你可以把层间数据传输放在这两个操作之间,让所有芯片把它们各自 1/P 的数据子集发送到下一层芯片。所以,如果 P 不小,那么每颗芯片在层间发送的数据量可以明显少于每颗芯片在层内发送的数据量。你也许应该考虑,能不能利用这一点做出更经济的芯片。AI 芯片常用的网络方案非常昂贵。下一章关于网络拓扑会进一步讨论这一点。

给硬核读者看的网络 bit 数

你的带宽需求也取决于芯片上的压缩和低 bit 数值格式等功能。需要注意的是,网络传输的是求和结果,所以它们可能非常不幸地需要比你用于乘法,也就是脉动阵列内部乘法,更高的 bit 精度。这会导致网络带宽需求高于你的预期,尤其是当你以为“既然脉动阵列乘法用 8 bit,那么求和传输也可以用 8 bit”时。事实上,你的脉动阵列做加法时也会使用超过 8 bit。

这又会涉及 router 和 torus 的问题,因为在 all-reduce / reduce-scatter 期间,torus 需要传输比 router 更宽的部分和(partial sums),所以这也会影响所需精度,具体还取决于 torus 的精确大小。你应该让负责并行化的工程师和对这类主题感兴趣的 AI 研究人员一起把这件事弄清楚。你可能找不到已经对这个主题感兴趣的 AI 研究人员——这很可能——那么你就需要把某个人训练到能理解这个主题。

这可以避免你像很多人那样,在网络加法中不得不使用 32 bit。8 bit 可能不够,32 bit 太多。16 bit 可能可以,但这需要特别小心,而且取决于你的数值格式。这也许是 FP16 唯一可能优于 BF16 的地方,而 int16 可能最好。也许你的客户没有成熟到能在求和,也就是层内并行化中使用低于 32 bit 的东西。如果是这样,当你配置网络带宽时必须意识到这一点,因为这需要两倍带宽。你和你的客户用 32 bit 会更轻松,但你需要购买一个贵两倍的网络。

专家混合模型

MoE 是 FF 的一个复杂版本。在 MoE 中,你不是有一个大矩阵,而是有 N 个不同的较小矩阵,称为专家(experts)。每个矩阵 / 专家拥有不同领域的知识,所以这个想法是:每个 token 只被路由到那些拥有与该 token 相关知识的专家那里。

这是一种加速,因为这样一来,这个 token 就不必和那些拥有无关知识的权重,也就是专家,进行计算,从而节省大量矩阵乘法。MoE 是一种加速 Transformer 模型的强大技术,但如果你想达到高利用率,它会在部署方面造成大量复杂性。

MoE 不一定是一种并行化技术;你可以在没有并行化的情况下使用它,例如把所有专家都放在一颗芯片上。但你也可以通过把每个专家放在不同芯片或不同芯片组上,来并行化它。单独来看,每个专家只是一个普通的 FF 层,所以它本身也可以像其他 FF 层一样做层内并行化,包括层内带宽与层间带宽之间的那些含义。

token 到专家、再从专家返回原位置的传输,可以选择做流水线化,从而避免等待网络;这样做会带来和前面几节关于流水线化解释过的相同存储和性能影响。

不同模型中的专家数量差异巨大。例如,一个像 Mixtral 8×7B 这样的模型可能有 8 个专家,其中 2 个专家激活,也就是 25% 激活;而另一个像 Llama 4 “Maverick” 的模型有 128 个专家,激活权重是 17B,而总权重是 400B,也就是 4% 激活。Google 甚至曾发布过一个叫 Switch-C 1.6T 的模型,有 2048 个专家,尽管这在今天并不是常态。

专家混合模型的一个大问题是,专家不一定被均匀使用。例如,如果专家 A 了解英语名词,而专家 B 了解公元前 405 年蒙古人戴的帽子,那么你可以看出,专家 A 会比专家 B 更经常被访问。结果是,你需要为专家 A 配置比专家 B 多得多的芯片。如果人们突然开始关心公元前 405 年蒙古人戴的帽子,那么你就需要在那个时候立刻给专家 B 增加容量。到目前为止,我们在这份文档中讨论过的内容,都还没有要求部署期间存在这种动态可变性,而这正是 MoE 的麻烦所在。

MoE 的训练通常会采用某种方式,让训练过程尽量避免专家之间负载不均衡,也就是让它们获得一种知识分布,目标是让所有专家的需求大致均匀。以这种方式训练 MoE 模型可以改善需求的均匀性,但不能保证完美均匀。

拥有许多专家的 MoE 模型存在一个问题:对于某个给定专家,可能咨询它的对话太少,以至于它没有足够的对话可处理,无法填满一个脉动阵列。于是它就必须以低效方式运行,利用率低于 100%,甚至可能低得多。同一颗芯片在等待累积足够多关于冷门主题的对话以填满脉动阵列时,可以服务其他专家;不过这会增加 token 延迟和 KV 缓存存储需求。

同样的问题即使在更高负载下也适用。如果某个专家有 120% 的负载,而你又没有办法让等待的 token 排队,那么你就需要复制这个专家,于是你会有两个负载为 60% 的专家。所以,尽管这个专家类型有显著负载,但这两个同类专家都只以 60% 的脉动阵列利用率运行。因此,对于 MoE 模型,你也许更希望允许一定排队。

你的安装规模越庞大,即使没有队列,也可以获得越高的 MoE 利用率,前提是你能用工作填满整个系统。这是因为在那种情况下,专家会有更高倍数的副本。我们看到,120% 负载会导致 60% 利用率。然而,如果某个专家有 1020% 负载,就会给这个专家分配 11 颗芯片,或者如果在层内并行,则是 11 组芯片,每颗芯片 / 每组芯片的利用率是:

1020% / 11 = 93%

你可以看到,在高负载下,“再多一个专家”带来的取整效应会变小。通过这种方式,在高倍数下,你可以让平均利用率接近 100%,但如果没有队列,它平均上仍然无法完全达到 100%。

如果不让某些 token 等待,也就是不增加 token 延迟和 KV 缓存存储需求,你不可能让 MoE 模型达到 100% 利用率。解决这个问题最简单的方法是接受较低利用率。从我听到的行业讨论来看,通常实现的 MoE 往往导致远低于 100% 的利用率,尽管我认为这可以通过更好的软件改进。

一种更高级的系统是:系统里有高优先级 token 和低优先级 token。高优先级 token 例如另一端有真实客户正在等待的 AI 助手 token;低优先级 token 则是批量离线工作,没有 token 延迟要求,或者只有很弱的 token 延迟要求。然后专家会优先立即服务高优先级 token,而低优先级 token 会在队列中等待,用来填满专家,从而提高利用率。

如果低优先级 token 的注意力长度较短,而你也许可以安排这一点,那么让它们在队列中等待所需的 KV 缓存存储量就会降低。把这些技术结合起来,就有可能让 MoE 模型也达到高利用率。你可以看看 vLLM 这类项目,它们用于 MoE 模型部署。

部署困难会随着专家数量增加而增加,这可能就是为什么有些 MoE 模型专家数量较少。除了部署麻烦之外,而部署麻烦确实是一个严重问题,MoE 在专家数量多时效果更好。

我在这里做一个预测:LLM 在权重中包含了海量知识,而其中大部分在大多数时候都无关。这就是为什么拥有许多专家的 MoE 如此有效——它丢掉了大量无关权重。这里的问题是,我们一开始就把太多知识存进了权重里。这是错的。

我们应该改用层内检索(within-layer retrieval)从向量数据库中取出大部分知识。注意,这和 RAG 不一样,RAG 不是层内的。这样可以在减少权重数量的同时显著提升模型准确率。这也会显著降低 MoE 的正面影响,不过我认为我们仍然会使用 MoE,只是专家数量会很少,比如 4 个或 8 个。相反,无关知识会被转移到一个数据库中,那里有大量行没有被检索到,因此这些行是“免费的”。我不知道事情是否会这样发展,但我怀疑会。

未来我们也可能会更多地检索少量权重,比如 QLoRA,这同样会把今天 MoE 所实现的一部分东西卸载出去。MoE 比什么都没有好,但我们仍然需要更好的东西。

给硬核读者看的 MoE 对 torus 和 router 的影响细节

Torus 拓扑的一大优势是,在我们目前讨论过的所有内容中,一颗芯片永远只需要和它在 torus 中的邻居通信,这让 torus 拓扑极其经济且高效。对于 MoE 模型来说,这一点不再成立,因为你可能必须把 token 发送到几跳之外,才能到达那边的某个专家。

在路由拓扑(routed topology)中,假设目标专家在同一个 router 上,你可以直接跳过去,当然它也可能不在同一个 router 上。但在 torus 中,这种情况下数据必须经过多跳传输。这会降低 torus 拓扑的有效性,但也许还没有降低到 router 更可取的程度。即使 MoE 模型相当流行,Google TPU 仍然使用 3D torus。下一章关于网络拓扑会更详细地讨论这一点。

给硬核读者看的强制均匀分布细节

有一种技术非常适合训练,它也会在推理期间带来完全均匀的负载。这项技术是强制 AI 无论如何都均匀地路由 token。这样一来,一个利用率不足的专家会收到本来更适合另一个专家的 token……但那也没办法。

所以,通过这种方式,你的系统仍然可以获得 100% 的脉动阵列利用率,因为一切都是均匀的。然而,如果在推理期间使用这种方法,也意味着你从 AI 得到的回答有时可能会随机变差,因为回答质量会取决于其他人在同一时刻问了什么主题。如果别人问的主题和你问的是同一个,也许那个专家正忙,那你也只能自认倒霉。

例如,如果你用丹麦语提问,而只有一个专家懂丹麦语,并且那个专家当时不可用,那么你可能会得到一些奇怪的回答,因为这个 AI 平常明明懂丹麦语,但现在突然又不懂了。我的理解是,有些 AI 助手实际上确实这样做。哎呀!不过在这种情况下,它们大概已经投入了大量努力来移除或减少最坏结果。

[^4]: 注意力头不是会先拼接(concatenate),而不是求和,然后再做矩阵乘法吗?是的,那样更高效,但这等价于先乘到 d_model 的宽度,然后再求和。所以我这样描述是因为更简单。不过,是的,如果你以分布式方式拼接,比如 all-scatter,而不是求和,那么你也许可以更高效地使用网络。这是一种优化。

持续创作高质量文章,欢迎关注公众号。

  • 拓驰猎头2010年成立,总部在深圳,全职猎头80人+,全球Office10个+,专注在芯片半导体、AI大模型、智能硬件、新能源、汽车5大赛道30个+细分行业。

  • Jason,拓驰猎头CEO,20年猎头经验。

    长期招募合伙人、猎头顾问、SOHO顾问等,欢迎+V:hunterjason