La cuantización de modelos de lenguaje grandes (LLMs) es crucial para desplegarlos en hardware con recursos limitados. Un modelo LLM sin cuantizar, como Qwen3:30b, puede ocupar cerca de 60GB, lo que impide su ejecución en una sola GPU de consumo (por ejemplo, una 4090/5090). La cuantización, especialmante a 4 bits (q4), permite la inferencia en una única tarjeta gráfica.
Estrategias de Cuantización
Existen dos enfoques principales para la cuantización:
- Cuantización en Tiempo de Inferencia (On-the-fly Quantization): El motor de inferencia cuantiza los pesos del modelo en tiempo real al cargarlos.
quantization='awq'
Este método puede resultar en una carga de modelo más lenta y pérdida de precisión. El algoritmo AWQ, en particular, requiere un conjunto de datos de calibración para minimizar la discrepancia entre los parámetros de alta y baja precisión, ya que la conversión no siempre es una correspondencia simétrica.
- Pre-cuantización (Pre-quantization): Los pesos del modelo se cuantizan previamente utilizando herramientas como
autoawq. Posteriormente, el modelo cuantizado se carga directamente para la inferencia. Este enfoque acelera la carga y puede ofrecer un mejor control sobre la precisión.
Uso de LLM-Compressor para Cuantización AWQ
LLM-Compressor es una herramienta dentro del ecosistema vLLM que integra algoritmos de cuantización comunes, incluyendo AWQ. A continuación, se detalla su uso:
from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer
from llmcompressor import oneshot
from llmcompressor.modifiers.awq import AWQModifier
from llmcompressor.utils import dispatch_for_generation
# --- Configuración del Modelo y Directorios ---
MODEL_ID = '/ruta/a/tu/modelo/Qwen3-30B-A3B/'
SAVE_DIR = "/ruta/a/tu/directorio/salida/qwen3-30b-a3b-awq"
# --- Configuración del Conjunto de Datos de Calibración ---
DATASET_ID = "/ruta/a/tu/datos/calibracion/" # Ejemplo: "mit-han-lab/pile-val-backup" si se usa el predeterminado
DATASET_SPLIT = "validation"
# --- Parámetros de Calibración ---
# Aumentar el número de muestras y la longitud de secuencia mejora la precisión.
NUM_CALIBRATION_SAMPLES = 512 # Aumentado desde 128
MAX_SEQUENCE_LENGTH = 4096 # Aumentado para mayor contexto
# --- Carga y Preprocesamiento del Conjunto de Datos ---
ds = load_dataset(DATASET_ID, split=f"{DATASET_SPLIT}[:{NUM_CALIBRATION_SAMPLES}]")
ds = ds.shuffle(seed=42)
# --- Carga del Modelo y Tokenizador ---
model = AutoModelForCausalLM.from_pretrained(MODEL_ID, torch_dtype="auto", device_map="auto")
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
# --- Función de Preprocesamiento (Ejemplo para datos de chat) ---
def preprocess_chat_data(example):
# Adapta esta función a la estructura de tu conjunto de datos y plantilla de chat.
# Este es un ejemplo simplificado.
formatted_prompt = tokenizer.apply_chat_template(
[{"role": "user", "content": example.get("text", "")}], # Asume una clave 'text'
tokenize=False,
add_generation_prompt=True
)
return {"text": formatted_prompt}
ds = ds.map(preprocess_chat_data)
# --- Tokenización de las Entradas ---
def tokenize_input(sample):
return tokenizer(
sample["text"],
padding=False,
max_length=MAX_SEQUENCE_LENGTH,
truncation=True,
add_special_tokens=False,
)
# --- Configuración de la Receta AWQ ---
# La configuración 'ignore' es crucial para mantener la calidad.
# Se pueden ajustar parámetros como 'group_size' para optimizar.
recipe = [
AWQModifier(
scheme="W4A16", # Cuantización de 4 bits con activación de 16 bits
targets=["Linear"], # Aplicar a capas lineales
ignore=[
"lm_head", # Ignorar la cabeza del modelo
"embed_tokens", # Ignorar la capa de embeddings
"re:.*norm.*", # Ignorar todas las capas de normalización
"re:.*mlp.gate$", # Ignorar puertas de capas MLP (si existen)
"re:.*mlp.shared_expert_gate$", # Ignorar puertas de expertos compartidos en MLP
"re:.*attention.*output.*", # Considerar ignorar capas de salida de atención
],
# Parámetros opcionales para mejorar la calidad de la cuantización:
# group_size=128, # Reducir el tamaño del grupo puede mejorar la precisión.
# dampening_frac=0.01, # Ajustar el factor de amortiguación.
# max_damp_count=200, # Incrementar el número máximo de pasos de amortiguación.
),
]
# --- Aplicación de la Cuantización ---
print("Iniciando proceso de cuantización one-shot...")
oneshot(
model=model,
dataset=ds,
recipe=recipe,
max_seq_length=MAX_SEQUENCE_LENGTH,
num_calibration_samples=NUM_CALIBRATION_SAMPLES,
)
print("Cuantización completada.")
# --- Verificación de Generación del Modelo Cuantizado ---
print("\n========== EJEMPLO DE GENERACIÓN ===========")
dispatch_for_generation(model) # Prepara el modelo para generación (si es necesario por vLLM)
input_text = "Hello my name is"
input_ids = tokenizer(input_text, return_tensors="pt").input_ids.to(model.device)
output = model.generate(input_ids, max_new_tokens=100, do_sample=True, top_p=0.9, temperature=0.7)
print(f"Input: {input_text}")
print(f"Output: {tokenizer.decode(output[0])}")
print("==========================================\n")
# --- Guardado del Modelo y Tokenizador Cuantizados ---
print(f"Guardando modelo cuantizado en: {SAVE_DIR}")
model.save_pretrained(SAVE_DIR, save_compressed=True)
tokenizer.save_pretrained(SAVE_DIR)
print("Modelo y tokenizador guardados exitosamente.")
Notas Importantes
- Conjunto de Datos de Calibración: Si no se especifica
DATASET_ID, el código descargará un conjunto de datos predeterminado ("mit-han-lab/pile-val-backup"). En entornos con proxies de red restrictivos (como servidores en la nube), es preferible descargar manualmente los datos de calibración y colocarlos en el directorio especificado. - Campo
ignoreen la Receta: La configuración del campoignorees fundamental para minimizar la pérdida de rendimiento. Además de las capas comúnmente excluidas (lm_head, capas MLP específicas), se han añadido capas de embeddings y normalización, así como capas de salida de atención, para preservar la calidad del modelo. Experimentar con parámetros comogroup_size,dampening_fracymax_damp_countpuede refinar aún más la calidad de la cuantización.
Tamaño del Modelo Post-Cuantización
Tras la cuantización, el tamaño total del modelo en formato safetensors se reduce significativamente. Para el modelo Qwen3-30B-A3B, el tamaño disminuye de unos 56GB a aproximadamente 15.6GB. Las pruebas de inferencia con vLLM han demostrado que funcionalidades como la extracción de entidades y la descomposición de tareas se mantienen sin una degradación perceptible del rendimiento. Se planean evaluaciones más exhaustivas con conjuntos de datos específicos una vez que las funcionalidades del producto estén completamente implementadas.