1. 简介
Hugging Face是一个大型开源社区,它迅速成为自然语言处理 (NLP)、自动语音识别 (ASR) 和计算机视觉 (CV) 领域的预训练深度学习模型的诱人中心。
Optimum Intel提供了一个简单的界面来优化 Transformer 模型并将其转换为OpenVINO™中间表示 (IR) 格式,以使用 OpenVINO™ 运行时加速英特尔® 架构上的端到端管道。
情感分类(sentimental classification)是流行的 NLP 任务之一,是识别文本中的观点并将其标记为正面或负面的自动化过程。在本博客中,我们使用 DistilBERT 进行情感分类任务作为示例,展示 Optimum Intel 如何帮助使用神经网络压缩框架 (NNCF)优化模型并使用 OpenVINO™ 运行时加速推理。
2. 设置环境
在新的 Python 虚拟环境中安装 optimum-intel 及其依赖项,如下所示:
conda create -n optimum-intel python=3.8
conda activate optimum-intel
python -m pip install torch==1.9.1 onnx py-cpuinfo
python -m pip install optimum[openvino,nncf]
3. 使用 OpenVINO™ Runtime 进行模型推理
Optimum 推理模型与 Hugging Face Transformers 模型的 API 兼容;这意味着您只需将 Hugging Face Transformer 的“AutoModelXXX”类替换为“OVModelXXX”类,即可切换模型推理与 OpenVINO™ 运行时。您可以在使用 from_pretrained() 方法加载模型时设置“from_transformers=True”,加载的模型将自动转换为 OpenVINO™ IR,以便使用 OpenVINO™ 运行时进行推理。
下面是如何使用 OpenVINO™ 运行时对情感分类任务进行推理的示例,管道的输出由分类标签(正/负)和相应的置信度组成。
from optimum.intel.openvino import OVModelForSequenceClassification
from transformers import AutoTokenizer, pipeline
model_id = "distilbert-base-uncased-finetuned-sst-2-english"
hf_model = OVModelForSequenceClassification.from_pretrained(
model_id, from_transformers=True)
tokenizer = AutoTokenizer.from_pretrained(model_id)
hf_pipe_cls = pipeline("text-classification",
model=hf_model, tokenizer=tokenizer)
text = "He's a dreadful magician."
fp32_outputs = hf_pipe_cls(text)
print("FP32 model outputs: ", fp32_outputs)
4. 使用 NNCF 框架进行模型量化
大多数深度学习模型都是使用 32 位浮点精度 (FP32) 构建的。量化是使用较少内存以最小精度损失表示模型的过程。为了通过英特尔® 深度学习加速进一步优化英特尔® 架构上的模型性能,需要将模型量化为 8 位整数精度 (INT8)。
Optimum Intel 支持使用 NNCF 对 Hugging Face Transformer 模型进行量化。NNCF 提供两种主流量化方法 – 训练后量化 (PTQ) 和量化感知训练 (QAT)。
- 训练后量化(PTQ)是指使用代表性校准数据集对模型进行量化,而无需进行微调。
- 量化感知训练(QAT)用于模拟训练过程中量化的影响,以减轻其对模型准确性的影响
4.1. 使用 NNCF PTQ 进行模型量化
NNCF 训练后静态量化引入了一个额外的校准步骤,其中数据通过网络输入以计算激活量化参数。以下是使用通用语言理解评估 (GLUE) 数据集作为校准数据集对预训练的 DistilBERT 应用静态量化的方法:
from functools import partial
from optimum.intel.openvino import OVQuantizer, OVConfig
from transformers import AutoTokenizer, AutoModelForSequenceClassification
model_id = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModelForSequenceClassification.from_pretrained(model_id)
tokenizer = AutoTokenizer.from_pretrained(model_id)
def preprocess_fn(examples, tokenizer):
return tokenizer(
examples["sentence"], padding=True, truncation=True, max_length=128
)
quantizer = OVQuantizer.from_pretrained(model)
calibration_dataset = quantizer.get_calibration_dataset(
"glue",
dataset_config_name="sst2",
preprocess_function=partial(preprocess_fn, tokenizer=tokenizer),
num_samples=100,
dataset_split="train",
preprocess_batch=True,
)
# Load the default quantization configuration
ov_config = OVConfig()
# The directory where the quantized model will be saved
save_dir = "nncf_ptq_results"
# Apply static quantization and save the resulting model in the OpenVINO IR format
quantizer.quantize(calibration_dataset=calibration_dataset,
save_directory=save_dir, quantization_config=ov_config)
quantize() 方法应用训练后静态量化,并将生成的量化模型导出到 OpenVINO™ 中间表示 (IR),该模型可部署在任何目标英特尔® 架构上。
4.2. 使用 NNCF QAT 进行模型量化
量化感知训练 (QAT) 旨在通过模拟训练期间量化的影响来缓解模型准确性问题。如果训练后量化导致准确性下降,则可以使用 QAT。
NNCF 提供了一个“OVTrainer”类来替代 Hugging Face Transformer 的“Trainer”类,以便在训练期间通过额外的量化配置实现量化。以下是如何在应用量化感知训练 (QAT) 的同时使用斯坦福情绪树库 (SST) 数据集对 DistilBERT 进行微调的示例:
import numpyThe as np
import evaluate
from datasets import load_dataset
from transformers import AutoModelForSequenceClassification, AutoTokenizer, TrainingArguments, default_data_collator
from optimum.intel.openvino import OVConfig, OVTrainer
model_id = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModelForSequenceClassification.from_pretrained(model_id)
tokenizer = AutoTokenizer.from_pretrained(model_id)
dataset = load_dataset("glue", "sst2")
dataset = dataset.map(
lambda examples: tokenizer(examples["sentence"], padding=True, truncation=True, max_length=128), batched=True
)
metric = evaluate.load("accuracy")
def compute_metrics(p): return metric.compute(
predictions=np.argmax(p.predictions, axis=1), references=p.label_ids
)
# The directory where the quantized model will be saved
save_dir = "nncf_qat_results"
# Load the default quantization configuration
ov_config = OVConfig()
trainer = OVTrainer(
model=model,
args=TrainingArguments(save_dir, num_train_epochs=1.0,
do_train=True, do_eval=True),
train_dataset=dataset["train"].select(range(300)),
eval_dataset=dataset["validation"],
compute_metrics=compute_metrics,
tokenizer=tokenizer,
data_collator=default_data_collator,
ov_config=ov_config,
feature="sequence-classification",
)
train_result = trainer.train()
metrics = trainer.evaluate()
trainer.save_model()
4.3. FP32 与 INT8 模型输出比较
“OVModelForXXX” 类提供了相同的 API,通过设置“from_transformers=False”来加载 FP32 和量化 INT8 OpenVINO™ 模型。下面是一个如何加载由 NNCF 优化的量化 INT8 模型并使用 OpenVINO™ 运行时进行推理的示例。
ov_ptq_model = OVModelForSequenceClassification.from_pretrained(“nncf_ptq_results”, from_transformers=False)
ov_ptq_pipe_cls = pipeline("text-classification", model=ov_ptq_model, tokenizer=tokenizer)
ov_ptq_outputs = ov_ptq_pipe_cls(text)
print("PTQ quantized INT8 model outputs: ", ov_ptq_outputs)
ov_qat_model = OVModelForSequenceClassification.from_pretrained("nncf_qat_results", from_transformers=False)
ov_qat_pipe_cls = pipeline("text-classification", model=ov_qat_model, tokenizer=tokenizer)
ov_qat_outputs = ov_qat_pipe_cls(text)
print("QAT quantized INT8 model outputs: ", ov_qat_outputs)
以下是 FP32 和 INT8 模型的情感分类输出示例:
5. 缓解饱和引起的准确性问题
旧 CPU 代数(基于 SSE、AVX-2、AVX-512 指令集)的 8 位指令在计算点积时容易出现中间缓冲区所谓的饱和(溢出) ,而点积是卷积或 MatMul 运算的重要组成部分。在上述架构上运行 8 位量化模型的推理时,这种饱和会导致准确率下降。采用英特尔®深度学习加速 (VNNI) 技术及后续代数的GPU 或 CPU 不会出现此问题。
如果使用 NNCF 默认量化配置进行量化后准确度出现显著差异(>1%),则可以使用以下示例代码来检查部署的平台是否支持 Intel ® Deep Learning Boost (VNNI) 及后续版本:
import cpuinfo
flags = cpuinfo.get_cpu_info()['flags']
brand_raw = cpuinfo.get_cpu_info()['brand_raw']
w = "without"
overflow_fix = 'enable'
for flag in flags:
if "vnni" in flag or "amx_int8" in flag:
w = "with"
overflow_fix = 'disable'
print("Detected CPU platform {0} {1} support of Intel(R) Deep Learning Boost (VNNI) technology \
and further generations, overflow fix should be {2}d".format(brand_raw, w, overflow_fix))
虽然量化激活使用了全部 8 位数据类型,但有一种解决方法,即仅使用 7 位来表示权重(卷积层或连接层),以缓解旧 CPU 平台上许多模型的饱和问题。
NNCF 提供了三个选项来处理饱和问题。可以使用“overflow_fix”参数在 NNCF 量化配置中启用这些选项:
- “disable”:(默认)选项根本不应用饱和修复
- “enable”:适用于模型中所有层的选项
- “first_layer_only”:用于修复第一层饱和问题的选项
以下是在量化配置中启用溢出修复以缓解旧 CPU 平台上的准确性问题的示例:
from optimum.intel.openvino.configuration import DEFAULT_QUANTIZATION_CONFIG
ov_config_dict = DEFAULT_QUANTIZATION_CONFIG
ov_config_dict["overflow_fix"] = "enable"
ov_config = OVConfig(compression=ov_config_dict)
使用 NNCF PTQ/NNCF 更新的量化配置对模型量化后,可以重复步骤 4.3 来验证量化后的 INT8 模型推理结果是否与 FP32 模型输出一致。
文章评论