Connect with us

La única guía que necesitas para afinar Llama 3 o cualquier otro modelo de código abierto

Inteligencia artificial

La única guía que necesitas para afinar Llama 3 o cualquier otro modelo de código abierto

mm
FINE TUNING OPEN SOURCE LLM PYTHON GUIDE

Afinar grandes modelos de lenguaje (LLM) como Llama 3 implica adaptar un modelo preentrenado a tareas específicas utilizando un conjunto de datos específico del dominio. Este proceso aprovecha el conocimiento preexistente del modelo, lo que lo hace eficiente y rentable en comparación con el entrenamiento desde cero. En esta guía, recorreremos los pasos para afinar Llama 3 utilizando QLoRA (Quantized LoRA), un método eficiente en parámetros que minimiza el uso de memoria y los costos computacionales.

Visión general del afinado

El afinado implica varios pasos clave:

  1. Selección de un modelo preentrenado: Elija un modelo base que se alinee con la arquitectura deseada.
  2. Recopilación de un conjunto de datos relevante: Recopile y preprocese un conjunto de datos específico de la tarea.
  3. Afinado: Ajuste el modelo utilizando el conjunto de datos para mejorar su rendimiento en tareas específicas.
  4. Evaluación: Evalúe el rendimiento del modelo afinado utilizando métricas cualitativas y cuantitativas.

Conceptos y técnicas

Afinar grandes modelos de lenguaje

Afinar grandes modelos de lenguaje

Afinado completo

Afinado completo actualiza todos los parámetros del modelo, lo que lo hace específico para la nueva tarea. Este método requiere recursos computacionales significativos y a menudo es impráctico para modelos muy grandes.

Afinado eficiente de parámetros (PEFT)

PEFT actualiza solo un subconjunto de los parámetros del modelo, lo que reduce los requisitos de memoria y costo computacional. Esta técnica evita el olvido catastrófico y mantiene el conocimiento general del modelo.

Adaptación de baja rango (LoRA) y Quantized LoRA (QLoRA)

LoRA afina solo unas pocas matrices de baja rango, mientras que QLoRA cuantiza estas matrices para reducir aún más la huella de memoria.

Métodos de afinado

  1. Afinado completo: Esto implica entrenar todos los parámetros del modelo en el conjunto de datos específico de la tarea. Si bien este método puede ser muy efectivo, también es computacionalmente costoso y requiere una gran cantidad de memoria.
  2. Afinado eficiente de parámetros (PEFT): PEFT actualiza solo un subconjunto de los parámetros del modelo, lo que lo hace más eficiente en términos de memoria. Técnicas como Low-Rank Adaptation (LoRA) y Quantized LoRA (QLoRA) caen en esta categoría.

¿Qué es LoRA?

Comparando métodos de afinado: QLORA mejora LoRA con cuantificación de 4 bits y optimizadores paginados para la gestión de picos de memoria

Comparando métodos de afinado: QLORA mejora LoRA con cuantificación de 4 bits y optimizadores paginados para la gestión de picos de memoria

LoRA es un método de afinado mejorado donde, en lugar de afinar todos los pesos del modelo preentrenado, se afinan dos matrices más pequeñas que aproximan la matriz más grande. Estas matrices constituyen el adaptador LoRA. Este adaptador afinado se carga en el modelo preentrenado y se utiliza para la inferencia.

Ventajas clave de LoRA:

  • Eficiencia de memoria: LoRA reduce la huella de memoria al afinar solo matrices pequeñas en lugar de todo el modelo.
  • Reutilización: El modelo original permanece sin cambios, y se pueden utilizar varios adaptadores LoRA con él, lo que facilita el manejo de múltiples tareas con requisitos de memoria más bajos.

¿Qué es Quantized LoRA (QLoRA)?

QLoRA lleva a LoRA un paso más allá al cuantizar los pesos de los adaptadores LoRA a una precisión más baja (por ejemplo, 4 bits en lugar de 8 bits). Esto reduce aún más el uso de memoria y los requisitos de almacenamiento, mientras mantiene un nivel comparable de eficacia.

Ventajas clave de QLoRA:

  • Mayor eficiencia de memoria: Al cuantizar los pesos, QLoRA reduce significativamente la huella de memoria y los requisitos de almacenamiento del modelo.
  • Mantiene el rendimiento: A pesar de la precisión reducida, QLoRA mantiene niveles de rendimiento cercanos a los de los modelos de precisión completa.

Adaptación específica de la tarea

Durante el afinado, los parámetros del modelo se ajustan en función del nuevo conjunto de datos, lo que ayuda al modelo a comprender y generar contenido relevante para la tarea específica. Este proceso mantiene el conocimiento general del lenguaje adquirido durante el preentrenamiento, mientras se adapta el modelo a las nuances del dominio objetivo.

Afinado en la práctica

Afinado completo vs. PEFT

  • Afinado completo: Implica entrenar todo el modelo, lo que puede ser computacionalmente costoso y requiere una gran cantidad de memoria.
  • PEFT (LoRA y QLoRA): Afinar solo un subconjunto de parámetros, lo que reduce los requisitos de memoria y evita el olvido catastrófico, lo que lo hace una alternativa más eficiente.

Pasos de implementación

  1. Configuración del entorno: Instale las bibliotecas necesarias y configure el entorno de computación.
  2. Carga y preprocesamiento del conjunto de datos: Cargue el conjunto de datos y preproceselo en un formato adecuado para el modelo.
  3. Carga del modelo preentrenado: Cargue el modelo base con configuraciones de cuantificación si se utiliza QLoRA.
  4. Tokenización: Tokenice el conjunto de datos para prepararlo para el entrenamiento.
  5. Entrenamiento: Afinar el modelo utilizando el conjunto de datos preparado.
  6. Evaluación: Evalúe el rendimiento del modelo en tareas específicas utilizando métricas cualitativas y cuantitativas.

Guía paso a paso para afinar LLM

Configuración del entorno

Utilizaremos un cuaderno Jupyter para este tutorial. Plataformas como Kaggle, que ofrecen uso gratuito de GPU, o Google Colab son ideales para ejecutar estos experimentos.

1. Instalar bibliotecas requeridas

Primero, asegúrese de tener las bibliotecas necesarias instaladas:

!pip install -qqq -U bitsandbytes transformers peft accelerate datasets scipy einops evaluate trl rouge_score

2. Importar bibliotecas y configurar el entorno


import os
import torch
from datasets import load_dataset
from transformers import (
AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig, TrainingArguments,
pipeline, HfArgumentParser
)
from trl import ORPOConfig, ORPOTrainer, setup_chat_format, SFTTrainer
from tqdm import tqdm
import gc
import pandas as pd
import numpy as np
from huggingface_hub import interpreter_login

# Deshabilitar registro de Weights and Biases
os.environ['WANDB_DISABLED'] = "true"
interpreter_login()

3. Cargar el conjunto de datos

Utilizaremos el conjunto de datos DialogSum para este tutorial:

Preprocese el conjunto de datos según los requisitos del modelo, incluyendo la aplicación de plantillas adecuadas y asegurándose de que el formato de los datos sea adecuado para el afinado​ (Hugging Face)​​ (DataCamp)​.


dataset_name = "neil-code/dialogsum-test"
dataset = load_dataset(dataset_name)

Inspeccione la estructura del conjunto de datos:


print(dataset['test'][0])

4. Crear configuración de BitsAndBytes

Para cargar el modelo en formato de 4 bits:


compute_dtype = getattr(torch, "float16")
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type='nf4',
bnb_4bit_compute_dtype=compute_dtype,
bnb_4bit_use_double_quant=False,
)

5. Cargar el modelo preentrenado

Utilizando el modelo Phi-2 de Microsoft para este tutorial:


model_name = 'microsoft/phi-2'
device_map = {"": 0}
original_model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map=device_map,
quantization_config=bnb_config,
trust_remote_code=True,
use_auth_token=True
)

6. Tokenización

Configure el tokenizador:


tokenizer = AutoTokenizer.from_pretrained(
model_name,
trust_remote_code=True,
padding_side="left",
add_eos_token=True,
add_bos_token=True,
use_fast=False
)
tokenizer.pad_token = tokenizer.eos_token

Afinar Llama 3 o otros modelos

Al afinar modelos como Llama 3 o cualquier otro modelo de código abierto de última generación, hay consideraciones y ajustes específicos que se requieren para garantizar un rendimiento óptimo. A continuación, se presentan los pasos detallados y las perspectivas sobre cómo abordar esto para diferentes modelos, incluyendo Llama 3, GPT-3 y Mistral.

5.1 Utilizando Llama 3

Selección del modelo:

  • Asegúrese de tener el identificador de modelo correcto desde el hub de modelos de Hugging Face. Por ejemplo, el modelo Llama 3 podría estar identificado como meta-llama/Meta-Llama-3-8B en Hugging Face.
  • Asegúrese de solicitar acceso e iniciar sesión en su cuenta de Hugging Face si es necesario para modelos como Llama 3​ (Hugging Face)​​

Tokenización:

  • Utilice el tokenizador adecuado para Llama 3, asegurándose de que sea compatible con el modelo y admita las características necesarias como relleno y tokens especiales.

Memoria y cálculo:

  • Afinar modelos grandes como Llama 3 requiere recursos computacionales significativos. Asegúrese de que su entorno, como una configuración de GPU potente, pueda manejar los requisitos de memoria y procesamiento. Asegúrese de que el entorno pueda manejar los requisitos de memoria, que pueden mitigarse utilizando técnicas como QLoRA para reducir la huella de memoria​ (Hugging Face Forums)

Ejemplo:

model_name = 'meta-llama/Meta-Llama-3-8B'
device_map = {"": 0}
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True,
)
original_model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map=device_map,
quantization_config=bnb_config,
trust_remote_code=True,
use_auth_token=True
)

Tokenización:

Dependiendo del caso de uso específico y los requisitos del modelo, asegúrese de que la configuración del tokenizador sea correcta y no tenga configuraciones redundantes. Por ejemplo, use_fast=True se recomienda para un mejor rendimiento​ (Hugging Face)​​ (GitHub)​.

tokenizer = AutoTokenizer.from_pretrained(
model_name,
trust_remote_code=True,
padding_side="left",
add_eos_token=True,
add_bos_token=True,
use_fast=False
)
tokenizer.pad_token = tokenizer.eos_token

5.2 Utilizando otros modelos populares (por ejemplo, GPT-3, Mistral)

Selección del modelo:

  • Para modelos como GPT-3 y Mistral, asegúrese de utilizar el nombre y el identificador de modelo correctos desde el hub de modelos de Hugging Face o otras fuentes.

Tokenización:

  • De manera similar a Llama 3, asegúrese de que el tokenizador esté configurado correctamente y sea compatible con el modelo.

Memoria y cálculo:

  • Cada modelo puede tener diferentes requisitos de memoria. Ajuste su configuración de entorno según sea necesario.

Ejemplo para GPT-3:

model_name = 'openai/gpt-3'
device_map = {"": 0}
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True,
)
original_model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map=device_map,
quantization_config=bnb_config,
trust_remote_code=True,
use_auth_token=True
)

Ejemplo para Mistral:

model_name = 'mistral-7B'
device_map = {"": 0}
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True,
)
original_model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map=device_map,
quantization_config=bnb_config,
trust_remote_code=True,
use_auth_token=True
)

Consideraciones de tokenización: Cada modelo puede tener requisitos de tokenización únicos. Asegúrese de que el tokenizador coincida con el modelo y esté configurado correctamente.

Ejemplo de tokenizador de Llama 3:

tokenizer = AutoTokenizer.from_pretrained(
model_name,
trust_remote_code=True,
padding_side="left",
add_eos_token=True,
add_bos_token=True,
use_fast=False
)
tokenizer.pad_token = tokenizer.eos_token

Ejemplo de tokenizador de GPT-3 y Mistral:

tokenizer = AutoTokenizer.from_pretrained(
model_name,
use_fast=True
)

7. Probar el modelo con inferencia de disparo cero

Evalúe el modelo base con una entrada de muestra:

from transformers import set_seed

set_seed(42)
index = 10
prompt = dataset['test'][index]['dialogue']
formatted_prompt = f"Instruct: Resumir la siguiente conversación.\n{prompt}\nOutput:\n"

# Generar salida
def gen(model, prompt, max_length):
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
outputs = model.generate(**inputs, max_length=max_length)
return tokenizer.batch_decode(outputs, skip_special_tokens=True)

res = gen(original_model, formatted_prompt, 100)
output = res[0].split('Output:\n')[1]

print(f'ENTRADA DE PROMPT:\n{formatted_prompt}')
print(f'GENERACIÓN DEL MODELO - DISPARO CERO:\n{output}')

8. Preprocesar el conjunto de datos

Convierta pares de diálogo-resumen en prompts:


def create_prompt_formats(sample):
blurb = "A continuación se describe una tarea. Escriba una respuesta que complete adecuadamente la solicitud."
instruction = "### Instruct: Resumir la conversación a continuación."
input_context = sample['dialogue']
response = f"### Output:\n{sample['summary']}"
end = "### End"

parts = [blurb, instruction, input_context, response, end]
formatted_prompt = "\n\n".join(parts)
sample["text"] = formatted_prompt
return sample

dataset = dataset.map(create_prompt_formats)

Tokenice el conjunto de datos formateado:


def preprocess_batch(batch, tokenizer, max_length):
return tokenizer(batch["text"], max_length=max_length, truncation=True)

max_length = 1024
train_dataset = dataset["train"].map(lambda batch: preprocess_batch(batch, tokenizer, max_length), batched=True)
eval_dataset = dataset["validation"].map(lambda batch: preprocess_batch(batch, tokenizer, max_length), batched=True)

9. Preparar el modelo para QLoRA

Prepare el modelo para el afinado eficiente de parámetros:


original_model = prepare_model_for_kbit_training(original_model)

Hiperparámetros y su impacto

Los hiperparámetros juegan un papel crucial en la optimización del rendimiento del modelo. A continuación, se presentan algunos hiperparámetros clave que se deben considerar:

  1. Tasa de aprendizaje: Controla la velocidad a la que el modelo actualiza sus parámetros. Una tasa de aprendizaje alta puede llevar a una convergencia más rápida, pero puede pasar por alto la solución óptima. Una tasa de aprendizaje baja garantiza una convergencia estable, pero puede requerir más épocas.
  2. Tamaño de lote: El número de muestras procesadas antes de que el modelo actualice sus parámetros. Los tamaños de lote más grandes pueden mejorar la estabilidad, pero requieren más memoria. Los tamaños de lote más pequeños pueden llevar a más ruido en el proceso de entrenamiento.
  3. Pasos de acumulación de gradientes: Este parámetro ayuda a simular tamaños de lote más grandes al acumular gradientes durante varios pasos antes de realizar una actualización de parámetros.
  4. Número de épocas: El número de veces que se pasa todo el conjunto de datos a través del modelo. Más épocas pueden mejorar el rendimiento, pero pueden llevar a sobreajuste si no se manejan adecuadamente.
  5. Decaimiento de peso: Técnica de regularización para prevenir el sobreajuste al penalizar los pesos grandes.
  6. Programador de tasa de aprendizaje: Ajusta la tasa de aprendizaje durante el entrenamiento para mejorar el rendimiento y la convergencia.

Personalice la configuración de entrenamiento ajustando hiperparámetros como la tasa de aprendizaje, el tamaño de lote y los pasos de acumulación de gradientes según los requisitos específicos del modelo y la tarea. Por ejemplo, los modelos Llama 3 pueden requerir diferentes tasas de aprendizaje en comparación con modelos más pequeños​ (Weights & Biases)​​ (GitHub)

Ejemplo de configuración de entrenamiento

orpo_args = ORPOConfig(
learning_rate=8e-6,
lr_scheduler_type="linear",max_length=1024,max_prompt_length=512,
beta=0.1,per_device_train_batch_size=2,per_device_eval_batch_size=2,
gradient_accumulation_steps=4,optim="paged_adamw_8bit",num_train_epochs=1,
evaluation_strategy="steps",eval_steps=0.2,logging_steps=1,warmup_steps=10,
report_to="wandb",output_dir="./results/",)

10. Entrenar el modelo

Configure el entrenador y comience a entrenar:

trainer = ORPOTrainer(
model=original_model,
args=orpo_args,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
tokenizer=tokenizer,
)
trainer.train()
trainer.save_model("fine-tuned-llama-3")

Evaluación del modelo afinado

Después del entrenamiento, evalúe el rendimiento del modelo utilizando métodos cualitativos y cuantitativos.

1. Evaluación humana

Compare las resúmenes generados con los escritos por humanos para evaluar la calidad.

2. Evaluación cuantitativa

Utilice métricas como ROUGE para evaluar el rendimiento:


from rouge_score import rouge_scorer

scorer = rouge_scorer.RougeScorer(['rouge1', 'rouge2', 'rougeL'], use_stemmer=True)
scores = scorer.score(reference_summary, generated_summary)
print(scores)

Desafíos comunes y soluciones

1. Limitaciones de memoria

Utilizar QLoRA ayuda a mitigar los problemas de memoria al cuantizar los pesos del modelo a 4 bits. Asegúrese de tener suficiente memoria de GPU para manejar el tamaño de lote y el tamaño del modelo.

2. Sobreajuste

Monitoree las métricas de validación para prevenir el sobreajuste. Utilice técnicas como detención temprana y decaimiento de peso.

3. Entrenamiento lento

Optimice la velocidad de entrenamiento ajustando el tamaño de lote, la tasa de aprendizaje y utilizando la acumulación de gradientes.

4. Calidad de los datos

Asegúrese de que su conjunto de datos esté limpio y bien preprocesado. La mala calidad de los datos puede afectar significativamente el rendimiento del modelo.

Conclusión

Afinar LLM utilizando QLoRA es una forma eficiente de adaptar modelos preentrenados grandes a tareas específicas con costos computacionales reducidos. Siguiendo esta guía, puede afinar PHI, Llama 3 o cualquier otro modelo de código abierto para lograr un rendimiento alto en sus tareas específicas.

He pasado los últimos cinco años sumergiéndome en el fascinante mundo del Aprendizaje Automático y el Aprendizaje Profundo. Mi pasión y experiencia me han llevado a contribuir a más de 50 proyectos de ingeniería de software diversos, con un enfoque particular en AI/ML. Mi curiosidad continua también me ha llevado hacia el Procesamiento de Lenguaje Natural, un campo que estoy ansioso por explorar más a fondo.