AI DevelopmentLLMs locales

MLX + mlx-lm: LLMs en tu Mac, de verdad

22 de septiembre de 2026
12 min de lectura
Chip Apple Silicon corriendo una terminal con LLM local
Compartir:

Correr un LLM capaz en tu propia Mac solía significar pelear con límites de VRAM, tooling solo-CUDA o pagar GPUs en la nube por hora. El stack MLX de Apple cambia la ecuación: trata la memoria unificada del Mac como un solo pool, habla Metal de forma nativa y con el paquete mlx-lm generás, cuantizás y fine-tuneás modelos con un puñado de comandos.

En este deep-dive te muestro el loop completo: instalar mlx-lm, chatear con el Llama-3.2-3B default, convertir y cuantizar a 4-bit cualquier modelo de Hugging Face en segundos, fine-tunearlo con LoRA sobre los pesos cuantizados y publicarlo en mlx-community. Más números reales M5 vs M4 del research post de Apple de septiembre 2026.

1. El Problema: gran Mac, sin buena forma de correr LLMs

La mayoría del tooling LLM open se diseñó alrededor de GPUs NVIDIA: VRAM discreta, kernels CUDA y servidores que nunca duermen. Una MacBook con 18GB o 36GB de memoria unificada parece débil en papel frente a una H100 de 80GB, pero en la práctica el Mac puede sostener un modelo 14B en memoria mientras la factura del cloud sigue corriendo. La pieza faltante era software que use de verdad la arquitectura Apple Silicon en vez de emular el mundo CUDA.

El Error Más Común

Instalar un stack genérico x86 en el Mac y concluir “Apple Silicon es muy lento para LLMs”. Sin ejecución consciente de la memoria unificada ni kernels Metal, dejás la mayor parte del ancho de banda sin usar. MLX existe justamente para cerrar ese gap.

Eso es lo que resuelve MLX: un framework de arrays open-source (API tipo NumPy) construido para Apple Silicon, con soporte de primera clase para entrenamiento e inferencia. Y mlx-lm es su capa LLM: miles de modelos de Hugging Face con un comando, cuantización, fine-tuning LoRA y QLoRA, servidor compatible con OpenAI e inferencia distribuida entre varios Macs.

2. Los Conceptos Mínimos que de verdad necesitás

🧠

Memoria unificada

CPU y GPU comparten el mismo pool de memoria, así que MLX corre ops en cualquiera sin copiar pesos. Más memoria unificada = modelos más grandes: ~8B con 18GB, clase 30B+ con 64GB+, y 70B en Macs de alta memoria.

Metal + Neural Accelerators del M5

MLX apunta a TensorOps de Metal 4 y cada núcleo GPU del M5 trae un Neural Accelerator dedicado a multiplicación de matrices. El prefill (time to first token) vuela; el decode sigue limitado por ancho de banda.

🗜

Cuantización 4-bit

Bajar los pesos a 4 bits recorta la memoria ~4x con poca pérdida de calidad. mlx_lm.convert cuantiza un 7B en segundos en un Mac, y cada repo *-4bit de mlx-community está listo para descargar y correr.

🧪

LoRA / QLoRA

Low-Rank Adaptation entrena matricitas adaptadoras en vez de todos los pesos. Apuntá mlx_lm.lora a un modelo cuantizado y tenés QLoRA: fine-tuning de un 7B en un Mac de 32GB a ~250 tokens/seg.

Versiones usadas en esta guía

mlx-lm 0.31.3 (abril 2026, última estable en PyPI), Python 3.8+, macOS 15+ recomendado para wiring de memoria en modelos grandes. Modelo default de generate y chat: mlx-community/Llama-3.2-3B-Instruct-4bit.

3. Tutorial: de cero a modelo fine-tuneado

Todo lo de abajo corre en cualquier Mac Apple Silicon. Sin cloud, sin Docker, sin CUDA. Uso los comandos exactos del README y la doc de LoRA de mlx-lm, así que corré mlx_lm.<comando> --help cuando quieras la lista completa de opciones.

Paso 1 — Instalar

pip install mlx-lm
pip install "mlx-lm[train]"  # necesario para fine-tuning LoRA

Paso 2 — Generar y chatear

El modelo default se descarga solo en la primera corrida. El chat conserva contexto durante toda la sesión REPL.

mlx_lm.generate --model mlx-community/Llama-3.2-3B-Instruct-4bit --prompt "Explicá la memoria unificada en un párrafo"
mlx_lm.chat --model mlx-community/Llama-3.2-3B-Instruct-4bit

Paso 3 — API Python

from mlx_lm import load, generate
model, tokenizer = load("mlx-community/Mistral-7B-Instruct-v0.3-4bit")
messages = [{"role": "user", "content": "Escribí un haiku sobre Metal"}]
prompt = tokenizer.apply_chat_template(messages, add_generation_prompt=True)
text = generate(model, tokenizer, prompt=prompt, verbose=True)

Paso 4 — Convertir y cuantizar a 4-bit cualquier modelo HF

El flag -q cuantiza; --upload-repo publica directo en la org mlx-community. Convertir un 7B toma segundos en un Mac.

mlx_lm.convert --model mistralai/Mistral-7B-Instruct-v0.3 -q
mlx_lm.convert --model mistralai/Mistral-7B-Instruct-v0.3 -q --upload-repo mlx-community/my-4bit-mistral

Paso 5 — Fine-tunear con LoRA (o QLoRA)

Si --model apunta a un modelo cuantizado, tenés QLoRA automáticamente. Data es una carpeta con train.jsonl más valid.jsonl opcional, o un dataset id de Hugging Face. Para la lista completa corré mlx_lm.lora --help; para corridas repetibles usá YAML con mlx_lm.lora --config config.yaml.

mlx_lm.lora --model mistralai/Mistral-7B-v0.1 --train --data ./my-data --iters 600
mlx_lm.lora --model mlx-community/Mistral-7B-v0.3-4bit --train --data ./my-data --iters 1000 --batch-size 1 --num-layers 4

Paso 6 — Evaluar, generar, fusionar, subir

Medí perplejidad con --test, generá con el adapter puesto, y fusioná adapters en los pesos para un modelo standalone, opcionalmente con upload o export a GGUF.

mlx_lm.lora --model <path_to_model> --adapter-path ./adapters --data ./my-data --test
mlx_lm.generate --model <path_to_model> --adapter-path ./adapters --prompt "Traducí al español: good morning"
mlx_lm.fuse --model mistralai/Mistral-7B-v0.1 --upload-repo mlx-community/my-lora-mistral-7b --hf-path mistralai/Mistral-7B-v0.1

Paso 7 — Servirlo (compatible OpenAI)

mlx_lm.server --model mlx-community/Mistral-7B-Instruct-v0.3-4bit
curl localhost:8080/v1/models -H "Content-Type: application/json"

Regla de oro

Siempre corré mlx_lm.<comando> --help antes de scriptear nada. mlx-lm evoluciona rápido (0.31.3 en abril 2026) y el help del CLI es la única lista de flags que no puede estar desactualizada.

4. Benchmarks: M5 vs M4 con MLX (Apple, sep 2026)

El equipo de Machine Learning Research de Apple publicó números de inferencia LLM en MacBook Pro M5 vs M4 usando MLX, con la release de macOS que habilita los Neural Accelerators del M5. El patrón es limpio: el procesamiento del prompt (prefill) está limitado por cómputo y mejora hasta ~4x, mientras la generación de tokens (decode) está limitada por ancho de banda y mejora 19–27%.

TTFT 14B denso

<10s en M5

Qwen3-14B 4-bit pasó de ~36s en M4 a ~8s en M5 en el test de Apple

TTFT MoE 30B

<3s en M5

Mixture-of-experts activa pocos parámetros por token, ideal para memoria unificada

Speedup de decode

+19–27% M5 vs M4

Explicado por ancho de banda: 153 GB/s (M5) vs 120 GB/s (M4), ~28% más

Imagen FLUX-dev 1024px

3.8x más rápido en M5

Modelo de difusión 12B, la misma historia de Neural Accelerators que el prefill LLM

Lectura honesta: las ganancias en decode son modestas porque ninguna unidad tensorial eleva el techo de memoria, y tests independientes muestran que runtimes como llama.cpp pueden ganarle a MLX en prefill puro para algunos modelos. Pero para el loop agéntico (prompts enormes, tool results, codebases enteros) el ~4x en procesamiento de prompt del M5 es el número que importa.

5. Errores Comunes (y fixes)

Cada uno de estos muerde a alguien en la primera semana. Todos son baratos de arreglar una vez que los conocés.

🔧

El modelo muere al cargar (modelo grande, Mac chico)

Revisá el límite de memoria wired con sysctl iogpu.wired_limit_mb y subilo si tu Mac tiene margen; en macOS 15+ MLX puede wirear memoria de modelos grandes. Si no, bajá a un quant menor (4-bit) o menos parámetros activos (MoE).

🔧

Out of memory durante LoRA

En orden: modelo base cuantizado (QLoRA), --batch-size 1 más --grad-accumulation-steps, --num-layers 4 u 8 en vez de 16, --grad-checkpoint, secuencias más cortas. La receta de 32GB de la doc entrena Mistral 7B sin drama.

🔧

Los adapters no hacen nada al generar

Te olvidaste --adapter-path en mlx_lm.generate, o evaluaste un modelo fusionado contra pesos sin fusionar. Generá con el flag de adapters; fusioná solo cuando quieras un modelo standalone.

🔧

El chat ignora tu formato de instrucciones

Te salteaste el chat template. Siempre armá el prompt con tokenizer.apply_chat_template(messages, add_generation_prompt=True) en vez de concatenar strings a mano.

🔧

Sin speedup de Neural Accelerators en M5

Necesitás la release de macOS con soporte de Neural Accelerators del M5 (MLX apunta a TensorOps de Metal 4 ahí). Actualizá macOS, actualizá mlx-lm y re-corré; sin cambios de código, MLX elige el mejor kernel por máquina.

🔧

Flags viejos copiados de un blog (quizás este)

Corré mlx_lm.generate --help, mlx_lm.convert --help, mlx_lm.lora --help, mlx_lm.fuse --help. Los repos se mueven rápido; el CLI es la verdad.

Conclusión

El loop es toda la historia: mlx_lm.generate para probar, mlx_lm.convert -q para adueñarte de los pesos, mlx_lm.lora para especializar, mlx_lm.fuse para shipear, mlx_lm.server para servir. Todo en una laptop, todo sobre la memoria unificada de Apple Silicon, y en M5 con Neural Accelerators haciendo la matemática pesada de matrices.

Si solo hacés una cosa esta semana: pip install mlx-lm y chateá con el modelo 3B default. El momento en que un modelo local responde desde tu propia máquina, el cloud deja de sentirse obligatorio.

Cheat Sheet

Correr

  • • mlx_lm.generate --model ... --prompt ...
  • • mlx_lm.chat --model ...
  • • mlx_lm.server --model ...

Adueñarse

  • • mlx_lm.convert --model ... -q
  • • --upload-repo mlx-community/...
  • • load() + generate() en Python

Especializar

  • • mlx_lm.lora --train --data ...
  • • --test, --adapter-path, --config
  • • mlx_lm.fuse [--export-gguf]

Fuentes

Diego Rodriguez

Diego Rodriguez

Ingeniero Senior Full-Stack & AI

Diego tiene mas de 9 anos de experiencia construyendo aplicaciones potenciadas por IA de produccion, desde orquestacion de LLMs y pipelines RAG hasta deteccion de riesgos con ML y sistemas de trading algoritmico.

Conoce mas sobre Diego