Ajuste de LoRA con Granite LLM

Ajuste de Granite con LoRA

La Adaptación de bajo rango (LoRA) es un método eficiente de ajuste que reduce la cantidad de parámetros entrenables que aumentan la velocidad y el uso de recursos del entrenamiento mientras retienen la misma calidad de salida. En lugar de actualizar todos los parámetros en una red neuronal durante el ajuste, LoRA congela los pesos originales previamente entrenados y agrega matrices pequeñas y entrenables de bajo rango que se aproximan a los cambios necesarios para la nueva tarea. Este enfoque se basa en la hipótesis de que las actualizaciones de peso durante la adaptación tienen un "rango intrínseco" bajo.

Un beneficio adicional de LoRA es que, debido a que los pesos previamente entrenados se mantienen congelados, el adaptador generado es liviano y portátil y se puede almacenar fácilmente.

En este tutorial, utilizará LLaMa Factory. LLaMa Factory es una plataforma de entrenamiento y ajuste bajo y sin código de un modelo de lenguaje grande (LLM) que permite a los usuarios ajustar los LLM en conjuntos de datos personalizados, evaluar el rendimiento y servir modelos. Tiene una interfaz de usuario (IU) web y una CLI que son fáciles de usar y admiten más de 100 LLM. La plataforma admite conjuntos de datos en formatos Alpaca y ShareGPT. LLaMa Factory no es la única forma de ajustar los LLM; la biblioteca PEFT para el refinamiento eficiente de los parámetros es otra opción para actualizar modelos grandes. PEFT proporciona la capacidad de realizar quantized LoRA (QLoRA) para compactar aún más el modelo ajustado. En este tutorial, utilizará una versión no cuantificada de Granite 3.3.

Aunque LLaMa Factory puede ejecutarse sin el uso de amplios recursos informáticos, requiere una GPU y recursos de memoria significativos. En este tutorial, utilizará LLaMa Factory en watsonx para proporcionar recursos de GPU y almacenamiento para el adaptador generado.

Configuración

Configuración de Watson Studio

a. Inicie sesión en watsonx.ai mediante su cuenta de IBM® Cloud.

b. Cree un proyecto watsonx.ai. Tome nota del ID de su proyecto en proyecto > Manage > General > Project ID.  
Necesitará este ID para este tutorial.

c. Cree una instancia de servicio de watsonx.ai Runtime. Para este tutorial, deberá crear una instancia de pago para acceder a una GPU.

d. Genere una interfaz de programación de aplicaciones (clave de API).

e. Asocie el servicio watsonx.ai Runtime al proyecto que creó en watsonx.ai.

Cloud Object Storage

a. Para crear Cloud Object Storage para su notebook, vaya a https://cloud.ibm.com/ y luego seleccione “Create Instance”.

b. Eso lo llevará a un cuadro de diálogo de creación donde puede seleccionar un plan de precios. Para este tutorial, un plan estándar será adecuado.

c. Luego, asigne un nombre a su instancia de Cloud Object Storage.

d. Una vez que haya creado su instancia, vuelva al proyecto y seleccione “New Asset” y luego “Connect to a data source”.

Imagen que muestra la conexión de datos de watsonx para Cloud Object Storage Configuración de la conexión de datos para Cloud Object Storage

e. Seleccione "Cloud Object Storage" f. En el siguiente cuadro de diálogo, seleccione la instancia que creó en los pasos ad por nombre.

g. Seleccione "Create".

Cree un Jupyter Notebook

Cree un Jupyter Notebook.

a. Seleccione la pestaña Assets en el entorno de su proyecto.

b. Haga clic en New asset.

c. Seleccione la opción Working with models en el panel izquierdo.

d. Haga clic en Working with data and models by using Python and R notebooks.

e. Ingrese un nombre para su computadora portátil en el campo Name. Elija Runtime 23.1 en Python (4 vCPU 16 GB RAM) para definir la configuración.

f. Seleccione Create.

Configuración

A continuación, instalará dependencias en el tiempo de ejecución. Primero, llama-factory para generar los adaptadores de rango bajo y, luego, Pandas para formatear el conjunto de datos en formato Alpaca.

!pip install -q llamafactory 2>/dev/null
# pandas needed to format the dataset
!pip install -q --upgrade pandas 2>/dev/null

Verifique el entorno de GPU

A continuación, se asegurará de que su entorno de watsonx haya proporcionado una GPU compatible con Torch que será necesaria para usar LLaMa-Factory.

import torch

try:
  assert torch.cuda.is_available() is True
except AssertionError:
  print("No GPU found, please set up a GPU before using LLaMA Factory.")

Si el fragmento de código anterior no muestra "No se encontró GPU", entonces todo bien.

A continuación, importará bibliotecas para manipular datos y crear el archivo de configuración de LLaMa Factory utilizado para el entrenamiento.

# Import libraries
import pandas as pd
import json
import yaml

Descargue y procese el conjunto de datos de MedReason En este tutorial, utilizará una parte del conjunto de datos MedReason . MedReason es un conjunto de datos de razonamiento médico a gran escala y de alta calidad diseñado para ayudar a permitir la resolución de problemas médicos explicables en los LLM. Si bien MedReason se centra en el razonamiento de un modelo y en la validación de las cadenas de pensamiento que utiliza un modelo, en este caso también es útil proporcionar un conjunto de datos que sea demasiado reciente para incluirse en los datos de entrenamiento de IBM® Granite 3.3.

Granite 3.3 ha sido diseñado para aprender a través de ajustes, los cuales se ejecutarán con LLaMa Factory. Los modelos Granite se pueden ajustar de manera eficiente incluso con recursos informáticos limitados.

Cargará una selección del conjunto de datos de MedReason desde GitHub:

from datasets import load_dataset

training = pd.read_json("https://raw.githubusercontent.com/UCSC-VLAA/MedReason/refs/heads/main/eval_data/medbullets_op4.jsonl", lines=True)

LLaMa Factory requiere que el conjunto de datos esté preformateado en formatos Alpaca o ShareGPT. Por ello, reformateamos los campos de pregunta y respuesta del conjunto de datos legal original para contener los campos de instrucción, entrada y resultados según el formato Alpaca.

Alpaca es un formato JSON para representar una instrucción, entrada de usuario y salida del sistema, así:

{
    "instruction": "user instruction (required)",
    "input": "user input (optional)",
    "output": "model response (required)",
    "system": "system prompt (optional)",
}

Debido a que MedReason no está formateado en Alpaca, creará un conjunto de datos de Alpaca en la siguiente celda:

!mkdir -p data

# Format Med Dataset to Alpaca Format
formatted_data = [
    {
        "instruction": row["question"] + str(row["options"]),
        "input": "",
        "output": row["answer"]
    }
    for _, row in training.iterrows()
]

# output formatted MedReason dataset
with open("data/med.json", "w", encoding="utf-8") as f:
  json.dump(formatted_data, f, indent=2, ensure_ascii=False)

Llama Factory utiliza un archivo específico para comprender cómo cargar conjuntos de datos para el entrenamiento. Este archivo debe existir en la ruta data/dataset_info.json. Por lo tanto, debemos crear un dataset_info.json que incluya la ruta hacia el nuevo conjunto de datos médico formateado que creamos para la CLI de Llama Factory para acceder al conjunto de datos. Para obtener detalles sobre dataset_info.json xonsulte la documentación. Dentro del repositorio de Llama Factory hay conjuntos de datos disponibles para usar, sin embargo, debido a que estamos usando nuestro propio conjunto de datos personalizado, debemos agregar nuestro conjunto de datos al archivo JSON.

# "med" will be the identifier for the dataset 
# which points to the local file that contains the dataset
dataset_info = {
  "med": {
    "file_name": "med.json",
  }
}

# Create dataset_info.json with legal dataset so can reference with llama factory
with open("data/dataset_info.json", "w", encoding="utf-8") as f:
  json.dump(dataset_info, f, indent=2, ensure_ascii=False)

Ahora que el objeto JSON con formato Alpaca se ha guardado en el entorno, usted está listo para comenzar a entrenar.

Refinamiento

El siguiente paso es establecer las configuraciones de entrenamiento y luego escribir las configuraciones en un archivo YAML que LLaMa-Factory utiliza para ejecutar el entrenamiento.

Ahora ejecutará un ajuste supervisado (SFT) en el subconjunto del conjunto de datos de MedReason. LLaMa Factory admite varios tipos diferentes de capacitación. Algunos de los más utilizados son:

  • Preentrenamiento: cuando un modelo se somete a un entrenamiento inicial mediante el uso de un amplio conjunto de datos para generar respuestas al lenguaje y las ideas fundamentales.

  • Ajuste supervisado (SFT): cuando un modelo recibe entrenamiento adicional con datos anotados para mejorar la precisión de una función en particular o sobre un tema específico.

  • Modelado de recompensas: donde el modelo adquiere conocimientos sobre cómo lograr un incentivo o recompensa específica que informará su optimización de políticas próximas a la salida (PPO).

  • Entrenamiento: una técnica de aprendizaje por refuerzo (RL) en la que el modelo se perfecciona aún más mediante técnicas de gradiente de políticas para aumentar su eficacia en un entorno específico.

Se utilizan muchos ajustes para configurar LoRA, pero algunos de los más importantes y de uso común son: - Tasa de aprendizaje (LR): La tasa de aprendizaje determina la importancia de la actualización de cada parámetro del modelo durante cada iteración del entrenamiento. Un LR más alto puede acelerar la convergencia al permitir actualizaciones más grandes, pero corre el riesgo de sobrepasar la solución óptima u oscilar en torno a ella. Un LR más bajo conduce a una convergencia más lenta pero más estable, lo que reduce el riesgo de inestabilidad cerca de la solución óptima.

  • loraplus_lr_ratio: este paso establece la proporción de las tasas de aprendizaje. En general, debe ser > 1, pero la elección óptima de loraplus_lr_ratio depende del modelo y de la tarea. Como pauta, loraplus_lr_ratio debe ser mayor cuando la tarea es más difícil y el modelo necesita actualizar sus características para aprender bien. En este caso, ayuda a que la tasa de aprendizaje sea ligeramente menor (por ejemplo, por un factor de 2) que las tasas de aprendizaje típicas de LoRA.

  • Tamaño de lote efectivo: Configurar correctamente el tamaño de su lote es crítico para equilibrar la estabilidad del entrenamiento con las limitaciones de VRAM de la GPU que está utilizando. El tamaño efectivo del lote se establece mediante el producto de per_device_train_batch_size * gradient_accumulation_steps. Un tamaño de lote efectivo mayor generalmente conduce a un entrenamiento más fluido y estable, pero también puede requerir más VRAM de la que contiene su GPU. Un tamaño de lote efectivo más pequeño podría introducir más variación.

Este es el código que configura el entrenamiento:

# setup training configurations
args = dict(
  stage="sft",  # do supervised fine-tuning
  do_train=True,  # we're actually training
  model_name_or_path="ibm-granite/granite-3.3-2b-instruct",  # use IBM Granite 3.3 2b instruct model
  dataset="med",  # use medical datasets we created
  template="granite3",   # use granite3 prompt template
  finetuning_type="lora", # use LoRA adapters to save memory
  lora_target="all",  # attach LoRA adapters to all linear layers
  loraplus_lr_ratio=16.0,  # use LoRA+ algorithm with lambda=16.0
  output_dir="granite3_lora",  # the path to save LoRA adapters
  per_device_train_batch_size=4,  # the batch size
  gradient_accumulation_steps=2,  # the gradient accumulation steps
  learning_rate=1e-4,  # the learning rate
  num_train_epochs=3.0, # the epochs of training
  max_samples=500,  # use 500 examples in each dataset
  fp16=True,  # use float16 mixed precision training
  report_to="none", # disable wandb logging
)

# create training config file to run with llama factory
with open("train_granite3_lora_med.yaml", "w", encoding="utf-8") as file:
  yaml.dump(args, file, indent=2)

La siguiente celda entrenará al modelo y puede tardar hasta 10 minutos en ejecutarse:

!llamafactory-cli train train_granite3_lora_med.yaml;

Uso de Cloud Object Storage

A continuación, creará dos métodos para cargar y descargar datos de IBM Cloud Object Storage:

from ibm_botocore.client import Config
import ibm_boto3

def upload_file_cos(credentials, local_file_name, key):  
    cos = ibm_boto3.client(service_name='s3',
    ibm_api_key_id=credentials['IBM_API_KEY_ID'],
    ibm_service_instance_id=credentials['IAM_SERVICE_ID'],
    ibm_auth_endpoint=credentials['IBM_AUTH_ENDPOINT'],
    config=Config(signature_version='oauth'),
    endpoint_url=credentials['ENDPOINT'])
    try:
        res=cos.upload_file(Filename=local_file_name, Bucket=credentials['BUCKET'],Key=key)
    except Exception as e:
        print(Exception, e)
    else:
        print(' File Uploaded')


def download_file_cos(credentials,local_file_name,key):  
    cos = ibm_boto3.client(service_name='s3',
    ibm_api_key_id=credentials['IBM_API_KEY_ID'],
    ibm_service_instance_id=credentials['IAM_SERVICE_ID'],
    ibm_auth_endpoint=credentials['IBM_AUTH_ENDPOINT'],
    config=Config(signature_version='oauth'),
    endpoint_url=credentials['ENDPOINT'])
    try:
        res=cos.download_file(Bucket=credentials['BUCKET'],Key=key,Filename=local_file_name)
    except Exception as e:
        print(Exception, e)
    else:
        print('File Downloaded')

La siguiente celda contiene las credenciales de Cloud Object Storage.

En su cuaderno, haga clic en la pestaña Fragmentos de código en la esquina derecha. Este paso abre un menú con varias opciones para fragmentos de código generados. Seleccione "Leer datos":

El cuadro de diálogo para usar un fragmento de código en Watson Studio Uso de un fragmento de código preparado en Watson Studio

Este paso abre un menú para seleccionar un archivo de datos. Si no ha cargado nada en su instancia de Cloud Object Storage, deberá cargar algo para generar credenciales y puede ser un conjunto de datos clásico como wine.csv.

Selección de un activo de datos en Watson Studio Selección de un activo de datos en Watson Studio

Después de hacer clic en "Select", ahora puede generar el fragmento de credenciales en la opción "Load as". Seleccione "Insert code to cell":

Inserción de un fragmento de código generado en Watson Studio Inserción de un fragmento de código generado en Watson Studio

Este paso genera una celda como la siguiente con credenciales que contienen los ID correctos y los endpoint generados:

# @hidden_cell
# The following code contains metadata for a file in your project storage.
# You might want to remove secret properties before you share your notebook.

storage_metadata = {
    'IAM_SERVICE_ID': '',
    'IBM_API_KEY_ID': '',
    'ENDPOINT': '',
    'IBM_AUTH_ENDPOINT': '',
    'BUCKET': '',
    'FILE': ''
}

Ahora la carpeta zip que contiene el adaptador y la información sobre el propio adaptador:

!zip -r "granite3_lora.zip" "granite3_lora"

Compruebe que ha creado el zip correctamente:

!ls

Inferencia

Ahora es el momento de ejecutar la inferencia. La inferencia estará respaldada por la generación HuggingFace, que proporciona un model.generate() método para la generación de texto mediante el uso de PyTorch.

Este tutorial muestra cómo formular al modelo base una pregunta médica extraída del conjunto de datos de MedReason. Es razonable que el modelo base no pueda responder a esta pregunta porque es un modelo de propósito general entrenado en conjuntos de datos grandes y diversos.

Primero, establezca las configuraciones de inferencia:

# setup inference configurations
args = dict(
  model_name_or_path="ibm-granite/granite-3.3-2b-instruct",  # use IBM Granite 3.3 2b instruct model
  template="granite3",  # set to the same one used in training, template for constructing prompts
  infer_backend="huggingface"  # choices: [huggingface, vllm]
)

# create inference config file to run with llama factory
with open("inference_config.yaml", "w", encoding="utf-8") as file:
  yaml.dump(args, file, indent=2)

Ahora le hará al chatbot una de las preguntas del conjunto de datos de MedReason:

from llamafactory.chat import ChatModel
chat_model = ChatModel(args)
messages = []

# run inference chatbot
question = '''
A 1-year-old girl is brought to a neurologist due to increasing seizure frequency over the past 2 months. 
She recently underwent a neurology evaluation which revealed hypsarrhythmia on electroencephalography (EEG) with a mix of slow waves, multifocal spikes, and asynchrony. 
Her parents have noticed the patient occasionally stiffens and spreads her arms at home. She was born at 38-weeks gestational age without complications. 
She has no other medical problems. Her medications consist of lamotrigine and valproic acid. Her temperature is 98.3\u00b0F (36.8\u00b0C), blood pressure is 90/75 mmHg, pulse is 94/min, and respirations are 22/min. 
Physical exam reveals innumerable hypopigmented macules on the skin and an irregularly shaped, thickened, and elevated plaque on the lower back. 
Which of the following is most strongly associated with this patient's condition?"
"A": "Cardiac rhabdomyoma", "B": "Glaucoma", "C": "Optic glioma", "D": "Polyostotic fibrous dysplasia"
'''

Cree un nuevo mensaje usando la pregunta y pásela al modelo base:

messages.append({"role": "user", "content": question})

response = ""
for new_text in chat_model.stream_chat(messages):
    response += new_text

print(response)
messages.append({"role": "assistant", "content": response})

Aquí está el resultado de muestra del modelo base Granite 3.3: Usuario:

A 1-year-old girl is brought to a neurologist due to increasing seizure frequency over the past 2 months. 
She recently underwent a neurology evaluation which revealed hypsarrhythmia on electroencephalography (EEG) with a mix of slow waves, multifocal spikes, and asynchrony. 
Her parents have noticed the patient occasionally stiffens and spreads her arms at home. She was born at 38-weeks gestational age without complications. 
She has no other medical problems. Her medications consist of lamotrigine and valproic acid. Her temperature is 98.3\F (36.8\C), blood pressure is 90/75 mmHg, pulse is 94/min, and respirations are 22/min. 
Physical exam reveals innumerable hypopigmented macules on the skin and an irregularly shaped, thickened, and elevated plaque on the lower back. 
Which of the following is most strongly associated with this patient's condition?"
"A": "Cardiac rhabdomyoma", "B": "Glaucoma", "C": "Optic glioma", "D": "Polyostotic fibrous dysplasia"

Respuesta:

The most strongly associated condition with this patient's condition is "C": "Optic glioma".

The patient's symptoms of hypsarrhythmia on EEG, seizure frequency increase, and the presence of hypopigmented macules and a thickened plaque on the lower back are indicative of a neurological disorder. Optic glioma is a type of brain tumor that can present with these symptoms, including seizures and visual disturbances.

Option A, "Cardiac rhabdomyoma", typically presents with cardiac involvement and is not associated with the described EEG findings or skin manifestations.

Option B, "Glaucoma", is an eye disease that can lead to vision loss but is not associated with the EEG findings or skin lesions described.

Option D, "Polyostotic fibrous dysplasia", is a bone disorder characterized by multiple bone lesions and is not associated with the neurological symptoms and EEG findings presented.

Therefore, based on the clinical presentation, the most likely diagnosis is an optic glioma.

La respuesta correcta del conjunto de datos es:

answer: Cardiac rhabdomyoma

Entonces, el modelo base no genera la respuesta correcta.

Inferir con el adaptador ajustado de LoRA Comparamos los resultados con el modelo base y el adaptador ajustado de LoRA. Luego hacemos la misma pregunta para ver cómo el ajuste con el conjunto de datos legal permitió que el modelo entendiera y respondiera mejor a las preguntas médicas.

La siguiente celda no será necesaria si ha realizado LoRA en la misma sesión. Sin embargo, si vuelve a Jupyter Notebook y no quiere volver a entrenar, puede descargar los adaptadores ajustados desde su instancia de COS.

download_file_cos(credentials, "granite3_lora.zip", "granite3_lora.zip")
!unzip granite3_lora.zip

Ahora configurará las opciones para ChatModel para que incorpore los adaptadores.

# setup inference configurations
args = dict(
  model_name_or_path="ibm-granite/granite-3.3-2b-instruct",  # use IBM Granite 3.3 2b instruct model
  adapter_name_or_path="granite3_lora", # load the saved LoRA adapters
  template="granite3", # set to the same one used in training, template for constructing prompts
  finetuning_type="lora", # which fine-tuning technique used in training
  infer_backend="huggingface" # choices: [huggingface, vllm]
)

# create inference config file to run with llama factory
with open("inference_config.yaml", "w", encoding="utf-8") as file:
  yaml.dump(args, file, indent=2)


from llamafactory.chat import ChatModel
chat_model = ChatModel(args)

Ahora podemos probar el mismo desafío de razonamiento para el modelo ajustado:

messages = []

# run inference chatbot
question = '''
A 1-year-old girl is brought to a neurologist due to increasing seizure frequency over the past 2 months. 
She recently underwent a neurology evaluation which revealed hypsarrhythmia on electroencephalography (EEG) with a mix of slow waves, multifocal spikes, and asynchrony. 
Her parents have noticed the patient occasionally stiffens and spreads her arms at home. She was born at 38-weeks gestational age without complications. 
She has no other medical problems. Her medications consist of lamotrigine and valproic acid. Her temperature is 98.3\u00b0F (36.8\u00b0C), blood pressure is 90/75 mmHg, pulse is 94/min, and respirations are 22/min. 
Physical exam reveals innumerable hypopigmented macules on the skin and an irregularly shaped, thickened, and elevated plaque on the lower back. 
Which of the following is most strongly associated with this patient's condition?"
"A": "Cardiac rhabdomyoma", "B": "Glaucoma", "C": "Optic glioma", "D": "Polyostotic fibrous dysplasia"
'''

Cree un nuevo mensaje usando la pregunta y páselo al modelo ajustado:

messages.append({"role": "user", "content": question})

response = ""
for new_text in chat_model.stream_chat(messages):
    response += new_text

print(response)
messages.append({"role": "assistant", "content": response})
Cardiac rhabdomyoma

Ejemplo de resultado del modelo ajustado: ** Usuario**:

A 1-year-old girl is brought to a neurologist due to increasing seizure frequency over the past 2 months. 
She recently underwent a neurology evaluation which revealed hypsarrhythmia on electroencephalography (EEG) with a mix of slow waves, multifocal spikes, and asynchrony. 
Her parents have noticed the patient occasionally stiffens and spreads her arms at home. She was born at 38-weeks gestational age without complications. 
She has no other medical problems. Her medications consist of lamotrigine and valproic acid. Her temperature is 98.3\u00b0F (36.8\u00b0C), blood pressure is 90/75 mmHg, pulse is 94/min, and respirations are 22/min. 
Physical exam reveals innumerable hypopigmented macules on the skin and an irregularly shaped, thickened, and elevated plaque on the lower back. 
Which of the following is most strongly associated with this patient's condition?"
"A": "Cardiac rhabdomyoma", "B": "Glaucoma", "C": "Optic glioma", "D": "Polyostotic fibrous dysplasia"

Respuesta:

Cardiac rhabdomyoma

Esta vez, el modelo generó la respuesta correcta, gracias al adaptador entrenado.

Un aspecto a tener en cuenta es que el modelo ya no responde con su razonamiento. Este resultado se debe a que el conjunto de datos que se utilizó para LoRA solo tiene la respuesta correcta como resultado esperado del modelo. El ajuste fino de LoRa se puede utilizar tanto para proporcionar nueva información como para instruir al modelo cómo responder.

Resumen

En este tutorial, ajustó el modelo LoRA IBM Granite-3.3-2b-Instruct con nuevos conocimientos médicos y una plantilla detallada sobre cómo responder. Usted vio la capacidad de Granite 3.3 para aprender incluso con un modelo pequeño y muestras limitadas del conjunto de datos.

Soluciones relacionadas
Modelos fundacionales

Explore la biblioteca de modelos fundacionales de IBM en la cartera de watsonx para escalar la IA generativa para su negocio con confianza.

Descubra watsonx.ai
Soluciones de inteligencia artificial

Ponga la IA a trabajar en su negocio con la experiencia en IA líder en la industria y la cartera de soluciones de IBM a su lado.

Explore las soluciones de IA
Consultoría y servicios de IA

Reinvente los flujos de trabajo y las operaciones críticas mediante la incorporación de IA para maximizar las experiencias, la toma de decisiones en tiempo real y el valor empresarial.

Conozca los servicios de IA
Dé el siguiente paso

Explore la biblioteca de modelos fundacionales de IBM watsonx en la cartera de IBM watsonx para escalar la IA generativa para su negocio con confianza.

Descubra watsonx.ai Explorar los modelos de IA de IBM Granite