Llama 的推理特性
关于推理数学的入门知识以及对 Llama 惊人成本的考察。
发布者Aman
19分钟阅读
Llama-2-70B 是 gpt-3.5 的一个诱人的替代品,但如果正在寻找廉价的语言模型,那么偏离 OpenAI 的 API 可能不值得。
当考虑价格和延迟时
您不应该为完成型工作负载提供 Llama-2 服务
相反,Llama 最适合以 prompt 为主导的任务,例如分类。在以下情况下,Llama-2 也可能适用
- 您的工作负载没有 prompt tokens(违反直觉,但我们稍后会解释)
- 您正在执行批量处理作业
否则,gpt-3.5 应该更便宜且更快。
快速声明一下,使用 Llama 而不是 gpt-3.5 的一个原因是微调。但在这篇文章中,我们只探讨成本和延迟。我没有将 Llama-2 与 GPT-4 进行比较,因为它更接近 3.5 级别的模型。基准性能也支持这一说法
我将通过比较在延迟大致相似的情况下,为 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 便宜 倍。
Transformer 数学入门
通过一些简单的数学计算,我们将展示以下 Llama-2 的结果。对于序列长度为 和批量大小为 的情况
来自模型参数数量的两倍,而 320 KB/s 是通过一些算术运算得出的。在以下部分中,我们将解释我们如何得出这些数字。
还有其他论文和/或博客文章对 transformer 数学进行了精彩的解释。对于推理,Kipply 的文章是一个很好的参考。而且,我相信 Scaling Laws推广了用于 transformer FLOPs 的简化方程。
为了验证这些数字,我们从 Llama 的架构细节开始。隐藏层维度为 4096,注意力头的数量为 64,层数为 80,每个注意力头的维度为 128
计算模型 Flops
前向传递的 FLOPs 数量约为 ,其中 是模型中的参数数量。我们模型中的每个参数都属于某个权重矩阵 。对于每个输入 token,每个矩阵在与表示该 token 的向量的矩阵乘法中都只使用一次。
对于每个 ,我们左乘一个维度为 的向量 。此向量-矩阵乘法的总 FLOPs 为 ,或权重矩阵中条目数的 2 倍。transformer 所有权重矩阵中的条目总数是参数总数 ,这给出了 总 FLOPs,不包括注意力机制。
对于像 Llama 这样的大型模型和(相对)较短的序列,注意力机制对 FLOPs 的贡献可以忽略不计。对于每一层和每个注意力头,注意力操作是
需要将一个 向量乘以一个 矩阵,即 flops。缩放因子和 Softmax 可以忽略不计。最后,将注意力向量乘以 需要额外的 FLOPs。将所有注意力头和层加起来,我们得到 MFLOPs。因此,对于我们最大的序列 8192,注意力机制仍然只占完整 GFLOPs 中的 GFLOPs。它足够小,为了简单起见,我们可以忽略它。
完成的内存需求高于 prompts
当生成 tokens 时,我们需要重新读取所有模型的权重和 KV 缓存以生成每个 token。这意味着什么?为了执行任何矩阵乘法,我们需要将每个矩阵的权重从 RAM 加载到 GPU 的寄存器中。如果有足够多的唯一矩阵,则权重的实际加载会成为瓶颈,而不是矩阵乘法本身。因此,让我们比较一下 token 通过模型的路径,分别针对 prompts 和 completions。
通过 transformer 生成 tokens 的内存路径
为了说明这一点,我们可以跟踪(非常粗略地勾勒出的)一个简单的 1 层 transformer 生成一批 tokens 的路径
- 我们读取输入嵌入矩阵 ,并计算批次中每个输入的相应嵌入向量。
- 我们从内存中读取每个 矩阵,以计算批次中每个输入的 (向量)。
- 我们执行注意力操作 - 这需要读取缓存的键和值。这为每个输入返回一个向量。
- 我们从内存中读取 ,并与上一步的输出相乘
- 我们读取步骤 1 的输出并将其添加到步骤 4 的输出中,然后执行层归一化。
- 我们读取 并相乘,得到第一个前馈层的输出。
- 我们读取 并相乘,得到第二个前馈层的输出。
- 我们读取步骤 5 的输出并将其添加到步骤 7 的输出中,然后执行层归一化。
- 我们读取反嵌入层 ,然后进行矩阵-矩阵乘法,以获得批次中每个输入的 token 对数概率。
- 我们对下一个 token 进行采样,并将其反馈到步骤 1。
让我们计算一下内存需求。在步骤 1、2、4、6、7 和 9 中,我们大约读取了一次模型的所有参数。在步骤 3 中,我们读取每个批次元素的 KV 缓存。在所有步骤中,我们读取中间激活值,与模型大小相比,这些激活值可以忽略不计。因此,内存带宽需求为模型权重 + KV 缓存。随着我们增加批量大小,除了 KV 缓存之外,内存需求大致保持不变!我们稍后会回到这一点。请注意,这是每个 token 的内存需求
通过 transformer 处理 prompt tokens 的内存路径
当处理 prompts 时,我们读取一次模型的所有权重,但会产生注意力机制的内存成本。考虑一批序列通过同一 transformer 的大致路径
- 我们读取输入嵌入矩阵 ,并计算批次中每个序列的相应嵌入矩阵。
- 我们从内存中读取每个 矩阵,以计算 (矩阵)
- 我们执行注意力操作
- 我们从内存中读取 ,并与上一步的输出相乘
- 我们读取步骤 1 的输出并将其添加到步骤 4 的输出中,然后执行层归一化
- 我们读取 并相乘,得到第一个前馈层的输出
- 我们读取 并相乘,得到第二个前馈层的输出
- 我们读取步骤 5 的输出并将其添加到步骤 7 的输出中,然后执行层归一化
- 我们读取反嵌入层 ,然后相乘,得到 prompt 序列的 token 对数概率
在步骤 1、2、4、6、7 中,我们读取了模型的所有参数。在步骤 3 中,我们执行注意力操作,使用 FlashAttention 时,所需的内存带宽远小于读取模型权重(对于合理的序列长度和批量大小)。在所有步骤中,我们读取激活值,与模型大小相比,这些激活值可以忽略不计(对于合理的序列长度和/或批量大小)8。请注意,这是所有 tokens 的内存需求。
最重要的是,prompt 处理的每个 token 的内存需求远小于生成 tokens,因为我们为 prompts 批量处理了跨序列维度的矩阵乘法!
模型权重所需的内存带宽
16 位精度的模型权重占用 GB 的内存。
KV 缓存所需的内存带宽
我们的 KV 缓存的大小是神经网络中所有层的所有头的键和值的大小,对于之前的所有 tokens,即每个 token 和批次元素 MB。
Llama 2 决定移除多头注意力机制。但他们没有使用多查询注意力机制,而是使用了分组查询注意力机制,这提高了性能。这导致键和值有 8 个头(或组),,而不是多头注意力的正常值 128 和多查询注意力的 1。
对于 个 tokens,我们的 KV 缓存的大小将为 。使用 16 位精度,这将使其变为 KB。给定批量大小 ,我们得到 KB。
对于 completions,这给出了每个 token 的内存需求:
对于较短的序列/小批量,第一项占主导地位。否则,第二项要大得多。但是,由于我们只有 160GB 的内存,并且模型占用 140GB,因此 KV 缓存将在我们的实验中对内存带宽造成较小的成本。
prompts 的内存带宽约为
通信开销
为简单起见,我们忽略了通信成本,因为考虑模型并行性会使事情变得非常复杂。我们可以合理地假设,对于我们的任何计算来说,它都不会增加足够大的减速(特别是由于我们仅在 2 个 GPU 上拆分 Llama)。
Prompt 处理非常便宜
Prompt 处理或首个 token 的时间是 transformer 推理中最有效的部分,您应该期望相对于 gpt-3.5 降低 3 倍的价格。
对于具有 参数的模型和 长度的 prompt,处理 prompt 的内存需求约为 字节,而计算需求为 FLOPs。由于 A100 可以处理 TFLOPs 的矩阵乘法和 TB/s 的内存带宽,因此对于序列长度 ,我们受计算限制。
在 A100 上,FLOPs 利用率可能最大达到略低于 70% MFU。这相当于大约 200TFLOPs。2-80GB A100 的成本约为$4.42 美元/小时,这相当于 美元/秒。Llama 的 FLOPs 需求为 TFLOPs/token。给定 2 个 A100 的总 FLOPs,我们可以计算出每秒的 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 的成本和延迟。
正如我们所见,价格明显优于 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 上的吞吐量将是
我们的新价格要差得多。以 $0.0012/秒的价格,我们得到的成本是...
$0.066 / 1K tokens对于 gpt-3.5 级别的模型来说,这个定价和速度非常糟糕!但请记住前面关于批量大小的注释。我们受内存瓶颈的限制,以至于我们可以增加批量大小,而生成速度不会下降。我们的批量大小越大,我们的成本就越低。
我们无法无限增加,因为我们的 KV 缓存最终会占用所有 GPU RAM。幸运的是,分组查询注意力机制有助于缓解这个问题。对于 个 tokens,批量大小为 ,并且使用 16 位精度,我们的缓存将为 字节。在 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 上的数字
测量的生成性能
这些数字与根据内存带宽计算得出的预期值非常接近。
正如我们所见,增加批量大小直接导致价格/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 有意义的工作负载类型是
- 具有大量 prompt 但几乎没有生成的 tokens — 处理纯 prompts 非常简单。
- 生成 tokens,prompt 很小或没有 prompt — 我们可以将批量大小调整到 >64,并在不牺牲延迟的情况下获得与 gpt-3.5-turbo 具有竞争力的定价。
- 非延迟关键的离线批量处理作业。
提高批量大小需要持续的大工作负载,而大多数初创公司不会有!对于大多数用户和大多数工作负载,使用量都非常突发。当然,一个可能的解决方案是按需自动扩展和缩减 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 延迟的测量数据点,以及一些额外的计算指标