Ajuste fino de Qwen3 con LoRA usando NVIDIA NeMo AutoModel: un tutorial completo de flujo de trabajo de Google Colab con una sola GPU

En este tutorial, creamos un flujo de trabajo NVIDIA NeMo AutoModel de extremo a extremo en Google Colab y utilizamos una única GPU para explorar la misma arquitectura de entrenamiento basada en configuración que escala a entornos distribuidos de múltiples GPU. Verificamos el hardware CUDA disponible y el soporte de precisión, instalamos NeMo AutoModel directamente desde su repositorio de origen, cargamos una receta oficial de ajuste fino de LoRA Qwen3-0.6B y adaptamos mediante programación su precisión, tamaño de lote, puntos de control y configuración del programador para un tiempo de ejecución de Colab restringido. Luego lanzamos un ajuste fino eficiente en los parámetros a través de la interfaz de línea de comandos del automodelo, localizamos y recargamos el punto de control LoRA generado y comparamos los resultados de los modelos originales y ajustados. Finalmente, utilizamos NeMoAutoModelForCausalLM a través de la API de Python para demostrar cómo NeMo AutoModel integra rutas de ejecución optimizadas por NVIDIA mientras conserva la interfaz familiar del modelo Hugging Face.

Configurar el espacio de trabajo de Colab y el asistente de Shell

importar os, sys, glob, json, subproceso, Shuil, textwrap REPO_DIR = “/content/Automodel” WORK_DIR = “/content/automodel_demo” CKPT_DIR = os.path.join(WORK_DIR, “checkpoints”) os.makedirs(WORK_DIR, exist_ok=True) def sh(cmd, check=True): print(f”\n$ {cmd}\n” + “-” * 78) p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1) para la línea en p.stdout: print(line, end=””) p.wait() if check y p.returncode!= 0: elevar RuntimeError(f”Command falló ({p.returncode}): {cmd}”) devuelve p.returncode

Importamos las bibliotecas principales de Python necesarias para el manejo de archivos, la ejecución de procesos, la gestión de rutas y la salida formateada. Definimos los directorios de repositorio, de trabajo y de punto de control utilizados en todo el flujo de trabajo. También creamos una función de comando de shell reutilizable que transmite la salida del comando y genera errores cuando falla la ejecución.

Verificación de la GPU e instalación de NeMo AutoModel

print(“=” * 78) print(“PASO 0: Comprobar el tiempo de ejecución de la GPU”) print(“=” * 78) import torch afirmar torch.cuda.is_available(), (“¡No se encontró ninguna GPU! En Colab: Tiempo de ejecución -> Cambiar tipo de tiempo de ejecución -> seleccionar una GPU.” ) GPU_NAME = torch.cuda.get_device_name(0) BF16_OK = torch.cuda.is_bf16_supported() VRAM_GB = torch.cuda.get_device_properties(0).total_memory / 1e9 print(f”GPU: {GPU_NAME} | VRAM: {VRAM_GB:.1f} GB | bf16 compatible: {BF16_OK}”) print(“\n” + “=” * 78) print(“PASO 1: Instalar NeMo AutoModel (tarda unos minutos)”) print(“=” * 78) si no os.path.isdir(REPO_DIR): sh(f”git clone –profundidad 1 https://github.com/NVIDIA-NeMo/Automodel.git {REPO_DIR}”) sh(f”pip -q install -e {REPO_DIR}”) sh(“pip -q install pyyaml peft”) sh(‘python -c “import nemo_automodel; print(\’NeMo AutoModel version:\’, ‘ ‘getattr(nemo_automodel, \’__version__\’, \’source\’))”‘)

Verificamos que el tiempo de ejecución de Colab proporcione una GPU habilitada para CUDA e inspeccionamos su nombre, capacidad de memoria y compatibilidad con bfloat16. Clonamos el repositorio de NVIDIA NeMo AutoModel cuando aún no está disponible e instalamos el paquete directamente desde la fuente. Luego instalamos las bibliotecas YAML y PEFT compatibles y confirmamos que el paquete NeMo AutoModel se importa correctamente.

Cargando y parcheando la receta Qwen3 LoRA

print(“\n” + “=” * 78) print(“PASO 2: Preparar la receta”) print(“=” * 78) importar candidatos yaml = sorted(glob.glob( os.path.join(REPO_DIR, “examples”, “llm_finetune”, “qwen”, “*0p6b*peft*.yaml”) )) or sorted(glob.glob( os.path.join(REPO_DIR, “examples”, “llm_finetune”, “**”, “*peft*.yaml”), recursive=True, )) afirman candidatos, “No se pudo encontrar una receta PEFT en el repositorio clonado”. BASE_RECIPE = candidatos[0]
print(f”Receta base: {os.path.relpath(BASE_RECIPE, REPO_DIR)}”) con open(BASE_RECIPE) como f: cfg = yaml.safe_load(f) print(“\n— Receta original (tal como se envió) —“) print(yaml.dump(cfg, sort_keys=False)[:2500]) def parche(nodo): si isinstance(nodo, dict): para k, v en lista(node.items()): si isinstance(v, str) y no BF16_OK y v.lower() in ( “bf16”, “bfloat16”, “torch.bfloat16”): nodo[k] = “float32” elif k in (“batch_size”, “local_batch_size”) e isinstance(v, int): nodo[k] = min(v, 4) elif k == “global_batch_size” e isinstance(v, int): nodo[k] = min(v, 8) else: parche(v) elif isinstance(nodo, lista): para elemento en el nodo: parche(elemento) parche(cfg) cfg.setdefault(“step_scheduler”, {}) cfg[“step_scheduler”][“max_steps”] = 40 pies cúbicos[“step_scheduler”][“ckpt_every_steps”] = 40 pies cúbicos[“step_scheduler”][“num_epochs”] = 1 si esinstancia(cfg.get(“punto de control”), dict): cfg[“checkpoint”][“enabled”] = cfg verdadero[“checkpoint”][“checkpoint_dir”] = CKPT_DIR DEMO_RECIPE = os.path.join(WORK_DIR, “qwen3_0p6b_colab_lora.yaml”) con open(DEMO_RECIPE, “w”) como f: yaml.dump(cfg, f, sort_keys=False) print(“\n— Receta parcheada (lo que realmente ejecutaremos) —“) print(yaml.dump(cfg, sort_keys=Falso)[:2500]) MODEL_ID = “Qwen/Qwen3-0.6B” prueba: MODEL_ID = cfg[“model”][“pretrained_model_name_or_path”]

excepto Excepción: pasar print(f”\nModelo base: {MODEL_ID}”)

Localizamos una receta PEFT oficial, cargamos su configuración YAML e inspeccionamos la configuración de entrenamiento original. Adaptamos recursivamente los parámetros de precisión y tamaño de lote para que se ajusten a la receta en una única GPU Colab preservando su estructura original. También limitamos la duración del entrenamiento, configuramos la salida del punto de control, guardamos la receta parcheada y extraemos el identificador del modelo Hugging Face.

Ejecutando el ajuste fino de LoRA en HellaSwag

print(“\n” + “=” * 78) print(“PASO 3 — Entrenamiento (ajuste LoRA de Qwen3-0.6B en HellaSwag)”) print(“=” * 78) env_prefix = “HF_HUB_ENABLE_HF_TRANSFER=0 TOKENIZERS_PARALLELISM=false” rc = sh(f”cd {WORK_DIR} && {env_prefix} automodel {DEMO_RECIPE}”, check=False) if rc != 0: print(“\nReintentando con la sintaxis CLI heredada…”) sh(f”cd {WORK_DIR} && {env_prefix} automodel finetune llm -c {DEMO_RECIPE}”)

Lanzamos el ajuste fino de Qwen3-0.6B LoRA en el conjunto de datos de HellaSwag a través de la interfaz de línea de comandos de NeMo AutoModel. Desactivamos las funciones innecesarias de transferencia de Hugging Face y paralelismo del tokenizador para que Colab se ejecute de manera más predecible. También incluimos un comando alternativo que admite la sintaxis anterior de la CLI de NeMo AutoModel cuando falla la invocación principal.

Comparación de los resultados del modelo base y ajustado

print(“\n” + “=” * 78) print(“PASO 4 — Evaluación: modelo base versus modelo ajustado LoRA”) print(“=” * 78) from transformadores import AutoModelForCausalLM, AutoTokenizer DTYPE = torch.bfloat16 if BF16_OK else torch.float32 PROMPT = (“Un hombre está sentado en un techo. Comienza a levantar tejas. ” “¿Qué pasa después?”) def generar (modelo, tok, solicitud, max_new_tokens = 60): entradas = tok (prompt, return_tensors=”pt”). pad_token_id=tok.eos_token_id) devolver tok.decode(fuera[0][inputs[“input_ids”].forma[1]:], skip_special_tokens=True) tok = AutoTokenizer.from_pretrained(MODEL_ID) base = AutoModelForCausalLM.from_pretrained( MODEL_ID, torch_dtype=DTYPE, device_map=”cuda”) print(“\n[BASE MODEL]”) print(textwrap.fill(generate(base, tok, PROMPT), 90)) ckpt_glob = sorted(glob.glob(os.path.join(CKPT_DIR, “**”, “model”), recursive=True)) si no ckpt_glob: ckpt_glob = sorted(glob.glob(os.path.join(WORK_DIR, “**”, “adapter_model.safetensors”), recursivo=Verdadero)) ckpt_glob = [os.path.dirname(p) for p in ckpt_glob]
si ckpt_glob: ADAPTER_DIR = ckpt_glob[-1]
print(f”\nPunto de control encontrado: {ADAPTER_DIR}”) prueba: desde peft import PeftModel tuned = PeftModel.from_pretrained(base, ADAPTER_DIR) print(“\n[FINE-TUNED MODEL (base + LoRA adapter)]”) print(textwrap.fill(generate(tuned, tok, PROMPT), 90)) excepto excepción como e: print(f”\nNo se pudo cargar automáticamente el adaptador con peft ({e}).”) print(“Inspeccionar el contenido del punto de control manualmente:”) para p en glob.glob(os.path.join(ADAPTER_DIR, “*”))[:20]: print(” “, p) else: print(“\nNo se encontró ningún punto de control; verifique los registros de entrenamiento anteriores.”) del base torch.cuda.empty_cache()

Cargamos el tokenizador y el modelo de lenguaje causal base, generamos una respuesta determinista y establecemos una línea de base para la comparación. Buscamos en los directorios de resultados de capacitación los últimos archivos de adaptador o punto de control de LoRA creados durante el ajuste fino. Luego conectamos el adaptador con PEFT, generamos la respuesta ajustada y liberamos la memoria de la GPU después de la evaluación.

Uso de la API Python de NeMo AutoModel

print(“\n” + “=” * 78) print(“PASO 5 – Bonificación: API de Python integrada”) print(“=” * 78) intente: desde nemo_automodel import NeMoAutoModelForCausalLM nm = NeMoAutoModelForCausalLM.from_pretrained( MODEL_ID, torch_dtype=DTYPE).to(“cuda”) print(“[NeMoAutoModelForCausalLM]”) print(textwrap.fill(generate(nm, tok, “La idea clave de LoRA es”), 90)) del nm torch.cuda.empty_cache() excepto Excepción como e: print(f”Demo de Python-API omitida en esta versión/GPU: {e}”) print(“\n” + “=” * 78) print(“¡LISTO! A dónde ir a continuación:”) print(“=” * 78) print(f””” 1. Recetas para explorar (en {REPO_DIR}/examples/): llm_finetune/ — SFT + LoRA para Llama, Qwen, Gemma, Phi, GPT-OSS, … llm_pretrain/ — por ejemplo, nanoGPT en FineWeb, entrenamiento previo de DeepSeek-V3 vlm_finetune/ — Qwen-VL, Gemma-3-VL y otros modelos de lenguaje visual diffusion/ — Ajuste fino de FLUX / Wan / Qwen-Image LoRA 2. Intercambie el modelo: anule un campo — automodel receta.yaml –model.pretrained_model_name_or_path 3. Ampliación horizontal: la MISMA receta se ejecuta en 8 GPU con `–nproc-per-node 8`, o en múltiples nodos a través de los lanzadores slurm.sub / SkyPilot / Kubernetes enviados 4. Documentos: https://docs.nvidia.com/nemo/automodel/latest/index.html “””)

Demostramos la interfaz directa de Python cargando el modelo a través de NeMoAutoModelForCausalLM y ejecutando un ejemplo de generación adicional. Manejamos las fallas específicas de la versión o del hardware con elegancia para que la computadora portátil aún pueda completarse exitosamente. Concluimos presentando las categorías de recetas disponibles, la sintaxis de anulación de modelo, las opciones de escalamiento distribuido y la ruta de documentación oficial.

Conclusión

En conclusión, establecimos un proceso práctico de NeMo AutoModel que cubre la validación del entorno, la instalación de fuentes, la inspección de recetas, la aplicación de parches de configuración, la capacitación de LoRA, la recuperación de puntos de control, la evaluación de modelos y la inferencia directa de la API de Python. Vimos cómo NeMo AutoModel separa la estrategia de entrenamiento distribuido del código de la aplicación especificando el modelo, el conjunto de datos, el optimizador, la precisión, el paralelismo y el comportamiento del punto de control a través de recetas YAML reutilizables. Aunque ejecutamos el flujo de trabajo en una sola GPU Colab, conservamos la misma estructura orientada a SPMD utilizada para implementaciones más grandes de FSDP2, paralelo a tensor, paralelo a contexto, paralelo a secuencia y paralelo a canalización. Nos brinda un punto de partida técnicamente fundamentado para adaptar recetas adicionales de modelo de lenguaje, lenguaje de visión, preentrenamiento y difusión mientras escalamos el mismo flujo de trabajo desde la experimentación hasta la infraestructura NVIDIA de múltiples nodos.

Consulte el código completo aquí. Además, no dude en seguirnos en Twitter y no olvide unirse a nuestro SubReddit de más de 150.000 ml y suscribirse a nuestro boletín. ¡Esperar! estas en telegrama? Ahora también puedes unirte a nosotros en Telegram.

¿Necesita asociarse con nosotros para promocionar su repositorio de GitHub O su página principal de Hugging O su lanzamiento de producto O seminario web, etc.? Conéctate con nosotros

A Sana Hassan, pasante de consultoría en Marktechpost y estudiante de doble titulación en IIT Madras, le apasiona aplicar la tecnología y la inteligencia artificial para abordar los desafíos del mundo real. Con un gran interés en resolver problemas prácticos, aporta una nueva perspectiva a la intersección de la IA y las soluciones de la vida real.