<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Local AI on kenji.blog</title><link>http://kenji.blog/es/tags/local-ai/</link><description>Recent content in Local AI on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>es</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 16:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/es/tags/local-ai/index.xml" rel="self" type="application/rss+xml"/><item><title>Cómo ajustar TinyLLaMA lo más rápido posible en un entorno local (On-Premises)</title><link>http://kenji.blog/es/p/tinyllama-on-premises-fast-tuning-guide/</link><pubDate>Fri, 11 Sep 2026 16:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/tinyllama-on-premises-fast-tuning-guide/</guid><description>&lt;img src="http://kenji.blog/p/tinyllama-on-premises-fast-tuning-guide/img/eyecatch.jpg" alt="Featured image of post Cómo ajustar TinyLLaMA lo más rápido posible en un entorno local (On-Premises)" />&lt;h2 id="1-introducción-por-qué-tinyllama-y-entornos-locales-ahora">1. Introducción: ¿Por qué TinyLLaMA y entornos locales ahora?
&lt;/h2>&lt;p>La evolución de los modelos de lenguaje grande (LLM) avanza a un ritmo vertiginoso, y en consecuencia, el número de parámetros de los modelos sigue expandiéndose a escalas de cientos de miles de millones. Si bien los modelos gigantes como GPT-4 o Claude 3 ostentan un rendimiento inigualable, los costos computacionales de inferencia y entrenamiento, así como las preocupaciones sobre seguridad y privacidad de datos al usar APIs externas, se han convertido en grandes obstáculos para las empresas. Especialmente en tareas que manejan datos internos confidenciales o información personal, enviar datos a APIs de LLM públicas en la nube a menudo es inaceptable desde el punto de vista del cumplimiento normativo (como GDPR o APPI).&lt;/p>
&lt;p>Aquí es donde los &lt;strong>modelos de lenguaje pequeños (SLM: Small Language Models)&lt;/strong> y el &lt;strong>funcionamiento local en entornos On-Premises&lt;/strong> están ganando protagonismo. Entre ellos, &amp;ldquo;&lt;strong>TinyLLaMA&lt;/strong>&amp;rdquo; destaca por su tamaño compacto de tan solo 1.1B (1.100 millones) de parámetros, pero habiendo sido pre-entrenado con un inmenso conjunto de datos de aproximadamente 3 billones de tokens, ofreciendo un rendimiento asombroso en comparación con modelos de su misma clase.&lt;/p>
&lt;p>En este artículo, proporcionaremos una guía completa para ajustar (fine-tune) TinyLLaMA en un entorno local (servidores locales o estaciones de trabajo) para tareas específicas de su empresa de la manera &amp;ldquo;más rápida y eficiente&amp;rdquo;. Cubriremos exhaustivamente desde los fundamentos matemáticos hasta las últimas tecnologías de optimización, incluyendo código de implementación específico en PyTorch.&lt;/p>
&lt;hr>
&lt;h2 id="2-arquitectura-y-características-de-tinyllama">2. Arquitectura y características de TinyLLaMA
&lt;/h2>&lt;p>TinyLLaMA sigue la arquitectura LLaMA (Large Language Model Meta AI) desarrollada por Meta. Aunque mantiene el número de parámetros en 1.1B, utiliza la misma pila tecnológica que LLaMA 2, lo que le otorga una compatibilidad de ecosistema extremadamente alta.&lt;/p>
&lt;h3 id="componentes-arquitectónicos-principales">Componentes arquitectónicos principales
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>RMSNorm (Root Mean Square Normalization):&lt;/strong>
Un método de normalización que omite la resta de la media del cálculo tradicional de LayerNorm, mejorando la eficiencia computacional. Mejora el rendimiento (throughput) manteniendo la estabilidad del entrenamiento.&lt;/li>
&lt;li>&lt;strong>Función de activación SwiGLU:&lt;/strong>
En la red neuronal prealimentada (Feed Forward Network - FFN), adopta SwiGLU en lugar de las tradicionales ReLU o GELU. Matemáticamente, esto se expresa como:
$$ \text{SwiGLU}(x, W, V) = \text{Swish}(xW) \otimes (xV) $$
Aquí, $\otimes$ representa el producto elemento a elemento (producto de Hadamard), y la función Swish es $\text{Swish}(z) = z \cdot \sigma(\beta z)$. Esto mejora significativamente la capacidad de representación.&lt;/li>
&lt;li>&lt;strong>RoPE (Rotary Position Embedding):&lt;/strong>
Un método que combina las ventajas de la codificación de posición absoluta y la codificación de posición relativa. Posee un alto rendimiento de generalización incluso cuando se expande la longitud de la secuencia.&lt;/li>
&lt;li>&lt;strong>Grouped Query Attention (GQA):&lt;/strong>
Un enfoque intermedio entre Multi-Head Attention (MHA) y Multi-Query Attention (MQA) que, al agrupar los cabezales (heads) de las claves (keys) y valores (values), ahorra ancho de banda de memoria y mejora drásticamente la velocidad de inferencia.&lt;/li>
&lt;/ol>
&lt;p>El siguiente diagrama Mermaid muestra el flujo de datos general de TinyLLaMA y la estructura del bloque Transformer.&lt;/p>
&lt;div class="mermaid">graph TD
A["Texto de Entrada"] --> B["Tokenizador (BPE)"]
B --> C["Capa de Incrustación (Embedding)"]
C --> D["Bloques Transformer (x22 Capas para TinyLLaMA)"]
D --> E["RMSNorm (Final)"]
E --> F["Proyección Lineal (Tamaño del Vocabulario)"]
F --> G["Probabilidades de Salida (Softmax)"]
subgraph "Anatomía del Bloque Transformer"
D1["Estado Oculto de Entrada"] --> D2["RMSNorm"]
D2 --> D3["Atención de Consulta Agrupada (GQA)"]
D3 --> D4["Suma Residual"]
D4 --> D5["RMSNorm"]
D5 --> D6["SwiGLU FFN"]
D6 --> D7["Suma Residual"]
D7 --> D8["Salida a la Siguiente Capa"]
D1 -.-> D4
D4 -.-> D7
end&lt;/div>
&lt;hr>
&lt;h2 id="3-el-avance-del-ajuste-fino-lora-y-qlora">3. El avance del ajuste fino: LoRA y QLoRA
&lt;/h2>&lt;p>Realizar un ajuste fino (fine-tuning) con todos los parámetros en un entorno local consume decenas de GB de VRAM (memoria de video) para mantener el estado del optimizador y los gradientes, incluso con un modelo de 1.1B. Para entrenar eficientemente con recursos limitados, es imprescindible usar &amp;ldquo;&lt;strong>LoRA&lt;/strong>&amp;rdquo;, una técnica de &lt;strong>PEFT (Parameter-Efficient Fine-Tuning)&lt;/strong>, y su extensión cuantizada, &amp;ldquo;&lt;strong>QLoRA&lt;/strong>&amp;rdquo;.&lt;/p>
&lt;h3 id="31-fundamentos-matemáticos-de-lora-low-rank-adaptation">3.1 Fundamentos matemáticos de LoRA (Low-Rank Adaptation)
&lt;/h3>&lt;p>LoRA es una técnica que fija (congela) las matrices de peso pre-entrenadas y aproxima la cantidad de actualización de esos pesos ($\Delta W$) como el producto de dos matrices pequeñas de bajo rango.&lt;/p>
&lt;p>Supongamos que los pesos pre-entrenados son $W_0 \in \mathbb{R}^{d \times k}$. En un fine-tuning completo, actualizaríamos el propio $W_0$ a $W_0 + \Delta W$, pero en LoRA descomponemos la matriz de actualización $\Delta W$ de la siguiente manera:&lt;/p>
$$ \Delta W = B \times A $$
&lt;p>Aquí, $B \in \mathbb{R}^{d \times r}$ y $A \in \mathbb{R}^{r \times k}$, donde $r$ es un hiperparámetro llamado Rango (Rank) y es un valor muy pequeño (usualmente 8, 16, 32, etc.) que satisface $r \ll \min(d, k)$.&lt;/p>
&lt;p>El cálculo del paso hacia adelante (forward pass) es el siguiente:&lt;/p>
$$ h = W_0 x + \Delta W x = W_0 x + B A x $$
&lt;p>En el estado inicial, la matriz $A$ se inicializa aleatoriamente con una distribución normal (distribución gaussiana), y la matriz $B$ se inicializa como una matriz cero. Por lo tanto, $\Delta W$ es cero al inicio del entrenamiento, lo que permite comenzar a entrenar manteniendo completamente la salida del modelo base.&lt;/p>
&lt;div class="mermaid">graph LR
X["Vector de Entrada x"] --> W0["Peso Pre-entrenado Congelado (W_0)"]
X --> A["Matriz LoRA Entrenable A (r x k)"]
A --> B["Matriz LoRA Entrenable B (d x r)"]
W0 --> Add["Suma de Vectores"]
B --> Add
Add --> Y["Vector de Salida h"]&lt;/div>
&lt;h3 id="32-la-innovación-de-qlora-quantized-lora">3.2 La innovación de QLoRA (Quantized LoRA)
&lt;/h3>&lt;p>QLoRA lleva el enfoque de LoRA un paso más allá cuantizando el modelo base $W_0$ a precisión de 4 bits (NormalFloat 4, NF4) antes de cargarlo en la memoria. Esto reduce drásticamente el consumo de VRAM.&lt;/p>
&lt;p>QLoRA incorpora tres tecnologías importantes:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Cuantización NormalFloat de 4 bits (NF4):&lt;/strong> Un tipo de datos teóricamente óptimo para pesos que siguen una distribución normal.&lt;/li>
&lt;li>&lt;strong>Doble Cuantización (Double Quantization):&lt;/strong> Cuantiza las constantes de cuantización (factores de escala) mismas, ahorrando aún más memoria.&lt;/li>
&lt;li>&lt;strong>Paged Optimizers:&lt;/strong> Utiliza la función de memoria unificada de NVIDIA para trasladar temporalmente el estado del optimizador a la RAM de la CPU cuando la VRAM escasea.&lt;/li>
&lt;/ol>
&lt;p>Como resultado, el ajuste que normalmente requiere 16GB a 24GB de VRAM se puede ejecutar con holgura incluso en GPUs de nivel de consumidor (como RTX 3060 de 12GB o RTX 4070).&lt;/p>
&lt;hr>
&lt;h2 id="4-requisitos-de-hardware-y-configuración-en-entornos-locales">4. Requisitos de hardware y configuración en entornos locales
&lt;/h2>&lt;p>Los requisitos de hardware al ajustar TinyLLaMA (1.1B) con QLoRA se mantienen muy bajos.&lt;/p>
&lt;h3 id="especificaciones-de-hardware-recomendadas">Especificaciones de hardware recomendadas
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>GPU:&lt;/strong> NVIDIA RTX 3060 (12GB), RTX 3090/4090 (24GB), o NVIDIA A10G/A100, etc. Funcionará con al menos 8GB de VRAM, pero se recomiendan 12GB o más para aumentar el tamaño del lote (batch size).&lt;/li>
&lt;li>&lt;strong>CPU:&lt;/strong> Una CPU moderna de 8 núcleos o más (Intel Core i7/i9, AMD Ryzen 7/9).&lt;/li>
&lt;li>&lt;strong>RAM:&lt;/strong> 32GB o más (importante como destino de paginación desde la VRAM cuando se usan Paged Optimizers).&lt;/li>
&lt;li>&lt;strong>Almacenamiento:&lt;/strong> SSD NVMe (para acelerar la carga de conjuntos de datos y guardar el modelo).&lt;/li>
&lt;/ul>
&lt;h3 id="configuración-del-entorno-de-software">Configuración del entorno de software
&lt;/h3>&lt;p>Este es un procedimiento de configuración asumiendo un entorno Ubuntu 22.04 LTS. Utilizaremos Python 3.10 o superior.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Creación y activación de un entorno virtual&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python3 -m venv tinyllama_env
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> tinyllama_env/bin/activate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Instalación de PyTorch (para CUDA 12.1)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Instalación de bibliotecas relacionadas con Transformers&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install transformers datasets peft trl accelerate bitsandbytes
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="5-técnicas-de-optimización-para-el-ajuste-más-rápido">5. Técnicas de optimización para el ajuste más rápido
&lt;/h2>&lt;p>Para completar el ajuste de la manera &amp;ldquo;más rápida&amp;rdquo; posible, no basta con ejecutar un script; es necesario combinar las siguientes técnicas de optimización.&lt;/p>
&lt;h3 id="51-flash-attention-2">5.1 Flash Attention 2
&lt;/h3>&lt;p>El mecanismo de Atención estándar tiene una complejidad computacional temporal y espacial de $O(N^2)$ con respecto a la longitud de la secuencia $N$. Flash Attention 2 optimiza el acceso a la memoria entre la SRAM de la GPU y la HBM (High Bandwidth Memory), resolviendo los cuellos de botella de entrada/salida (IO) sin reducir la cantidad de cálculos, lo que multiplica la velocidad de entrenamiento por varias veces y reduce drásticamente el consumo de memoria.&lt;/p>
&lt;h3 id="52-gradient-checkpointing-puntos-de-control-de-gradientes">5.2 Gradient Checkpointing (Puntos de control de gradientes)
&lt;/h3>&lt;p>En lugar de guardar en la VRAM todas las activaciones intermedias calculadas en el paso hacia adelante, este método guarda solo algunas y recalcula las necesarias durante el paso hacia atrás (backward pass). Aunque el tiempo de cálculo aumenta aproximadamente un 20%, el consumo de memoria se reduce drásticamente. Esto permite establecer un tamaño de lote mayor, mejorando el rendimiento (throughput) general en consecuencia.&lt;/p>
&lt;h3 id="53-entrenamiento-de-precisión-mixta-mixed-precision-training-y-bfloat16">5.3 Entrenamiento de Precisión Mixta (Mixed Precision Training) y Bfloat16
&lt;/h3>&lt;p>Para maximizar el uso de los Tensor Cores de la GPU, los cálculos durante el entrenamiento se realizan en &lt;code>bfloat16&lt;/code> (Brain Floating Point). A diferencia de &lt;code>float16&lt;/code>, su longitud de bits para el exponente es la misma que en &lt;code>float32&lt;/code>, por lo que el riesgo de desbordamiento (overflow o underflow) es extremadamente bajo y el entrenamiento es estable.&lt;/p>
&lt;hr>
&lt;h2 id="6-práctica-código-de-ajuste-fino-qlora-para-tinyllama">6. Práctica: Código de ajuste fino QLoRA para TinyLLaMA
&lt;/h2>&lt;p>Ahora explicaremos un script de PyTorch para el ajuste más rápido que incorpora todas las optimizaciones anteriores. Aquí utilizaremos el &lt;code>SFTTrainer&lt;/code> de la biblioteca &lt;code>trl&lt;/code> (Transformer Reinforcement Learning) de Hugging Face.&lt;/p>
&lt;h3 id="61-preparación-del-conjunto-de-datos-y-carga-del-modelo">6.1 Preparación del conjunto de datos y carga del modelo
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">torch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">datasets&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">load_dataset&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">transformers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">AutoModelForCausalLM&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">AutoTokenizer&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">BitsAndBytesConfig&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TrainingArguments&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">peft&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">LoraConfig&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">get_peft_model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">prepare_model_for_kbit_training&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">trl&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">SFTTrainer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. Especificar el modelo y el tokenizador&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;TinyLlama/TinyLlama-1.1B-Chat-v1.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. Configuración de cuantización a 4 bits para QLoRA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">bnb_config&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">BitsAndBytesConfig&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">load_in_4bit&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bnb_4bit_use_double_quant&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bnb_4bit_quant_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;nf4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bnb_4bit_compute_dtype&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bfloat16&lt;/span> &lt;span class="c1"># Realizar cálculos en bfloat16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. Cargar el modelo (Habilitar Flash Attention 2)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Loading model...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoModelForCausalLM&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">quantization_config&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">bnb_config&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">device_map&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;auto&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">use_flash_attention_2&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span> &lt;span class="c1"># La clave para la máxima velocidad&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 4. Cargar el tokenizador&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoTokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">trust_remote_code&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pad_token&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">eos_token&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">padding_side&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;right&amp;#34;&lt;/span> &lt;span class="c1"># Configurar en &amp;#39;right&amp;#39; para evitar bugs durante el entrenamiento fp16/bf16&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="62-aplicación-del-adaptador-lora-y-formato-del-conjunto-de-datos">6.2 Aplicación del adaptador LoRA y formato del conjunto de datos
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 5. Preparación para entrenamiento en k bits y habilitación del Gradient Checkpointing&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">gradient_checkpointing_enable&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">prepare_model_for_kbit_training&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 6. Configuración de LoRA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">peft_config&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LoraConfig&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">r&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Rango&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lora_alpha&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Factor de escala&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lora_dropout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.05&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bias&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;none&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;CAUSAL_LM&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">target_modules&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;q_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;k_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;v_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;o_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;gate_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;up_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;down_proj&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># El rendimiento mejora al apuntar a todas las capas Lineales&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">get_peft_model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">peft_config&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">print_trainable_parameters&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Ejemplo de salida: trainable params: 14,286,848 || all params: 1,114,335,232 || trainable%: 1.282%&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 7. Cargar el conjunto de datos (Aquí usamos un conjunto de datos de instrucciones en japonés como ejemplo)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># En la práctica, cargarías archivos JSONL privados locales, etc.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dataset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">load_dataset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;kunishou/databricks-dolly-15k-ja&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">split&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;train&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">format_instruction&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Formatea las cadenas de texto para que coincidan con el formato ChatML o plantillas de prompts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;|im_start|&amp;gt;user&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;instruction&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">sample&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;input&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;|im_end|&amp;gt;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;lt;|im_start|&amp;gt;assistant&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;output&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;lt;|im_end|&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dataset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dataset&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">format_instruction&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="63-ejecución-del-entrenamiento">6.3 Ejecución del entrenamiento
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 8. Configuración de los argumentos de entrenamiento&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">training_args&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">TrainingArguments&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output_dir&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-lora-output&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">per_device_train_batch_size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Aumentar si hay VRAM disponible&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">gradient_accumulation_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Tamaño de lote efectivo = 8 * 2 = 16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">optim&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;paged_adamw_32bit&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Ahorro de VRAM gracias a Paged Optimizer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">save_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logging_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">learning_rate&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">2e-4&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fp16&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bf16&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Entrenamiento de precisión mixta (bfloat16)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_grad_norm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.3&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 500 pasos para pruebas. En producción, especificar el número de épocas&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">warmup_ratio&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.03&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">group_by_length&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lr_scheduler_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;cosine&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 9. Iniciar el entrenamiento con SFTTrainer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">trainer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">SFTTrainer&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">train_dataset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">dataset&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">peft_config&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">peft_config&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dataset_text_field&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_seq_length&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1024&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Ajustar según la longitud de entrada esperada&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokenizer&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">tokenizer&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">args&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">training_args&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Starting training...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">trainer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">train&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 10. Guardar el adaptador LoRA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">trainer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-lora-final&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-lora-final&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Training complete and model saved.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="7-evaluación-de-rendimiento-y-solución-de-problemas">7. Evaluación de rendimiento y solución de problemas
&lt;/h2>&lt;p>Estos son los problemas más comunes que se enfrentan al entrenar en un entorno local y sus soluciones.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Ocurre un OOM (Out Of Memory):&lt;/strong>
&lt;ul>
&lt;li>Reducir &lt;code>per_device_train_batch_size&lt;/code> a &lt;code>1&lt;/code>.&lt;/li>
&lt;li>Aumentar &lt;code>gradient_accumulation_steps&lt;/code> para mantener el tamaño del lote efectivo.&lt;/li>
&lt;li>Reducir &lt;code>max_seq_length&lt;/code> de &lt;code>2048&lt;/code> a &lt;code>1024&lt;/code> o &lt;code>512&lt;/code>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>La pérdida (Loss) no disminuye o diverge:&lt;/strong>
&lt;ul>
&lt;li>La tasa de aprendizaje (&lt;code>learning_rate&lt;/code>) podría ser demasiado alta. Intente reducirla de &lt;code>2e-4&lt;/code> a algo como &lt;code>5e-5&lt;/code>.&lt;/li>
&lt;li>Si está usando Float16 en lugar de Bfloat16, es posible que esté ocurriendo un underflow en los gradientes. Asegúrese de que &lt;code>bf16=True&lt;/code> esté configurado.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Se generan cadenas de texto extrañas durante la inferencia:&lt;/strong>
&lt;ul>
&lt;li>Verifique que &lt;code>padding_side=&amp;quot;right&amp;quot;&lt;/code> esté configurado correctamente. Además, es necesario asegurarse de que el formato del conjunto de datos (tokens especiales como &lt;code>&amp;lt;|im_start|&amp;gt;&lt;/code>) coincida con el utilizado durante el pre-entrenamiento del modelo base.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-despliegue-del-modelo-después-del-ajuste-deployment">8. Despliegue del modelo después del ajuste (Deployment)
&lt;/h2>&lt;p>Una vez completado el ajuste, lo que se guarda no es &amp;ldquo;todo el modelo base&amp;rdquo;, sino solo el &amp;ldquo;&lt;strong>Adaptador LoRA (pesos diferenciales)&lt;/strong>&amp;rdquo; de unos pocos MB a decenas de MB. Para realizar inferencias a alta velocidad, es necesario fusionar (merge) estos pesos LoRA en el modelo base original y exportarlo como un modelo único.&lt;/p>
&lt;h3 id="script-de-fusión-de-modelos">Script de fusión de modelos
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">torch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">peft&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AutoPeftModelForCausalLM&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">transformers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AutoTokenizer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">output_dir&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;./tinyllama-lora-final&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Cargar el modelo y el adaptador en FP16/BF16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoPeftModelForCausalLM&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output_dir&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">device_map&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;auto&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">torch_dtype&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bfloat16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoTokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">output_dir&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Fusionar los pesos y guardar&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">merged_model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">merge_and_unload&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">merged_model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-merged&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">safe_serialization&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-merged&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Model merged and saved successfully!&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="inicio-de-un-servidor-de-inferencia-ultrarrápido-con-vllm">Inicio de un servidor de inferencia ultrarrápido con vLLM
&lt;/h3>&lt;p>Al desplegar en un entorno local, para maximizar la velocidad de inferencia (Tokens por segundo), recomendamos encarecidamente usar &lt;strong>vLLM&lt;/strong> o &lt;strong>TGI (Text Generation Inference)&lt;/strong> en lugar del &lt;code>pipeline&lt;/code> estándar de Hugging Face. vLLM utiliza la tecnología PagedAttention para evitar la fragmentación de la memoria de la GPU, mejorando drásticamente la capacidad de procesar solicitudes concurrentes.&lt;/p>
&lt;p>El siguiente diagrama Mermaid muestra el pipeline desde el entrenamiento hasta el despliegue del servidor de inferencia.&lt;/p>
&lt;div class="mermaid">graph TD
A["Datos Privados en Crudo"] --> B["Preprocesamiento y Formateo (JSONL)"]
B --> C["Ajuste Fino QLoRA (SFTTrainer)"]
C --> D["Pesos del Adaptador LoRA (.safetensors)"]
D --> E["Fusionar con Base TinyLLaMA 1.1B"]
E --> F["Modelo Fusionado"]
F --> G["Desplegar vía Servidor vLLM"]
G --> H["Endpoint de API / UI (Ej. Chatbot)"]&lt;/div>
&lt;p>Iniciar un servidor API con vLLM se completa con el siguiente comando:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">python -m vllm.entrypoints.openai.api_server &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --model ./tinyllama-merged &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --host 0.0.0.0 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --port &lt;span class="m">8000&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --max-model-len &lt;span class="m">2048&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --dtype bfloat16
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Con esto, se establecerá un endpoint compatible con la API de OpenAI en su entorno local, permitiéndole aprovechar la IA local de manera segura y rápida.&lt;/p>
&lt;hr>
&lt;h2 id="9-conclusión">9. Conclusión
&lt;/h2>&lt;p>En este artículo, explicamos el método para realizar un ajuste fino de la manera más rápida y eficiente en memoria en un entorno local, centrándonos en &amp;ldquo;TinyLLaMA&amp;rdquo;, que es liviano con 1.1B de parámetros pero de alto rendimiento.&lt;/p>
&lt;ul>
&lt;li>Gracias a &lt;strong>LoRA / QLoRA&lt;/strong>, es posible realizar un ajuste fino auténtico de LLM incluso en GPUs de consumidor.&lt;/li>
&lt;li>Al aprovechar &lt;strong>Flash Attention 2&lt;/strong> y &lt;strong>Gradient Checkpointing&lt;/strong>, el tiempo de entrenamiento y el consumo de VRAM se optimizan al límite.&lt;/li>
&lt;li>El despliegue utilizando &lt;strong>vLLM&lt;/strong> logra un alto rendimiento (throughput) incluso en entornos de producción.&lt;/li>
&lt;/ul>
&lt;p>Operar un LLM localmente en entornos On-Premises no solo protege la confidencialidad de los datos, sino que también se convierte en su arma más poderosa para construir IA especializada para dominios específicos (legal, médico, regulaciones internas de la empresa, etc.) a bajo costo. Por favor, utilice esta guía como referencia para entrenar su propio TinyLLaMA personalizado.&lt;/p></description></item><item><title>Guía de desarrollo de modelos de IA a pequeña escala (como TinyLLaMA) con C++</title><link>http://kenji.blog/es/p/cpp-small-ai-model-tinyllama-dev-guide/</link><pubDate>Fri, 11 Sep 2026 14:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/cpp-small-ai-model-tinyllama-dev-guide/</guid><description>&lt;img src="http://kenji.blog/p/cpp-small-ai-model-tinyllama-dev-guide/img/eyecatch.jpg" alt="Featured image of post Guía de desarrollo de modelos de IA a pequeña escala (como TinyLLaMA) con C++" />&lt;h1 id="guía-de-desarrollo-de-modelos-de-ia-a-pequeña-escala-como-tinyllama-con-c">Guía de desarrollo de modelos de IA a pequeña escala (como TinyLLaMA) con C++
&lt;/h1>&lt;p>En los últimos años, el interés por ejecutar grandes modelos de lenguaje (LLM) en entornos locales ha crecido rápidamente. Especialmente, los modelos pequeños como TinyLLaMA (1.1B parámetros) pueden realizar inferencias a velocidades prácticas incluso en dispositivos periféricos (edge devices) con recursos limitados o computadoras portátiles comunes (incluyendo entornos Windows). Mientras que el desarrollo con Python y PyTorch es la corriente principal, cuando se busca el rendimiento máximo y la eficiencia de memoria, la combinación de C++ y &amp;ldquo;ggml&amp;rdquo;, una biblioteca de tensores basada en C, se ha convertido en el estándar de facto.&lt;/p>
&lt;p>En este artículo, explicaremos en gran detalle el proceso de desarrollo para construir un motor de inferencia desde cero (o comprender profundamente la estructura interna del existente llama.cpp) para cargar TinyLLaMA y generar texto usando C++.&lt;/p>
&lt;hr>
&lt;h2 id="1-por-qué-c-y-ggml">1. ¿Por qué C++ y ggml?
&lt;/h2>&lt;p>En la fase de entrenamiento de IA, Python tiene una ventaja abrumadora debido a su flexibilidad y rico ecosistema. Sin embargo, en las fases de despliegue e &amp;ldquo;Inferencia&amp;rdquo;, C++ se convierte en una opción poderosa por las siguientes razones:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Reducción de sobrecarga&lt;/strong>: Se puede eliminar por completo la sobrecarga del entorno de ejecución y del Global Interpreter Lock (GIL) de Python.&lt;/li>
&lt;li>&lt;strong>Eficiencia de memoria y asignación por arenas&lt;/strong>: Al poder controlar manualmente la asignación y liberación de memoria, se evitan los picos impredecibles causados por el recolector de basura.&lt;/li>
&lt;li>&lt;strong>Acceso directo al hardware&lt;/strong>: Se pueden invocar directamente funciones intrínsecas SIMD como AVX-512, AVX2 y ARM NEON para maximizar la capacidad de procesamiento de la CPU.&lt;/li>
&lt;li>&lt;strong>Eliminación de dependencias&lt;/strong>: ggml es una biblioteca C/C++ sin dependencias (Zero dependencies), y se puede compilar fácilmente incluso en un entorno MSVC en Windows siempre que haya un compilador.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="2-visión-general-de-la-arquitectura">2. Visión general de la arquitectura
&lt;/h2>&lt;p>El flujo completo del pipeline de inferencia se muestra en el siguiente diagrama Mermaid. Es un proceso continuo que comienza con el texto de entrada del usuario hasta que se genera el siguiente token final.&lt;/p>
&lt;div class="mermaid">graph TD
A["Texto de entrada del usuario"] --> B["Tokenizador BPE"]
B --> C["Matriz de IDs de tokens"]
C --> D["Búsqueda en la capa de incrustación"]
D --> E["Bloques Transformer"]
E --> F["RMSNorm"]
F --> G["Capa LM Head"]
G --> H["Matriz de Logits"]
H --> I["Módulo muestreador"]
I --> J["Siguiente token ID"]
J --> K["Detokenizador"]
K --> L["Fragmento de texto de salida"]
J -.-> |"Añadir al contexto"| C&lt;/div>
&lt;p>Al ser un modelo autorregresivo, el token de salida se añade de nuevo al contexto y circula como entrada para la predicción del siguiente token (la parte punteada del diagrama).&lt;/p>
&lt;hr>
&lt;h2 id="3-formato-del-modelo-y-mapeo-de-memoria-mmap">3. Formato del modelo y mapeo de memoria (mmap)
&lt;/h2>&lt;p>La mayor barrera al manejar los pesos de una red neuronal gigante es la E/S del disco y el consumo de memoria. En la implementación de C++, esto se resuelve mediante el &lt;strong>mapeo de memoria (mmap)&lt;/strong>.&lt;/p>
&lt;h3 id="31-cómo-funciona-el-mapeo-de-memoria-y-su-implementación-en-windows">3.1 Cómo funciona el mapeo de memoria y su implementación en Windows
&lt;/h3>&lt;p>Al utilizar mmap, el contenido del archivo se puede mapear directamente en el espacio de memoria virtual del proceso.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Copia cero (Zero-copy)&lt;/strong>: Los datos se cargan directamente desde el disco a la caché de páginas del kernel, sin que ocurran copias adicionales al espacio de usuario.&lt;/li>
&lt;li>&lt;strong>Carga bajo demanda (Page Fault)&lt;/strong>: En el instante exacto en que la CPU accede a esa dirección de memoria, se produce un fallo de página y solo el fragmento necesario (típicamente 4KB) se carga en la memoria física.&lt;/li>
&lt;/ul>
&lt;p>En un entorno Windows, en lugar del &lt;code>mmap&lt;/code> de POSIX, se utilizan las API Win32 &lt;code>CreateFileMapping&lt;/code> y &lt;code>MapViewOfFile&lt;/code>.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant OS["Sistema Operativo Windows"]
participant RAM["Memoria Física"]
participant App["Aplicación C++"]
App->>OS: "CreateFileMapping / MapViewOfFile"
OS-->>App: "Puntero de dirección de memoria virtual"
App->>App: "Leer datos del tensor en el puntero"
OS->>RAM: "Fallo de página / Cargar página desde el disco"
RAM-->>App: "Datos listos para cálculo SIMD"&lt;/div>
&lt;h3 id="32-estructura-binaria-del-formato-gguf">3.2 Estructura binaria del formato GGUF
&lt;/h3>&lt;p>&lt;strong>GGUF (GPT-Generated Unified Format)&lt;/strong>, convertido a partir de formatos como &lt;code>.safetensors&lt;/code> de Hugging Face, es el formato definitivo para la inferencia. Tiene un diseño binario estricto como el siguiente:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Bytes Mágicos&lt;/strong>: &lt;code>0x46554747&lt;/code> (GGUF).&lt;/li>
&lt;li>&lt;strong>Versión&lt;/strong>: Número de versión del formato.&lt;/li>
&lt;li>&lt;strong>Conteo de tensores y metadatos&lt;/strong>: Número de tensores y pares clave-valor de metadatos.&lt;/li>
&lt;li>&lt;strong>Metadatos (Pares clave-valor)&lt;/strong>: Claves con prefijo de longitud de cadena y valores tipados.&lt;/li>
&lt;li>&lt;strong>Información del tensor&lt;/strong>: Nombre de cada tensor, número de dimensiones, tipo de datos (FP16, Q4_K, etc.) y posición de desplazamiento (offset) en el archivo.&lt;/li>
&lt;li>&lt;strong>Relleno (Padding)&lt;/strong>: Relleno insertado para que los datos del tensor se alineen en límites específicos (generalmente 32 o 64 bytes). Es indispensable para un acceso a memoria rápido con instrucciones SIMD (especialmente AVX).&lt;/li>
&lt;li>&lt;strong>Datos del tensor&lt;/strong>: Matriz de datos de pesos reales alineados.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="4-base-matemática-de-tinyllama-y-algoritmos-en-c">4. Base matemática de TinyLLaMA y algoritmos en C++
&lt;/h2>&lt;p>TinyLLaMA incorpora varios ingenios arquitectónicos avanzados para mejorar la eficiencia. Explicaremos las representaciones matemáticas para implementarlos correctamente en C++.&lt;/p>
&lt;h3 id="41-rmsnorm-root-mean-square-normalization">4.1 RMSNorm (Root Mean Square Normalization)
&lt;/h3>&lt;p>Reduce el costo computacional omitiendo el centrado de la media de LayerNorm y realizando solo el escalado de la varianza.&lt;/p>
$$ \text{RMSNorm}(x) = \frac{x}{\sqrt{\frac{1}{d}\sum_{i=1}^{d} x_i^2 + \epsilon}} \odot \gamma $$
&lt;p>$d$ es el número de dimensiones y $\gamma$ es el tensor de escalado entrenado.
Al implementar en C++, se optimiza calculando primero rápidamente la suma de los cuadrados de la matriz usando &lt;code>_mm256_fmadd_ps&lt;/code> de AVX2, y multiplicando por la raíz cuadrada inversa (por ejemplo, con la instrucción &lt;code>_mm256_rsqrt_ps&lt;/code>).&lt;/p>
&lt;h3 id="42-rope-rotary-position-embedding">4.2 RoPE (Rotary Position Embedding)
&lt;/h3>&lt;p>Es una técnica para aplicar la información de posición de los tokens como una rotación en el espacio del tensor. Se puede ver como una rotación en el plano complejo, y aplica la siguiente rotación a los pares de dimensiones adyacentes $(x_1, x_2)$ del vector $x$.&lt;/p>
$$ \text{RoPE}(x, m) = \begin{pmatrix} x_{1} \cos(m\theta) - x_{2} \sin(m\theta) \\ x_{1} \sin(m\theta) + x_{2} \cos(m\theta) \end{pmatrix} $$
&lt;p>Aquí, $m$ es el índice de posición absoluta del token y $\theta$ es la frecuencia base precalculada. En ggml, se ejecuta en paralelo simplemente añadiendo el operador &lt;code>ggml_rope&lt;/code> durante la construcción del grafo de inferencia.&lt;/p>
&lt;h3 id="43-grouped-query-attention-gqa">4.3 Grouped-Query Attention (GQA)
&lt;/h3>&lt;p>En la Atención Multicabezal normal (Multi-Head Attention, MHA), se tiene el mismo número de cabezas para Query, Key y Value. Sin embargo, TinyLLaMA adopta &lt;strong>Grouped-Query Attention (GQA)&lt;/strong> para reducir drásticamente el ancho de banda de la memoria y el consumo de caché KV.&lt;/p>
$$ \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{Q K^T}{\sqrt{d_k}}\right) V $$
&lt;p>En GQA, múltiples cabezas de Query comparten una sola cabeza de Key/Value. En la implementación de C++, antes de ejecutar el producto matricial &lt;code>ggml_mul_mat&lt;/code>, se requiere una operación para transmitir (broadcast) los tensores KV de acuerdo con el número de Queries.&lt;/p>
&lt;h3 id="44-función-de-activación-swiglu">4.4 Función de activación SwiGLU
&lt;/h3>&lt;p>En la capa de la Red Feed-Forward (FFN), se utiliza SwiGLU en lugar de GELU.&lt;/p>
$$ \text{SwiGLU}(x) = \text{Swish}(x W_{\text{gate}}) \otimes (x W_{\text{up}}) $$
$$ \text{Swish}(z) = z \cdot \sigma(z) = z \cdot \frac{1}{1 + e^{-z}} $$
&lt;p>En el grafo computacional, se expresa combinando el operador &lt;code>ggml_silu&lt;/code> y &lt;code>ggml_mul&lt;/code>.&lt;/p>
&lt;hr>
&lt;h2 id="5-construcción-del-grafo-computacional-y-gestión-de-memoria-con-ggml">5. Construcción del grafo computacional y gestión de memoria con ggml
&lt;/h2>&lt;p>ggml adopta un enfoque de &amp;ldquo;Definir y Ejecutar&amp;rdquo; (Define-and-Run), donde construye un grafo computacional estático para la inferencia y luego lo evalúa.&lt;/p>
&lt;h3 id="51-ggml_context-y-el-asignador-de-arena">5.1 ggml_context y el asignador de arena
&lt;/h3>&lt;p>La característica más singular de ggml es la &amp;ldquo;asignación por arena&amp;rdquo;, que no realiza ninguna asignación dinámica de memoria (&lt;code>malloc&lt;/code> o &lt;code>new&lt;/code>) dentro del bucle de inferencia.
Al inicializar, se reserva una gran área de memoria contigua (arena), y cada vez que se llama a funciones como &lt;code>ggml_new_tensor&lt;/code>, el puntero de esta área se incrementa. Cuando se completa un paso de inferencia, simplemente se restablece el puntero de asignación a su posición inicial, completando instantáneamente la asignación de memoria para el siguiente paso de inferencia.&lt;/p>
&lt;h3 id="52-ejemplo-concreto-de-construcción-de-grafo">5.2 Ejemplo concreto de construcción de grafo
&lt;/h3>&lt;p>En cada paso de inferencia, se construye un grafo computacional en memoria como el siguiente:&lt;/p>
&lt;div class="mermaid">graph TD
A["ID de Entrada de Tokens"] --> B["Búsqueda de Embedding"]
B --> C["ggml_rms_norm"]
C --> D["Proyecciones Q / K / V"]
D --> E["Posicional ggml_rope"]
E --> F["Almacenar Caché KV"]
E --> G["Cargar Caché KV"]
G --> H["Autoatención"]
H --> I["Escala y Softmax"]
I --> J["Salida de Atención"]
J --> K["Proyección de Salida"]
K --> L["Añadir Residual"]&lt;/div>
&lt;hr>
&lt;h2 id="6-cuantización-y-optimización-para-windows--simd">6. Cuantización y optimización para Windows / SIMD
&lt;/h2>&lt;p>Manejar TinyLLaMA (1.1B) en FP16 requiere aproximadamente 2.2GB de memoria, pero mediante cuantización de 4 bits (como Q4_K), se puede comprimir drásticamente a unos 600MB.&lt;/p>
&lt;h3 id="61-arquitectura-de-cuantización-por-bloques">6.1 Arquitectura de cuantización por bloques
&lt;/h3>&lt;p>ggml no cuantiza todo el tensor uniformemente, sino en unidades de &amp;ldquo;bloques&amp;rdquo;.
En el formato &lt;code>Q4_0&lt;/code>, 32 valores FP16 se agrupan en un bloque.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Factor de escala&lt;/strong>: 1 valor FP16 (2 bytes)&lt;/li>
&lt;li>&lt;strong>Datos cuantizados&lt;/strong>: 32 valores de 4 bits (16 bytes)
Esto minimiza el impacto de los valores atípicos (outliers) locales.&lt;/li>
&lt;/ul>
&lt;h3 id="62-aceleración-del-producto-punto-con-avx2">6.2 Aceleración del producto punto con AVX2
&lt;/h3>&lt;p>Al compilar para las CPU x86 más recientes en entornos Windows, se utilizan banderas del compilador como &lt;code>/arch:AVX2&lt;/code> y el procesamiento SIMD se realiza con el siguiente flujo:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Carga&lt;/strong>: Cargar datos cuantizados de 4 bits desde la memoria a registros AVX de 256 bits.&lt;/li>
&lt;li>&lt;strong>Expansión y desempaquetado&lt;/strong>: Expandir los valores de 4 bits a Int8 o Int16 usando máscaras de bits y operaciones de desplazamiento.&lt;/li>
&lt;li>&lt;strong>Descuantización&lt;/strong>: Multiplicar por el factor de escala para convertir a punto flotante.&lt;/li>
&lt;li>&lt;strong>Operación FMA&lt;/strong>: Ejecutar en paralelo la operación de multiplicación y suma (multiply-add) con los valores de activación y &lt;code>_mm256_fmadd_ps&lt;/code> (Fused Multiply-Add).&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="7-detalles-de-implementación-de-la-caché-kv">7. Detalles de implementación de la caché KV
&lt;/h2>&lt;p>En la generación autorregresiva, la &amp;ldquo;caché KV&amp;rdquo; para omitir el cálculo de las Keys y Values de los tokens pasados es una función esencial.&lt;/p>
&lt;p>Los puntos clave para la implementación en C++ son los siguientes:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Reserva previa del tensor&lt;/strong>: Se inicializa un enorme tensor para la caché KV con la longitud máxima del contexto (ej: 2048 tokens) (se recomienda FP16).&lt;/li>
&lt;li>&lt;strong>Copia por desplazamiento&lt;/strong>: Cuando se realiza el cálculo para la posición del token $N$, los vectores K y V obtenidos en ese paso se almacenan en la fila $N$ del tensor de la caché KV usando &lt;code>ggml_cpy&lt;/code> u otras funciones.&lt;/li>
&lt;li>&lt;strong>Creación de vista durante la atención&lt;/strong>: Al calcular la atención, se crea una &amp;ldquo;vista&amp;rdquo; que apunta solo a la parte de los tokens del 0 al $N$-ésimo y se pasa al producto matricial.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-tokenizador-bpe-y-decodificación">8. Tokenizador BPE y decodificación
&lt;/h2>&lt;p>La cadena de entrada se trata como una secuencia de bytes UTF-8 y se compara con un vocabulario predefinido. En C++, para acelerar la búsqueda en el vocabulario, se implementa un &lt;strong>árbol Trie (árbol de prefijos)&lt;/strong> o algoritmos que utilizan colas de prioridad.&lt;/p>
&lt;p>A partir de los logits generados por el LM Head, se escalan las probabilidades usando el parámetro Temperature, los candidatos se filtran usando métodos Top-K o Top-P (Nucleus Sampling), y se utiliza un número aleatorio para determinar el siguiente token final.&lt;/p>
&lt;hr>
&lt;h2 id="9-inicio-del-proyecto-en-c-entorno-windows--powershell">9. Inicio del proyecto en C++ (Entorno Windows / PowerShell)
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cmake_minimum_required&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">VERSION&lt;/span> &lt;span class="s">3.14&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">TinyLLaMACpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CMAKE_CXX_STANDARD&lt;/span> &lt;span class="s">17&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Configuración de banderas de optimización y AVX2 para Windows (MSVC)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MSVC&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">/O2&lt;/span> &lt;span class="s">/arch:AVX2&lt;/span> &lt;span class="s">/fp:fast&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_link_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">/STACK:8388608&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">else&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">-O3&lt;/span> &lt;span class="s">-march=native&lt;/span> &lt;span class="s">-ffast-math&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">endif&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">add_library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ggml&lt;/span> &lt;span class="s">OBJECT&lt;/span> &lt;span class="s">ggml/ggml.c&lt;/span> &lt;span class="s">ggml/ggml-alloc.c&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">target_compile_definitions&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ggml&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">GGML_USE_AVX2&lt;/span> &lt;span class="s">GGML_USE_F16C&lt;/span> &lt;span class="s">GGML_USE_FMA&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">add_executable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">main&lt;/span> &lt;span class="s">main.cpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">main&lt;/span> &lt;span class="s">ggml&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Ejemplo de comando de compilación en PowerShell:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">mkdir&lt;/span> &lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd &lt;/span>&lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmake&lt;/span> &lt;span class="p">..&lt;/span> &lt;span class="n">-G&lt;/span> &lt;span class="s2">&amp;#34;Visual Studio 17 2022&amp;#34;&lt;/span> &lt;span class="n">-A&lt;/span> &lt;span class="n">x64&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmake&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-build&lt;/span> &lt;span class="p">.&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-config&lt;/span> &lt;span class="n">Release&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="10-resumen">10. Resumen
&lt;/h2>&lt;p>Implementar desde cero un motor de inferencia para modelos de IA pequeños como TinyLLaMA usando C++ y ggml es una excelente oportunidad para descubrir la caja negra del aprendizaje profundo y aprender la belleza del control de hardware de bajo nivel. Disfrutemos plenamente de la esencia de la programación de sistemas, como la carga con copia cero mediante mapeo de memoria, la optimización SIMD y la construcción de la caché KV, mientras abrimos camino hacia el futuro de la IA periférica (Edge AI).&lt;/p></description></item><item><title>【Introducción a la implementación de RAG】Cómo hacer que una IA local lea tus propios documentos</title><link>http://kenji.blog/es/p/rag-local-ai-implementation-guide/</link><pubDate>Fri, 11 Sep 2026 13:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/rag-local-ai-implementation-guide/</guid><description>&lt;img src="http://kenji.blog/p/rag-local-ai-implementation-guide/img/eyecatch.jpg" alt="Featured image of post 【Introducción a la implementación de RAG】Cómo hacer que una IA local lea tus propios documentos" />&lt;h1 id="introducción">Introducción
&lt;/h1>&lt;p>En los últimos años, la evolución de los Grandes Modelos de Lenguaje (LLM) ha sido notable, con inteligencias artificiales como ChatGPT y Claude infiltrándose en nuestras vidas y trabajos. Sin embargo, los LLM generales tienen una clara debilidad: solo conocen la &amp;ldquo;información pública en el momento de su entrenamiento&amp;rdquo;. Naturalmente, no pueden responder preguntas sobre &amp;ldquo;documentos privados&amp;rdquo; como reglamentos internos de la empresa, notas personales o materiales de proyectos no publicados. Si se les fuerza a responder, aumenta el riesgo de que generen mentiras plausibles que no se ajustan a los hechos (alucinaciones).&lt;/p>
&lt;p>Por lo tanto, la arquitectura tecnológica que está experimentando una adopción explosiva en todo el mundo en este momento es &lt;strong>RAG (Retrieval-Augmented Generation: Generación Aumentada por Recuperación)&lt;/strong>. Al utilizar RAG, es posible proporcionar de forma dinámica conocimientos propios al LLM desde una base de datos externa, permitiéndole generar respuestas precisas y fundamentadas basadas en dichos conocimientos.&lt;/p>
&lt;p>Además, al manejar información confidencial de empresas o individuos, el envío de datos a API basadas en la nube como las de OpenAI a menudo no está permitido por las políticas de seguridad. Lo que se necesita en estos casos es la construcción de un &amp;ldquo;RAG local&amp;rdquo; combinado con una &lt;strong>IA local&lt;/strong> (un LLM que opera de manera autónoma en tu propia PC o en un servidor on-premise).&lt;/p>
&lt;p>En este artículo, explicaremos exhaustivamente desde la teoría fundamental de RAG hasta métodos específicos para implementar un RAG local con Python, los antecedentes matemáticos (cómo funciona la búsqueda vectorial) y técnicas avanzadas para poner el sistema en producción.&lt;/p>
&lt;hr>
&lt;h1 id="1-arquitectura-general-de-rag">1. Arquitectura general de RAG
&lt;/h1>&lt;p>RAG no es un modelo de IA único, sino una arquitectura de sistema en la que múltiples componentes colaboran entre sí. Se divide principalmente en dos fases: la &amp;ldquo;Fase de ingesta (importación de datos)&amp;rdquo; y la &amp;ldquo;Fase de recuperación y generación (búsqueda y generación)&amp;rdquo;.&lt;/p>
&lt;p>El siguiente diagrama de Mermaid muestra la imagen general del sistema RAG.&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "Fase de Ingesta (Preparación previa)"
Doc["Documentos propios (PDF, TXT, etc.)"] --> Loader["Cargador de documentos"]
Loader --> Splitter["División de texto (Chunking)"]
Splitter --> EmbedModel1["Modelo de incrustación (Embedding)"]
EmbedModel1 --> VectorDB["Base de datos vectorial"]
end
subgraph "Fase de Inferencia (Al consultar el usuario)"
User["Pregunta del usuario (Consulta)"] --> EmbedModel2["Modelo de incrustación (Embedding)"]
EmbedModel2 --> QueryVector["Vector de consulta"]
QueryVector --> Search["Búsqueda de similitud (Búsqueda vectorial)"]
VectorDB --> Search
Search --> Context["Extracción de fragmentos relevantes (Contexto)"]
User --> PromptBuilder["Construcción del prompt"]
Context --> PromptBuilder
PromptBuilder --> LocalLLM["LLM Local"]
LocalLLM --> Answer["Generación de respuesta final"]
end&lt;/div>
&lt;h2 id="fase-de-ingesta-preparación-previa">Fase de ingesta (Preparación previa)
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Carga de documentos&lt;/strong>: Se leen datos no estructurados como archivos PDF, Word o de texto.&lt;/li>
&lt;li>&lt;strong>Chunking (División de texto)&lt;/strong>: Se dividen los textos largos en fragmentos significativos (chunks) para que se ajusten al límite de entrada del LLM (ventana de contexto) y para mejorar la precisión de la búsqueda.&lt;/li>
&lt;li>&lt;strong>Embedding (Vectorización)&lt;/strong>: Se introducen los fragmentos divididos en un modelo de incrustación (Embedding Model) y se convierten en matrices numéricas (vectores) de cientos a miles de dimensiones.&lt;/li>
&lt;li>&lt;strong>Almacenamiento en la base de datos&lt;/strong>: Los vectores convertidos se vinculan con los datos de texto originales y se guardan en una base de datos vectorial (Vector DB).&lt;/li>
&lt;/ol>
&lt;h2 id="fase-de-inferencia-tiempo-de-ejecución">Fase de inferencia (Tiempo de ejecución)
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Vectorización de la consulta&lt;/strong>: Se vectoriza la pregunta del usuario usando el mismo modelo de incrustación de la fase de preparación.&lt;/li>
&lt;li>&lt;strong>Búsqueda de similitud&lt;/strong>: Se calcula la similitud entre el vector de consulta y los vectores de los documentos en la base de datos para recuperar los fragmentos de texto con mayor relevancia (cercanía semántica).&lt;/li>
&lt;li>&lt;strong>Construcción del prompt&lt;/strong>: Los textos relevantes recuperados se combinan con la pregunta del usuario como &amp;ldquo;contexto (conocimiento de fondo)&amp;rdquo; para crear el prompt de entrada para el LLM.&lt;/li>
&lt;li>&lt;strong>Generación de la respuesta&lt;/strong>: El LLM, tras recibir el prompt aumentado, genera una respuesta basándose en la información de contexto proporcionada.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="2-entendimiento-profundo-de-la-búsqueda-vectorial-y-las-incrustaciones-embeddings">2. Entendimiento profundo de la búsqueda vectorial y las incrustaciones (Embeddings)
&lt;/h1>&lt;p>El núcleo de RAG es la &amp;ldquo;búsqueda vectorial (búsqueda semántica)&amp;rdquo;. Mientras que la búsqueda por palabras clave tradicional (como BM25) se basa en coincidencias exactas y frecuencias de palabras, la búsqueda vectorial se basa en la &amp;ldquo;similitud de significado&amp;rdquo;. Por ejemplo, palabras diferentes como &amp;ldquo;perro&amp;rdquo; y &amp;ldquo;cachorro&amp;rdquo;, o &amp;ldquo;PC&amp;rdquo; y &amp;ldquo;ordenador&amp;rdquo;, aparecerán en los resultados si su significado es parecido.&lt;/p>
&lt;h2 id="qué-es-un-modelo-de-incrustación-embedding-model">¿Qué es un modelo de incrustación (Embedding Model)?
&lt;/h2>&lt;p>Un modelo de incrustación es una red neuronal que toma textos en lenguaje natural como entrada y emite un vector denso (Dense Vector) de longitud fija. Los modelos comunes (como &lt;code>text-embedding-3-small&lt;/code> o el de código abierto &lt;code>multilingual-e5-large&lt;/code>) mapean el texto a vectores de números reales de 384 o 1024 dimensiones.&lt;/p>
&lt;p>En este espacio multidimensional (espacio latente), el modelo está entrenado para que los textos con significados similares tengan distancias más cortas en las coordenadas espaciales.&lt;/p>
&lt;h2 id="antecedentes-matemáticos-del-cálculo-de-similitud-similitud-del-coseno">Antecedentes matemáticos del cálculo de similitud: Similitud del coseno
&lt;/h2>&lt;p>Cuando una base de datos vectorial busca documentos relevantes, la métrica de distancia más comúnmente utilizada es la &lt;strong>similitud del coseno (Cosine Similarity)&lt;/strong>. A diferencia de la distancia euclidiana (distancia espacial absoluta), la similitud del coseno se centra en el &amp;ldquo;ángulo entre dos vectores&amp;rdquo;. Es altamente adecuada para calcular la similitud de textos, ya que no se ve tan afectada por la longitud del texto (la norma del vector).&lt;/p>
&lt;p>Expresado matemáticamente, la similitud del coseno entre los vectores $\mathbf{A}$ y $\mathbf{B}$ es la siguiente:&lt;/p>
$$ \text{Cosine Similarity}(\mathbf{A}, \mathbf{B}) = \cos(\theta) = \frac{\mathbf{A} \cdot \mathbf{B}}{\|\mathbf{A}\| \|\mathbf{B}\|} = \frac{\sum_{i=1}^{n} A_i B_i}{\sqrt{\sum_{i=1}^{n} A_i^2} \sqrt{\sum_{i=1}^{n} B_i^2}} $$
&lt;ul>
&lt;li>$\mathbf{A} \cdot \mathbf{B}$ representa el producto escalar (Dot Product).&lt;/li>
&lt;li>$\|\mathbf{A}\|$ representa la norma L2 (longitud) del vector $\mathbf{A}$.&lt;/li>
&lt;li>$n$ es el número de dimensiones del vector.&lt;/li>
&lt;/ul>
&lt;p>La similitud del coseno toma valores entre -1 y 1.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Cercano a 1&lt;/strong>: La dirección de los dos vectores es casi la misma (sus significados son muy similares).&lt;/li>
&lt;li>&lt;strong>Cercano a 0&lt;/strong>: Los dos vectores son ortogonales (no están relacionados).&lt;/li>
&lt;li>&lt;strong>Cercano a -1&lt;/strong>: Los dos vectores apuntan en direcciones opuestas (significados opuestos).&lt;/li>
&lt;/ul>
&lt;p>Las bases de datos vectoriales recientes (Chroma, FAISS, Qdrant, etc.) adoptan un algoritmo de búsqueda de vecino más cercano aproximado (ANN) llamado HNSW (Hierarchical Navigable Small World), que está optimizado para buscar documentos con una alta similitud de coseno entre millones de datos vectoriales en milisegundos.&lt;/p>
&lt;hr>
&lt;h1 id="3-pila-tecnológica-para-construir-un-rag-local">3. Pila tecnológica para construir un RAG local
&lt;/h1>&lt;p>Para construir un RAG local completo que no dependa de la nube, aprovechamos el ecosistema de código abierto. A continuación se presentan las pilas tecnológicas recomendadas:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Modelo de lenguaje (LLM)&lt;/strong>
&lt;ul>
&lt;li>Herramienta: &lt;code>Ollama&lt;/code> o &lt;code>Llama.cpp&lt;/code>&lt;/li>
&lt;li>Modelo: Modelos abiertos ligeros y de alto rendimiento como &lt;code>Llama-3-8B-Instruct&lt;/code>, &lt;code>Gemma-2-9B-It&lt;/code>, &lt;code>Qwen2-7B-Instruct&lt;/code>. Para tareas en japonés, son adecuados modelos ajustados al japonés como &lt;code>Llama-3-ELYZA-JP-8B&lt;/code>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Modelo de incrustación (Embedding)&lt;/strong>
&lt;ul>
&lt;li>Modelo: &lt;code>intfloat/multilingual-e5-large&lt;/code> o &lt;code>BAAI/bge-m3&lt;/code>. Si se ejecuta localmente, lo común es descargarlo desde Hugging Face y ejecutarlo con Sentence-Transformers.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Base de datos vectorial (Vector DB)&lt;/strong>
&lt;ul>
&lt;li>&lt;code>ChromaDB&lt;/code>: Basado en Python y extremadamente fácil de configurar. Ideal para el desarrollo local.&lt;/li>
&lt;li>&lt;code>FAISS&lt;/code>: Una biblioteca de búsqueda vectorial rápida desarrollada por Meta.&lt;/li>
&lt;li>&lt;code>Qdrant&lt;/code> / &lt;code>Milvus&lt;/code>: Orientado a entornos de producción de mayor escala.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Marco de orquestación&lt;/strong>
&lt;ul>
&lt;li>&lt;code>LangChain&lt;/code>: El estándar de facto para encadenar componentes (Chain).&lt;/li>
&lt;li>&lt;code>LlamaIndex&lt;/code>: Un marco de conexión de datos especializado especialmente en RAG.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>En esta ocasión, lo implementaremos con la combinación más sencilla de introducir: &lt;strong>LangChain + ChromaDB + Ollama + HuggingFaceEmbeddings&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h1 id="4-tutorial-de-implementación-construcción-de-un-rag-local-completo-con-python">4. Tutorial de implementación: Construcción de un RAG local completo con Python
&lt;/h1>&lt;p>A partir de aquí, construiremos el RAG local mientras escribimos código Python real. Asegúrate de tener Ollama instalado en tu PC y ejecutándose en segundo plano con antelación. Además, obtén un modelo en Ollama (ej. &lt;code>ollama run llama3&lt;/code>).&lt;/p>
&lt;h2 id="paso-1-instalación-de-las-bibliotecas-necesarias">Paso 1: Instalación de las bibliotecas necesarias
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pip install langchain langchain-community langchain-huggingface
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install chromadb sentence-transformers pypdf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="paso-2-visión-general-del-código-de-implementación">Paso 2: Visión general del código de implementación
&lt;/h2>&lt;p>A continuación, se muestra un script de Python completo para leer un archivo PDF, vectorizarlo y hacer que un LLM local responda preguntas basadas en él.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;span class="lnt">86
&lt;/span>&lt;span class="lnt">87
&lt;/span>&lt;span class="lnt">88
&lt;/span>&lt;span class="lnt">89
&lt;/span>&lt;span class="lnt">90
&lt;/span>&lt;span class="lnt">91
&lt;/span>&lt;span class="lnt">92
&lt;/span>&lt;span class="lnt">93
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.document_loaders&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PyPDFLoader&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_text_splitters&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">RecursiveCharacterTextSplitter&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_huggingface&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">HuggingFaceEmbeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.vectorstores&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Chroma&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.llms&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Ollama&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.chains&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">RetrievalQA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 1. Cargar el documento&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Cargando el documento...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Especifica la ruta del PDF que deseas cargar&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">file_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;sample_company_policy.pdf&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">loader&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PyPDFLoader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">documents&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">loader&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 2. División en fragmentos (Text Splitting)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Divide en un tamaño adecuado para no romper el significado del texto&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">text_splitter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">RecursiveCharacterTextSplitter&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunk_size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Número máximo de caracteres por fragmento&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunk_overlap&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Número de caracteres superpuestos con el fragmento anterior y siguiente (evita la ruptura del contexto)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">separators&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;。&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;、&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34; &amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunks&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">text_splitter&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split_documents&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">documents&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Se ha dividido en &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">chunks&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> fragmentos.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 3. Inicialización del modelo de incrustación (Local HuggingFace Model)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Usando un modelo multilingüe fuerte en japonés (y otros idiomas)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Cargando el modelo de incrustación...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">HuggingFaceEmbeddings&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;intfloat/multilingual-e5-large&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;device&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;cpu&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="c1"># Si tienes GPU, usa &amp;#39;cuda&amp;#39; o &amp;#39;mps&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 4. Construcción de la base de datos vectorial (Chroma)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Construyendo la base de datos vectorial...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">persist_directory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;./chroma_db&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">vectorstore&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Chroma&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_documents&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">documents&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">chunks&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embedding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">embeddings&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">persist_directory&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">persist_directory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Creación del recuperador (Retriever). Configurado para obtener los 3 mejores documentos relevantes&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">retriever&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">as_retriever&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">search_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;k&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 5. Inicialización del LLM local (Ollama)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Conectando al LLM local...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Asegúrate de haber obtenido el modelo de antemano con &amp;#39;ollama pull llama3&amp;#39; o similar&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ollama&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;llama3&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 6. Definición de la plantilla del prompt&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt_template&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Eres un asistente excelente, experto en los reglamentos y la información interna de la empresa.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Usa únicamente el siguiente contexto (información de fondo) para responder a la pregunta del usuario detalladamente en español.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Si no puedes encontrar la respuesta en el contexto, no adivines; responde honestamente con un &amp;#34;No se puede determinar a partir de la información proporcionada&amp;#34;.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">【Contexto】
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{context}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">【Pregunta】
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{question}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">【Respuesta】:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">PROMPT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">template&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">prompt_template&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">input_variables&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;context&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;question&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 7. Construcción de la cadena RAG&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qa_chain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">RetrievalQA&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_chain_type&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chain_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;stuff&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">retriever&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">retriever&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">return_source_documents&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Configura si deseas devolver la fuente de información&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chain_type_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;prompt&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">PROMPT&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 8. Ejecución de la pregunta&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Cuéntame sobre las condiciones de pago de gastos de transporte para el trabajo remoto.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">Pregunta: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">qa_chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;query&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">query&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;【Respuesta】&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;result&amp;#39;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">---&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;【Fuentes consultadas】&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">doc&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;source_documents&amp;#39;&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;- Página &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;page&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;desconocido&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">page_content&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="explicación-de-los-puntos-clave-del-código">Explicación de los puntos clave del código
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>RecursiveCharacterTextSplitter&lt;/strong>:
Es el separador más recomendado para dividir el lenguaje natural. Intenta dividir en el orden de párrafo (&lt;code>\n\n&lt;/code>), línea (&lt;code>\n&lt;/code>) y punto (&lt;code>。&lt;/code>), dividiendo el texto para que se ajuste al &lt;code>chunk_size&lt;/code> especificado mientras se mantiene el significado en la medida de lo posible. Al configurar el &lt;code>chunk_overlap&lt;/code>, se evita que se rompa el límite del contexto y se pierda información.&lt;/li>
&lt;li>&lt;strong>HuggingFaceEmbeddings&lt;/strong>:
&lt;code>intfloat/multilingual-e5-large&lt;/code> es un modelo de incrustación de código abierto muy potente que soporta múltiples idiomas. Permite vectorizar texto en la memoria local sin conexión, sin utilizar una API en la nube (como &lt;code>text-embedding-ada-002&lt;/code> de OpenAI).&lt;/li>
&lt;li>&lt;strong>ChromaDB&lt;/strong>:
Dado que funciona en memoria o en el almacenamiento local (basado en SQLite), no requiere la configuración de un servidor de base de datos complejo. Al especificar un &lt;code>persist_directory&lt;/code>, se puede omitir el proceso de vectorización en ejecuciones posteriores y cargar la base de datos desde el disco.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="5-técnicas-avanzadas-de-rag-advanced-rag-techniques">5. Técnicas avanzadas de RAG (Advanced RAG Techniques)
&lt;/h1>&lt;p>El sistema básico de RAG (Naive RAG) construido en el tutorial anterior funcionará, pero si se requiere una alta precisión de respuesta en un entorno de producción, será necesario introducir técnicas avanzadas como las siguientes.&lt;/p>
&lt;h2 id="51-búsqueda-híbrida-hybrid-search">5.1 Búsqueda Híbrida (Hybrid Search)
&lt;/h2>&lt;p>La búsqueda vectorial es buena capturando el &amp;ldquo;significado&amp;rdquo;, pero a veces puede tener dificultades con búsquedas estrictas de palabras clave como &amp;ldquo;nombres propios específicos&amp;rdquo;, &amp;ldquo;números de modelo de productos&amp;rdquo; o &amp;ldquo;identificadores de empleados&amp;rdquo;.
Por lo tanto, se puede reducir drásticamente las omisiones de búsqueda ejecutando en paralelo la &lt;strong>búsqueda semántica&lt;/strong> basada en vectores y la &lt;strong>búsqueda por palabras clave&lt;/strong> usando algoritmos como BM25, y luego integrando los resultados de ambos (usando técnicas como Reciprocal Rank Fusion; RRF).&lt;/p>
&lt;h2 id="52-reclasificación-re-ranking">5.2 Reclasificación (Re-ranking)
&lt;/h2>&lt;p>La búsqueda vectorial es rápida, pero no siempre evalúa la idoneidad contextual exacta. Una canalización general para mejorar la precisión de la búsqueda es la siguiente:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Búsqueda inicial (First-stage Retrieval)&lt;/strong>: Se recuperan de 20 a 30 fragmentos relevantes de la base de datos vectorial, buscando de forma amplia y superficial.&lt;/li>
&lt;li>&lt;strong>Reevaluación (Re-ranking)&lt;/strong>: Se utiliza otro modelo de aprendizaje automático más pesado llamado Cross-Encoder (por ejemplo, &lt;code>bge-reranker&lt;/code>) para introducir los pares de la consulta del usuario y los fragmentos recuperados, y recalcular la puntuación de adecuación semántica.&lt;/li>
&lt;li>&lt;strong>Selección&lt;/strong>: Solo los 3 a 5 mejores resultados con las puntuaciones más altas se pasan como contexto final al prompt del LLM.&lt;/li>
&lt;/ol>
&lt;p>Con este enfoque, se evita pasar información de ruido irrelevante al LLM, aumentando significativamente la precisión (Precision) de la respuesta.&lt;/p>
&lt;div class="mermaid">graph LR
Query["Consulta"] --> VSearch["Búsqueda vectorial (Top 20)"]
VSearch --> Reranker["Modelo reclasificador (Cross-Encoder)"]
Query --> Reranker
Reranker --> TopK["Top 3 de alta precisión"]
TopK --> LLM["Generación por LLM"]&lt;/div>
&lt;h2 id="53-chunking-semántico-y-búsqueda-de-documentos-primarios">5.3 Chunking semántico y búsqueda de documentos primarios
&lt;/h2>&lt;p>Existe una técnica llamada &amp;ldquo;Semantic Chunking&amp;rdquo; (división semántica) que en lugar de dividir el texto mecánicamente con un número fijo de caracteres, utiliza IA para detectar cambios en el significado de las oraciones y dividir allí.
Además, la técnica del &amp;ldquo;Parent Document Retriever&amp;rdquo; (Búsqueda de documentos primarios) permite lograr búsquedas altamente precisas vectorizando unidades muy pequeñas (como oraciones) para la búsqueda, pero al pasarlo al LLM, proporciona el &amp;ldquo;párrafo original grande (documento primario)&amp;rdquo; que contiene esa oración, entregando un contexto suficiente al LLM.&lt;/p>
&lt;hr>
&lt;h1 id="6-retos-y-contramedidas-al-operar-un-rag-local">6. Retos y contramedidas al operar un RAG local
&lt;/h1>&lt;p>Existen obstáculos específicos al construir y operar RAG en un entorno local.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Agotamiento de VRAM (Memoria de video)&lt;/strong>:
Para ejecutar un LLM local a una velocidad práctica (decenas de tokens por segundo), es necesario cargar el modelo en la VRAM de la GPU. Ejecutar un modelo de clase 8B en fp16 (punto flotante de 16 bits) requiere aproximadamente 16 GB de VRAM. Sin embargo, al usar tecnología de &lt;strong>cuantización (Quantization)&lt;/strong> (compresión a 4 bits u 8 bits, como los formatos GGUF o AWQ), es posible que funcione lo suficientemente rápido incluso con 8 GB de VRAM (como en una PC de juegos típica). Llama.cpp y Ollama son compatibles con estos formatos cuantizados por defecto.&lt;/li>
&lt;li>&lt;strong>Límite de la ventana de contexto&lt;/strong>:
Si la cantidad de contexto recuperado es demasiada, puede exceder el límite de entrada (límite de tokens) del LLM o causar que el modelo olvide la información en el medio (fenómeno de &amp;ldquo;Lost in the middle&amp;rdquo;). Es esencial ajustar el número de fragmentos a extraer y realizar una selección rigurosa utilizando la técnica de reclasificación mencionada anteriormente.&lt;/li>
&lt;li>&lt;strong>Gestión de la frescura de los datos&lt;/strong>:
Si se actualiza el documento de origen, los vectores correspondientes en la base de datos vectorial también deben ser actualizados o eliminados (operaciones CRUD). Dado que ChromaDB soporta actualizaciones basadas en el ID del documento, es práctico gestionar los valores hash de los archivos y establecer un proceso por lotes que sincronice solo las diferencias.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="conclusión">Conclusión
&lt;/h1>&lt;p>RAG (Generación Aumentada por Recuperación) es un potente paradigma que evoluciona a la IA de ser un asistente generalizado a un &amp;ldquo;experto personal exclusivo&amp;rdquo; o un &amp;ldquo;especialista en tareas internas de la empresa&amp;rdquo;.&lt;/p>
&lt;p>Hemos visto que incluso con requisitos altamente confidenciales donde no se pueden usar servicios en la nube, es relativamente fácil construir un entorno &amp;ldquo;RAG local&amp;rdquo; completo combinando ecosistemas de código abierto como Ollama, LangChain y ChromaDB.&lt;/p>
&lt;p>Basándote en el entendimiento matemático del espacio vectorial explicado en este artículo, y enfoques avanzados como la división de texto y la reclasificación, te animamos a desarrollar tu propio sistema de IA utilizando tus propios datos. La velocidad de evolución de la IA local es asombrosa, y el sistema que construyes hoy puede ser actualizado instantáneamente reemplazando el modelo por uno más ligero e inteligente que aparezca mañana.&lt;/p>
&lt;hr>
&lt;p>&lt;em>En este blog continuaremos publicando artículos en profundidad sobre la tecnología de IA y RAG. Si tienes preguntas o comentarios, por favor déjalos en la sección de comentarios.&lt;/em>&lt;/p></description></item><item><title>【Último en 2026】Guía completa para ejecutar LLM locales en un entorno Windows</title><link>http://kenji.blog/es/p/local-llm-windows-2026/</link><pubDate>Fri, 11 Sep 2026 10:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/local-llm-windows-2026/</guid><description>&lt;img src="http://kenji.blog/p/local-llm-windows-2026/img/eyecatch.jpg" alt="Featured image of post 【Último en 2026】Guía completa para ejecutar LLM locales en un entorno Windows" />&lt;h1 id="1-introducción-por-qué-llm-locales-en-windows-ahora">1. Introducción: ¿Por qué LLM locales en Windows ahora?
&lt;/h1>&lt;p>En 2026, la evolución de la IA generativa y los Grandes Modelos de Lenguaje (LLM) muestra un cambio de paradigma importante: desde servicios API gigantes en la nube hasta &amp;ldquo;LLM locales&amp;rdquo; que se ejecutan en PCs personales y entornos on-premise. Aunque las IA en la nube como GPT-5 de OpenAI o Claude 3.5 de Anthropic son extremadamente potentes, no todas las empresas o individuos pueden enviar todos sus datos a la nube. Desde el punto de vista de la privacidad, seguridad, latencia y los costes sostenibles a largo plazo, la demanda de LLM locales ha crecido explosivamente como nunca antes.&lt;/p>
&lt;p>Especialmente en el entorno Windows, la evolución del ecosistema de LLM locales es notable. Hasta hace unos años, el sentido común dictaba que &amp;ldquo;el desarrollo y la ejecución de IA significaba Linux&amp;rdquo;, pero en 2026 Windows se ha transformado en una plataforma de IA extremadamente potente y accesible.&lt;/p>
&lt;p>Este artículo, basado en las últimas tendencias tecnológicas de 2026, proporciona una guía completa para construir, operar y optimizar un LLM local en un entorno Windows. Cubriremos exhaustivamente desde una configuración simple con Ollama para principiantes, hasta una optimización extrema usando llama.cpp para usuarios avanzados, además de profundizar en el enfoque matemático del cálculo de VRAM, una comprensión profunda de la arquitectura y el fine-tuning local.&lt;/p>
&lt;h2 id="11-tendencias-tecnológicas-que-rodean-a-los-llm-locales-en-2026">1.1 Tendencias tecnológicas que rodean a los LLM locales en 2026
&lt;/h2>&lt;p>Las principales tendencias que conforman el ecosistema actual de los LLM locales son las siguientes:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Adopción total del formato GGUF&lt;/strong>: El GGUF (GPT-Generated Unified Format), que integra metadatos y tensores en un solo archivo, se ha convertido en el estándar de facto. Esto permite que con solo descargar un archivo desde Hugging Face, se pueda ejecutar en cualquier entorno.&lt;/li>
&lt;li>&lt;strong>Democratización de la arquitectura MoE (Mixture of Experts)&lt;/strong>: Se han lanzado numerosos modelos MoE de pequeña escala pero de alto rendimiento, que al activar solo ciertos expertos durante la inferencia, logran un rendimiento comparable al de modelos gigantes mientras mantienen bajo el uso de computación en PCs de consumo.&lt;/li>
&lt;li>&lt;strong>Abstracción y optimización avanzadas de los motores de inferencia&lt;/strong>: Herramientas como Ollama, LM Studio y AnythingLLM se han perfeccionado, eliminando la necesidad de que los usuarios se preocupen por dependencias complejas como la instalación de controladores CUDA. Además, el soporte nativo de FlashAttention 3 en Windows ha mejorado drásticamente la velocidad de inferencia.&lt;/li>
&lt;li>&lt;strong>Utilización de NPU y el auge de las PC Windows Copilot+&lt;/strong>: Incluso en laptops sin GPU, la tecnología para ejecutar pequeños LLM (SLM: Small Language Models) con bajo consumo energético utilizando la NPU (Neural Processing Unit) integrada ha alcanzado una etapa práctica.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="2-requisitos-de-hardware-y-preparación-del-so">2. Requisitos de hardware y preparación del SO
&lt;/h1>&lt;p>Para ejecutar un LLM local a una velocidad práctica (más de 15-30 tokens por segundo), la selección del hardware es lo más importante.&lt;/p>
&lt;h2 id="21-configuración-de-hardware-recomendada">2.1 Configuración de hardware recomendada
&lt;/h2>&lt;p>Con la evolución de los PC con IA, las especificaciones requeridas también están cambiando.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>SO&lt;/strong>: Windows 11 Pro (24H2 o posterior). Indispensable para utilizar las funcionalidades completas de WSL2, la gestión avanzada de memoria y las últimas API de DirectML.&lt;/li>
&lt;li>&lt;strong>CPU&lt;/strong>: Intel Core Ultra serie 200 o superior, o AMD Ryzen serie 9000 o superior. Si se utiliza la inferencia por CPU de forma combinada, es esencial una comunicación de memoria de gran ancho de banda.&lt;/li>
&lt;li>&lt;strong>RAM&lt;/strong>: Mínimo 32 GB, recomendado 64 GB o más. El ancho de banda de la memoria principal (MB/s) se convierte en el cuello de botella decisivo durante la inferencia por CPU o la descarga (offloading). Una memoria rápida como DDR5-6000 o superior es ideal.&lt;/li>
&lt;li>&lt;strong>GPU&lt;/strong>: NVIDIA RTX serie 4000/5000. Lo más importante en los LLM locales no es el rendimiento de cálculo, sino la &amp;ldquo;capacidad de VRAM&amp;rdquo;.
&lt;ul>
&lt;li>&lt;strong>Entrada&lt;/strong>: RTX 4060 Ti (versión de 16 GB) - La mejor relación calidad-precio. Ideal para modelos de clase 8B a 14B.&lt;/li>
&lt;li>&lt;strong>Gama media&lt;/strong>: RTX 4070 Ti SUPER (16 GB) / RTX 4080 SUPER (16 GB)&lt;/li>
&lt;li>&lt;strong>Gama alta&lt;/strong>: RTX 4090 (24 GB) / RTX 5090 (32 GB) - Necesaria para ejecutar modelos cuantizados de clase 30B a 70B.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Almacenamiento&lt;/strong>: NVMe SSD PCIe Gen4 o Gen5. Reduce drásticamente el tiempo de carga de modelos de decenas de GB.&lt;/li>
&lt;/ul>
&lt;h2 id="22-configuración-de-wsl2-windows-subsystem-for-linux-2">2.2 Configuración de WSL2 (Windows Subsystem for Linux 2)
&lt;/h2>&lt;p>Aunque muchas herramientas GUI funcionan de forma nativa en Windows, WSL2 es muy útil para el desarrollo con Python, la compilación de las últimas herramientas y el fine-tuning LoRA que veremos más adelante. En los entornos modernos de Windows 11, simplemente instalando el controlador NVIDIA en el host, la GPU (CUDA) se puede utilizar de forma transparente desde WSL2.&lt;/p>
&lt;p>Abre PowerShell con privilegios de administrador y ejecuta lo siguiente:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Instalación de WSL2 y el último Ubuntu&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">wsl&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-install&lt;/span> &lt;span class="n">-d&lt;/span> &lt;span class="n">Ubuntu&lt;/span>&lt;span class="p">-&lt;/span>&lt;span class="mf">24.04&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Actualización del kernel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">wsl&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-update&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Después de la instalación, ejecuta &lt;code>nvidia-smi&lt;/code> dentro de la terminal de WSL2. Si la GPU se reconoce correctamente, ha sido un éxito.&lt;/p>
&lt;hr>
&lt;h1 id="3-arquitectura-del-llm-local-y-mecanismo-de-inferencia">3. Arquitectura del LLM local y mecanismo de inferencia
&lt;/h1>&lt;p>Comprender cómo el modelo genera texto en un entorno local y su estructura interna es extremadamente útil para la resolución de problemas y la optimización.&lt;/p>
&lt;p>El siguiente diagrama Mermaid muestra la ruta de inferencia típica de un LLM local.&lt;/p>
&lt;div class="mermaid">graph TD
User["Entrada del usuario (Prompt)"] --> Tokenizer["Tokenizador (Tokenizer)"]
Tokenizer --> Embedding["Capa de incrustación (Embedding)"]
subgraph "Bloque Transformer (x Capas)"
Embedding --> Attn["Auto-atención (Self-Attention)"]
Attn --> KVCache["Caché KV (Retención Key/Value)"]
Attn --> FFN["Red feed-forward (FFN)"]
end
FFN --> Logits["Cálculo de logits (Logits)"]
Logits --> Sampler["Muestreador (Temperature, Top-K, Top-P)"]
Sampler --> OutputToken["Token de salida"]
OutputToken --> |"Generación autorregresiva"| Tokenizer
OutputToken --> Decoder["Detokenizador (Detokenizer)"]
Decoder --> FinalOutput["Texto de salida final"]&lt;/div>
&lt;h2 id="31-dos-fases-prefill-y-decode">3.1 Dos fases: Prefill y Decode
&lt;/h2>&lt;p>La generación de texto por parte de un LLM se divide en dos fases con diferentes características computacionales:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Fase de Prefill (Procesamiento del prompt)&lt;/strong>: Fase en la que todo el prompt introducido se procesa y comprende a la vez. Dado que es posible el cálculo paralelo, la capacidad de cálculo de la GPU (FLOPS) está directamente relacionada con la velocidad. Si el prompt es largo, esta fase puede tardar varios segundos.&lt;/li>
&lt;li>&lt;strong>Fase de Decode (Generación de tokens)&lt;/strong>: Fase en la que se predice un token a la vez y se pasa a la siguiente entrada (autorregresiva). Dado que el cálculo paralelo está limitado en esta fase, el ancho de banda de la VRAM (Memory Bandwidth) de la GPU se convierte en un cuello de botella decisivo.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="4-cálculo-del-consumo-de-vram-y-comprensión-matemática-del-tamaño-del-modelo">4. Cálculo del consumo de VRAM y comprensión matemática del tamaño del modelo
&lt;/h1>&lt;p>Para determinar correctamente &amp;ldquo;qué modelos pueden ejecutarse en mi PC&amp;rdquo;, es necesario comprender la fórmula para calcular la VRAM. Cuando ocurre un fallback hacia la memoria del sistema (RAM) debido a la falta de VRAM, la velocidad de inferencia se ralentiza de 10 a 100 veces.&lt;/p>
&lt;h2 id="41-vram-base-según-el-tamaño-de-los-parámetros">4.1 VRAM base según el tamaño de los parámetros
&lt;/h2>&lt;p>Esta es la cantidad de memoria requerida para cargar los pesos (weights) del modelo en la VRAM.
Se calcula usando el tamaño del modelo $P$ (número de parámetros, unidad: 1000 millones = 1B) y el número de bytes por parámetro $B$.&lt;/p>
$$
V_{base} = P \times B \quad \text{(GB)}
$$
&lt;p>Por ejemplo, al cargar un modelo de parámetros 8B (8 mil millones) en FP16 (punto flotante de media precisión, 16 bits = 2 bytes):&lt;/p>
$$
V_{base} = 8 \times 2 = 16 \text{ GB}
$$
&lt;p>En otras palabras, incluso con una GPU de 16 GB de VRAM, se llega casi al límite solo con cargar el modelo.&lt;/p>
&lt;h2 id="42-la-magia-de-la-cuantización-quantization">4.2 La magia de la cuantización (Quantization)
&lt;/h2>&lt;p>Aquí es donde entra en juego la &amp;ldquo;cuantización&amp;rdquo;. Al reducir la precisión de los parámetros, el tamaño del modelo se reduce drásticamente. En el caso más común de la cuantización de 4 bits (ej: Q4_K_M), el promedio es de aproximadamente 0.55 bytes por parámetro.&lt;/p>
$$
V_{base\_4bit} = 8 \times 0.55 = 4.4 \text{ GB}
$$
&lt;p>Gracias a esto, si se tienen 16 GB de VRAM, se puede ejecutar un modelo 8B con mucho margen.&lt;/p>
&lt;h2 id="43-cálculo-de-la-caché-kv-versión-compatible-con-gqa">4.3 Cálculo de la caché KV (Versión compatible con GQA)
&lt;/h2>&lt;p>Durante la inferencia, la &amp;ldquo;caché KV&amp;rdquo;, que retiene el contexto pasado, consume VRAM. En los modelos más recientes como Llama 3, se adopta GQA (Grouped Query Attention) para ahorrar memoria.&lt;/p>
&lt;p>El consumo de la caché KV $V_{kv}$ (en gigabytes) se expresa mediante la siguiente fórmula matemática:&lt;/p>
$$
V_{kv} = 2 \times b \times s \times l \times \left( \frac{h_{kv}}{h_q} \right) \times h_q \times d \times B_{kv} \div 10^9
$$
&lt;p>Simplificando, se puede calcular fácilmente usando el número de cabezales (heads) de Key y Value $h_{kv}$:&lt;/p>
$$
V_{kv} = 2 \times b \times s \times l \times h_{kv} \times d \times B_{kv} \div 10^9
$$
&lt;p>Donde:&lt;/p>
&lt;ul>
&lt;li>$b$: Tamaño del lote (normalmente 1 para uso personal y local)&lt;/li>
&lt;li>$s$: Longitud de la secuencia (longitud del contexto, ej: 8192)&lt;/li>
&lt;li>$l$: Número de capas (ej: 32)&lt;/li>
&lt;li>$h_{kv}$: Número de cabezales KV (ej: 8)&lt;/li>
&lt;li>$d$: Número de dimensiones por cabezal (ej: 128)&lt;/li>
&lt;li>$B_{kv}$: Número de bytes de la caché KV (2 para FP16)&lt;/li>
&lt;/ul>
&lt;p>Ejemplo de cálculo (Llama 3 8B, contexto 8192, caché FP16):
$V_{kv} = 2 \times 1 \times 8192 \times 32 \times 8 \times 128 \times 2 \div 10^9 \approx 1.07 \text{ GB}$&lt;/p>
&lt;p>Tenga en cuenta que cuanto mayor sea la longitud del contexto $s$, la VRAM requerida aumentará de forma lineal.&lt;/p>
&lt;hr>
&lt;h1 id="5-práctica-1-configuración-más-rápida-y-corta-usando-ollama">5. Práctica 1: Configuración más rápida y corta usando Ollama
&lt;/h1>&lt;p>Una vez entendida la teoría, ejecutemos un LLM en el entorno Windows.
En 2026, la herramienta más amigable para el usuario es &amp;ldquo;Ollama&amp;rdquo;. Proporciona una CLI intuitiva tipo Docker.&lt;/p>
&lt;h2 id="51-instalación-y-ejecución">5.1 Instalación y ejecución
&lt;/h2>&lt;ol>
&lt;li>Descarga el instalador para Windows desde el &lt;a class="link" href="https://ollama.com/" target="_blank" rel="noopener"
>sitio web oficial de Ollama&lt;/a> y ejecútalo.&lt;/li>
&lt;li>Abre PowerShell e introduce el siguiente comando. Aquí usaremos &lt;code>llama3:8b&lt;/code>, que soporta japonés.&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">ollama&lt;/span> &lt;span class="n">run&lt;/span> &lt;span class="n">llama3&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="n">8b&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>En la primera ejecución, se descargará el modelo. Una vez finalizado, podrás interactuar directamente en la terminal.&lt;/p>
&lt;h2 id="52-creación-de-una-ia-personalizada-mediante-modelfile">5.2 Creación de una IA personalizada mediante Modelfile
&lt;/h2>&lt;p>Puedes crear fácilmente una IA con una personalidad específica. Crea un &lt;code>Modelfile&lt;/code> en cualquier ubicación.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">FROM llama3:8b
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">SYSTEM &amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Eres un ingeniero de software senior muy talentoso.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Al responder a las preguntas de los usuarios, debes incluir siempre ejemplos de código y responder de forma lógica y concisa.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">PARAMETER temperature 0.3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">PARAMETER num_ctx 8192
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Ejecuta el siguiente comando para compilar y ejecutar tu propio modelo.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">ollama&lt;/span> &lt;span class="n">create&lt;/span> &lt;span class="n">SeniorDev&lt;/span> &lt;span class="o">-f&lt;/span> &lt;span class="p">./&lt;/span>&lt;span class="n">Modelfile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ollama&lt;/span> &lt;span class="n">run&lt;/span> &lt;span class="n">SeniorDev&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="53-uso-desde-aplicaciones-externas-editores-de-ia">5.3 Uso desde aplicaciones externas (Editores de IA)
&lt;/h2>&lt;p>Ollama expone un punto de conexión de la API compatible con OpenAI en &lt;code>http://localhost:11434&lt;/code>.
Simplemente configurando esta URL en los ajustes de backend de extensiones de VS Code como Cursor o Continue.dev y especificando un modelo como &lt;code>SeniorDev&lt;/code>, obtendrás un potente asistente de programación local y gratuito.&lt;/p>
&lt;hr>
&lt;h1 id="6-práctica-2-ajuste-de-rendimiento-extremo-con-llamacpp">6. Práctica 2: Ajuste de rendimiento extremo con llama.cpp
&lt;/h1>&lt;p>Si quieres probar opciones como una gestión detallada de la memoria o los últimos formatos (EXL2, cuantización IQ, etc.) lo antes posible, interactúa directamente con el motor central, &lt;code>llama.cpp&lt;/code>.&lt;/p>
&lt;h2 id="61-pasos-para-compilar-llamacpp">6.1 Pasos para compilar llama.cpp
&lt;/h2>&lt;p>En un entorno Windows, lo mejor es compilar desde el código fuente utilizando CUDA Toolkit y CMake.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">git&lt;/span> &lt;span class="n">clone&lt;/span> &lt;span class="n">https&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="p">//&lt;/span>&lt;span class="n">github&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">com&lt;/span>&lt;span class="p">/&lt;/span>&lt;span class="n">ggerganov&lt;/span>&lt;span class="p">/&lt;/span>&lt;span class="n">llama&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nb">cpp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd &lt;/span>&lt;span class="n">llama&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nb">cpp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">&lt;/span>&lt;span class="n">mkdir&lt;/span> &lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd &lt;/span>&lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Configurar para CUDA y compilar&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmake&lt;/span> &lt;span class="p">..&lt;/span> &lt;span class="n">-DLLAMA_CUBLAS&lt;/span>&lt;span class="p">=&lt;/span>&lt;span class="n">ON&lt;/span> &lt;span class="n">-DBUILD_SHARED_LIBS&lt;/span>&lt;span class="p">=&lt;/span>&lt;span class="n">OFF&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmake&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-build&lt;/span> &lt;span class="p">.&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-config&lt;/span> &lt;span class="n">Release&lt;/span> &lt;span class="n">-j&lt;/span> &lt;span class="mf">16&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="62-inicio-avanzado-en-modo-servidor">6.2 Inicio avanzado en modo servidor
&lt;/h2>&lt;p>Utiliza el ejecutable &lt;code>llama-server.exe&lt;/code> compilado para alojar el modelo.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="p">.\&lt;/span>&lt;span class="n">bin&lt;/span>&lt;span class="p">\&lt;/span>&lt;span class="n">Release&lt;/span>&lt;span class="p">\&lt;/span>&lt;span class="nb">llama-server&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="py">exe&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-model&lt;/span> &lt;span class="s2">&amp;#34;C:\models\Llama-3-8B-Instruct.Q4_K_M.gguf&amp;#34;&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-ctx-size&lt;/span> &lt;span class="mf">8192&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-n-gpu-layers&lt;/span> &lt;span class="mf">99&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-threads&lt;/span> &lt;span class="mf">8&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-flash-attn&lt;/span> &lt;span class="p">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">-&lt;/span>&lt;span class="n">-port&lt;/span> &lt;span class="mf">8080&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ul>
&lt;li>&lt;code>--n-gpu-layers 99&lt;/code>: Descarga (offload) todas las capas posibles a la VRAM de la GPU.&lt;/li>
&lt;li>&lt;code>--flash-attn&lt;/code>: Habilita FlashAttention 3, logrando una mejora en la velocidad de inferencia y una reducción en el consumo de VRAM por parte de la caché KV.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="7-interfaz-gráfica-gui-construcción-de-lm-studio-y-rag-local">7. Interfaz gráfica (GUI): Construcción de LM Studio y RAG local
&lt;/h1>&lt;p>Si no te sientes cómodo con la línea de comandos o si deseas realizar RAG (Generación Aumentada por Recuperación) de manera intuitiva, puedes usar una GUI.&lt;/p>
&lt;h2 id="71-lm-studio">7.1 LM Studio
&lt;/h2>&lt;p>LM Studio es una excelente aplicación que integra la búsqueda y descarga de modelos, verificación previa de los requisitos del sistema y una interfaz de chat. Simplemente con presionar el botón &amp;ldquo;Local Server&amp;rdquo; en la aplicación, se levantará una API compatible con OpenAI.&lt;/p>
&lt;h2 id="72-arquitectura-rag-usando-anythingllm">7.2 Arquitectura RAG usando AnythingLLM
&lt;/h2>&lt;p>Este es un diagrama de la arquitectura de un entorno RAG para cargar documentos internos o notas personales.&lt;/p>
&lt;div class="mermaid">graph LR
Document["Documentos (PDF, MD)"] --> Chunking["División en fragmentos (Chunking)"]
Chunking --> EmbedModel["Modelo de incrustación (Embedding)"]
EmbedModel --> VectorDB["Base de datos vectorial"]
UserQuery["Consulta del usuario"] --> EmbedQuery["Incrustación de la consulta"]
EmbedQuery --> VectorDB
VectorDB --> |"Búsqueda de similitud"| RetrievedDocs["Extracción de documentos relevantes"]
UserQuery --> PromptBuilder["Generador de prompts"]
RetrievedDocs --> PromptBuilder
PromptBuilder --> LocalLLM["LLM local"]
LocalLLM --> Answer["Respuesta final"]&lt;/div>
&lt;p>Si usas la versión de escritorio de AnythingLLM (Windows), simplemente configurando Ollama (LLM y Embedding) desde la pantalla de configuración y utilizando la VectorDB local (LanceDB), esta arquitectura se completará en minutos. Así nace una IA privada que no envía ningún dato al exterior.&lt;/p>
&lt;hr>
&lt;h1 id="8-fine-tuning-en-windows-wsl2-lora">8. Fine-tuning en Windows WSL2 (LoRA)
&lt;/h1>&lt;p>Si además de ejecutarlo localmente quieres que el modelo sea más inteligente con tus propios datos, es posible realizar fine-tuning mediante LoRA (Low-Rank Adaptation). En 2026, utilizando la biblioteca &amp;ldquo;Unsloth&amp;rdquo;, se puede completar el entrenamiento de un modelo 8B en unas pocas horas en un entorno WSL2 de Windows, incluso con 16 GB de VRAM.&lt;/p>
&lt;p>Ejecuta lo siguiente dentro de Ubuntu en WSL2 para preparar el entorno:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">conda create --name unsloth_env &lt;span class="nv">python&lt;/span>&lt;span class="o">=&lt;/span>3.11
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">conda activate unsloth_env
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install &lt;span class="s2">&amp;#34;unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install --no-deps trl peft accelerate bitsandbytes
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Unsloth optimiza los núcleos CUDA al extremo, y en comparación con la biblioteca estándar de Hugging Face, la velocidad de entrenamiento es aproximadamente el doble, mientras que el consumo de VRAM se reduce a la mitad. Al abrir un Jupyter Notebook y cargar un conjunto de datos (formato JSONL), el entrenamiento de varias épocas es posible incluso en una RTX 4060 Ti con 12 GB-16 GB de VRAM.&lt;/p>
&lt;hr>
&lt;h1 id="9-resolución-de-problemas-de-rendimiento">9. Resolución de problemas de rendimiento
&lt;/h1>&lt;p>Estos son los problemas más frecuentes a los que te puedes enfrentar y sus soluciones.&lt;/p>
&lt;h3 id="1-la-velocidad-de-inferencia-es-extremadamente-lenta-1-2-tokenss">1. La velocidad de inferencia es extremadamente lenta (1-2 tokens/s)
&lt;/h3>&lt;p>&lt;strong>Causa&lt;/strong>: El modelo no cabe en la VRAM y se está descargando (offloading) a la memoria del sistema (RAM).
&lt;strong>Solución&lt;/strong>: Verifica la &amp;ldquo;Memoria dedicada de la GPU&amp;rdquo; en el Administrador de tareas. Si se ha alcanzado el límite, reduce el tamaño del contexto (&lt;code>-c&lt;/code>) o utiliza un modelo con cuantización de menos bits (como Q4_K_M).&lt;/p>
&lt;h3 id="2-error-de-cuda-out-of-memory">2. Error de &amp;ldquo;CUDA out of memory&amp;rdquo;
&lt;/h3>&lt;p>&lt;strong>Causa&lt;/strong>: La VRAM se ha agotado por completo. Esto ocurre especialmente cuando la interacción se prolonga y la caché KV aumenta de tamaño.
&lt;strong>Solución&lt;/strong>: Limita intencionadamente el valor de &lt;code>num_ctx&lt;/code> en el caso de Ollama, o de &lt;code>-c&lt;/code> en el caso de llama.cpp a un número menor.&lt;/p>
&lt;h3 id="3-la-generación-en-japonés-u-otro-idioma-es-extraña">3. La generación en japonés (u otro idioma) es extraña
&lt;/h3>&lt;p>&lt;strong>Causa&lt;/strong>: Incompatibilidad en la plantilla del prompt, o el modelo no es compatible.
&lt;strong>Solución&lt;/strong>: Asegúrate de usar un modelo que contenga &lt;code>Instruct&lt;/code> en su nombre y verifica que la herramienta tenga seleccionada la plantilla correcta especificada por el creador del modelo, como el formato ChatML o Llama3.&lt;/p>
&lt;hr>
&lt;h1 id="10-conclusión-y-perspectivas-de-futuro">10. Conclusión y perspectivas de futuro
&lt;/h1>&lt;p>En 2026, la construcción de un LLM local en un entorno Windows ya no es un privilegio reservado para unos pocos ingenieros. Gracias a la estandarización del formato GGUF, la aparición de ecosistemas sofisticados como Ollama o LM Studio y las optimizaciones de hardware como FlashAttention, cualquier persona puede obtener fácilmente un entorno de IA de nivel empresarial.&lt;/p>
&lt;p>Aprovecha los siguientes puntos explicados en este artículo:&lt;/p>
&lt;ol>
&lt;li>Elegir lógicamente el tamaño del modelo y el nivel de cuantización óptimos para las especificaciones de tu PC utilizando el &lt;strong>cálculo matemático de la VRAM&lt;/strong>.&lt;/li>
&lt;li>Construir el entorno lo más rápido posible con &lt;strong>Ollama&lt;/strong> y mejorar drásticamente la productividad vinculándolo con un editor de IA.&lt;/li>
&lt;li>Extraer el rendimiento límite del hardware mediante el control avanzado de parámetros en &lt;strong>llama.cpp&lt;/strong>.&lt;/li>
&lt;li>Construir un sistema RAG local y seguro para manejar datos confidenciales con &lt;strong>AnythingLLM&lt;/strong>.&lt;/li>
&lt;li>Fomentar tu propia IA personalizada con conocimientos especializados aprovechando &lt;strong>Unsloth (WSL2)&lt;/strong>.&lt;/li>
&lt;/ol>
&lt;p>La &amp;ldquo;democratización&amp;rdquo; de la IA ya no es una palabra de moda; es un sistema real que funciona en tu escritorio de Windows. Libérate de los costes de uso de API en la nube y los riesgos de filtración de información, e ingresa hoy mismo al poderoso y libre mundo de la IA privada.&lt;/p></description></item></channel></rss>