Transformers 4.37 中文文档(十)(1)https://developer.aliyun.com/article/1564902
TensorFlow 模型的 XLA 集成
加速线性代数,简称 XLA,是用于加速 TensorFlow 模型运行时的编译器。来自官方文档:
XLA(加速线性代数)是一个专门用于线性代数的编译器,可以加速 TensorFlow 模型,可能不需要源代码更改。
在 TensorFlow 中使用 XLA 很简单 - 它已经打包在tensorflow
库中,并且可以通过任何创建图形函数(例如tf.function
)中的jit_compile
参数触发。当使用 Keras 方法如fit()
和predict()
时,您可以通过将jit_compile
参数传递给model.compile()
来简单启用 XLA。但是,XLA 不仅限于这些方法 - 它还可以用于加速任何任意的tf.function
。
🤗 Transformers 中的几个 TensorFlow 方法已经重写为与 XLA 兼容,包括用于模型的文本生成,如GPT2、T5和OPT,以及用于语音处理的模型,如Whisper。
在🤗 Transformers 内部的 TensorFlow 文本生成模型中,加速的确切数量非常依赖于模型,我们注意到速度提升了约 100 倍。本文将解释如何在这些模型中使用 XLA 来获得最大的性能。我们还将提供额外资源的链接,如果您有兴趣了解更多关于基准测试和我们在 XLA 集成背后的设计理念。
使用 XLA 运行 TF 函数
让我们考虑以下 TensorFlow 模型:
import tensorflow as tf model = tf.keras.Sequential( [tf.keras.layers.Dense(10, input_shape=(10,), activation="relu"), tf.keras.layers.Dense(5, activation="softmax")] )
上述模型接受维度为(10, )
的输入。我们可以使用该模型来运行前向传递,如下所示:
# Generate random inputs for the model. batch_size = 16 input_vector_dim = 10 random_inputs = tf.random.normal((batch_size, input_vector_dim)) # Run a forward pass. _ = model(random_inputs)
为了使用 XLA 编译函数运行前向传递,我们需要执行以下操作:
xla_fn = tf.function(model, jit_compile=True) _ = xla_fn(random_inputs)
model
的默认call()
函数用于编译 XLA 图。但是,如果有任何其他模型函数您想要编译成 XLA,也是可能的,例如:
my_xla_fn = tf.function(model.my_xla_fn, jit_compile=True)
使用🤗 Transformers 中的 XLA 运行 TF 文本生成模型
要在🤗 Transformers 内启用 XLA 加速生成,您需要安装最新版本的transformers
。您可以通过运行以下命令来安装:
pip install transformers --upgrade
然后您可以运行以下代码:
import tensorflow as tf from transformers import AutoTokenizer, TFAutoModelForCausalLM # Will error if the minimal version of Transformers is not installed. from transformers.utils import check_min_version check_min_version("4.21.0") tokenizer = AutoTokenizer.from_pretrained("gpt2", padding_side="left", pad_token="</s>") model = TFAutoModelForCausalLM.from_pretrained("gpt2") input_string = ["TensorFlow is"] # One line to create an XLA generation function xla_generate = tf.function(model.generate, jit_compile=True) tokenized_input = tokenizer(input_string, return_tensors="tf") generated_tokens = xla_generate(**tokenized_input, num_beams=2) decoded_text = tokenizer.decode(generated_tokens[0], skip_special_tokens=True) print(f"Generated -- {decoded_text}") # Generated -- TensorFlow is an open-source, open-source, distributed-source application # framework for the
正如您可以注意到的,在generate()
上启用 XLA 只是一行代码。其余代码保持不变。但是,上面代码片段中有一些特定于 XLA 的注意事项。您需要注意这些才能实现 XLA 带来的加速。我们将在下一节中讨论这些。
需要注意的事项
当您首次执行启用 XLA 的函数(如上面的xla_generate()
)时,它将内部尝试推断计算图,这是耗时的。这个过程被称为“跟踪”。
您可能会注意到生成时间不够快。连续调用xla_generate()
(或任何其他启用 XLA 的函数)不需要推断计算图,只要函数的输入遵循最初构建计算图时的相同形状。虽然对于具有固定输入形状的模态(例如图像)这不是问题,但如果您正在处理具有可变输入形状的模态(例如文本),则必须注意。
为了确保xla_generate()
始终使用相同的输入形状,您可以在调用分词器时指定padding
参数。
import tensorflow as tf from transformers import AutoTokenizer, TFAutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("gpt2", padding_side="left", pad_token="</s>") model = TFAutoModelForCausalLM.from_pretrained("gpt2") input_string = ["TensorFlow is"] xla_generate = tf.function(model.generate, jit_compile=True) # Here, we call the tokenizer with padding options. tokenized_input = tokenizer(input_string, pad_to_multiple_of=8, padding=True, return_tensors="tf") generated_tokens = xla_generate(**tokenized_input, num_beams=2) decoded_text = tokenizer.decode(generated_tokens[0], skip_special_tokens=True) print(f"Generated -- {decoded_text}")
这样,您可以确保xla_generate()
的输入始终接收与其跟踪时相同形状的输入,从而加快生成时间。您可以使用下面的代码进行验证:
import time import tensorflow as tf from transformers import AutoTokenizer, TFAutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("gpt2", padding_side="left", pad_token="</s>") model = TFAutoModelForCausalLM.from_pretrained("gpt2") xla_generate = tf.function(model.generate, jit_compile=True) for input_string in ["TensorFlow is", "TensorFlow is a", "TFLite is a"]: tokenized_input = tokenizer(input_string, pad_to_multiple_of=8, padding=True, return_tensors="tf") start = time.time_ns() generated_tokens = xla_generate(**tokenized_input, num_beams=2) end = time.time_ns() print(f"Execution time -- {(end - start) / 1e6:.1f} ms\n")
在 Tesla T4 GPU 上,您可以期望输出如下:
Execution time -- 30819.6 ms Execution time -- 79.0 ms Execution time -- 78.9 ms
第一次调用xla_generate()
由于跟踪而耗时,但后续调用速度快得多。请记住,任何时候对生成选项进行更改都会触发重新跟踪,从而导致生成时间变慢。
我们没有在本文档中涵盖🤗 Transformers 提供的所有文本生成选项。我们鼓励您阅读高级用例的文档。
额外资源
在这里,如果您想深入了解🤗 Transformers 中的 XLA 和一般情况下的 XLA,我们为您提供了一些额外资源。
- 这个 Colab 笔记本提供了一个交互式演示,如果您想要尝试 XLA 兼容的编码器-解码器(如T5)和仅解码器(如GPT2)文本生成模型。
- 这篇博客文章提供了 XLA 兼容模型的比较基准概述,以及对 TensorFlow 中 XLA 的友好介绍。
- 这篇博客文章讨论了我们在🤗 Transformers 中为 TensorFlow 模型添加 XLA 支持的设计理念。
- 学习更多关于 XLA 和 TensorFlow 图的推荐帖子:
使用 torch.compile() 优化推理
huggingface.co/docs/transformers/v4.37.2/en/perf_torch_compile
本指南旨在提供有关在🤗 Transformers 中使用 torch.compile()
为计算机视觉模型引入的推理加速的基准测试信息。
torch.compile 的好处
根据模型和 GPU,torch.compile()
在推理过程中可以提高高达 30%的速度。要使用 torch.compile()
,只需安装任何版本高于 2.0 的torch
。
编译模型需要时间,因此如果您只编译模型一次而不是每次推理时都编译,这将非常有用。要编译您选择的任何计算机视觉模型,请按照以下示例在模型上调用 torch.compile()
:
from transformers import AutoModelForImageClassification model = AutoModelForImageClassification.from_pretrained(MODEL_ID).to("cuda") + model = torch.compile(model)
compile()
有多种编译模式,它们在编译时间和推理开销方面有所不同。max-autotune
比 reduce-overhead
花费更长的时间,但推理速度更快。默认模式在编译速度上最快,但与 reduce-overhead
相比在推理时间上效率不高。在本指南中,我们使用了默认模式。您可以在这里了解更多信息。
我们对torch.compile
在不同的计算机视觉模型、任务、硬件类型和批处理大小上进行了基准测试,使用的是torch
版本 2.0.1。
基准测试代码
下面您可以找到每个任务的基准测试代码。我们在推理之前对 GPU 进行预热,并使用相同的图像进行 300 次推理的平均时间。
使用 ViT 进行图像分类
import torch from PIL import Image import requests import numpy as np from transformers import AutoImageProcessor, AutoModelForImageClassification url = 'http://images.cocodataset.org/val2017/000000039769.jpg' image = Image.open(requests.get(url, stream=True).raw) processor = AutoImageProcessor.from_pretrained("google/vit-base-patch16-224") model = AutoModelForImageClassification.from_pretrained("google/vit-base-patch16-224").to("cuda") model = torch.compile(model) processed_input = processor(image, return_tensors='pt').to(device="cuda") with torch.no_grad(): _ = model(**processed_input)
使用 DETR 进行目标检测
from transformers import AutoImageProcessor, AutoModelForObjectDetection processor = AutoImageProcessor.from_pretrained("facebook/detr-resnet-50") model = AutoModelForObjectDetection.from_pretrained("facebook/detr-resnet-50").to("cuda") model = torch.compile(model) texts = ["a photo of a cat", "a photo of a dog"] inputs = processor(text=texts, images=image, return_tensors="pt").to("cuda") with torch.no_grad(): _ = model(**inputs)
使用 Segformer 进行图像分割
from transformers import SegformerImageProcessor, SegformerForSemanticSegmentation processor = SegformerImageProcessor.from_pretrained("nvidia/segformer-b0-finetuned-ade-512-512") model = SegformerForSemanticSegmentation.from_pretrained("nvidia/segformer-b0-finetuned-ade-512-512").to("cuda") model = torch.compile(model) seg_inputs = processor(images=image, return_tensors="pt").to("cuda") with torch.no_grad(): _ = model(**seg_inputs)
下面您可以找到我们进行基准测试的模型列表。
图像分类
- google/vit-base-patch16-224
- microsoft/beit-base-patch16-224-pt22k-ft22k
- facebook/convnext-large-224
- microsoft/resnet-50
图像分割
- nvidia/segformer-b0-finetuned-ade-512-512
- facebook/mask2former-swin-tiny-coco-panoptic
- facebook/maskformer-swin-base-ade
- google/deeplabv3_mobilenet_v2_1.0_513
目标检测
下面您可以找到使用和不使用 torch.compile()
的推理持续时间的可视化,以及每个模型在不同硬件和批处理大小下的百分比改进。
下面您可以找到每个模型使用和不使用 compile()
的推理持续时间(毫秒)。请注意,OwlViT 在较大批处理大小时会导致 OOM。
A100(批处理大小:1)
任务/模型 | torch 2.0 - 无编译 | torch 2.0 - 编译 |
图像分类/ViT | 9.325 | 7.584 |
图像分割/Segformer | 11.759 | 10.500 |
目标检测/OwlViT | 24.978 | 18.420 |
图像分类/BeiT | 11.282 | 8.448 |
目标检测/DETR | 34.619 | 19.040 |
图像分类/ConvNeXT | 10.410 | 10.208 |
图像分类/ResNet | 6.531 | 4.124 |
图像分割/Mask2former | 60.188 | 49.117 |
图像分割/Maskformer | 75.764 | 59.487 |
图像分割/MobileNet | 8.583 | 3.974 |
目标检测/Resnet-101 | 36.276 | 18.197 |
目标检测/Conditional-DETR | 31.219 | 17.993 |
Transformers 4.37 中文文档(十)(3)https://developer.aliyun.com/article/1564904