Saltar al contenido principal

Capítulo 2: Cuantización de 4 bits con bitsandbytes

Comprensión e implementación de cuantización eficiente​

La biblioteca bitsandbytes permite la cuantización de 4 y 8 bits para una carga y entrenamiento de modelos eficiente.


Fundamentos de la cuantización​

¿Qué es la cuantización?​

Conversión de valores de alta precisión a menor precisión:

Float16: 2 bytes por parámetro
Int8: 1 byte por parámetro
Int4: 0.5 bytes por parámetro

Ahorro de memoria​

# Memoria de un modelo de 7B
fp32 = 7e9 * 4 / 1e9 # 28 GB
fp16 = 7e9 * 2 / 1e9 # 14 GB
int8 = 7e9 * 1 / 1e9 # 7 GB
int4 = 7e9 * 0.5 / 1e9 # 3.5 GB

BitsAndBytesConfig​

Configuración completa​

from transformers import BitsAndBytesConfig
import torch

bnb_config = BitsAndBytesConfig(
# Usar cuantización de 4 bits
load_in_4bit=True,

# Tipo de cuantización NF4 (recomendado)
bnb_4bit_quant_type="nf4",

# Calcular en bfloat16 para mayor estabilidad
bnb_4bit_compute_dtype=torch.bfloat16,

# Doble cuantización para mayor ahorro
bnb_4bit_use_double_quant=True,
)

Explicación de los parámetros​

ParámetroOpcionesRecomendación
load_in_4bitTrue/FalseTrue para QLoRA
bnb_4bit_quant_type"nf4", "fp4""nf4" (mejor calidad)
bnb_4bit_compute_dtypetorch.bfloat16, torch.float16bfloat16 (más estable)
bnb_4bit_use_double_quantTrue/FalseTrue (mayor ahorro)

NF4 vs FP4​

NF4 (NormalFloat4)​

  • Diseñado para pesos con distribución normal
  • Valores iguales en cada bin de cuantización
  • Mejor calidad para redes neuronales

FP4 (Float4)​

  • Cuantización uniforme
  • Más simple pero menos óptima para pesos
# Se recomienda NF4
bnb_4bit_quant_type="nf4"

Carga de un modelo cuantizado​

from transformers import AutoModelForCausalLM, AutoTokenizer

model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3.1-8B",
quantization_config=bnb_config,
device_map="auto",
torch_dtype=torch.bfloat16,
)

tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.1-8B")

# Verificar memoria
print(f"Memory: {torch.cuda.memory_allocated() / 1e9:.2f} GB")

Preparación para el entrenamiento​

from peft import prepare_model_for_kbit_training

# Pasos necesarios de preparación:
# 1. Habilitar gradient checkpointing
# 2. Convertir layer norms a float32
# 3. Habilitar gradientes de entrada

model = prepare_model_for_kbit_training(model)

Qué hace prepare_model_for_kbit_training​

  1. Habilita gradient checkpointing para eficiencia de memoria
  2. Convierte layer norms a float32 para mayor estabilidad
  3. Configura requires_grad adecuadamente
  4. Gestiona embeddings para gradientes de entrada

Configuración completa de QLoRA​

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training

# 1. Configuración de cuantización
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True,
)

# 2. Cargar modelo cuantizado
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3.1-8B",
quantization_config=bnb_config,
device_map="auto",
)

# 3. Preparar para entrenamiento
model = prepare_model_for_kbit_training(model)

# 4. Añadir LoRA
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.05,
task_type="CAUSAL_LM",
)

model = get_peft_model(model, lora_config)
model.print_trainable_parameters()

Puntos clave​

✅ bitsandbytes permite cuantización de 4 y 8 bits
✅ NF4 es óptimo para pesos de redes neuronales
✅ La doble cuantización añade ~11% de ahorro adicional
✅ Use prepare_model_for_kbit_training antes de añadir LoRA
✅ El tipo de dato de cómputo debe ser bfloat16 para mayor estabilidad


Continuar al Capítulo 3: Entrenamiento eficiente en memoria →