使用 Optimum-Intel 和 OpenVINO GenAI 优化和部署模型
在边缘或客户端部署 Transformers 模型需要仔细考虑性能和兼容性。Python 虽然功能强大,但并非总是此类部署的理想选择,尤其是在 C++ 主导的环境中。本博客将指导您使用 Optimum-Intel 和 OpenVINO™ GenAI 优化和部署 Hugging Face Transformers 模型,确保高效的 AI 推理,并最大限度地减少依赖关系。
目录
- 为何在边缘部署中使用 OpenVINO™
- 步骤 1:设置环境
- 步骤 2:将模型导出到 OpenVINO IR
- 步骤 3:模型优化
- 步骤 4:使用 OpenVINO GenAI API 部署
- 结论
为何在边缘部署中使用 OpenVINO™
OpenVINO™ 最初是作为 C++ AI 推理解决方案开发的,这使其成为边缘和客户端部署的理想选择,在这些部署中,最大限度地减少依赖关系至关重要。随着 GenAI API 的引入,将大型语言模型 (LLM) 集成到 C++ 或 Python 应用程序中变得更加简单,其功能旨在简化部署并提高性能。
步骤 1:设置环境
先决条件
首先,确保您的环境已正确配置 Python 和 C++。安装必要的 Python 包
pip install --upgrade --upgrade-strategy eager "optimum[openvino]"
以下是本博客文章中使用的具体包
transformers==4.44
openvino==24.3
openvino-tokenizers==24.3
optimum-intel==1.20
lm-eval==0.4.3
有关 GenAI C++ 库的安装说明,请参见此处。
步骤 2:将模型导出到 OpenVINO IR
Hugging Face 和 Intel 的合作催生了 Optimum-Intel 项目。它旨在优化 Transformers 模型,使其在 Intel 硬件上进行推理。Optimum-Intel 支持 OpenVINO 作为推理后端,其 API 具有基于 OpenVINO 推理 API 构建的各种模型架构的包装器。所有这些包装器都以 OV
为前缀,例如 OVModelForCausalLM
。否则,它与 🤗 Transformers 库的 API 类似。
要将 Transformers 模型导出到 OpenVINO 中间表示 (IR),可以使用两种选项:这可以通过 Python 的 .from_pretrained()
方法或 Optimum 命令行界面 (CLI) 来完成。以下是使用这两种方法的示例
使用 Python API
from optimum.intel import OVModelForCausalLM
model_id = "meta-llama/Meta-Llama-3.1-8B"
model = OVModelForCausalLM.from_pretrained(model_id, export=True)
model.save_pretrained("./llama-3.1-8b-ov")
使用命令行界面 (CLI)
optimum-cli export openvino -m meta-llama/Meta-Llama-3.1-8B ./llama-3.1-8b-ov
./llama-3.1-8b-ov
文件夹将包含 .xml
和 .bin
IR 模型文件以及源模型所需的配置文件。🤗 分词器也将转换为 openvino-tokenizers
库的格式,并在同一文件夹中创建相应的配置文件。
步骤 3:模型优化
在资源受限的边缘和客户端设备上运行 LLM 时,强烈建议进行模型优化。仅权重量化是一种主流方法,可显著减少延迟和模型占用空间。Optimum-Intel 通过神经网络压缩框架 (NNCF) 提供仅权重量化,该框架具有专门为 LLM 设计的各种优化技术:从无数据 INT8 和 INT4 权重量化到数据感知方法,如 AWQ、GPTQ、量化尺度估计、混合精度量化。默认情况下,大于十亿参数的模型权重被量化为 INT8 精度,这在精度方面是安全的。这意味着上述导出步骤将生成 8 位权重的模型。然而,4 位整数仅权重量化可以实现更好的精度-性能权衡。
对于 meta-llama/Meta-Llama-3.1-8B
模型,我们建议结合 AWQ、量化尺度估计以及使用反映部署用例的校准数据集进行 INT4/INT8 混合精度权重量化。与导出情况一样,有两种选项可以对 LLM 模型应用 4 位仅权重量化
使用 Python API
- 在
.from_pretrained()
方法中指定quantization_config
参数。在这种情况下,应创建OVWeightQuantizationConfig
对象并将其设置为此参数,如下所示
from optimum.intel import OVModelForCausalLM, OVWeightQuantizationConfig
MODEL_ID = "meta-llama/Meta-Llama-3.1-8B"
quantization_config = OVWeightQuantizationConfig(bits=4, awq=True, scale_estimation=True, group_size=64, dataset="c4")
model = OVModelForCausalLM.from_pretrained(MODEL_ID, export=True, quantization_config=quantization_config)
model.save_pretrained("./llama-3.1-8b-ov")
使用命令行界面 (CLI):
optimum-cli export openvino -m meta-llama/Meta-Llama-3.1-8B --weight-format int4 --awq --scale-estimation --group-size 64 --dataset wikitext2 ./llama-3.1-8b-ov
注意:模型优化过程可能需要时间,因为它会随后应用多种方法并对指定数据集进行模型推理。
使用 API 进行模型优化更为灵活,因为它允许使用可作为可迭代对象(例如 🤗 库的 Dataset
对象的实例或仅字符串列表)传递的自定义数据集。
权重量化通常会导致精度指标的下降。为了比较优化模型和源模型,我们报告在 Wikitext 数据集上使用 lm-evaluation-harness 项目测量的每字困惑度指标,该项目开箱即用支持 🤗 Transformers 和 Optimum-Intel 模型。
模型 | PPL PyTorch FP32 | OpenVINO INT8 | OpenVINO INT4 |
---|---|---|---|
meta-llama/Meta-Llama-3.1-8B | 7.3366 | 7.3463 | 7.8288 |
步骤 4:使用 OpenVINO GenAI API 部署
转换和优化后,使用 OpenVINO GenAI 部署模型非常简单。OpenVINO GenAI 中的 LLMPipeline 类提供 Python 和 C++ API,支持各种文本生成方法,并最大限度地减少依赖关系。
Python API 示例
import argparse
import openvino_genai
device = "CPU" # GPU can be used as well
pipe = openvino_genai.LLMPipeline(args.model_dir, device)
config = openvino_genai.GenerationConfig()
config.max_new_tokens = 100
print(pipe.generate(args.prompt, config))
要运行此示例,您需要将最少的依赖项安装到 Python 环境中,因为 OpenVINO GenAI 旨在提供轻量级部署。您可以将 OpenVINO GenAI 包安装到相同的 Python 环境中,或者创建一个单独的环境以比较应用程序占用空间
pip install openvino-genai==24.3
C++ API 示例
让我们看看如何使用 OpenVINO GenAI C++ API 运行相同的管道。GenAI API 旨在直观且提供从 🤗 Transformers API 的无缝迁移。
注意:在下面的示例中,“device”变量可以指定您环境中任何其他可用的设备。例如,如果您使用的是带有集成显卡的 Intel CPU,“GPU”是一个不错的选择。要检查可用设备,您可以使用 ov::Core::get_available_devices 方法(请参阅 query-device-properties)。
#include "openvino/genai/llm_pipeline.hpp"
#include <iostream>
int main(int argc, char* argv[]) {
std::string model_path = "./llama-3.1-8b-ov";
std::string device = "CPU" // GPU can be used as well
ov::genai::LLMPipeline pipe(model_path, device);
std::cout << pipe.generate("What is LLM model?", ov::genai::max_new_tokens(256));
}
自定义生成配置
LLMPipeline
还允许通过 ov::genai::GenerationConfig
指定自定义生成选项
ov::genai::GenerationConfig config;
config.max_new_tokens = 256;
std::string result = pipe.generate(prompt, config);
通过 LLMPipieline,用户不仅可以轻松利用各种解码算法(例如束搜索),还可以使用 Streamer 构建交互式聊天场景,如下例所示。此外,还可以利用 LLMPipeline 增强的内部优化,例如通过聊天方法 start_chat()
和 finish_chat()
利用先前聊天历史的 KV 缓存来减少提示处理时间(请参阅 using-genai-in-chat-scenario)。
ov::genai::GenerationConfig config;
config.max_new_tokens = 100;
config.do_sample = true;
config.top_p = 0.9;
config.top_k = 30;
auto streamer = [](std::string subword) {
std::cout << subword << std::flush;
return false;
};
// Since the streamer is set, the results will
// be printed each time a new token is generated.
pipe.generate(prompt, config, streamer);
最后,让我们看看如何在聊天场景中使用 LLMPipeline
pipe.start_chat()
for (size_t i = 0; i < questions.size(); i++) {
std::cout << "question:\n";
std::getline(std::cin, prompt);
std::cout << pipe.generate(prompt) << std::endl;
}
pipe.finish_chat();
结论
Optimum-Intel 和 OpenVINO™ GenAI 的结合为在边缘部署 Hugging Face 模型提供了强大、灵活的解决方案。通过遵循这些步骤,您可以在 Python 可能不理想的环境中实现优化、高性能的 AI 推理,确保您的应用程序在 Intel 硬件上流畅运行。
其他资源
- 您可以在此教程中找到更多详细信息。
- 要构建上述 C++ 示例,请参阅此文档。
- OpenVINO 文档
- Jupyter Notebooks
- Optimum 文档