使用 AWS Inferentia2 让你的 Llama 生成时间飞起来

发布于 2023 年 11 月 7 日
在 GitHub 上更新

更新 (2024年2月): 性能已进一步提升!请查看我们更新后的基准测试

Hugging Face 博客上的一篇先前文章中,我们介绍了 AWS Inferentia2,这是第二代 AWS Inferentia 加速器,并解释了如何使用 optimum-neuron 在 AWS Inferentia 2 实例上为标准的文本和视觉任务快速部署 Hugging Face 模型。

作为与 AWS Neuron SDK 集成的进一步步骤,现在可以使用 🤗 optimum-neuron 在 AWS Inferentia2 上部署用于文本生成的 LLM 模型。

还有什么模型比 Llama 2 更适合这次演示呢?它是 Hugging Face hub 上最受欢迎的模型之一。

在你的 Inferentia2 实例上设置 🤗 optimum-neuron

我们推荐使用 Hugging Face Neuron 深度学习 AMI (DLAMI)。DLAMI 预装了所有必需的库,包括 Optimum Neuron、Neuron 驱动程序、Transformers、Datasets 和 Accelerate。

或者,您可以使用 Hugging Face Neuron SDK DLC 在 Amazon SageMaker 上进行部署。

注意:敬请关注即将发布的关于 SageMaker 部署的专门文章。

最后,这些组件也可以按照 optimum-neuron安装说明,在一个全新的 Inferentia2 实例上手动安装。

将 Llama 2 模型导出到 Neuron

正如 optimum-neuron 文档 中所述,模型在 Neuron 设备上运行之前需要被编译并导出为序列化格式。

幸运的是,🤗 optimum-neuron 提供了一个非常简单的 API,用于将标准的 🤗 transformers 模型导出为 Neuron 格式。

>>> from optimum.neuron import NeuronModelForCausalLM

>>> compiler_args = {"num_cores": 24, "auto_cast_type": 'fp16'}
>>> input_shapes = {"batch_size": 1, "sequence_length": 2048}
>>> model = NeuronModelForCausalLM.from_pretrained(
        "meta-llama/Llama-2-7b-hf",
        export=True,
        **compiler_args,
        **input_shapes)

这值得稍作解释

  • 使用 compiler_args,我们指定希望模型部署在多少个核心上(每个 neuron 设备有两个核心),以及使用哪种精度(这里是 float16),
  • 使用 input_shape,我们设置模型的静态输入和输出维度。所有模型编译器都需要静态形状,neuron 也不例外。请注意,sequence_length 不仅限制了输入上下文的长度,还限制了 KV 缓存的长度,从而也限制了输出长度。

根据您选择的参数和 inferentia 主机,这可能需要几分钟到超过一个小时的时间。

幸运的是,您只需要做一次这个操作,因为您可以保存模型并在以后重新加载它。

>>> model.save_pretrained("a_local_path_for_compiled_neuron_model")

更好的是,您可以将其推送到 Hugging Face hub

>>> model.push_to_hub(
        "a_local_path_for_compiled_neuron_model",
        repository_id="aws-neuron/Llama-2-7b-hf-neuron-latency")

在 AWS Inferentia2 上使用 Llama 2 生成文本

一旦您的模型被导出,您就可以使用 transformers 库生成文本,正如这篇先前的文章中详细描述的那样。

>>> from optimum.neuron import NeuronModelForCausalLM
>>> from transformers import AutoTokenizer

>>> model = NeuronModelForCausalLM.from_pretrained('aws-neuron/Llama-2-7b-hf-neuron-latency')
>>> tokenizer = AutoTokenizer.from_pretrained("aws-neuron/Llama-2-7b-hf-neuron-latency")

>>> inputs = tokenizer("What is deep-learning ?", return_tensors="pt")
>>> outputs = model.generate(**inputs,
                             max_new_tokens=128,
                             do_sample=True,
                             temperature=0.9,
                             top_k=50,
                             top_p=0.9)
>>> tokenizer.batch_decode(outputs, skip_special_tokens=True)
['What is deep-learning ?\nThe term “deep-learning” refers to a type of machine-learning
that aims to model high-level abstractions of the data in the form of a hierarchy of multiple
layers of increasingly complex processing nodes.']

注意:当向模型传递多个输入提示时,生成的 token 序列必须在左侧用流结束 token 进行填充。与导出模型一起保存的 tokenizers 已相应配置。

支持以下生成策略

  • 贪心搜索,
  • 带 top-k 和 top-p 的多项式采样(带温度参数)。

大多数 logits 预处理/过滤器(如重复惩罚)都受支持。

使用 optimum-neuron pipelines 实现一体化

对于喜欢简单的人来说,还有一种更简单的方法来在 AWS inferentia 2 上使用 LLM 模型,即使用 optimum-neuron pipelines

使用它们就像这样简单

>>> from optimum.neuron import pipeline

>>> p = pipeline('text-generation', 'aws-neuron/Llama-2-7b-hf-neuron-budget')
>>> p("My favorite place on earth is", max_new_tokens=64, do_sample=True, top_k=50)
[{'generated_text': 'My favorite place on earth is the ocean. It is where I feel most
at peace. I love to travel and see new places. I have a'}]

基准测试

但是在 Inferentia2 上生成文本的效率究竟有多高呢?让我们来看看吧!

我们已经在 hub 上上传了 LLama 2 7B 和 13B 模型的预编译版本,它们具有不同的配置

模型类型 核心数 批处理大小 Hugging Face Hub 模型
Llama2 7B - B (预算型) 2 1 aws-neuron/Llama-2-7b-hf-neuron-budget
Llama2 7B - L (延迟优化型) 24 1 aws-neuron/Llama-2-7b-hf-neuron-latency
Llama2 7B - T (吞吐量优化型) 24 4 aws-neuron/Llama-2-7b-hf-neuron-throughput
Llama2 13B - L (延迟优化型) 24 1 aws-neuron/Llama-2-13b-hf-neuron-latency
Llama2 13B - T (吞吐量优化型) 24 4 aws-neuron/Llama-2-13b-hf-neuron-throughput

注意:所有模型都以 2048 的最大序列长度进行编译。

llama2 7B "预算型" 模型旨在部署在 inf2.xlarge 实例上,该实例只有一个 neuron 设备,并有足够的 cpu 内存来加载模型。

所有其他模型都经过编译,以充分利用 inf2.48xlarge 实例上可用的全部核心。

注意:有关可用实例的详细信息,请参阅 inferentia2 产品页面

我们为 llama2 7Bllama2 13B 模型创建了两个“延迟”导向的配置,它们一次只能处理一个请求,但速度最快。

我们还创建了两个“吞吐量”导向的配置,最多可以并行处理四个请求。

为了评估模型,我们从 256 个输入 token 开始,生成 tokens 直到总序列长度达到 1024(即我们生成 256、512 和 768 个 tokens)。

注意:“预算型”模型的数据会被报告,但为了图表的可读性,不会包含在图中。

编码时间

编码时间是处理输入 tokens 并生成第一个输出 token 所需的时间。这是一个非常重要的指标,因为它对应于用户在流式传输生成的 tokens 时直接感知的延迟。

我们针对不断增加的上下文大小测试编码时间,256 个输入 token 大致对应于典型的问答(Q/A)用法,而 768 个输入 token 则更典型于检索增强生成(RAG)用例。

"预算型"模型 (Llama2 7B-B) 部署在 inf2.xlarge 实例上,而其他模型则部署在 inf2.48xlarge 实例上。

编码时间以为单位表示。

输入 token 数 Llama2 7B-L Llama2 7B-T Llama2 13B-L Llama2 13B-T Llama2 7B-B
256 0.5 0.9 0.6 1.8 0.3
512 0.7 1.6 1.1 3.0 0.4
768 1.1 3.3 1.7 5.2 0.5

Llama2 inferentia2 encoding-time

我们可以看到,所有部署的模型都表现出优异的响应时间,即使是在长上下文的情况下。

端到端延迟

端到端延迟对应于达到 1024 个 token 序列长度所需的总时间。

因此,它包括编码和生成时间。

"预算型"模型 (Llama2 7B-B) 部署在 inf2.xlarge 实例上,而其他模型则部署在 inf2.48xlarge 实例上。

延迟以为单位。

新 token 数 Llama2 7B-L Llama2 7B-T Llama2 13B-L Llama2 13B-T Llama2 7B-B
256 2.3 2.7 3.5 4.1 15.9
512 4.4 5.3 6.9 7.8 31.7
768 6.2 7.7 10.2 11.1 47.3

Llama2 inferentia2 end-to-end latency

所有部署在高端实例上的模型都表现出良好的延迟,即使是那些实际上配置为优化吞吐量的模型也是如此。

“预算型”部署模型的延迟明显更高,但仍然可以接受。

吞吐量

我们采用与其他基准测试相同的惯例来评估吞吐量,即将端到端延迟除以输入和输出 token 的总和。换句话说,我们将端到端延迟除以 batch_size * sequence_length,以获得每秒生成的 token 数。

"预算型"模型 (Llama2 7B-B) 部署在 inf2.xlarge 实例上,而其他模型则部署在 inf2.48xlarge 实例上。

吞吐量以**令牌/秒(tokens/second)**为单位。

新 token 数 Llama2 7B-L Llama2 7B-T Llama2 13B-L Llama2 13B-T Llama2 7B-B
256 227 750 145 504 32
512 177 579 111 394 24
768 164 529 101 370 22

Llama2 inferentia2 throughput

同样,部署在高端实例上的模型具有非常好的吞吐量,即使是那些为延迟优化的模型也是如此。

“预算型”模型的吞吐量要低得多,但对于流式使用场景来说仍然可以接受,考虑到普通读者的阅读速度大约是每秒 5 个单词。

结论

我们已经展示了使用 🤗 optimum-neuronAWS Inferentia2 上部署来自 Hugging Face hubllama2 模型是多么容易。

部署的模型在编码时间、延迟和吞吐量方面表现出非常好的性能。

有趣的是,已部署模型的延迟对批量大小(batch size)不太敏感,这为它们在并行处理多个请求的推理端点上的部署开辟了道路。

不过,仍有很大的改进空间

  • 在当前的实现中,增加吞吐量的唯一方法是增加批处理大小,但目前这受限于设备内存。诸如流水线(pipelining)之类的替代方案目前正在集成中,
  • 静态序列长度限制了模型编码长上下文的能力。看看 attention sinks 是否是解决这个问题的有效选项将会很有趣。

社区

注册登录 发表评论