LoRA 어댑터로 파인튜닝 모델 실행하기

LoRA 어댑터로 파인튜닝 모델 실행하기

파인튜닝한 모델을 ONNX Runtime으로 실행할 수 있는 형식(모델 + 어댑터)으로 만들어 보는 튜토리얼이에요. LoRA는 Low Rank Adaptation의 줄임말로, 그래프에서 일부 레이어를 고정(freeze) 하고, 변하는 레이어의 가중치 값을 어댑터(adapter) 라는 산출물로 따로 제공하는 인기 있는 파인튜닝 방법이에요. 같은 모델에 여러 어댑터를 켜고 끄는 Multi LoRA 방식도 지원해서, 시나리오별·테넌트/고객별·사용자별로 어댑터를 몇 개에서 수백·수천 개까지 쓸 수 있답니다. Olive가 모델과 어댑터를 ONNX 형식으로 만들면, 그걸 ONNX Runtime에서 실행하는 구조예요.

출처: Generate and run fine-tuned models with LoRA adapters (공식 문서)

준비

  1. Olive 설치 — main 브랜치에서 설치하며, 0.8.0이 릴리스되면 그 버전으로 바꿔요.

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

    pip install onnxruntime-genai
    
  3. 기타 의존성 설치

    pip install optimum peft
    
  4. torch·transformers 다운그레이드 — torch 2.5.0에는 내보내기 버그가 있고, transformers 4.45.0 이상과 호환 문제가 있는 상태예요(TODO).

    pip uninstall torch
    pip install torch==2.4
    pip uninstall transformers
    pip install transformers==4.44
    
  5. 모델 고르기 — HuggingFace 모델이나 직접 만든 모델을 써요. 이 모델은 PyTorch 모델이어야 해요.

  6. 파인튜닝인지, 기존 어댑터를 쓰는 건지 결정 — HuggingFace에는 이미 만들어진 어댑터가 많아요. 여러 어댑터를 쓸 거라면, 모두 원본 모델의 같은 파인튜닝 레이어를 사용해야 해요.

ONNX 형식의 모델·어댑터 만들기

  1. 파인튜닝이라면 Olive로 파인튜닝 — NVIDIA GPU와 CUDA가 설치된 시스템이 필요해요. olive fine-tune 명령을 쓰며, 예시는 다음과 같아요.

    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 CLI fine-tune 문서

  2. (선택) 모델 양자화olive quantize 명령을 써요. Olive CLI quantize 문서

  3. 양자화한 모델로 ONNX 모델·어댑터 생성olive auto-opt 명령을 써요. --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>
    
  4. 어댑터를 .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 <system prompt> -p <prompt>

더 알아보기