推理优化是生成式人工智能应用在生产中部署的关键部分。大规模高效地使用 LLM 是一项挑战,过去几年中开发了许多技术,使推理更快、更便宜。让我们在本文中回顾一下这些技术。
大型语言模型(LLM)都是基于 Vaswani 等人在 2017 年发明的转换器架构。转换器架构在各种语言任务中实现了卓越的准确性、少量学习和接近人类的能力。然而,这些基础模型通常包含数百亿到数千亿个参数,训练成本高昂,推理过程中资源密集。由于输入数据量大,需要大量的处理能力,因此推理成本会随着长输入上下文的增加而增加。这使得高效推理成为一项严峻挑战,尤其是在管理内存和计算资源方面。

更具体地说,大多数著名的 LLM 都是纯解码器 LLM,如 GPT-3、GPT-4、LLaMA、Mistral、DeepSeek 等。这些模型在因果建模任务中进行预训练,作为下一个单词的预测器。它们处理作为输入的词块序列,并自回归地生成后续词块,直到达到停止条件。
纯解码器模型中的 LLM 推理包括两个关键阶段:预填充阶段和解码阶段。在预填充阶段,模型处理输入标记,计算生成第一个新标记的中间状态(键和值)。这一阶段类似于矩阵-矩阵运算,具有高度并行性,能有效利用 GPU 性能。相反,解码阶段则依靠之前的标记状态,一次生成一个标记。这种矩阵-矢量操作受内存限制,因为数据传输到 GPU 的时间主要取决于延迟,而不是计算速度,从而导致 GPU 计算能力利用不足。
优化解码阶段是应对推理挑战的重点。解决方案包括开发高效的注意机制和更好地管理键和值,以减少内存瓶颈。假设读者对变压器架构和注意力机制有基本的了解,本文章将重点介绍提高推理性能的实用方法。在实际的 LLM 部署中,这些优化对于提高吞吐量和减少延迟至关重要。
由于 LLM 使用不同的标记符号化器,影响了标记符号的可比性,从而造成了进一步的复杂问题。令牌大致相当于四个英文字符,其表现形式因令牌化器的不同而不同,这使得直接比较推理吞吐量(如每秒令牌数)会产生误导。这种差异性突出表明,需要标准化的评估指标来准确评估和比较 LLM 在推理过程中的性能。
批处理是提高大型语言模型(LLM)的 GPU 利用率和吞吐量的关键策略。通过使用同一模型同时处理多个请求,批处理可以将模型权重的内存成本分摊到各个请求中,从而使更大的批处理可以利用更多的 GPU 计算能力。不过,批处理的大小也有限制,因为过大的批处理可能会因 LLM 的内存需求而导致内存溢出,尤其是与键值 (KV) 缓存相关的内存需求(稍后将详细介绍)。

传统或静态批处理有其局限性,因为批处理中的请求通常会生成不同数量的完成令牌,从而导致执行时间不同。这就导致所有请求都要等待最慢的一个完成,而当生成长度变化很大时,就会出现问题。为了解决这个问题,我们开发了飞行中批处理等先进技术来优化性能。
飞行中批处理(也称为连续批处理)可应对 LLM 工作负载的动态性质所带来的挑战,这些工作负载的范围从简单的聊天机器人响应到复杂的文档汇总或代码生成。这些任务产生的输出大小大不相同,因此很难并行高效地批处理和执行请求。与静态批处理不同,飞行中批处理允许服务器立即从批处理中剔除已完成的序列,并在其他请求仍在处理过程中开始处理新请求。这种方法可以适应实际场景中请求的不同执行时间,从而大大提高 GPU 的利用率。
模型并行化是管理大规模机器学习模型内存和计算需求的关键策略,它将模型分布在多个 GPU 上。这种方法可以处理超过单个设备内存容量的大型模型或输入批次,因此在内存紧张的情况下,它对训练和推理至关重要。目前有多种用于拆分模型权重的技术,包括流水线并行、张量并行和序列并行,每种技术都能解决模型分布的不同方面。数据并行的重点是在训练过程中跨设备复制模型权重以处理更大的输入批次,与此不同,这些方法更适合在训练和推理过程中减少内存占用。

流水线并行将模型纵向划分为顺序块,每个块包含分配给单独设备的图层子集。例如,在四路流水线设置中,每个设备处理模型四分之一的层,并依次将输出传递给下一个设备。虽然这大大降低了每个设备的内存需求,但也带来了被称为 "流水线气泡 "的低效率问题,即设备在等待前一层输出时可能处于空闲状态。微批处理(将输入批次分割成更小的子批次进行顺序处理)可以减少这些气泡,但不能完全消除,因为空闲时间在前向和后向传递过程中都会持续。
相比之下,张量并行将单个层横向分割成更小的计算块,可跨设备独立执行。这对注意力块和多层感知器(MLP)等转换器组件特别有效,例如,不同的注意力头可以分配到不同的设备上进行并行计算。然而,张量并行对于层规范(LayerNorm)和剔除(Dropout)等操作的效果较差,因为这些操作不能轻易分割,必须在不同设备间复制,从而导致存储激活的内存使用冗余。这种限制凸显了优化内存效率的互补方法的必要性。
序列并行性可以解决 LayerNorm 和 Dropout 等操作的内存低效问题,方法是沿着输入序列维度对它们进行分割,利用它们在序列元素间的独立性。这种方法减少了冗余激活的内存占用,是对张量并行的重要补充。这些并行化技术并不相互排斥,可以结合起来进一步优化大型语言模型(LLM)。此外,针对注意力模块的特定优化策略可以提高可扩展性,降低对每 GPU 内存的需求,从而提高大型模型的训练和推理效率。
瓦斯瓦尼(Vaswani)等人在2017年发表的论文《注意力就是你所需要的一切》(*Attention Is All You Need*)介绍了以自我注意力为基石的Transformer模型。自我注意使该模型能够评估句子中不同词语之间的相关性,从而增强自然语言处理等任务的上下文理解能力。论文将自我注意正式化,特别是通过缩放点积注意(SDPA)机制,将查询和键值对映射到输出中,使其成为现代神经网络中的关键组成部分。以下是优化注意力计算的一些最重要的技术:

多头注意力(MHA)以 SDPA 为基础,并行运行多个注意力操作,每个操作都有不同的查询、键和值矩阵投影。这些并行操作(或称 "头")侧重于不同的表征子空间,丰富了模型对输入的理解。这些 "头 "的输出被串联起来并进行线性投影,通过降低每个 "头 "的维度(例如,用模型维度除以 "头 "的数量,如 8)来保持与单 "头 "注意力相当的计算效率。
多查询注意(MQA)通过在多个注意头之间共享键和值投影,同时保留多个查询投影,优化了 MHA 的推理能力。这就降低了内存带宽需求和键值(KV)缓存的大小,使批处理规模更大,计算利用率更高。不过,MQA 可能会略微降低准确性,利用它的模型需要在启用 MQA 的情况下进行训练或微调,以保持性能。
分组查询关注(GQA)通过将查询头分组并在每组内共享键值投影,在 MHA 和 MQA 之间取得平衡,从而以接近 MQA 的计算效率实现接近 MHA 的质量。像 Llama 2 70B 这样的模型就使用了 GQA,而那些用 MHA 训练过的模型只需极少的额外训练就能适应 GQA。MQA 和 GQA 都降低了 KV 缓存内存需求,但仍需进一步优化缓存管理。
FlashAttention 通过重新安排计算顺序,更有效地利用 GPU 内存分层,从而增强了注意力机制。与传统的逐层处理不同,FlashAttention 融合了各种操作,并使用 "平铺 "技术一次性计算输出矩阵的一小部分,最大限度地减少了内存读/写操作。这种 I/O 感知的精确注意力算法无需修改即可无缝集成到现有模型中,通过优化数据移动显著提高了速度。
KV 缓存是大型语言模型(LLM)解码阶段的一项关键优化技术,用于提高自注意计算的效率。在这一阶段,每个生成的标记都取决于之前所有标记的键(K)和值(V)张量,包括在预填充阶段和后续解码步骤中计算的张量。KV 缓存无需在每个时间步骤为每个令牌重新计算这些张量,而是将它们存储在 GPU 内存中,并在计算出新张量时将其添加到缓存中。通常情况下,模型的每一层都有一个单独的 KV 缓存,从而大大减少了冗余计算,加快了解码过程。

GPU 上 LLM 的内存需求主要由两部分驱动:模型权重和 KV 缓存。模型权重由模型的参数组成,占用大量内存;例如,像 Llama 2 7B 这样拥有 70 亿个参数的 16 位精度模型需要大约 14 GB 的内存。另一方面,KV 缓存存储自我注意张量,以避免重新计算,其大小由层数、注意头、注意头尺寸和精度等因素决定。对于每个标记,缓存大小的计算公式为:2 * num_layers * (num_heads * dim_head) * precision_in_bytes,其中 2 因子用于 K 和 V 矩阵。对于一批输入,KV 缓存的总大小会随批次大小和序列长度的变化而变化,可能会达到相当大的规模,例如对于序列长度为 4,096 和批次大小为 1 的 Llama 2 7B 模型,其缓存大小约为 2 GB。
由于 KV 缓存随批次大小和序列长度的线性增长而增长,这就给高效管理 KV 缓存带来了挑战,不仅会限制吞吐量,还会使长上下文输入的处理复杂化。一种常见的低效率现象是静态过量预留,即为最大支持序列长度(如 2,048 个标记)预留内存,而不考虑实际输入大小。这会导致严重的内存浪费或碎片化,因为大部分预留空间在请求的整个生命周期内都未被使用,占用了宝贵的 GPU 内存资源。
为了解决这些低效问题,PagedAttention 算法引入了一种受操作系统分页启发的新方法。它将 KV 缓存划分为固定大小的区块,每个区块代表一定数量的标记,这些标记可以非连续地存储在内存中。区块表会跟踪这些区块,在注意力计算过程中根据需要获取它们。随着新令牌的生成,会动态分配额外的块。这种方法无需进行连续分配和超额配置,从而最大限度地减少了内存浪费,使批处理规模更大,吞吐量更高,因此在管理 LLM 的 KV 缓存内存方面取得了重大进展。
在本节中,我们将讨论优化大型语言模型(LLM)的各种技术,以减少其内存消耗并提高其在 GPU 上的性能。主要方法包括量化、稀疏性和蒸馏,每种方法都针对模型效率的不同方面。这些技术可以修改模型权重,利用 GPU 硬件加速,并将知识转移到更小的模型中,从而使更大的模型可以在有限的硬件上运行,同时保持性能。这些方法可能会降低模型的准确性,因此应谨慎使用。
量化可以降低模型权重和激活度的精度,通常从 32 或 16 位降低到 8 位或更低,从而使模型占用更少的内存,更高效地传输数据。由于权重在训练后是固定的,因此量化权重非常简单,但由于异常值会扩大权重的动态范围,因此量化激活值就比较复杂了。LLM.int8()等技术通过有选择性地将更高精度应用于某些激活,或将量化权重的动态范围重新用于激活来解决这一问题,不过 GPU 可能需要将权重转换回更高精度才能进行操作。
稀疏性涉及修剪接近零的模型值,从而创建需要更少内存的稀疏矩阵。GPU 支持结构化稀疏性,例如将每四个值中的两个表示为零,从而加快计算速度。将稀疏性与量化相结合可以进一步提高执行速度。相关研究仍在继续探索 LLM 的最佳稀疏表示法,这为提高推理速度提供了一条大有可为的途径。
蒸馏将知识从较大的 "教师 "模型转移到较小的 "学生 "模型中,在保持性能的同时压缩了体积。例如,与 BERT 相比,DistilBERT 的规模缩小了 40%,速度提高了 60%,同时保留了 97% 的功能。蒸馏可以模仿教师的输出或使用教师生成的数据进行训练,"逐步蒸馏!"(Distilling Step by Step!然而,许多高级 LLM 的限制性许可证限制了用于蒸馏的合适教师模型的可用性。
推测推理(又称推测采样或辅助生成)是一种并行执行自回归大型语言模型(LLM)(如 GPT 风格模型)的方法,这些模型通常逐个标记生成文本。在标准执行中,每个标记都依赖于所有先前标记的上下文,这使得并行生成成为不可能,因为第 n 个标记必须在第 (n+1)th 个标记之前生成。投机推理可以解决这个问题,它使用一个 "更便宜 "的草稿模型来同时预测多个未来标记,然后由主模型并行验证或拒绝这些标记,从而更快地生成文本。
这一过程包括使用资源消耗较少的方法生成一个由若干令牌组成的延续草案,然后由主模型使用该草案作为推测上下文进行并行验证。如果验证模型与草案令牌匹配,则接受这些令牌;否则,不匹配的令牌和后续令牌将被丢弃,然后用新的草案重复该过程。草稿令牌可以通过多种方法生成,例如训练多个模型、在预训练模型上微调多个头部以预测未来令牌,或者在使用较大、能力更强的验证模型的同时使用较小的草稿模型,每种方法都有自己的权衡。
分解推理是一种将计算任务分割到不同硬件上以优化性能、成本和资源使用的技术。具体来说,它将预填充和解码阶段分开。通过分解这些阶段,可以将每个阶段分配给最适合其计算需求的硬件,从而提高效率和可扩展性。

预填充是计算密集型的,需要大量的矩阵乘法来处理整个输入提示并生成 KV 缓存。这一阶段得益于 GPU 或 TPU 等高性能硬件,它们擅长并行计算。由于预填充是每个推理请求的一次性任务,因此可以将其卸载到针对此类工作负载进行了优化的集中式强大计算节点上。这种设置可以更快地处理大型提示,并减轻能力较弱设备的负担,因此非常适合有高吞吐量硬件的云环境或数据中心环境。
相比之下,解码受内存限制,涉及迭代令牌生成,在很大程度上依赖于访问 KV 缓存。它所需的计算能力较低,但需要快速的内存访问,因此适合 CPU 或边缘设备等功能较弱、内存优化较差的硬件。通过将解码转移到单独的硬件(可能更接近终端用户,如内部服务器或边缘设备),分解推理减少了延迟和网络带宽需求。这种分离实现了灵活的部署,预填充在高端云服务器上运行,而解码则在本地或边缘设备上进行,从而优化了资源分配,实现了实时聊天机器人或交互式人工智能系统等应用的高效扩展。
为了提高 LLM 的性能,最近发明了许多推理优化技术。
要实现这些技术,需要深入了解 LLM 架构和您正在使用的硬件,因此一般来说,使用已经实现了这些技术的现有推理引擎(如 vLLM、TensorRT-LLM、LMDeploy 等)会更容易一些。实际上,我们已经在 NLP Cloud 自己的推理引擎中实现了这些技术,如果你想部署自己的模型,我们还写了一篇关于推理引擎的博文: 您可以在这里阅读.
如果您不能或不想自己部署 LLM,您可以使用 NLP Cloud,在生产中大规模利用快速生成的人工智能模型。 现在就尝试在 NLP Cloud 上进行快速推理!
如果您对推理引擎有任何疑问,请随时咨询我们,我们很乐意为您提供建议!
Julien
NLP Cloud 首席技术官