Ejecutando LLMs con llama.cpp

Introducción

llama.cpp es una implementación ligera y de alto rendimiento en C/C++ para ejecutar LLMs. Permite una inferencia LLM eficiente en hardware de consumo, incluyendo dispositivos ARM64 como NVIDIA Jetson. Se ha convertido en el estándar predeterminado para la inferencia LLM local debido a su eficiencia y amplio soporte de formatos.

Mientras que Ollama proporciona un contenedor fácil de usar alrededor de llama.cpp, entender llama.cpp directamente te brinda:

  • Control máximo sobre los parámetros de inferencia
  • Mejor comprensión de cómo funcionan los LLMs internamente
  • Capacidad para optimizar para casos de uso específicos
  • Soporte para formatos de cuantización personalizados

llama-cpp

¿Por qué llama.cpp?

FunciónDescripción
C/C++ puroSin dependencias pesadas, huella mínima
Múltiples backendsSoporte para CPU, CUDA, Metal, OpenCL, Vulkan
Formato GGUFSoporte nativo para modelos cuantizados
IncrustableFácil de integrar en aplicaciones
Fallback de CPUFunciona incluso sin GPU
StreamingStreaming de tokens en tiempo real

Instalación en Jetson

Requisitos previos

Asegúrate de tener las herramientas de compilación instaladas:

bash
# Actualizar lista de paquetes
sudo apt-get update

# Instalar herramientas de compilación
sudo apt-get install -y build-essential git cmake

# Agregar variables de entorno de CUDA a .bashrc
echo '
# CUDA Environment
export CUDA_HOME=/usr/local/cuda
export PATH=$CUDA_HOME/bin:$PATH
export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH
export CUDACXX=$CUDA_HOME/bin/nvcc
' >> ~/.bashrc

# Recargar configuración del shell
source ~/.bashrc

# Verificar instalación de CUDA
nvcc --version

Clonar y compilar llama.cpp

bash
# Clonar el repositorio
cd ~
git clone https://github.com/ggerganov/llama.cpp.git
cd llama.cpp

# Compilar con soporte CUDA para Jetson
#make -j$(nproc) GGML_CUDA=1

# Alternativa: Usar CMake
mkdir build && cd build
cmake .. -DGGML_CUDA=ON
cmake --build . --config Release -j$(nproc)

builded_llamacpp

Verificar la instalación

bash
# Verificar si los binarios fueron compilados
ls ~/llama.cpp/build/bin/llama-cli

# Probar versión
~/llama.cpp/build/bin/llama-cli --version

llama_v

Obtener tu primer modelo

llama.cpp utiliza el formato GGUF (GGML Universal Format) — un formato binario eficiente para almacenar modelos cuantizados.

Descargar un modelo

bash
# Crear directorio de modelos
mkdir -p ~/llama.cpp/models
cd ~/llama.cpp/models

# Descargar Llama 3.2 3B (cuantización Q4_K_M - 4-bit)
wget https://huggingface.co/bartowski/Llama-3.2-3B-Instruct-GGUF/resolve/main/Llama-3.2-3B-Instruct-Q4_K_M.gguf

# O descargar desde Hugging Face usando curl
huggingface-cli download bartowski/Llama-3.2-3B-Instruct-GGUF Llama-3.2-3B-Instruct-Q4_K_M.gguf --local-dir ./models

Entendiendo la cuantización GGUF

CuantizaciónBitsTamaño de archivoCalidadVelocidad
Q2_K2-bitMuy pequeñoMenorMás rápido
Q3_K_M3-bitPequeñoAceptableMuy rápido
Q4_K_M4-bitMedioBuenoRápido
Q5_K_M5-bitMedio-GrandeMuy buenoModerado
Q6_K6-bitGrandeExcelenteMás lento
Q8_08-bitMuy grandeCasi originalMás lento

Para dispositivos Jetson:

  • Orin Nano 4GB: Usar Q2_K o Q3_K_M
  • Orin Nano 8GB: Usar Q3_K_M o Q4_K_M
  • Orin NX 16GB+: Usar Q4_K_M o Q5_K_M

Modelos recomendados para Jetson

bash
# Descargar Qwen3 4B (excelente soporte multilingüe)
wget https://huggingface.co/bartowski/Qwen3-4B-Instruct-GGUF/resolve/main/Qwen3-4B-Instruct-Q4_K_M.gguf

# Descargar DeepSeek-R1 1.5B (modelo de razonamiento destilado)
wget https://huggingface.co/bartowski/DeepSeek-R1-Distill-Qwen-1.5B-GGUF/resolve/main/DeepSeek-R1-Distill-Qwen-1.5B-Q4_K_M.gguf

# Descargar Gemma3 4B
wget https://huggingface.co/bartowski/gemma-3-4b-it-GGUF/resolve/main/gemma-3-4b-it-Q4_K_M.gguf

Uso básico

Generación de texto simple

bash
cd ~/llama.cpp

# Inferencia básica
./build/bin/llama-cli \
  -m models/Llama-3.2-3B-Instruct-Q4_K_M.gguf \
  -p "The future of edge AI is"

Modo chat (conversacional)

bash
# Iniciar sesión de chat interactiva
./build/bin/llama-cli \
  -m models/Llama-3.2-3B-Instruct-Q4_K_M.gguf \
  -cnv \
  --chat-template llama3

Nota: La bandera -cnv activa el modo conversacional, y --chat-template especifica cómo formatear la conversación.

Banderas comunes de llama-cli

BanderaDescripciónEjemplo
-m, --modelRuta del archivo del modelo-m models/model.gguf
-p, --promptPrompt inicial-p "Hello world"
-cnvModo conversación-cnv
-n, --n-predictNúmero de tokens a generar-n 256
-c, --ctx-sizeTamaño del contexto (en tokens)-c 4096
--tempTemperatura (creatividad)--temp 0.7
--top-pMuestreo nucleus--top-p 0.9
-ngl, --n-gpu-layersCapas GPU a descargar-ngl 35
-t, --threadsNúmero de hilos CPU-t 4

Optimizando para GPU de Jetson

Descarga de GPU

Descargar capas del modelo a la GPU mejora dramáticamente el rendimiento:

bash
# Determinar capas GPU óptimas
# Comenzar con -ngl 999 (descargar todas las capas)
./build/bin/llama-cli \
  -m models/Llama-3.2-3B-Instruct-Q4_K_M.gguf \
  -p "Explain quantum computing" \
  -ngl 35 \
  -n 256

Consejo: Usa -ngl 999 para descargar automáticamente todas las capas posibles a la GPU.

Midiendo el rendimiento

bash
# Ejecutar con estadísticas de rendimiento
./build/bin/llama-cli \
  -m models/Llama-3.2-3B-Instruct-Q4_K_M.gguf \
  -p "What is the capital of France?" \
  -n 50 \
  -ngl 35 \
  --perf

llamacpp_test

Métrica clave: eval time per token debe ser menor a 100ms para un buen rendimiento en tiempo real.

Funciones avanzadas

Ejecutando el servidor API

llama.cpp incluye un modo servidor para acceso REST API:

bash
# Iniciar el servidor
./llama-server \
  -m models/Llama-3.2-3B-Instruct-Q4_K_M.gguf \
  --host 0.0.0.0 \
  --port 8000 \
  -ngl 35 \
  -c 4096

Acceder al servidor:

bash
# Consulta simple
curl -X POST http://localhost:8000/completion \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Once upon a time",
    "n_predict": 100
  }'

# Completado de chat
curl -X POST http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {"role": "user", "content": "What is machine learning?"}
    ],
    "max_tokens": 256
  }'

Prompts del sistema

Establecer el comportamiento del modelo con prompts del sistema:

bash
./llama-cli \
  -m models/Llama-3.2-3B-Instruct-Q4_K_M.gguf \
  -p "You are a helpful coding assistant. Explain Python list comprehensions." \
  --system "You are an expert Python programmer. Provide concise, practical code examples."

Procesamiento por lotes

Procesar múltiples prompts eficientemente:

python
# batch_inference.py
import subprocess
import json

prompts = [
    "Explain neural networks",
    "What is GPU acceleration?",
    "Describe edge computing"
]

model_path = "~/llama.cpp/models/Llama-3.2-3B-Instruct-Q4_K_M.gguf"

for prompt in prompts:
    result = subprocess.run(
        ["./llama-cli", "-m", model_path, "-p", prompt, "-n", "100", "--temp", "0.7"],
        capture_output=True,
        text=True
    )
    print(f"Prompt: {prompt}")
    print(f"Response: {result.stdout}\n")

Convirtiendo modelos a GGUF

Si tienes un modelo en formato Hugging Face (PyTorch/SafeTensors), conviértelo a GGUF:

bash
cd ~/llama.cpp

# Instalar requisitos de Python
pip install -r requirements.txt

# Convertir modelo de Hugging Face a GGUF
python convert_hf_to_gguf.py \
  /path/to/model \
  --outfile output-model.gguf \
  --outtype q4_k_m

Tipos de salida disponibles:

  • f16: 16-bit float (sin cuantización)
  • q8_0: cuantización de 8-bit
  • q6_k: cuantización de 6-bit
  • q5_k_m: cuantización de 5-bit
  • q4_k_m: cuantización de 4-bit (balance recomendado)
  • q3_k_m: cuantización de 3-bit
  • q2_k: cuantización de 2-bit (más comprimido)

Ejemplos de integración

Enlace Python

bash
# Instalar enlaces Python
pip install llama-cpp-python

# Para soporte CUDA (Jetson)
CMAKE_ARGS="-DGGML_CUDA=on" pip install llama-cpp-python --upgrade --force-reinstall --no-cache-dir
python
from llama_cpp import Llama

# Cargar modelo
llm = Llama(
    model_path="/path/to/model-Q4_K_M.gguf",
    n_gpu_layers=35,
    n_ctx=4096
)

# Generar texto
output = llm(
    "Q: What is the capital of France?\nA:",
    max_tokens=50,
    temperature=0.7
)
print(output["choices"][0]["text"])

# Completado de chat
output = llm.create_chat_completion(
    messages=[
        {"role": "user", "content": "Tell me a joke"}
    ],
    max_tokens=100
)
print(output["choices"][0]["message"]["content"])

Benchmark de rendimiento

Optimizaciones específicas para Jetson

bash
# Configuración óptima para Jetson Orin Nano 8GB
./llama-cli \
  -m models/Llama-3.2-3B-Instruct-Q4_K_M.gguf \
  -p "Explain transformers in machine learning" \
  -ngl 35 \
  -t 6 \
  -c 4096 \
  -n 200 \
  --temp 0.7

Script de benchmark

bash
#!/bin/bash
# benchmark.sh

MODEL="models/Llama-3.2-3B-Instruct-Q4_K_M.gguf"
PROMPT="Explain the concept of artificial intelligence and its applications."

echo "Benchmarking llama.cpp on Jetson"
echo "Model: $MODEL"
echo "Prompt: $PROMPT"
echo ""

for gpu_layers in 0 10 20 30 35; do
    echo "Testing with $gpu_layers GPU layers..."
    timeout 120 ./llama-cli \
        -m $MODEL \
        -p "$PROMPT" \
        -ngl $gpu_layers \
        -n 100 \
        --per-test 2>&1 | grep "eval time"
done

Problemas comunes y soluciones

Problema 1: CUDA no detectado

Problema: La descarga de GPU no funciona

Solución:

bash
# Recompilar con CUDA
make clean
make -j$(nproc) GGML_CUDA=1

# Verificar instalación de CUDA
nvidia-smi
nvcc --version

Problema 2: Sin memoria

Problema: La carga del modelo falla con OOM

Solución:

bash
# Reducir capas GPU
./llama-cli -m model.gguf -ngl 10  # En lugar de -ngl 35

# Usar contexto más pequeño
./llama-cli -m model.gguf -c 2048  # En lugar de 4096 por defecto

# Usar cuantización más agresiva
# Cambiar de Q5_K_M a Q4_K_M o Q3_K_M

Problema 3: Rendimiento lento solo con CPU

Problema: La generación es muy lenta sin GPU

Solución:

bash
# Habilitar más hilos
./llama-cli -m model.gguf -t 8  # Usar 8 hilos CPU

# Usar modelo más pequeño o cuantización más agresiva

# Asegurarse de que el regulador CPU esté en modo rendimiento
sudo apt-get install cpufrequtils
sudo cpufreq-set -g performance

Ejercicio práctico

Completa estas tareas:

  1. Compilar llama.cpp con soporte CUDA
  2. Descargar 3 modelos diferentes y comparar sus tamaños
  3. Ejecutar inferencia en cada modelo con -ngl 35
  4. Medir rendimiento usando la bandera --perf
  5. Iniciar el servidor y probar solicitudes API
  6. Crear un script Python que procese 5 prompts diferentes en lote

Referencias


Siguiente: Continúa a Módulo 5.4: Inferencia de alto rendimiento con vLLM para LLM serving de nivel de producción.