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ámetro | Opciones | Recomendación |
|---|---|---|
load_in_4bit | True/False | True para QLoRA |
bnb_4bit_quant_type | "nf4", "fp4" | "nf4" (mejor calidad) |
bnb_4bit_compute_dtype | torch.bfloat16, torch.float16 | bfloat16 (más estable) |
bnb_4bit_use_double_quant | True/False | True (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
- Habilita gradient checkpointing para eficiencia de memoria
- Convierte layer norms a float32 para mayor estabilidad
- Configura requires_grad adecuadamente
- 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 →