返回主博客

Llama 的推理特性

关于推理数学的入门知识以及对 Llama 惊人成本的考察。

发布者Aman

19分钟阅读


Llama-2-70B 是 gpt-3.5 的一个诱人的替代品,但如果正在寻找廉价的语言模型,那么偏离 OpenAI 的 API 可能不值得。

当考虑价格和延迟时

您不应该为完成型工作负载提供 Llama-2 服务

相反,Llama 最适合以 prompt 为主导的任务,例如分类。在以下情况下,Llama-2 也可能适用

  1. 您的工作负载没有 prompt tokens(违反直觉,但我们稍后会解释)
  2. 您正在执行批量处理作业

否则,gpt-3.5 应该更便宜且更快。

快速声明一下,使用 Llama 而不是 gpt-3.5 的一个原因是微调。但在这篇文章中,我们只探讨成本和延迟。我没有将 Llama-2 与 GPT-4 进行比较,因为它更接近 3.5 级别的模型。基准性能也支持这一说法

图 1:GPT-3.5 在此处的所有基准测试中都优于 llama

我将通过比较在延迟大致相似的情况下,为 Llama-2-70B 和 gpt-3.5-turbo 提供服务的成本来证明这些断言。我们在 2 个 80-GB A100 GPU 上服务 Llama,因为这是将 Llama 放入内存所需的最低配置(使用 16 位精度).

在 2 个 A100 上,我们发现 Llama 的完成 tokens 定价比 gpt-3.5 更差。我们推测在 8 个 A100 上的定价具有竞争力,但代价是延迟高得令人无法接受。

另一方面,对于 prompt tokens,Llama 比 gpt-3.5 便宜 >3>3 倍。

Transformer 数学入门

通过一些简单的数学计算,我们将展示以下 Llama-2 的结果。对于序列长度为 NN 和批量大小为 BB 的情况

每 token 的 FLOPS=140109 FLOPs\text{每 token 的 FLOPS} = 140 \cdot 10^9 \text{ FLOPs}
内存带宽 / token =140 GB/s+320NB KB/s\text{内存带宽 / token } = 140 \text{ GB/s} + 320\cdot N\cdot B \text{ KB/s}

140140 来自模型参数数量的两倍,而 320 KB/s 是通过一些算术运算得出的。在以下部分中,我们将解释我们如何得出这些数字。

还有其他论文和/或博客文章对 transformer 数学进行了精彩的解释。对于推理,Kipply 的文章是一个很好的参考。而且,我相信 Scaling Laws推广了用于 transformer FLOPs 的简化方程。

为了验证这些数字,我们从 Llama 的架构细节开始。隐藏层维度为 4096,注意力头的数量为 64,层数为 80,每个注意力头的维度为 128

d模型=4096d_\text{model} = 4096
nh=64n_h = 64
nl=80n_l = 80
dhead=128d_\text{head} = 128

计算模型 Flops

前向传递的 FLOPs 数量约为 2P\approx 2P,其中 PP 是模型中的参数数量。我们模型中的每个参数都属于某个权重矩阵 MRm×nM \in \mathbb{R}^{m \times n}。对于每个输入 token,每个矩阵在与表示该 token 的向量的矩阵乘法中都只使用一次。

对于每个 MM,我们左乘一个维度为 mm 的向量 xx。此向量-矩阵乘法的总 FLOPs 为 2mn2mn,或权重矩阵中条目数的 2 倍。transformer 所有权重矩阵中的条目总数是参数总数 PP,这给出了 2P2P 总 FLOPs,不包括注意力机制。

对于像 Llama 这样的大型模型和(相对)较短的序列,注意力机制对 FLOPs 的贡献可以忽略不计。对于每一层和每个注意力头,注意力操作是

softmax(QTKdhead)V\text{softmax}\Biggl(\frac{Q^TK}{\sqrt{d_\text{head}}}\Biggr)V

QTKQ^TK 需要将一个 dheadd_\text{head} 向量乘以一个 dhead×Nd_\text{head} \times N 矩阵,即 2dheadN2d_\text{head}N flops。缩放因子和 Softmax 可以忽略不计。最后,将注意力向量乘以 VV 需要额外的 2dheadN2d_\text{head}N FLOPs。将所有注意力头和层加起来,我们得到 4dmodelnlN=1.3N4\cdot d_\text{model}\cdot n_l\cdot N = 1.3 \cdot N MFLOPs。因此,对于我们最大的序列 8192,注意力机制仍然只占完整 140140 GFLOPs 中的 10.510.5 GFLOPs。它足够小,为了简单起见,我们可以忽略它。

完成的内存需求高于 prompts

当生成 tokens 时,我们需要重新读取所有模型的权重和 KV 缓存以生成每个 token。这意味着什么?为了执行任何矩阵乘法,我们需要将每个矩阵的权重从 RAM 加载到 GPU 的寄存器中。如果有足够多的唯一矩阵,则权重的实际加载会成为瓶颈,而不是矩阵乘法本身。因此,让我们比较一下 token 通过模型的路径,分别针对 prompts 和 completions。

通过 transformer 生成 tokens 的内存路径

为了说明这一点,我们可以跟踪(非常粗略地勾勒出的)一个简单的 1 层 transformer 生成一批 tokens 的路径

  1. 我们读取输入嵌入矩阵 WeW_e,并计算批次中每个输入的相应嵌入向量。
  2. 我们从内存中读取每个 Wq,Wk,WvW_q, W_k, W_v 矩阵,以计算批次中每个输入的 qi,ki,viq_i, k_i, v_i(向量)。
  3. 我们执行注意力操作 - 这需要读取缓存的键和值。这为每个输入返回一个向量。
  4. 我们从内存中读取 WoW_o,并与上一步的输出相乘
  5. 我们读取步骤 1 的输出并将其添加到步骤 4 的输出中,然后执行层归一化。
  6. 我们读取 Wff1W_{ff_1} 并相乘,得到第一个前馈层的输出。
  7. 我们读取 Wff2W_{ff_2} 并相乘,得到第二个前馈层的输出。
  8. 我们读取步骤 5 的输出并将其添加到步骤 7 的输出中,然后执行层归一化。
  9. 我们读取反嵌入层 WeTW_e^T,然后进行矩阵-矩阵乘法,以获得批次中每个输入的 token 对数概率。
  10. 我们对下一个 token 进行采样,并将其反馈到步骤 1。

让我们计算一下内存需求。在步骤 1、2、4、6、7 和 9 中,我们大约读取了一次模型的所有参数。在步骤 3 中,我们读取每个批次元素的 KV 缓存。在所有步骤中,我们读取中间激活值,与模型大小相比,这些激活值可以忽略不计。因此,内存带宽需求为模型权重 + KV 缓存。随着我们增加批量大小,除了 KV 缓存之外,内存需求大致保持不变!我们稍后会回到这一点。请注意,这是每个 token 的内存需求

通过 transformer 处理 prompt tokens 的内存路径

当处理 prompts 时,我们读取一次模型的所有权重,但会产生注意力机制的内存成本。考虑一批序列通过同一 transformer 的大致路径

  1. 我们读取输入嵌入矩阵 WeW_e,并计算批次中每个序列的相应嵌入矩阵。
  2. 我们从内存中读取每个 Wq,Wk,WvW_q, W_k, W_v 矩阵,以计算 Qi,Ki,ViQ_i, K_i, V_i(矩阵)
  3. 我们执行注意力操作
  4. 我们从内存中读取 WoW_o,并与上一步的输出相乘
  5. 我们读取步骤 1 的输出并将其添加到步骤 4 的输出中,然后执行层归一化
  6. 我们读取 Wff1W_{ff_1} 并相乘,得到第一个前馈层的输出
  7. 我们读取 Wff2W_{ff_2} 并相乘,得到第二个前馈层的输出
  8. 我们读取步骤 5 的输出并将其添加到步骤 7 的输出中,然后执行层归一化
  9. 我们读取反嵌入层 Wu=WeTW_u = W_e^T,然后相乘,得到 prompt 序列的 token 对数概率

在步骤 1、2、4、6、7 中,我们读取了模型的所有参数。在步骤 3 中,我们执行注意力操作,使用 FlashAttention 时,所需的内存带宽远小于读取模型权重(对于合理的序列长度和批量大小)。在所有步骤中,我们读取激活值,与模型大小相比,这些激活值可以忽略不计(对于合理的序列长度和/或批量大小)8。请注意,这是所有 tokens 的内存需求。

最重要的是,prompt 处理的每个 token 的内存需求远小于生成 tokens,因为我们为 prompts 批量处理了跨序列维度的矩阵乘法!

模型权重所需的内存带宽

16 位精度的模型权重占用 270=1402 \cdot 70 = 140 GB 的内存。

KV 缓存所需的内存带宽

我们的 KV 缓存的大小是神经网络中所有层的所有头的键和值的大小,对于之前的所有 tokens,即每个 token 和批次元素 320320 MB。

Llama 2 决定移除多头注意力机制。但他们没有使用多查询注意力机制,而是使用了分组查询注意力机制,这提高了性能。这导致键和值有 8 个头(或组),ngn_g,而不是多头注意力的正常值 128 和多查询注意力的 1。

对于 NN 个 tokens,我们的 KV 缓存的大小将为 2ngnldheadN=1.6N1052n_gn_ld_\text{head} N = 1.6N \cdot 10^5。使用 16 位精度,这将使其变为 320N320N KB。给定批量大小 BB,我们得到 320NB320\cdot NB KB。

对于 completions,这给出了每个 token 的内存需求:

内存 / token=140GB+320NBKB\text{内存 / token} = 140 \text{GB} + 320\cdot N\cdot B \text{KB}

对于较短的序列/小批量,第一项占主导地位。否则,第二项要大得多。但是,由于我们只有 160GB 的内存,并且模型占用 140GB,因此 KV 缓存将在我们的实验中对内存带宽造成较小的成本。

prompts 的内存带宽约为

内存 / token=140NGB\text{内存 / token} = \frac{140} {N} \text{GB}

通信开销

为简单起见,我们忽略了通信成本,因为考虑模型并行性会使事情变得非常复杂。我们可以合理地假设,对于我们的任何计算来说,它都不会增加足够大的减速(特别是由于我们仅在 2 个 GPU 上拆分 Llama)。

Prompt 处理非常便宜

Prompt 处理或首个 token 的时间是 transformer 推理中最有效的部分,您应该期望相对于 gpt-3.5 降低 3 倍的价格。

对于具有 PP 参数的模型和 NN 长度的 prompt,处理 prompt 的内存需求约为 2P2P 字节,而计算需求为 2PN2PN FLOPs。由于 A100 可以处理 312312 TFLOPs 的矩阵乘法和 22 TB/s 的内存带宽,因此对于序列长度 N>156N > 156,我们受计算限制。

在 A100 上,FLOPs 利用率可能最大达到略低于 70% MFU。这相当于大约 200TFLOPs。2-80GB A100 的成本约为$4.42 美元/小时,这相当于 0.00120.0012 美元/秒。Llama 的 FLOPs 需求为 140140 TFLOPs/token。给定 2 个 A100 的总 FLOPs,我们可以计算出每秒的 tokens 数量应为多少

22001012/140109=2.86103tokens / 秒2\cdot 200\cdot 10^{12}/140\cdot 10^9 = 2.86\cdot 10^3\text{tokens / 秒}

价格是

$0.00042 / 1K tokens

与 gpt-3.5 的 $.0015 相比,这简直是太划算了!确切地说,价格几乎降低了 4 倍!

延迟也相当不错!在我们的 2 个 GPU 上,批量大小为 1,我们应该能够在 170 毫秒内处理 512 个 tokens,在 530 毫秒内处理 1536 个 tokens。

让我们用实际数字验证这些说法。我们使用 huggingface 的 text-generation-inference repo 的内部分支来测量 Llama-2 的成本和延迟。

图 2:每个数据点测量不同的批量大小。对于 prompt tokens,我们在定价方面始终比 gpt-3.5 更好,但在 gpt-3.5 的 3.6K tokens 的 0.4 秒延迟方面略有落后。

正如我们所见,价格明显优于 gpt-3.5 的 $0.0015/1k tokens!对于较长的序列,首个 token 的时间看起来确实有点落后,但解决方案非常简单。将 llama 并行化到 8 个 gpu(而不是 2 个)将使我们的速度提高近 4 倍,这意味着 llama-2 在 prompts 方面优于 gpt-3.5!

生成 tokens 速度慢且非常昂贵

理论上,在 completions 方面有可能获得与 gpt-3.5 具有竞争力的定价,但在实践中,您可能会做得更差。

当生成 tokens 时,我们从受计算限制转变为受内存限制。。假设批量大小为 1,让我们确定我们可以实现的吞吐量。

每个 80GB A100 的峰值内存带宽为每个 GPU 2TB/s。但是,与 FLOPs 利用率一样,您可能预计在推理工作负载中更接近该值的 60-70%(1.3 TB/s)。由于 KV 缓存在小批量时可以忽略不计,因此我们在 2-A100 上的吞吐量将是

21.31012 B/s140109 B/token=18.6 tokens / 秒\frac{2\cdot 1.3 \cdot 10^{12}\text{ B/s}}{140 \cdot 10^9 \text{ B/token}} = 18.6 \text{ tokens / 秒}

我们的新价格要差得多。以 $0.0012/秒的价格,我们得到的成本是...

$0.066 / 1K tokens

对于 gpt-3.5 级别的模型来说,这个定价和速度非常糟糕!但请记住前面关于批量大小的注释。我们受内存瓶颈的限制,以至于我们可以增加批量大小,而生成速度不会下降。我们的批量大小越大,我们的成本就越低。

我们无法无限增加,因为我们的 KV 缓存最终会占用所有 GPU RAM。幸运的是,分组查询注意力机制有助于缓解这个问题。对于 NN 个 tokens,批量大小为 BB,并且使用 16 位精度,我们的缓存将为 3.2NB1053.2 \cdot N\cdot B \cdot 10^5 字节。在 4096 个 tokens 的情况下,这相当于批量大小为 1 时为 1.3GB 的内存。我们的 2-A100 机器上有 160GB 的空间。我们的模型占用了其中的 135GB,仅剩下 25 GB 的空间用于 KV 缓存。由于内存存储方面的其他低效问题。对于较长的序列长度,我们的最大批量大小约为 8。

鉴于(大致)8 倍的速度提升,我们可以预期价格为 $0.00825 / 1K tokens。这仍然比 gpt-3.5-turbo 差,但更接近了。对于较短的序列长度(总共 1k tokens),我们应该能够将批量大小增加到 32,这意味着价格为 $0.00206 / 1K tokens。理论上,这与 gpt-3.5-turbo 具有竞争力。

另一种解决方案是增加 GPU 的数量。通过租用 8-80GB A100,我们获得 1.28TB 的内存。移除模型权重后,我们剩下超过 1TB 的内存用于 KV 缓存,这意味着批量大小可能 > 512 个 tokens。请注意,我们实际上不会看到 512 倍的成本降低,因为 KV 缓存现在占用的内存带宽比模型大小多 8 倍,这意味着成本降低更接近 64 倍。

使用更多计算资源也解决了延迟问题。GPT-3.5 的命中率约为 70 TPS。将模型拆分到 8 个 GPU 而不是 2 个应该使我们达到大约 74.4 tokens/秒。

我们在运行此实验时无法访问 8 个 A100,但让我们看一下 2-80GB A100 上的数字

测量的生成性能

图 3:对于所有数据点,我们测量生成 512 个 tokens 时每个生成的 token 的价格

这些数字与根据内存带宽计算得出的预期值非常接近。

正如我们所见,增加批量大小直接导致价格/1K tokens 的成本几乎呈线性下降。但是,我们仍然略低于 GPT-3.5 的 $0.002/1K tokens 定价 - 尤其是对于较长的序列长度。

大批量大小意味着不可接受的延迟

以大批量大小运行生成意味着 gpt-3.5 具有竞争力的定价,但它会飙升首个 token 的时间。随着我们增加批量大小,成本线性下降,但首个 token 的时间也线性增加。

批量大小为 64 使我们的定价优于 gpt-4。但是,批量大小为 64 给我们带来了

对于仅 512 个 tokens,首个 token 的时间几乎为 3 秒对于 3596 个 tokens,批量大小为 64 为 20.1 秒因此,相对于 OpenAI 的 API,Llama-2 有意义的工作负载类型是

  1. 具有大量 prompt 但几乎没有生成的 tokens — 处理纯 prompts 非常简单。
  2. 生成 tokens,prompt 很小或没有 prompt — 我们可以将批量大小调整到 >64,并在不牺牲延迟的情况下获得与 gpt-3.5-turbo 具有竞争力的定价。
  3. 非延迟关键的离线批量处理作业。

提高批量大小需要持续的大工作负载,而大多数初创公司不会有!对于大多数用户和大多数工作负载,使用量都非常突发。当然,一个可能的解决方案是按需自动扩展和缩减 GPU,但即便如此,您可能平均期望每个 GPU 的最大吞吐量为 50% - 尤其是在考虑到冷启动时间的情况下。

我们的建议是将开源模型用于以 prompt 为主的任务,并将以生成为主的任务留给像 gpt-3.5 这样的闭源模型

量化

大多数量化方法都是有损的,这意味着某些性能会下降。我们很可能通过 8 位量化实现大部分具有竞争力的性能,从而使所有计算数字的价格降低 2 倍!量化和不完美的利用率相互抵消,这意味着考虑到这两者,我们预计价格与我们测量的价格相似!

但是,大多数开源量化方法的目标是允许在少量/小型消费级 GPU 上轻松部署,而不是优化大规模吞吐量。

有几个开源库可以优化甚至更低精度的量化,同时保持性能。但是,这些库优化了在少量/小型非数据中心 GPU 上服务这些模型,而不是优化大规模吞吐量。具体来说,它们针对低批量推理情况(主要是批量大小为 1)进行了优化。尽管提供(最好)3-4 倍的加速,但这仍然对应于 $0.017/1K tokens 的价格。

Bits and Bytes

Bits and Bytes 提供(实际上)无损量化,这意味着性能没有差异。但是,它的主要好处是减少内存使用量,而不是速度。例如,最近的 NF4 表示的加速仅在于矩阵乘法速度,而不是推理吞吐量。根据经验,人们似乎并没有测量这方面的加速。

目前还不清楚它如何很好地扩展到更大的批量。

llama.cpp

我相信 Llama.cpp 主要针对 Apple 硬件进行了优化。它们也支持 Cuda,并支持用于推理的快速 4 位精度,但我怀疑这里幼稚的量化会导致性能显着下降。

此外,此库针对低批量机制进行了优化。

GPT-Q

GPT-Q 是另一个量化库。我尚未测试 GPT-Q,但计划这样做。希望我们能在这里看到 2 倍的价格降幅!

再一次,该实现针对低批量机制进行了优化。此外,论文中报告的 >3 倍加速仅适用于 3 位量化,这对于我们的用例来说损失太大。

闭源模型究竟是如何更便宜的?

闭源模型使用多种方法来显着加速推理

量化

如前所述,有几种可靠的开源量化方法 - 但我怀疑 OpenAI 的量化实现在更大的批量上进行了更好的优化。

混合专家模型

人们普遍推测 GPT-4 使用混合专家模型。如果 gpt-3.5-turbo 也使用 MOE,那么对于相同的性能水平,您可以服务于更小(因此更快)的模型

推测性采样

推测性采样是另一种有趣的技巧,它通过让较小的模型连续起草多个 tokens 来绕过语言模型缓慢的解码时间。。请注意,从极限的角度来看,这不会显着提高吞吐量,但可以大大减少延迟。作为参考,此repo 实现了它的简化版本。

大规模推理的奇特技巧

当大规模运行推理时,OpenAI 可能会做一些奇特的事情,例如为预填充一批请求分配多个 8-GPU 节点,然后为在该批请求上生成 tokens 分配单个 8-GPU 节点。这将为他们带来两全其美的效果,他们可以使用 > 64 的批量大小,并且仍然看到非常低的首个 token 的时间。

结束语

我们已使用这些发现来告知我们如何在 Anysphere 决定何时/如何使用开源模型。

回顾一下,我们发现将开源模型用于以 prompt 为主的任务最有帮助,例如分类重排序

Anysphere 与我们一起工作!

我们正在构建 Cursor,一款 AI 优先的代码编辑器。当从根本上重新构想开发环境时,我们得以解决大量非常有趣的问题。例如,以 OpenAI API 价格的一小部分微调和服务模型。或设计 新的抽象概念,用于使用 OpenAI 的 API 创建复杂的链和代理。

我们是一个由 9 人组成的小团队,总部位于旧金山,并由 OpenAI 提供支持。如果您有兴趣,请通过 hiring@anysphere.co 联系我们。

或者,如果你想和我交流关于语言模型的技术细节,请在 Twitter 上私信我。

附录

下表是关于 Llama-2-70B 延迟的测量数据点,以及一些额外的计算指标

批大小提示词令牌生成令牌首个令牌生成时间完成时间令牌/秒提示词 TFLOPs/GPU提示词内存带宽 / GPU提示词 FLOPS 利用率提示词内存利用率每 1k 提示词令牌价格每 1k 生成令牌价格
11282420.08412.63619.151106.1561.3410.3400.894$0.00081$0.06412
21285120.11627.75118.450154.4781.2910.4950.861$0.00056$0.03328
41285120.21628.15418.186165.7061.2730.5310.849$0.00052$0.01688
11282420.08412.66119.114106.1221.3380.3400.892$0.00081$0.06425
21285120.11627.82418.402154.1961.2880.4940.859$0.00056$0.03337
41285120.21528.20518.153167.0741.2710.5350.847$0.00051$0.01691
15125120.21727.75218.449164.9961.2910.5290.861$0.00052$0.06656
25125120.39228.54317.938183.0681.2560.5870.837$0.00047$0.03423
45125120.73429.04217.630195.3451.2340.6260.823$0.00044$0.01741
15123040.22516.39318.545158.9401.2980.5090.865$0.00054$0.06622
85125121.44529.75317.208198.4021.2050.6360.803$0.00043$0.00892
85125121.46029.74017.216196.3441.2050.6290.803$0.00044$0.00892
165125123.08132.70815.654186.1491.0960.5970.731$0.00046$0.00490
165125123.03832.66115.676188.7761.0970.6050.732$0.00046$0.00490
165125123.06832.70815.654186.8871.0960.5990.730$0.00046$0.00490
325125126.36341.30212.396180.2560.8680.5780.578$0.00048$0.00310
325125126.63241.09912.458172.9310.8720.5540.581$0.00050$0.00308
110245120.39728.96117.679180.7271.2380.5790.825$0.00048$0.06946
110245120.39628.98217.666181.0171.2370.5800.824$0.00047$0.06951
210245120.73229.67417.254195.7591.2080.6270.805$0.00044$0.03559
410245121.47130.15916.977194.9041.1880.6250.792$0.00044$0.01808
810245123.01531.16716.428190.2231.1500.6100.767$0.00045$0.00934
1610245126.54135.11114.582175.3251.0210.5620.681$0.00049$0.00526
135955121.43034.21014.966175.9291.0480.5640.698$0.00049$0.08205
235955122.84134.96414.644177.1681.0250.5680.683$0.00049$0.04193
435955125.84635.79514.304172.1991.0010.5520.667$0.00050$0.02146
175841663.08113.64112.170172.3300.8520.5520.568$0.00050$0.10091
275845126.29643.48211.775168.6420.8240.5410.550$0.00051$0.05214