Feinabgestimmte Modelle mit LoRA-Adaptern generieren und ausführen

Erfahren Sie, wie Sie Modelle und Adapter in Formaten generieren, die für die Ausführung mit ONNX Runtime geeignet sind.

LoRA steht für Low Rank Adaptation. Es ist eine beliebte Methode zur Feinabstimmung, bei der einige Schichten in einem Graphen eingefroren und die Werte der Gewichte der variablen Schichten in einem Artefakt namens Adapter bereitgestellt werden.

Multi LoRA verwendet zur Laufzeit mehrere Adapter, um verschiedene Feinabstimmungen desselben Modells auszuführen. Der Adapter kann pro Szenario, pro Mandant/Kunde oder pro Benutzer sein, d. h. es kann nur wenige Adapter bis zu vielen Hunderten oder Tausenden geben.

Olive generiert Modelle und Adapter im ONNX-Format. Diese Modelle und Adapter können dann mit ONNX Runtime ausgeführt werden.

Setup

  1. Olive installieren

    Dies installiert Olive aus dem Hauptzweig. Ersetzen Sie dies durch Version 0.8.0, wenn diese veröffentlicht wird.

    pip install git+https://github.com/microsoft/olive
    
  2. ONNX Runtime generate() installieren

    pip install onnxruntime-genai
    
  3. Andere Abhängigkeiten installieren

    pip install optimum peft
    
  4. torch und transformers downgraden

    TODO: Es gibt einen Exportfehler mit torch 2.5.0 und eine Inkompatibilität mit transformers>=4.45.0

    pip uninstall torch
    pip install torch==2.4
    pip uninstall transformers
    pip install transformers==4.44
    
  5. Ein Modell auswählen

    Sie können ein Modell von HuggingFace oder Ihr eigenes Modell verwenden. Das Modell muss ein PyTorch-Modell sein.

  6. Entscheiden Sie, ob Sie Ihr Modell feinabstimmen oder einen bereits vorhandenen Adapter verwenden

    Es gibt viele bereits vorhandene Adapter auf HuggingFace. Wenn Sie mehrere verschiedene Adapter verwenden, müssen diese alle dieselben feinabgestimmten Schichten des Originalmodells verwenden.

Modell und Adapter im ONNX-Format generieren

  1. Wenn Sie feinabstimmen, führen Sie Olive aus, um Ihr Modell feinabzustimmen

    Hinweis: Diese Operation erfordert ein System mit einer NVIDIA-GPU mit installiertem CUDA

    Verwenden Sie den Befehl olive fine-tune: https://msdocs.de/Olive/how-to/cli/cli-finetune.html

    Hier ist ein Beispiel für die Verwendung des Befehls

    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
    
  2. Optional Ihr Modell quantisieren

    Verwenden Sie den Befehl olive quantize: https://msdocs.de/Olive/how-to/cli/cli-quantize.html

  3. Das ONNX-Modell und den Adapter mit dem quantisierten Modell generieren

    Verwenden Sie für diesen Schritt den Befehl olive auto-opt: https://msdocs.de/Olive/how-to/cli/cli-auto-opt.html

    Der Parameter --adapter path kann entweder eine HuggingFace-Adapterreferenz oder ein Pfad zu dem von Ihnen oben feinabgestimmten Adapter sein.

    Das Argument --provider kann ein ONNX Runtime Execution Provider sein.

    olive auto-opt -m <path to your model folder> --adapter_path <path to your adapter> -o <output model folder> --device cpu\|gpu --provider <provider> 
    
  4. Adapter in das Format .onnx_adapter konvertieren

    Führen Sie diesen Schritt einmal für jeden von Ihnen generierten Adapter aus.

    olive convert-adapters --adapter_path <path to your fine-tuned adapter --output_path <path to .onnx_adapter location --dtype float32
    

Ihre Anwendung schreiben

Dieses Beispiel wird in Python gezeigt, aber Sie können auch die C/C++-API, die C#-API und die Java-API (bald verfügbar!) verwenden.

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()

Die Anwendung aufrufen

python app.py -m <model folder> -a <.onnx_adapter files> -t <prompt template> -s <systemm prompt> -p <prompt>

Referenzen