Pular para o conteúdo principal

Ajuste fino LoRA do Qwen2-0.5B

Este notebook demonstra como ajustar de forma eficiente o modelo de linguagem Qwen2-0.5B de grande porte usando técnicas que otimizam parâmetros. Você aprenderá como:

  • Aplique LoRA (Adaptação de Baixa Classificação) para reduzir os parâmetros treináveis em aproximadamente 99%, mantendo a qualidade do modelo.
  • Utilize kernels Liger para treinamento com uso eficiente de memória, utilizando kernels Triton otimizados.
  • Utilize TRL (Transformer Reinforcement Learning) para ajuste fino supervisionado.
  • Registre o modelo otimizado no Unity Catalog para governança e implantação.

conceitos-chave:

  • LoRa: Uma técnica que congela o modelo base e ensina pequenas camadas adaptadoras, reduzindo drasticamente os requisitos de memória e o tempo de treinamento.
  • Kernels Liger: Kernels otimizados para GPU que reduzem o uso de memória em até 80% por meio de operações combinadas.
  • TRL: Uma biblioteca para treinamento de modelos de linguagem com aprendizado por reforço e ajuste fino supervisionado.

Matriz de decisão LoRa versus ajuste fino completo

LoRA (Low-Rank Adaptation) congela o modelo base e ensina apenas pequenas camadas adaptadoras, reduzindo os parâmetros treináveis em cerca de 99%. Isso torna o treinamento mais rápido e com melhor aproveitamento da memória.

Cenário

Recomendação

Razão

Memória de GPU limitada

LoRA

Ajusta modelos maiores na memória treinando apenas 1% dos parâmetros.

Adaptação específica da tarefa

LoRA

Troque os adaptadores no mesmo modelo base para várias tarefas.

Mudança comportamental importante do modelo

Ajuste fino completo

Atualiza todos os parâmetros para alterações fundamentais no comportamento do modelo.

Implantação em produção

LoRA

Arquivos menores (MB vs GB), carregamento mais rápido, controle de versão mais fácil.

Benefícios do Liger Kernel

Os kernels Liger são operações otimizadas para GPU que fundem várias etapas do sistema operacional em um único kernel, reduzindo as transferências de memória e melhorando a eficiência. O documento técnico fornece parâmetros de comparação detalhados que demonstram melhorias significativas de desempenho.

  • Operações fundidas : Combina operações (por exemplo, linear + perda) para reduzir a sobrecarga de memória em até 80%
  • Kernels Triton : Kernels de GPU personalizados otimizados para operações de transformação (RMSNorm, RoPE, SwiGLU, CrossEntropy)
  • Eficiência de memória : Permite tamanhos de lote maiores ou modelos que não caberiam na memória da GPU de outra forma.
  • Otimização para GPU única : Particularmente eficaz para cenários de treinamento com uma única GPU A10/A100.

Este notebook utiliza a biblioteca TRL para simplificar a configuração do treinamento e aplicar automaticamente essas otimizações.

Conecte-se à computeGPU serverless

Para se conectar à compute GPU serverless , clique no menu suspenso Conectar no Notebook e selecione GPU sem servidor .

Para obter mais informações, consulte a documentação compute da GPU.

Instale a biblioteca necessária.

A próxima célula instala o pacote Python necessário para o ajuste fino:

Biblioteca principal do Windows:

  • trl==0.18.1 : Biblioteca de aprendizado por reforço Transformer para ajuste fino supervisionado e RLHF
  • peft : Biblioteca de ajuste fino com otimização de parâmetros que fornece implementação LoRA.
  • liger-kernel : Kernels de GPU otimizados para memória para treinamento eficiente de transformadores

Biblioteca de apoio:

  • hf_transfer : downloads acelerados do Hugging Face Hub usando transferência baseada em Rust

O comando %restart_python reinicia o interpretador Python para garantir que os pacotes recém-instalados sejam carregados corretamente.

Python
%pip install --upgrade peft==0.17.1
%pip install --upgrade hf_transfer==0.1.9
%pip install --upgrade transformers==4.56.1
%pip install trl==0.18.1
%pip install liger-kernel
%restart_python

Biblioteca de importação

A próxima célula importa a biblioteca necessária para o treinamento do modelo, manipulação dataset e acompanhamento MLflow .

Python
from datasets import load_dataset
from transformers import AutoConfig, AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, TaskType, get_peft_model
from trl import (
SFTConfig,
SFTTrainer,
setup_chat_format
)
import json
import os
import mlflow

Configuração

IntegraçãoUnity Catalog

A próxima célula configura onde seu modelo ajustado será armazenado e registrado:

  • Catálogo e Esquema : Organize os modelos dentro do seu namespace Unity Catalog (default: main.default)
  • Nome do modelo : O nome do modelo registrado no Unity Catalog para fins de governança e implantação.
  • Volume : Volume Unity Catalog para armazenar pontos de verificação do modelo durante o treinamento.

Esses widgets permitem personalizar o local de armazenamento sem editar o código. O modelo será registrado como {catalog}.{schema}.{model_name} para facilitar o acesso e o controle de versão.

Hiperparâmetros de treinamento

A célula também define parâmetros- key de treinamento:

  • Modelo e conjunto de dados : Qwen2-0.5B com datasetconversacionais Capybara
  • Tamanho dos lotes (8) : Número de exemplos por GPU por treinamento o passo
  • Acumulação de gradiente (4) : Acumula gradientes em 4 lotes para um tamanho efetivo de lote de 32
  • Taxa de Aprendizagem (1e-4) : Taxa conservadora, automaticamente escalada 10 vezes maior para treinamento LoRa.
  • Épocas (1) : Passagem única pelo dataset para evitar sobreajuste
  • Logging & Checkpointing : salva o progresso a cada 100 passos, logs estatísticas a cada 25 passos
Python
dbutils.widgets.text("uc_catalog", "main")
dbutils.widgets.text("uc_schema", "default")
dbutils.widgets.text("uc_model_name", "qwen2_liger_lora_assistant")
dbutils.widgets.text("uc_volume", "checkpoints")

UC_CATALOG = dbutils.widgets.get("uc_catalog")
UC_SCHEMA = dbutils.widgets.get("uc_schema")
UC_MODEL_NAME = dbutils.widgets.get("uc_model_name")
UC_VOLUME = dbutils.widgets.get("uc_volume")

print(f"UC_CATALOG: {UC_CATALOG}")
print(f"UC_SCHEMA: {UC_SCHEMA}")
print(f"UC_MODEL_NAME: {UC_MODEL_NAME}")
print(f"UC_VOLUME: {UC_VOLUME}")

# MLflow and Unity Catalog configuration

# Model selection - Choose based on your compute constraints
MODEL_NAME = "Qwen/Qwen2-0.5B"
DATASET_NAME = "trl-lib/Capybara"
OUTPUT_DIR = f"/Volumes/{UC_CATALOG}/{UC_SCHEMA}/{UC_VOLUME}/{UC_MODEL_NAME}"

# Training hyperparameters
BATCH_SIZE = 8
GRADIENT_ACCUMULATION_STEPS = 4
LEARNING_RATE = 1e-4
NUM_EPOCHS = 1
EVAL_STEPS = 100
LOGGING_STEPS = 25
SAVE_STEPS = 100

Configuração LoRa

A próxima célula configura os parâmetros LoRA (Adaptação de Baixa Classificação) que controlam como o modelo é ajustado. O LoRa congela os pesos do modelo base e ensina apenas pequenas matrizes adaptadoras, reduzindo drasticamente os requisitos de memória.

Seleção de parâmetros

  • Classificação (r=8) : Oferece um bom equilíbrio entre desempenho e parâmetros.
  • Alfa (32) : Fator de escala, tipicamente 2 a 4 vezes a classificação
  • Dropout (0.1) : Regularização para evitar sobreajuste

Módulos alvo para Qwen2

Este exemplo abrange todas as camadas de transformações key :

  • Atenção : q_proj, k_proj, v_proj, o_proj
  • MLP : gate_proj, up_proj, down_proj
Python
LORA_R = 8
LORA_ALPHA = 32
LORA_DROPOUT = 0.1
LORA_TARGET_MODULES = [
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"
]

Otimização do kernel de Liger

Otimização de memória e desempenho

Os kernels Liger proporcionam melhorias significativas de memória e desempenho através de:

  • Operações lineares fundidas : Combina camadas lineares com perdas.
  • Kernels otimizados : otimizações RMSNorm, RoPE, SwiGLU e CrossEntropy
  • Redução de memória : Economia de até 80% de memória em operações combinadas.
  • Foco em GPU única : Otimizado para treinamento com uma única GPU no A10.

Para obter mais detalhes, consulte o artigo "Efficient Triton Kernels".

Carregar e preparar dataset

A próxima célula carrega o dataset de treinamento e o prepara para o ajuste fino:

  • Conjunto de dados : trl-lib/Capybara - dados conversacionais de alta qualidade otimizados para seguir instruções.
  • divisão ensinar/validar : Cria uma divisão de 90/10 se não existir um conjunto de testes.
  • Validação de dados : Garante a formatação adequada para ajustes finos na conversação.
Python
dataset = load_dataset(DATASET_NAME)
print(f"✓ Dataset loaded: {dataset}")

if "test" not in dataset:
print("Creating validation split from training data...")
dataset = dataset["train"].train_test_split(test_size=0.1, seed=42)
print("✓ Data split: 90% train, 10% validation")


Inicialize o modelo e o tokenizador.

A próxima célula carrega o modelo base e o tokenizador e, em seguida, os configura para o ajuste fino da conversação:

  • Carregamento do modelo : baixa o Qwen2-0.5B da Hugging Face
  • Configuração do tokenizador : Configura o tokenizador rápido com o preenchimento adequado.
  • Formatação de bate-papo : Aplica o formato ChatML para conversas estruturadas.
  • Configuração de tokens : Define os tokens de preenchimento para tokens EOS para o correto processamento da sequência.
Python
model = AutoModelForCausalLM.from_pretrained(
MODEL_NAME,
trust_remote_code=True,
)

tokenizer = AutoTokenizer.from_pretrained(
MODEL_NAME,
trust_remote_code=True,
use_fast=True
)

# Chat template formatting for conversational fine-tuning
if tokenizer.chat_template is None:
print("Adding chat template for proper conversation formatting...")
model, tokenizer = setup_chat_format(model, tokenizer, format="chatml")
print("✓ ChatML format applied for structured conversations")

if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
print("✓ Padding token set to EOS token")

print("✓ Model and tokenizer loaded successfully")


Aplique adaptadores LoRa

A próxima célula aplica LoRA (Adaptação de Baixa Classificação) ao modelo:

  • Configuração : Configura o LoRa com rank=8, alpha=32, dropout=0.1
  • Módulos de destino : Aplica adaptadores às camadas de atenção e MLP.
  • Eficiência de parâmetros : Reduz os parâmetros treináveis para aproximadamente 1% do modelo original.
  • Economia de memória : Reduz drasticamente os requisitos de memória do gradiente.
  • Recurso alternativo : Retorna automaticamente ao ajuste fino completo caso a configuração do LoRa falhe.
Python
peft_config = None
try:
print("Configuring LoRA for parameter-efficient fine-tuning...")

peft_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
inference_mode=False,
r=LORA_R,
lora_alpha=LORA_ALPHA,
lora_dropout=LORA_DROPOUT,
target_modules=LORA_TARGET_MODULES,
bias="none",
use_rslora=False,
modules_to_save=None,
)

print(f"LoRA configuration: rank={LORA_R}, alpha={LORA_ALPHA}, dropout={LORA_DROPOUT}")
print(f"Target modules: {', '.join(LORA_TARGET_MODULES)}")

original_params = model.num_parameters()
model = get_peft_model(model, peft_config)

trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
total_params = sum(p.numel() for p in model.parameters())
efficiency_ratio = 100 * trainable_params / total_params

print(f"✓ LoRA applied successfully:")
print(f" • Original parameters: {original_params:,}")
print(f" • Trainable parameters: {trainable_params:,}")
print(f" • Training efficiency: {efficiency_ratio:.2f}% of parameters")
print(f" • Memory savings: ~{100-efficiency_ratio:.1f}% reduction in gradient memory")

except Exception as e:
print(f"✗ LoRA configuration failed: {e}")
print("Falling back to full fine-tuning...")
USE_LORA = False
peft_config = None

ensinar o modelo

A próxima célula configura e executa o processo de treinamento:

Configuração de treinamento

  • Ajuste da taxa de aprendizagem : Aumenta a taxa de aprendizagem em 10 vezes para LoRa (conforme recomendado no artigo sobre LoRa).
  • Configuração dos lotes : 8 amostras por dispositivo com 4 etapas de acumulação de gradiente (tamanho efetivo dos lotes: 32)
  • Otimização : Aquecimento dos passos, decaimento de peso e seleção do melhor modelo com base na perda de avaliação.
  • Logging : Reporta métricas ao MLflow para acompanhamento do experimento

principais otimizações ativadas

  • Kernels Liger : Operações de GPU integradas reduzem o uso de memória em até 80%.
  • Precisão mista (FP16) : Computação mais rápida com menor consumo de memória.
  • Acumulação de gradiente : Simula tamanhos de lotes maiores para treinamento estável.
  • Pontos de verificação : Salva o modelo a cada 100 passos, com limite de 2 pontos de verificação.

O loop de treinamento log o progresso a cada 25 passos e avaliará a cada 100 passos.

Python
with mlflow.start_run(run_name=f"{MODEL_NAME}_fine-tuning", log_system_metrics=True):
try:
# Learning rate adjustment for LoRA
adjusted_lr = LEARNING_RATE * 10
print(f"Learning rate: {adjusted_lr}")

training_args_dict = {
"output_dir": OUTPUT_DIR,
"per_device_train_batch_size": BATCH_SIZE,
"per_device_eval_batch_size": BATCH_SIZE,
"gradient_accumulation_steps": GRADIENT_ACCUMULATION_STEPS,
"learning_rate": adjusted_lr,
"num_train_epochs": NUM_EPOCHS,
"eval_steps": EVAL_STEPS,
"logging_steps": LOGGING_STEPS,
"save_steps": SAVE_STEPS,
"save_total_limit": 2,
"report_to": "mlflow", # Log to MLflow
"warmup_steps": 50,
"weight_decay": 0.01,
"metric_for_best_model": "eval_loss",
"greater_is_better": False,
"dataloader_pin_memory": False,
"remove_unused_columns": False,
"use_liger_kernel": True, # Enable Liger kernel optimizations
"fp16": True, # Mixed precision training
}

print("✓ Liger kernel optimizations enabled")

training_args = SFTConfig(**training_args_dict)

trainer = SFTTrainer(
model=model,
args=training_args,
train_dataset=dataset["train"],
eval_dataset=dataset["test"],
processing_class=tokenizer,
peft_config=peft_config,
)

print("\n" + "="*50)
print("STARTING TRAINING")
print("="*50)

print("🚀 Training with Liger kernels for memory-efficient single GPU training")
print("🎯 Using LoRA for parameter-efficient fine-tuning")

trainer.train()
print("\n✓ Training completed successfully!")

except Exception as e:
print(f"✗ Training failed: {e}")
raise

Salvar artefatos do modelo

A próxima célula salva o modelo treinado e o tokenizador no volume Unity Catalog :

  • Adaptadores LoRa : Salva apenas os pesos dos adaptadores treinados (muito menores que o modelo completo).
  • Tokenizador : Salva a configuração do tokenizador para inferência.
  • Local de armazenamento : Salva em /Volumes/{catalog}/{schema}/{volume}/{model_name}
  • Reutilização : Os adaptadores podem ser carregados com o modelo base para inferência.
Python
try:
print("\nSaving trained model...")

print("Saving LoRA adapter weights...")
trainer.save_model(training_args.output_dir)
print("✓ LoRA adapters saved - use with base model for inference")

tokenizer.save_pretrained(training_args.output_dir)
print("✓ Tokenizer saved with model")
print(f"\n🎉 All artifacts saved to: {training_args.output_dir}")

except Exception as e:
print(f"✗ Model saving failed: {e}")
raise

registrar modelo no Unity Catalog

A próxima célula registra o modelo otimizado no Unity Catalog para governança e implantação:

Fluxo de trabalho de registro de modelo

  1. Carregar modelo treinado : Carrega o modelo base e aplica os adaptadores LoRa.
  2. Adaptadores de mesclagem : Combina os pesos do LoRa com o modelo base para implantação.
  3. Preparar para registro : Cria um dicionário de modelos de transformadores com o modelo e o tokenizador.
  4. Registro no Unity Catalog : envia logs para MLflow e registra no Unity Catalog
  5. Adicionar metadados : Inclui tipo de tarefa, família de modelo e informações de tamanho

Benefícios do registro no Unity Catalog

  • Governança : Registro de modelo centralizado com controle de acesso e acompanhamento de linhagem
  • Versionamento : Gerenciamento automático de versões para o ciclo de vida do modelo
  • Implantação : Fácil implantação no endpoint do modelo de instalação
  • Facilidade de descoberta : os modelos podem ser pesquisados e estão documentados no Unity Catalog
Python
mlflow_run_id = mlflow.last_active_run().info.run_id
print("\nRegistering model with MLflow and Unity Catalog...")

with mlflow.start_run(run_id = mlflow_run_id):
try:
# Load the trained model for registration
print("Loading LoRA model for registration...")
# For LoRA models, we need both base model and adapter
base_model = AutoModelForCausalLM.from_pretrained(
MODEL_NAME,
trust_remote_code=True
)
# Load tokenizer
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)

from peft import PeftModel
peft_model = PeftModel.from_pretrained(base_model, training_args.output_dir)
model_type = "LoRA"
size_params = "0.5b_lora"

# Merge LoRA into base and drop PEFT wrappers
merged_model = peft_model.merge_and_unload()


# Prepare transformers model dictionary
transformers_model = {
"model": merged_model,
"tokenizer": tokenizer
}

# Create Unity Catalog model name
full_model_name = f"{UC_CATALOG}.{UC_SCHEMA}.{UC_MODEL_NAME}"

print(f"Registering model as: {full_model_name}")

# Start MLflow run and log model
task = "llm/v1/chat"
model_info = mlflow.transformers.log_model(
transformers_model=transformers_model,
task=task,
registered_model_name=full_model_name,
metadata={
"task": task,
"pretrained_model_name": MODEL_NAME,
"databricks_model_family": "QwenForCausalLM",
"databricks_model_size_parameters": size_params,
},
repo_type="local", # Fix: specify repo_type for local path
)

print(f"✓ Model successfully registered in Unity Catalog: {full_model_name}")
print(f"✓ MLflow model URI: {model_info.model_uri}")

# Print deployment information
print(f"\n📦 Model Registration Complete!")
print(f"Unity Catalog Path: {full_model_name}")
print(f"Model Type: {model_type}")
print(f"Optimization: Liger Kernels + LoRA")

except Exception as e:
print(f"✗ Model registration failed: {e}")
print("Model is still saved locally and can be registered manually")
print(f"Local model path: {training_args.output_dir}")

Próximos passos

Seu modelo Qwen2-0.5B foi ajustado com sucesso usando LoRA e registrado no Unity Catalog. Em seguida, você pode:

Exemplo de caderno

Ajuste fino LoRA do Qwen2-0.5B

Abrir notebook em uma nova aba