使用 LoRA 适配器生成和运行微调模型
了解如何生成适用于使用 ONNX Runtime 执行的模型和适配器。
LoRA 是 Low Rank Adaptation(低秩适应)的缩写。它是一种流行的微调方法,通过冻结图中的一些层,并在一个称为适配器的构件中提供可变层的权重值。
多 LoRA (Multi LoRA) 在运行时使用多个适配器来运行同一模型的不同微调版本。适配器可以是按场景、按租户/客户或按用户划分的,即适配器的数量可以从几个到数百或数千个不等。
Olive 生成 ONNX 格式的模型和适配器。这些模型和适配器随后可以使用 ONNX Runtime 运行。
设置
-
安装 Olive
这将从 main 分支安装 Olive。发布 0.8.0 版本后请替换为该版本。
pip install git+https://github.com/microsoft/olive
-
安装 ONNX Runtime generate()
pip install onnxruntime-genai
-
安装其他依赖项
pip install optimum peft
-
降级 torch 和 transformers
待办:torch 2.5.0 存在导出 bug,且与 transformers>=4.45.0 不兼容
pip uninstall torch pip install torch==2.4 pip uninstall transformers pip install transformers==4.44
-
选择模型
您可以使用 HuggingFace 上的模型,或者您自己的模型。模型必须是 PyTorch 模型。
-
确定您是要微调模型,还是使用预先存在的适配器
HuggingFace 上有很多预先存在的适配器。如果您使用多个不同的适配器,它们必须都使用原始模型的相同微调层。
生成 ONNX 格式的模型和适配器
-
如果需要微调,运行 Olive 来微调模型
注意:此操作需要具有 NVIDIA GPU 并安装 CUDA 的系统
使用
olive fine-tune
命令:https://msdocs.cn/Olive/how-to/cli/cli-finetune.html这里是该命令的示例用法
olive finetune --method qlora -m meta-llama/Meta-Llama-3-8B -d nampdn-ai/tiny-codes --train_split "train[:4096]" --eval_split "train[4096:4224]" --text_template "### Language: {programming_language} \n### Question: {prompt} \n### Answer: {response}" --per_device_train_batch_size 16 --per_device_eval_batch_size 16 --max_steps 150 --logging_steps 50 -o adapters\tiny-codes
-
(可选)量化模型
使用
olive quantize
命令:https://msdocs.cn/Olive/how-to/cli/cli-quantize.html -
使用量化模型生成 ONNX 模型和适配器
使用
olive auto-opt
命令执行此步骤:https://msdocs.cn/Olive/how-to/cli/cli-auto-opt.html--adapter path
可以是 HuggingFace 适配器引用,也可以是您上面微调的适配器的路径。--provider
参数可以是 ONNX Runtime 执行提供程序。olive auto-opt -m <path to your model folder> --adapter_path <path to your adapter> -o <output model folder> --device cpu\|gpu --provider <provider>
-
将适配器转换为
.onnx_adapter
格式对您生成的每个适配器运行此步骤一次。
olive convert-adapters --adapter_path <path to your fine-tuned adapter --output_path <path to .onnx_adapter location --dtype float32
编写您的应用
此示例以 Python 演示,但您也可以使用 C/C++ API、C# API 和 Java API(即将推出!)
import onnxruntime_genai as og
import numpy as np
import argparse
parser = argparse.ArgumentParser(description='Application to load and switch ONNX LoRA adapters')
parser.add_argument('-m', '--model', type=str, help='The ONNX base model')
parser.add_argument('-a', '--adapters', nargs='+', type=str, help='List of adapters in .onnx_adapters format')
parser.add_argument('-t', '--template', type=str, help='The template with which to format the prompt')
parser.add_argument('-s', '--system', type=str, help='The system prompt to pass to the model')
parser.add_argument('-p', '--prompt', type=str, help='The user prompt to pass to the model')
args = parser.parse_args()
model = og.Model(args.model)
if args.adapters:
adapters = og.Adapters(model)
for adapter in args.adapters:
adapters.load(adapter, adapter)
tokenizer = og.Tokenizer(model)
tokenizer_stream = tokenizer.create_stream()
prompt = args.template.format(system=args.system, input=args.prompt)
params = og.GeneratorParams(model)
params.set_search_options(max_length=2048, past_present_share_buffer=False)
# This input is generated for transformers versions > 4.45
#params.set_model_input("onnx::Neg_67", np.array(0, dtype=np.int64))
params.input_ids = tokenizer.encode(prompt)
generator = og.Generator(model, params)
if args.adapters:
for adapter in args.adapters:
print(f"[{adapter}]: {prompt}")
generator.set_active_adapter(adapters, adapter)
while not generator.is_done():
generator.compute_logits()
generator.generate_next_token()
new_token = generator.get_next_tokens()[0]
print(tokenizer_stream.decode(new_token), end='', flush=True)
else:
print(f"[Base]: {prompt}")
while not generator.is_done():
generator.compute_logits()
generator.generate_next_token()
调用应用
python app.py -m <model folder> -a <.onnx_adapter files> -t <prompt template> -s <systemm prompt> -p <prompt>