QLoRA: Efficient Finetuning of Quantized LLMs

QLoRA: Efficient Finetuning of Quantized LLMs QLoRA: Efficient Finetuning of Quantized LLMs

QLoRA: Efficient Finetuning of Quantized LLMslink image 0

Si bien LoRA proporciona una manera de hacer fine tuning de modelos de lenguaje sin necesidad de GPUs con grandes VRAMs, en el paper de QLoRA van más allá y proponen la manera de hacer fine tuning de modelo cuantizados, haciendo que se necesite aún menos memoria para hacer fine tuning de modelos de lenguaje.

LoRAlink image 1

Actualización de pesos en una red neuronallink image 2

Para entender cómo funciona LoRA, primero tenemos que recordar qué ocurre cuando entrenamos un modelo. Volvamos a la parte más básica del deep learning, tenemos una capa densa de una red neuronal que se define como:

$$ y = Wx + b $$

Donde $W$ es la matriz de pesos y $b$ es el vector de sesgos.

Para simplificar vamos a suponer que no hay sesgo, por lo que quedaría así

$$ y = Wx $$

Supongamos que para una entrada $x$ queremos que tenga una salida $ŷ$

  • Primero lo que hacemos es calcular la salida que obtenemos con nuestro valor actual de pesos $W$, es decir obtenemos el valor $y$
  • A continuación calculamos el error que existe entre el valor de $y$ que hemos obtenido y el valor que queríamos obtener $ŷ$. A ese error lo llamamos $loss$, y lo calculamos con alguna función matemática, ahora no importa cual
  • Calculamos el gardiente (la derivada) del error $loss$ con respecto a la matriz de pesos $W$, es decir $\Delta W = \frac{dloss}{dW}$
  • Actualizamos los pesos $W$ restando a cada uno de sus valores el valor del gradiente multiplicado por un factor de aprendizaje $\alpha$, es decir $W = W - \alpha \Delta W$

LoRAlink image 3

Los autores de LoRA lo que proponen es que la matriz de pesos $W$ se puede descomponer en

$$ W \sim W + \Delta W $$

De manera que congelando la matriz $W$ y entrenando solo la matriz $\Delta W$ se puede obtener un modelo que se adapte a nuevos datos sin tener que reentrenar todo el modelo

Pero podrás pensar que $\Delta W$ es una matriz de tamaño igual a $W$ por lo que no se ha ganado nada, pero aquí los autores se basan en Aghajanyan et al. (2020), un paper en el que demostraron que aunque los modelos de lenguaje son grandes y que sus parámetros son matrices con dimensiones muy grandes, para adaptarlos a nuevas tareas no es necesario cambiar todos los valores de las matrices, sino que cambiando unos pocos valores es suficiente, que en términos técnicos, se llama adaptación de bajo rango. De ahí el nombre de LoRA (Low Rank Adaptation)

Hemos congelado el modelo y ahora queremos entrenar la matriz $\Delta W$, supongamos que tanto $W$ como $\Delta W$ son matrices de tamaño $20 \times 10$, por lo que tenemos 200 parámetros entrenables

Ahora supongamos que la matriz $\Delta W$ se puede descomponer en el producto de dos matrices $A$ y $B$, es decir

$$ \Delta W = A \cdot B $$

Para que esta multiplicación se produzca los tamaños de las matrices $A$ y $B$ tienen que ser $20 \times n$ y $n \times 10$ respectivamente. Supongamos que $n = 5$, por lo que $A$ sería de tamaño $20 \times 5$, es decir 100 parámetros, y $B$ de tamaño $5 \times 10$, es decir 50 parámetros, por lo que tendríamos 100+50=150 parámetros entrenables. Ya tenemos menos parámetros entrenables que antes

Ahora supongamos que $W$ en realidad es una matriz de tamaño $10.000 \times 10.000$, por lo que tendríamos 100.000.000 parámetros entrenables, pero si descomponemos $\Delta W$ en $A$ y $B$ con $n = 5$, tendríamos una matriz de tamaño $10.000 \times 5$ y otra de tamaño $5 \times 10.000$, por lo que tendríamos 50.000 parámetros de una y otros 50.000 parámetros de otra, en total 100.000 parámetros entrenables, es decir hemos reducido el número de parámetros 1000 veces

Ya puedes ir viendo el poder de LoRA, cuando se tienen modelos muy grandes, el número de parámetros entrenables se puede reducir muchísimo

Si volvemos a ver la imagen de la arquitectura de LoRA, la entenderemos mejor

LoRA adapt

Pero se ve mejor aun, el ahorro en número de parámetros entrenables con esta imagen

LoRA matmul

QLoRAlink image 4

QLoRA se realiza en dos pasos, la primera consiste en cuantizar el moelo y la segunda en aplicar LoRA al modelo cuantizado

Cuantización QLoRAlink image 5

La cuantización de QLoRA se basa en tres conceptos, la cuantización del modelo a 4 bits con el formato normal float 4 (NF4), la doble cuantiazción y los optimizadores paginados. Todo ello junto hace que se pueda ahorrar mucha memoria al hacer fine tuning de los modelos de lenguaje, así que vamos a ver en qué consiste cada uno

Cuantización de los modelos de lenguaje en normal float 4 (NF4)link image 6

En QLoRA, para cuantizar, lo que se hace es cuantizar en formato normal float 4 (NF4), que es un tipo de cuantización a 4 bits de manera que sus datos tienen una distribución normal, es decir que siguen una campana de Gauss. Para conseguir que sigan esta distribución, lo que se hace es dividir los valores de los pesos en FP16 en quantiles, de manera que en cada quantil haya el mismo número de valores. Una vez tenemos los cuantiles, a cada cuantil se le asigna un valor en 4 bits

QLoRA-normal-float-quantization

Para realizar esta cuantización utiliza el algoritmo de cuantización SRAM, que es un algoritmo de cuantización por quantiles muy rápido, pero tiene mucho error con valores que están muy lejos en la distribución de la campana de Gauss, valores atípicos

Como normalmente los parámetros de los pesos de una red neuronal suelen seguir una distribución normal (es decir que siguen una campana de Gauss), centrada en cero y con una desviación estandar σ. Lo que se hace es normalizarlos para que tengan una desviación estandar entre -1 y 1, y después se cuantizan en formato NF4

Doble cuantizaciónlink image 7

Como hemos comentado, a la hora de cuantizar los parámetros de la red, tenemos que normalizarlos para que tengan una desviación estandar entre -1 y 1, y después cuantizarlos en formato NF4. Por lo que tenemos que guardar algunos parámetros como los valores para normalizar los parámetros, es decir, el valor por el que se dividen los datos para que tengan una desviación entre -1 y 1. Esos valores se almacenan en formato FP32, por lo que los autores del paper proponen cuantizar esos parámetros a formato FP8.

Aunque esto puede parecer que no ahorra mucha memoria, los autores calculan que esto puede ahorrar unos 0.373 bits por parámetro, pero si por ejemplo tenemos un modelo de 8B de parámetros, que no es un modelo excesivamente grande a día de hoy, nos ahorraríamos unos 3 GB de memoria, que no está mal. En el caso de un modelo de 70B de parámetros, nos ahorraríamos unos 26 GB de memoria

Optimizadores paginadoslink image 8

Las GPUs de Nvidia tienen la opción de compartir la RAM de la GPU y de la CPU, de manera que lo que hacen es guardar los estados del optimizador en la RAM de la CPU y acceder a ellos cuando lo necesitan. Así no se tienen que guardar en la RAM de la GPU y podemos ahorrar memoria de la GPU

Fine tuning con LoRAlink image 9

Una vez hemos cuantizado el modelo ya podemos hacer fine tuning del modelo cuantizado igual que se hace en LoRA

Cómo hacer fine tuning de un modelo cuantizado con QLoRAlink image 10

Ahora que hemos explicado QLoRA, vamos a ver un ejemplo de cómo hacer fine tuning a un modelo usando QLoRA

Login en el Hub de Hugging Facelink image 11

Primero nos logeamos para poder subir el modelo entrenado al Hub

	
from huggingface_hub import notebook_login
notebook_login()
Copy

Datasetlink image 12

Descargamos el dataset que vamos a usar, que es un dataset de reviews de Amazon

	
from huggingface_hub import notebook_login
notebook_login()
from datasets import load_dataset
dataset = load_dataset("mteb/amazon_reviews_multi", "en")
dataset
Copy
	
DatasetDict({
train: Dataset({
features: ['id', 'text', 'label', 'label_text'],
num_rows: 200000
})
validation: Dataset({
features: ['id', 'text', 'label', 'label_text'],
num_rows: 5000
})
test: Dataset({
features: ['id', 'text', 'label', 'label_text'],
num_rows: 5000
})
})

Creamos un subset por si quieres probar el código con un dataset más pequeño. En mi caso usaré el 100% del dataset

	
percentage = 1
subset_dataset_train = dataset['train'].select(range(int(len(dataset['train']) * percentage)))
subset_dataset_validation = dataset['validation'].select(range(int(len(dataset['validation']) * percentage)))
subset_dataset_test = dataset['test'].select(range(int(len(dataset['test']) * percentage)))
subset_dataset_train, subset_dataset_validation, subset_dataset_test
Copy
	
(Dataset({
features: ['id', 'text', 'label', 'label_text'],
num_rows: 200000
}),
Dataset({
features: ['id', 'text', 'label', 'label_text'],
num_rows: 5000
}),
Dataset({
features: ['id', 'text', 'label', 'label_text'],
num_rows: 5000
}))

Vemos una muestra

	
from random import randint
idx = randint(0, len(subset_dataset_train))
subset_dataset_train[idx]
Copy
	
{'id': 'en_0297000',
'text': 'Not waterproof at all Bought this after reading good reviews. But it’s not water proof at all. If my son has even a little accident in bed, it goes straight to mattress. I don’t see a point in having this. So I have to purchase another one.',
'label': 0,
'label_text': '0'}

Obtenemos el número de clases, para obtener el número de clases usamos dataset['train'] y no subset_dataset_train porque si el subset lo hamos muy pequeño es posible que no haya ejemplos con todas las posibles clases del dataset original

	
num_classes = len(dataset['train'].unique('label'))
num_classes
Copy
	
5

Creamos una función para crear el campo label en el dataset. El dataset descargado tiene el campo labels pero la librería transformers necesita que el campo se llame label y no labels

	
def set_labels(example):
example['labels'] = example['label']
return example
Copy

Aplicamos la función al dataset

	
def set_labels(example):
example['labels'] = example['label']
return example
subset_dataset_train = subset_dataset_train.map(set_labels)
subset_dataset_validation = subset_dataset_validation.map(set_labels)
subset_dataset_test = subset_dataset_test.map(set_labels)
subset_dataset_train, subset_dataset_validation, subset_dataset_test
Copy
	
(Dataset({
features: ['id', 'text', 'label', 'label_text', 'labels'],
num_rows: 200000
}),
Dataset({
features: ['id', 'text', 'label', 'label_text', 'labels'],
num_rows: 5000
}),
Dataset({
features: ['id', 'text', 'label', 'label_text', 'labels'],
num_rows: 5000
}))

Volvemos a ver una muestra

	
subset_dataset_train[idx]
Copy
	
{'id': 'en_0297000',
'text': 'Not waterproof at all Bought this after reading good reviews. But it’s not water proof at all. If my son has even a little accident in bed, it goes straight to mattress. I don’t see a point in having this. So I have to purchase another one.',
'label': 0,
'label_text': '0',
'labels': 0}

Tokenizerlink image 13

Implementamos el tokenizador. Para que no nos de error, asignamos el token de end of string al token de padding

	
from transformers import AutoTokenizer
checkpoint = "openai-community/gpt2"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
tokenizer.pad_token = tokenizer.eos_token
Copy

Creamos una función para tokenizar el dataset

	
from transformers import AutoTokenizer
checkpoint = "openai-community/gpt2"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
tokenizer.pad_token = tokenizer.eos_token
def tokenize_function(examples):
return tokenizer(examples["text"], padding="max_length", truncation=True, max_length=768, return_tensors="pt")
Copy

Aplicamos la función al dataset y de paso eliminamos las columnas que no necesitamos

	
from transformers import AutoTokenizer
checkpoint = "openai-community/gpt2"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
tokenizer.pad_token = tokenizer.eos_token
def tokenize_function(examples):
return tokenizer(examples["text"], padding="max_length", truncation=True, max_length=768, return_tensors="pt")
subset_dataset_train = subset_dataset_train.map(tokenize_function, batched=True, remove_columns=['text', 'label', 'id', 'label_text'])
subset_dataset_validation = subset_dataset_validation.map(tokenize_function, batched=True, remove_columns=['text', 'label', 'id', 'label_text'])
subset_dataset_test = subset_dataset_test.map(tokenize_function, batched=True, remove_columns=['text', 'label', 'id', 'label_text'])
subset_dataset_train, subset_dataset_validation, subset_dataset_test
Copy
	
(Dataset({
features: ['labels', 'input_ids', 'attention_mask'],
num_rows: 200000
}),
Dataset({
features: ['labels', 'input_ids', 'attention_mask'],
num_rows: 5000
}),
Dataset({
features: ['labels', 'input_ids', 'attention_mask'],
num_rows: 5000
}))

Volvemos a ver una muestra, pero en este caso solo vemos las keys

	
subset_dataset_train[idx].keys()
Copy
	
dict_keys(['labels', 'input_ids', 'attention_mask'])

Modelolink image 14

Descargamos primero el modelo sin cuantizar

	
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=num_classes)
model.config.pad_token_id = model.config.eos_token_id
Copy
	
Some weights of GPT2ForSequenceClassification were not initialized from the model checkpoint at openai-community/gpt2 and are newly initialized: ['score.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.

Vemos la memoria que ocupa

	
model_memory = model.get_memory_footprint()/(1024**3)
print(f"Model memory: {model_memory:.2f} GB")
Copy
	
Model memory: 0.48 GB

Pasamos el modelo a FP16 y volvemos a ver la memoria que ocupa

	
model = model.half()
Copy
	
model = model.half()
model_memory = model.get_memory_footprint()/(1024**3)
print(f"Model memory: {model_memory:.2f} GB")
Copy
	
Model memory: 0.24 GB

Vemos la arquitectura del modelo antes de cuantizar

	
model
Copy
	
GPT2ForSequenceClassification(
(transformer): GPT2Model(
(wte): Embedding(50257, 768)
(wpe): Embedding(1024, 768)
(drop): Dropout(p=0.1, inplace=False)
(h): ModuleList(
(0-11): 12 x GPT2Block(
(ln_1): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(attn): GPT2Attention(
(c_attn): Conv1D()
(c_proj): Conv1D()
(attn_dropout): Dropout(p=0.1, inplace=False)
(resid_dropout): Dropout(p=0.1, inplace=False)
)
(ln_2): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(mlp): GPT2MLP(
(c_fc): Conv1D()
(c_proj): Conv1D()
(act): NewGELUActivation()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
(ln_f): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
)
(score): Linear(in_features=768, out_features=5, bias=False)
)

Cuantización del modelolink image 15

Para cuantizar el modelo primero tenemos que crear la configuración de cuantización, para ello usamos la librería bitsandbytes, si no la tienes instalada la puedes instalar con

pip install bitsandbytes
      

Primero comprobamos si la arquitectura de nuestra GPU permite el formato BF16, si no lo permite usaremos FP16

A continuación creamos la configuración de cuantización, con load_in_4bits=True indicamos que cuantice a 4 bits, con bnb_4bit_quant_type="nf4" le indicamos que lo haga en formato NF4, con bnb_4bit_use_double_quant=True le indicamos que haga doble cuantización y con bnb_4bit_compute_dtype=compute_dtype le indicamos qué formato de datos tiene que usar cuando descuantice, que puede ser FP16 o BF16.

	
from transformers import BitsAndBytesConfig
import torch
compute_dtype = torch.bfloat16 if torch.cuda.is_bf16_supported() else torch.float16
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
bnb_4bit_compute_dtype=compute_dtype,
)
Copy

Y ahora cuantizamos el modelo

	
from transformers import BitsAndBytesConfig
import torch
compute_dtype = torch.bfloat16 if torch.cuda.is_bf16_supported() else torch.float16
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
bnb_4bit_compute_dtype=compute_dtype,
)
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=num_classes, quantization_config=bnb_config)
Copy
	
`low_cpu_mem_usage` was None, now set to True since model is quantized.
Some weights of GPT2ForSequenceClassification were not initialized from the model checkpoint at openai-community/gpt2 and are newly initialized: ['score.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.

Volvemos a ver la memoria que ocupa ahora que lo hemos cuantizado

	
model_memory = model.get_memory_footprint()/(1024**3)
print(f"Model memory: {model_memory:.2f} GB")
Copy
	
Model memory: 0.12 GB

Vemos que se ha reducido el tamaño del modelo

Volvemos a ver la arquitectura del modelo una vez cuantizado

	
model
Copy
	
GPT2ForSequenceClassification(
(transformer): GPT2Model(
(wte): Embedding(50257, 768)
(wpe): Embedding(1024, 768)
(drop): Dropout(p=0.1, inplace=False)
(h): ModuleList(
(0-11): 12 x GPT2Block(
(ln_1): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(attn): GPT2Attention(
(c_attn): Linear4bit(in_features=768, out_features=2304, bias=True)
(c_proj): Linear4bit(in_features=768, out_features=768, bias=True)
(attn_dropout): Dropout(p=0.1, inplace=False)
(resid_dropout): Dropout(p=0.1, inplace=False)
)
(ln_2): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(mlp): GPT2MLP(
(c_fc): Linear4bit(in_features=768, out_features=3072, bias=True)
(c_proj): Linear4bit(in_features=3072, out_features=768, bias=True)
(act): NewGELUActivation()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
(ln_f): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
)
(score): Linear(in_features=768, out_features=5, bias=False)
)

Vemos que la arquitectura ha cambiado

QLoRA-model-vs-quantized-model

Ha modificado las capas Conv1D por capas Linear4bits

LoRAlink image 16

Antes de implementar LoRA, tenemos que configurar el modelo para entrenar en 4 bits

	
from peft import prepare_model_for_kbit_training
model = prepare_model_for_kbit_training(model)
Copy

Vamos a ver si ha cambiado el tamaño del modelo

	
from peft import prepare_model_for_kbit_training
model = prepare_model_for_kbit_training(model)
model_memory = model.get_memory_footprint()/(1024**3)
print(f"Model memory: {model_memory:.2f} GB")
Copy
	
Model memory: 0.20 GB

Ha aumentado la memoria, así que volvemos a ver la arquitectura del modelo

	
model
Copy
	
GPT2ForSequenceClassification(
(transformer): GPT2Model(
(wte): Embedding(50257, 768)
(wpe): Embedding(1024, 768)
(drop): Dropout(p=0.1, inplace=False)
(h): ModuleList(
(0-11): 12 x GPT2Block(
(ln_1): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(attn): GPT2Attention(
(c_attn): Linear4bit(in_features=768, out_features=2304, bias=True)
(c_proj): Linear4bit(in_features=768, out_features=768, bias=True)
(attn_dropout): Dropout(p=0.1, inplace=False)
(resid_dropout): Dropout(p=0.1, inplace=False)
)
(ln_2): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
(mlp): GPT2MLP(
(c_fc): Linear4bit(in_features=768, out_features=3072, bias=True)
(c_proj): Linear4bit(in_features=3072, out_features=768, bias=True)
(act): NewGELUActivation()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
(ln_f): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
)
(score): Linear(in_features=768, out_features=5, bias=False)
)

La arquitectura sigue siendo la misma, por lo que suponemos que el aumento de memoria es por alguna configuración extra para poder aplicar LoRA en 4 bits

Creamos una configuración de LoRA, pero a diferencia del post de LoRA en el que solo configuramos en target_modeules la capa scores, ahora vamos a añadir también las capas c_attn, c_proj y c_fc ya que ahora son de tipo Linear4bits y no Conv1D

	
from peft import LoraConfig, TaskType
config = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.1,
task_type=TaskType.SEQ_CLS,
target_modules=['c_attn', 'c_fc', 'c_proj', 'score'],
bias="none",
)
Copy
	
from peft import LoraConfig, TaskType
config = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.1,
task_type=TaskType.SEQ_CLS,
target_modules=['c_attn', 'c_fc', 'c_proj', 'score'],
bias="none",
)
from peft import get_peft_model
model = get_peft_model(model, config)
Copy
	
from peft import LoraConfig, TaskType
config = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.1,
task_type=TaskType.SEQ_CLS,
target_modules=['c_attn', 'c_fc', 'c_proj', 'score'],
bias="none",
)
from peft import get_peft_model
model = get_peft_model(model, config)
model.print_trainable_parameters()
Copy
	
trainable params: 2,375,504 || all params: 126,831,520 || trainable%: 1.8730

Mientras que en el post de LoRA teníamos unos 12.000 parámetros entrenables, ahora tenemos unos 2 millones, ya que ahora hemos añadido las capas c_attn, c_proj y c_fc

Traininglink image 17

Una vez instanciado el modelo cuantizado y aplicado LoRA, es decir, una vez hemos hecho QLoRA, vamos a entrenarlo como siempre

	
from transformers import TrainingArguments
metric_name = "accuracy"
model_name = "GPT2-small-QLoRA-finetuned-amazon-reviews-en-classification"
LR = 2e-5
BS_TRAIN = 224
BS_EVAL = 224
EPOCHS = 3
WEIGHT_DECAY = 0.01
training_args = TrainingArguments(
model_name,
eval_strategy="epoch",
save_strategy="epoch",
learning_rate=LR,
per_device_train_batch_size=BS_TRAIN,
per_device_eval_batch_size=BS_EVAL,
num_train_epochs=EPOCHS,
weight_decay=WEIGHT_DECAY,
lr_scheduler_type="cosine",
warmup_ratio = 0.1,
fp16=True,
load_best_model_at_end=True,
metric_for_best_model=metric_name,
push_to_hub=True,
logging_dir="./runs",
)
Copy

En el post Fine tuning SMLs tuvimos que poner un BS de train de 28, en el post LoRA al poner las matrices de bajo rango en las capas lineales hizo que pudiéramos poner un batch size de train de 400. Ahora, como al cuantizar el modelo, la librería PEFT ha convertido algunas capas más a Linear no podemos poner un batch size tan grande y lo tenemos que poner de 224

	
from transformers import TrainingArguments
metric_name = "accuracy"
model_name = "GPT2-small-QLoRA-finetuned-amazon-reviews-en-classification"
LR = 2e-5
BS_TRAIN = 224
BS_EVAL = 224
EPOCHS = 3
WEIGHT_DECAY = 0.01
training_args = TrainingArguments(
model_name,
eval_strategy="epoch",
save_strategy="epoch",
learning_rate=LR,
per_device_train_batch_size=BS_TRAIN,
per_device_eval_batch_size=BS_EVAL,
num_train_epochs=EPOCHS,
weight_decay=WEIGHT_DECAY,
lr_scheduler_type="cosine",
warmup_ratio = 0.1,
fp16=True,
load_best_model_at_end=True,
metric_for_best_model=metric_name,
push_to_hub=True,
logging_dir="./runs",
)
import numpy as np
from evaluate import load
metric = load("accuracy")
def compute_metrics(eval_pred):
print(eval_pred)
predictions, labels = eval_pred
predictions = np.argmax(predictions, axis=1)
return metric.compute(predictions=predictions, references=labels)
Copy
	
from transformers import TrainingArguments
metric_name = "accuracy"
model_name = "GPT2-small-QLoRA-finetuned-amazon-reviews-en-classification"
LR = 2e-5
BS_TRAIN = 224
BS_EVAL = 224
EPOCHS = 3
WEIGHT_DECAY = 0.01
training_args = TrainingArguments(
model_name,
eval_strategy="epoch",
save_strategy="epoch",
learning_rate=LR,
per_device_train_batch_size=BS_TRAIN,
per_device_eval_batch_size=BS_EVAL,
num_train_epochs=EPOCHS,
weight_decay=WEIGHT_DECAY,
lr_scheduler_type="cosine",
warmup_ratio = 0.1,
fp16=True,
load_best_model_at_end=True,
metric_for_best_model=metric_name,
push_to_hub=True,
logging_dir="./runs",
)
import numpy as np
from evaluate import load
metric = load("accuracy")
def compute_metrics(eval_pred):
print(eval_pred)
predictions, labels = eval_pred
predictions = np.argmax(predictions, axis=1)
return metric.compute(predictions=predictions, references=labels)
from transformers import Trainer
trainer = Trainer(
model,
training_args,
train_dataset=subset_dataset_train,
eval_dataset=subset_dataset_validation,
tokenizer=tokenizer,
compute_metrics=compute_metrics,
)
Copy
trainer.train()
      
`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`...
      /usr/local/lib/python3.10/dist-packages/torch/utils/checkpoint.py:464: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.4 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.
        warnings.warn(
      
[1859/2679 2:10:29 < 57:37, 0.24 it/s, Epoch 2.08/3]
Epoch Training Loss Validation Loss Accuracy
1 2.088000 0.978048 0.584800
2 0.958800 0.894022 0.615600

<transformers.trainer_utils.EvalPrediction object at 0x7acac436c3d0>
      
/usr/local/lib/python3.10/dist-packages/torch/utils/checkpoint.py:464: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.4 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.
        warnings.warn(
      
<transformers.trainer_utils.EvalPrediction object at 0x7acac32580d0>
      
/usr/local/lib/python3.10/dist-packages/torch/utils/checkpoint.py:464: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.4 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.
        warnings.warn(
      
[2679/2679 3:08:13, Epoch 3/3]
Epoch Training Loss Validation Loss Accuracy
1 2.088000 0.978048 0.584800
2 0.958800 0.894022 0.615600
3 0.914700 0.891830 0.616800

<transformers.trainer_utils.EvalPrediction object at 0x7acac2f43c10>
      
TrainOutput(global_step=2679, training_loss=1.1650676093647934, metrics={'train_runtime': 11299.1288, 'train_samples_per_second': 53.101, 'train_steps_per_second': 0.237, 'total_flos': 2.417754341376e+17, 'train_loss': 1.1650676093647934, 'epoch': 3.0})

Evaluaciónlink image 18

Una vez entrenado evaluamos sobre el dataset de test

trainer.evaluate(eval_dataset=subset_dataset_test)
      
[23/23 00:27]
<transformers.trainer_utils.EvalPrediction object at 0x7acb316fe5c0>
      
{'eval_loss': 0.8883273601531982,
       'eval_accuracy': 0.615,
       'eval_runtime': 28.5566,
       'eval_samples_per_second': 175.091,
       'eval_steps_per_second': 0.805,
       'epoch': 3.0}

Publicar el modelolink image 19

Creamos una model card

	
from transformers import TrainingArguments
metric_name = "accuracy"
model_name = "GPT2-small-QLoRA-finetuned-amazon-reviews-en-classification"
LR = 2e-5
BS_TRAIN = 224
BS_EVAL = 224
EPOCHS = 3
WEIGHT_DECAY = 0.01
training_args = TrainingArguments(
model_name,
eval_strategy="epoch",
save_strategy="epoch",
learning_rate=LR,
per_device_train_batch_size=BS_TRAIN,
per_device_eval_batch_size=BS_EVAL,
num_train_epochs=EPOCHS,
weight_decay=WEIGHT_DECAY,
lr_scheduler_type="cosine",
warmup_ratio = 0.1,
fp16=True,
load_best_model_at_end=True,
metric_for_best_model=metric_name,
push_to_hub=True,
logging_dir="./runs",
)
import numpy as np
from evaluate import load
metric = load("accuracy")
def compute_metrics(eval_pred):
print(eval_pred)
predictions, labels = eval_pred
predictions = np.argmax(predictions, axis=1)
return metric.compute(predictions=predictions, references=labels)
from transformers import Trainer
trainer = Trainer(
model,
training_args,
train_dataset=subset_dataset_train,
eval_dataset=subset_dataset_validation,
tokenizer=tokenizer,
compute_metrics=compute_metrics,
)
trainer.train()
trainer.evaluate(eval_dataset=subset_dataset_test)
trainer.create_model_card()
Copy

Lo publicamos

	
from transformers import TrainingArguments
metric_name = "accuracy"
model_name = "GPT2-small-QLoRA-finetuned-amazon-reviews-en-classification"
LR = 2e-5
BS_TRAIN = 224
BS_EVAL = 224
EPOCHS = 3
WEIGHT_DECAY = 0.01
training_args = TrainingArguments(
model_name,
eval_strategy="epoch",
save_strategy="epoch",
learning_rate=LR,
per_device_train_batch_size=BS_TRAIN,
per_device_eval_batch_size=BS_EVAL,
num_train_epochs=EPOCHS,
weight_decay=WEIGHT_DECAY,
lr_scheduler_type="cosine",
warmup_ratio = 0.1,
fp16=True,
load_best_model_at_end=True,
metric_for_best_model=metric_name,
push_to_hub=True,
logging_dir="./runs",
)
import numpy as np
from evaluate import load
metric = load("accuracy")
def compute_metrics(eval_pred):
print(eval_pred)
predictions, labels = eval_pred
predictions = np.argmax(predictions, axis=1)
return metric.compute(predictions=predictions, references=labels)
from transformers import Trainer
trainer = Trainer(
model,
training_args,
train_dataset=subset_dataset_train,
eval_dataset=subset_dataset_validation,
tokenizer=tokenizer,
compute_metrics=compute_metrics,
)
trainer.train()
trainer.evaluate(eval_dataset=subset_dataset_test)
trainer.create_model_card()
trainer.push_to_hub()
Copy

Probar el modelolink image 20

Vamos aprobar el modelo

	
from transformers import TrainingArguments
metric_name = "accuracy"
model_name = "GPT2-small-QLoRA-finetuned-amazon-reviews-en-classification"
LR = 2e-5
BS_TRAIN = 224
BS_EVAL = 224
EPOCHS = 3
WEIGHT_DECAY = 0.01
training_args = TrainingArguments(
model_name,
eval_strategy="epoch",
save_strategy="epoch",
learning_rate=LR,
per_device_train_batch_size=BS_TRAIN,
per_device_eval_batch_size=BS_EVAL,
num_train_epochs=EPOCHS,
weight_decay=WEIGHT_DECAY,
lr_scheduler_type="cosine",
warmup_ratio = 0.1,
fp16=True,
load_best_model_at_end=True,
metric_for_best_model=metric_name,
push_to_hub=True,
logging_dir="./runs",
)
import numpy as np
from evaluate import load
metric = load("accuracy")
def compute_metrics(eval_pred):
print(eval_pred)
predictions, labels = eval_pred
predictions = np.argmax(predictions, axis=1)
return metric.compute(predictions=predictions, references=labels)
from transformers import Trainer
trainer = Trainer(
model,
training_args,
train_dataset=subset_dataset_train,
eval_dataset=subset_dataset_validation,
tokenizer=tokenizer,
compute_metrics=compute_metrics,
)
trainer.train()
trainer.evaluate(eval_dataset=subset_dataset_test)
trainer.create_model_card()
trainer.push_to_hub()
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
model_name = "GPT2-small-QLoRA-finetuned-amazon-reviews-en-classification"
user = "maximofn"
checkpoint = f"{user}/{model_name}"
num_classes = 5
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=num_classes).half().eval().to("cuda")
Copy
	
`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`...
/usr/local/lib/python3.10/dist-packages/torch/utils/checkpoint.py:464: UserWarning: torch.utils.checkpoint: the use_reentrant parameter should be passed explicitly. In version 2.4 we will raise an exception if use_reentrant is not passed. use_reentrant=False is recommended, but if you need to preserve the current default behavior, you can pass use_reentrant=True. Refer to docs for more details on the differences between the two variants.
warnings.warn(
/home/sae00531/miniconda3/envs/nlp_/lib/python3.11/site-packages/huggingface_hub/file_download.py:1132: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.
warnings.warn(
Some weights of GPT2ForSequenceClassification were not initialized from the model checkpoint at openai-community/gpt2 and are newly initialized: ['score.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
/home/sae00531/miniconda3/envs/nlp_/lib/python3.11/site-packages/peft/tuners/lora/layer.py:1119: UserWarning: fan_in_fan_out is set to False but the target module is `Conv1D`. Setting fan_in_fan_out to True.
warnings.warn(
Loading adapter weights from maximofn/GPT2-small-QLoRA-finetuned-amazon-reviews-en-classification led to unexpected keys not found in the model: ['score.modules_to_save.default.base_layer.weight', 'score.modules_to_save.default.lora_A.default.weight', 'score.modules_to_save.default.lora_B.default.weight', 'score.modules_to_save.default.modules_to_save.lora_A.default.weight', 'score.modules_to_save.default.modules_to_save.lora_B.default.weight', 'score.modules_to_save.default.original_module.lora_A.default.weight', 'score.modules_to_save.default.original_module.lora_B.default.weight'].
	
tokens = tokenizer.encode("I love this product", return_tensors="pt").to(model.device)
with torch.no_grad():
output = model(tokens)
logits = output.logits
lables = torch.softmax(logits, dim=1).cpu().numpy().tolist()
lables[0]
Copy
	
[0.0186614990234375,
0.483642578125,
0.048187255859375,
0.415283203125,
0.03399658203125]

Seguir leyendo

DoLa – Decoding by Contrasting Layers Improves Factuality in Large Language Models

DoLa – Decoding by Contrasting Layers Improves Factuality in Large Language Models

¿Alguna vez has hablado con un LLM y te ha respondido algo que suena como si hubiera estado bebiendo café de máquina durante toda la noche? 😂 ¡Eso es lo que llamamos una alucinación en el mundo de los LLMs! Pero no te preocupes, porque no es que tu modelo de lenguaje esté loco (aunque a veces puede parecerlo 🤪). La verdad es que los LLMs pueden ser un poco... creativos cuando se trata de generar texto. Pero gracias a DoLa, un método que utiliza capas de contraste para mejorar la factibilidad de los LLMs, podemos evitar que nuestros modelos de lenguaje se conviertan en escritores de ciencia ficción 😂. En este post, te explicaré cómo funciona DoLa y te mostraré un ejemplo de código para que puedas entender mejor cómo hacer que tus LLMs sean más fiables y menos propensos a inventar historias. ¡Vamos a salvar a nuestros LLMs de la locura y hacer que sean más útiles! 🚀

Últimos posts -->

¿Has visto estos proyectos?

Subtify

Subtify Subtify

Generador de subtítulos para videos en el idioma que desees. Además a cada persona le pone su subtítulo de un color

Ver todos los proyectos -->

¿Quieres aplicar la IA en tu proyecto? Contactame!

¿Quieres mejorar con estos tips?

Últimos tips -->

Usa esto en local

Los espacios de Hugging Face nos permite ejecutar modelos con demos muy sencillas, pero ¿qué pasa si la demo se rompe? O si el usuario la elimina? Por ello he creado contenedores docker con algunos espacios interesantes, para poder usarlos de manera local, pase lo que pase. De hecho, es posible que si pinchas en alún botón de ver proyecto te lleve a un espacio que no funciona.

Ver todos los contenedores -->

¿Quieres aplicar la IA en tu proyecto? Contactame!

¿Quieres entrenar tu modelo con estos datasets?

short-jokes-dataset

Dataset de chistes en inglés

opus100

Dataset con traducciones de inglés a español

netflix_titles

Dataset con películas y series de Netflix

Ver más datasets -->