<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>AI on kenji.blog</title><link>http://kenji.blog/es/categories/ai/</link><description>Recent content in AI on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>es</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 18:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/es/categories/ai/index.xml" rel="self" type="application/rss+xml"/><item><title>Casos de uso y código de ejemplo de la API más reciente de Microsoft.Windows.AI</title><link>http://kenji.blog/es/p/microsoft-windows-ai-api-guide/</link><pubDate>Fri, 11 Sep 2026 18:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/microsoft-windows-ai-api-guide/</guid><description>&lt;img src="http://kenji.blog/p/microsoft-windows-ai-api-guide/img/eyecatch.jpg" alt="Featured image of post Casos de uso y código de ejemplo de la API más reciente de Microsoft.Windows.AI" />&lt;h1 id="casos-de-uso-y-código-de-ejemplo-de-la-api-más-reciente-de-microsoftwindowsai-explorando-las-profundidades-de-windows-copilot-runtime">Casos de uso y código de ejemplo de la API más reciente de Microsoft.Windows.AI: Explorando las profundidades de Windows Copilot Runtime
&lt;/h1>&lt;h2 id="1-introducción-la-nueva-era-de-windows-con-ia-integrada-de-forma-nativa">1. Introducción: La nueva era de Windows con IA integrada de forma nativa
&lt;/h2>&lt;p>En los últimos años, la evolución de la tecnología de IA ha sido notable, y se ha producido un rápido cambio de paradigma desde la utilización de grandes modelos de lenguaje (LLM) en la nube hasta la inferencia de IA en dispositivos de borde (PC locales). El núcleo de esto es &amp;ldquo;Windows Copilot Runtime&amp;rdquo; proporcionado por Microsoft para Windows 11 y la API &amp;ldquo;Microsoft.Windows.AI&amp;rdquo; para operarlo.&lt;/p>
&lt;p>El desarrollo de aplicaciones utilizando APIs en la nube (como OpenAI y Azure OpenAI) es fácil, pero conlleva desafíos como la latencia, la privacidad y los costos continuos. Por otro lado, al ejecutar modelos de IA localmente, puede lograr aplicaciones de latencia ultrabaja que funcionan incluso sin conexión, sin que los datos confidenciales salgan del dispositivo.&lt;/p>
&lt;p>En este artículo, explicaremos exhaustiva y detalladamente cómo implementar funciones de IA local, que serán esenciales en el desarrollo futuro de aplicaciones de Windows, utilizando ejemplos de código práctico en C# y C++, desde la arquitectura hasta el ajuste de rendimiento. No se trata solo de llamar a la API, sino de profundizar en los detalles técnicos avanzados, como la utilización del hardware subyacente (NPU y GPU) y la integración con DirectML.&lt;/p>
&lt;h2 id="2-descripción-general-de-la-arquitectura-y-windows-copilot-runtime">2. Descripción general de la arquitectura y Windows Copilot Runtime
&lt;/h2>&lt;p>Windows Copilot Runtime es una serie de pilas de IA diseñadas para permitir a los desarrolladores integrar fácilmente modelos de IA en Windows y obtener el mejor rendimiento. Este tiempo de ejecución abstrae la aceleración de hardware a nivel del sistema operativo y proporciona una interfaz unificada a los desarrolladores.&lt;/p>
&lt;div class="mermaid">graph TD
App["Aplicación de Windows (C# / C++)"] --> API["APIs de Microsoft.Windows.AI"]
App --> ORT["ONNX Runtime"]
API --> WCR["Windows Copilot Runtime (Capa del SO)"]
WCR --> SLM["Modelos Locales (Phi-Silica, etc.)"]
ORT --> DML["Proveedor de Ejecución DirectML"]
SLM --> DML
DML --> DXCore["DXCore / DirectX 12"]
DXCore --> NPU["NPU (Unidad de Procesamiento Neuronal)"]
DXCore --> GPU["GPU (Unidad de Procesamiento Gráfico)"]
DXCore --> CPU["CPU"]&lt;/div>
&lt;p>Como muestra el diagrama de arquitectura anterior, las aplicaciones pueden acceder directamente a los modelos de lenguaje pequeños (SLM: Phi-Silica, etc.) integrados en el sistema operativo mediante el uso de la API de alto nivel &lt;code>Microsoft.Windows.AI&lt;/code>. Además, cuando se utilizan modelos personalizados, es posible utilizar explícitamente la aceleración de hardware a través de ONNX Runtime y DirectML. Debido a que la capa del sistema operativo optimiza la distribución de las cargas de trabajo a la CPU, GPU y NPU, los desarrolladores pueden crear aplicaciones de IA de alto rendimiento sin tener que preocuparse profundamente por las diferencias de hardware.&lt;/p>
&lt;h2 id="3-aceleración-de-hardware-y-evaluación-matemática-de-la-npu">3. Aceleración de hardware y evaluación matemática de la NPU
&lt;/h2>&lt;p>Los PC Copilot+ más recientes están equipados con una NPU (Unidad de Procesamiento Neuronal), un procesador especializado en el procesamiento de IA. El rendimiento de una NPU generalmente se evalúa en TOPS (Tera Operations Per Second).&lt;/p>
&lt;p>En la inferencia de modelos de IA, especialmente la capacidad computacional de la multiplicación de matrices (GEMM: General Matrix Multiply) determina el rendimiento. El rendimiento máximo teórico del hardware $P_{\text{peak}}$ se aproxima mediante la siguiente fórmula.&lt;/p>
$$
P_{\text{peak}} = f \times N_{\text{cores}} \times N_{\text{MACs/core}} \times 2
$$
&lt;p>Donde,&lt;/p>
&lt;ul>
&lt;li>$f$ es la frecuencia de reloj de la NPU (Hz)&lt;/li>
&lt;li>$N_{\text{cores}}$ es el número de núcleos en la NPU&lt;/li>
&lt;li>$N_{\text{MACs/core}}$ es el número de unidades MAC (Multiplicación-Acumulación) por núcleo&lt;/li>
&lt;li>El último $2$ se debe a que una sola operación MAC cuenta como dos operaciones (FLOPs/OPs) de multiplicación y suma.&lt;/li>
&lt;/ul>
&lt;p>Por ejemplo, para una NPU con una frecuencia de 1.5 GHz, 4 núcleos y cada núcleo con 4096 MACs,
&lt;/p>
$$
P_{\text{peak}} = 1.5 \times 10^9 \times 4 \times 4096 \times 2 \approx 49.15 \text{ TOPS}
$$
&lt;p>
Queda demostrado matemáticamente que este rendimiento supera los 40 TOPS, que es el requisito para un PC Copilot+ con Windows 11.&lt;/p>
&lt;p>Además, la inferencia de los modelos de IA, especialmente de los LLM (fase de decodificación), tiende a estar &lt;strong>limitada por la memoria (Memory-Bound)&lt;/strong>. El ancho de banda teórico de la memoria del sistema $BW$ se calcula de la siguiente manera.&lt;/p>
$$
BW = f_{\text{mem}} \times W_{\text{bus}} \times \frac{2}{8}
$$
&lt;p>En el caso de una memoria LPDDR5x-8533 ($f_{\text{mem}} = 8533 \text{ MT/s}$) y un bus de 128 bits ($W_{\text{bus}} = 128$), el ancho de banda es de aproximadamente $136 \text{ GB/s}$. En la optimización de aplicaciones de IA, es importante cómo ahorrar este ancho de banda, por lo que la cuantización (Quantization) del modelo, que se describe más adelante, es indispensable.&lt;/p>
&lt;h2 id="4-configuración-del-entorno-de-desarrollo">4. Configuración del entorno de desarrollo
&lt;/h2>&lt;p>Para utilizar la API de IA de Windows más reciente, debe preparar el siguiente entorno y cadena de herramientas.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>SO&lt;/strong>: Windows 11 versión 24H2 o posterior (se recomienda encarecidamente un dispositivo equipado con NPU que cumpla con los requisitos del PC Copilot+)&lt;/li>
&lt;li>&lt;strong>SDK&lt;/strong>: Windows App SDK (versión v1.5 o posterior compatible con la extensión de IA)&lt;/li>
&lt;li>&lt;strong>Entorno de desarrollo&lt;/strong>: Visual Studio 2022 (v17.10 o posterior), con cargas de trabajo de desarrollo de escritorio con C++ y desarrollo de escritorio de .NET&lt;/li>
&lt;li>&lt;strong>Paquetes&lt;/strong>: Instale &lt;code>Microsoft.Windows.AI&lt;/code> y &lt;code>Microsoft.ML.OnnxRuntime.DirectML&lt;/code> a través de NuGet&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;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-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Ejemplo de configuración de .csproj --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;ItemGroup&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PackageReference&lt;/span> &lt;span class="na">Include=&lt;/span>&lt;span class="s">&amp;#34;Microsoft.Windows.AI&amp;#34;&lt;/span> &lt;span class="na">Version=&lt;/span>&lt;span class="s">&amp;#34;1.0.0-preview1&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PackageReference&lt;/span> &lt;span class="na">Include=&lt;/span>&lt;span class="s">&amp;#34;Microsoft.WindowsAppSDK&amp;#34;&lt;/span> &lt;span class="na">Version=&lt;/span>&lt;span class="s">&amp;#34;1.5.240311000&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PackageReference&lt;/span> &lt;span class="na">Include=&lt;/span>&lt;span class="s">&amp;#34;Microsoft.ML.OnnxRuntime.DirectML&amp;#34;&lt;/span> &lt;span class="na">Version=&lt;/span>&lt;span class="s">&amp;#34;1.17.1&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/ItemGroup&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="5-deep-dive-1utilización-de-modelos-de-lenguaje-locales-phi-silica-con-c">5. 【Deep Dive 1】Utilización de modelos de lenguaje locales (Phi-Silica) con C#
&lt;/h2>&lt;p>Windows Copilot Runtime incluye &amp;ldquo;Phi-Silica&amp;rdquo;, un modelo de lenguaje pequeño y altamente eficiente desarrollado por Microsoft, como un componente estándar del sistema operativo. Esto permite el procesamiento avanzado del lenguaje natural (resumen de texto, generación de código, chatbots) en un entorno sin conexión, sin tener que descargar modelos del tamaño de gigabytes de la red.&lt;/p>
&lt;p>A continuación, se muestra un código de ejemplo avanzado en C# para construir una IA de chat utilizando el espacio de nombres &lt;code>Microsoft.Windows.AI.Generative&lt;/code>. Admite respuestas en streaming y genera texto en tiempo real sin bloquear el hilo de la interfaz de usuario (UI).&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-csharp" data-lang="csharp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">System&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">System.Text&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">System.Threading&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">System.Threading.Tasks&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">Microsoft.Windows.AI.Generative&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">namespace&lt;/span> &lt;span class="nn">WindowsAI.Sample&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="kd">public&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">LocalLanguageModelService&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="kd">private&lt;/span> &lt;span class="n">LanguageModel&lt;/span> &lt;span class="n">_languageModel&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="n">_isInitialized&lt;/span> &lt;span class="p">=&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// &amp;lt;summary&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// Inicializa el modelo de lenguaje. Verifica la disponibilidad de la NPU y carga el modelo en el dispositivo óptimo.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// &amp;lt;/summary&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kd">async&lt;/span> &lt;span class="n">Task&lt;/span> &lt;span class="n">InitializeAsync&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="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">_isInitialized&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&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">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Comprobando los requisitos del sistema y la disponibilidad del modelo de IA local (Phi-Silica)...&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">// Comprueba si el modelo está disponible en el sistema (si no es compatible, puede solicitar la descarga)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">var&lt;/span> &lt;span class="n">availability&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">LanguageModel&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">CheckAvailabilityAsync&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">availability&lt;/span> &lt;span class="p">!=&lt;/span> &lt;span class="n">LanguageModelAvailability&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Available&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="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">InvalidOperationException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;El modelo de IA local no está disponible actualmente. Estado: {availability}&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">// Crea una instancia del modelo (en este momento se realiza la asignación al espacio de memoria y la inicialización de la NPU)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_languageModel&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">LanguageModel&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">CreateAsync&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_isInitialized&lt;/span> &lt;span class="p">=&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Inicialización del modelo de lenguaje completada. La aceleración de hardware a través de DirectML está activa.&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="cs">/// &amp;lt;summary&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// Recibe el prompt del usuario y genera una respuesta en streaming.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// &amp;lt;/summary&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kd">async&lt;/span> &lt;span class="n">Task&lt;/span> &lt;span class="n">GenerateResponseStreamAsync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">string&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">CancellationToken&lt;/span> &lt;span class="n">cancellationToken&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="k">if&lt;/span> &lt;span class="p">(!&lt;/span>&lt;span class="n">_isInitialized&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">InitializeAsync&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">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;\n[Entrada del usuario]: {prompt}\n[Asistente de IA]: &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">// Establece los hiperparámetros para la generación&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">var&lt;/span> &lt;span class="n">options&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">LanguageModelOptions&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">Temperature&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">0.7f&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TopP&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">0.9f&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MaxTokens&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">2048&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">RepetitionPenalty&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">1.1f&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">// Construye el contexto de la conversación&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">var&lt;/span> &lt;span class="n">context&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">LanguageModelContext&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AddSystemMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Eres un asistente de IA avanzado que se ejecuta directamente en la NPU local de Windows. Piensa lógica y paso a paso, y responde de forma concisa.&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">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AddUserMessage&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="k">try&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">// Llamada a la API de inferencia en streaming&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">var&lt;/span> &lt;span class="n">responseStream&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">_languageModel&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GenerateResponseStreamAsync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">options&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">// Itera de forma asíncrona los fragmentos devueltos como IAsyncEnumerable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="k">foreach&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">var&lt;/span> &lt;span class="n">chunk&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="n">responseStream&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WithCancellation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cancellationToken&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="c1">// Imprime el token generado (fragmento) en la consola en tiempo real&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// En el caso de una aplicación de UI, refleje esto en un TextBox o similar usando DispatcherQueue aquí&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">chunk&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Text&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">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&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="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">OperationCanceledException&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">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;\n[La generación fue cancelada por el usuario o el sistema]&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="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">Exception&lt;/span> &lt;span class="n">ex&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">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;\n[Se produjo un error fatal: {ex.Message}]&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="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="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="51-explicación-de-la-arquitectura-en-la-implementación-de-c">5.1 Explicación de la arquitectura en la implementación de C#
&lt;/h3>&lt;p>El núcleo de este código es la validación previa a la ejecución mediante &lt;code>LanguageModel.CheckAvailabilityAsync()&lt;/code> y el streaming asíncrono mediante &lt;code>GenerateResponseStreamAsync&lt;/code>. Cuando Copilot Runtime, que se ejecuta en segundo plano en el sistema operativo, recibe esta llamada a la API, inicia internamente ONNX Runtime y selecciona el Proveedor de Ejecución óptimo (DirectML + NPU en muchas de las PC más nuevas) según la configuración del sistema.&lt;/p>
&lt;p>Los desarrolladores pueden integrar tuberías de inferencia de IA de última generación en sus aplicaciones con unas pocas líneas de código C#, sin tener que preocuparse en absoluto por la forma del tensor del modelo, la implementación del tokenizador o la gestión de la memoria de la caché KV.&lt;/p>
&lt;h2 id="6-deep-dive-2inferencia-rápida-de-modelos-personalizados-con-c-y-directml">6. 【Deep Dive 2】Inferencia rápida de modelos personalizados con C++ y DirectML
&lt;/h2>&lt;p>Para manejar dominios específicos que no pueden ser cubiertos solo por el modelo de lenguaje estándar del sistema operativo (por ejemplo, segmentación de imágenes propia, reconocimiento de voz, modelos personalizados de detección de objetos, etc.), los desarrolladores deben operar directamente ONNX Runtime y DirectML, que se encuentran en la capa inferior de &lt;code>Microsoft.Windows.AI&lt;/code>.&lt;/p>
&lt;p>Al usar C++, es posible optimizar la asignación de memoria al límite y extraer el rendimiento máximo de la NPU/GPU. A continuación se muestra la implementación principal de una tubería avanzada de inicialización e inferencia para ejecutar un modelo personalizado en formato ONNX (por ejemplo, YOLOv8) utilizando DirectML en C++.&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;span class="lnt">94
&lt;/span>&lt;span class="lnt">95
&lt;/span>&lt;span class="lnt">96
&lt;/span>&lt;span class="lnt">97
&lt;/span>&lt;span class="lnt">98
&lt;/span>&lt;span class="lnt">99
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;vector&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;string&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdexcept&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;onnxruntime_cxx_api.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;dml_provider_factory.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">CustomVisionAIProcessor&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Env&lt;/span> &lt;span class="n">env&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Session&lt;/span> &lt;span class="n">session&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="k">nullptr&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">AllocatorWithDefaultOptions&lt;/span> &lt;span class="n">allocator&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">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CustomVisionAIProcessor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">wstring&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">modelPath&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">:&lt;/span> &lt;span class="n">env&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ORT_LOGGING_LEVEL_WARNING&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;VisionAIProcessor&amp;#34;&lt;/span>&lt;span class="p">)&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">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">SessionOptions&lt;/span> &lt;span class="n">sessionOptions&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Optimización del número de hilos
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">sessionOptions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SetIntraOpNumThreads&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Establece el nivel de optimización del gráfico al máximo
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">sessionOptions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SetGraphOptimizationLevel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">GraphOptimizationLevel&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ORT_ENABLE_ALL&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">// 1. Añadir el Proveedor de Ejecución DirectML (DML EP)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// device_id = 0 es el adaptador recomendado predeterminado del sistema (NPU o GPU de alto rendimiento)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="n">OrtApi&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">ortApi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">GetApi&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">OrtDmlApi&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">dmlApi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">OrtStatus&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">status&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ortApi&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetExecutionProviderApi&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;DML&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ORT_API_VERSION&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">reinterpret_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="o">**&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">dmlApi&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="p">(&lt;/span>&lt;span class="n">status&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="k">nullptr&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">dmlApi&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ThrowOnError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dmlApi&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">SessionOptionsAppendExecutionProvider_DML&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sessionOptions&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Info] Proveedor de Ejecución DirectML conectado exitosamente.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&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="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Warning] Error al obtener la API de DirectML. Ejecutando en modo fallback de CPU.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">status&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">ortApi&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ReleaseStatus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status&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">// 2. Cargar el modelo y crear la sesión
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">session&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Session&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">env&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">modelPath&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">c_str&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">sessionOptions&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Info] Modelo ONNX cargado exitosamente y gráfico de cálculo compilado.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&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="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Exception&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Error] Fallo en la carga del modelo: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">what&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&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="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="kt">void&lt;/span> &lt;span class="nf">RunInference&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">&amp;gt;&amp;amp;&lt;/span> &lt;span class="n">imageTensor&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">int64_t&lt;/span>&lt;span class="o">&amp;gt;&amp;amp;&lt;/span> &lt;span class="n">inputShape&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 3. Crear el búfer del tensor de entrada
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">MemoryInfo&lt;/span> &lt;span class="n">memoryInfo&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">MemoryInfo&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">CreateCpu&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">OrtArenaAllocator&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">OrtMemTypeDefault&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Value&lt;/span> &lt;span class="n">inputTensor&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Value&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">CreateTensor&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memoryInfo&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">*&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">imageTensor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">()),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">imageTensor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">inputShape&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">inputShape&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&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">// 4. Obtención dinámica de los nombres de los nodos de entrada y salida
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">auto&lt;/span> &lt;span class="n">inputNamePtr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">session&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetInputNameAllocated&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">allocator&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">outputNamePtr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">session&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetOutputNameAllocated&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">allocator&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">inputNames&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">inputNamePtr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">outputNames&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">outputNamePtr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">()&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. Ejecución de la inferencia (se descarga a la NPU/GPU a través de DirectML)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Info] Iniciando la ejecución del motor de inferencia...&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">startTime&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">chrono&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">high_resolution_clock&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">now&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">auto&lt;/span> &lt;span class="n">outputTensors&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">session&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">RunOptions&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="k">nullptr&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="n">inputNames&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">inputTensor&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">outputNames&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&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">auto&lt;/span> &lt;span class="n">endTime&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">chrono&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">high_resolution_clock&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">duration&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">chrono&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">duration_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">chrono&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">milliseconds&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">endTime&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">startTime&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. Obtener y analizar el tensor de resultados
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">outputData&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">outputTensors&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">front&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">GetTensorMutableData&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">size_t&lt;/span> &lt;span class="n">outputSize&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">outputTensors&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">front&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">GetTensorTypeAndShapeInfo&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">GetElementCount&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">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Result] Inferencia completada: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">duration&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">count&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; ms&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Result] Número de elementos en el tensor de salida: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">outputSize&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// *Después de esto, implemente el procesamiento para dibujar bounding boxes o NMS (Non-Maximum Suppression) para el tensor de salida
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&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="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Ruta del modelo ONNX a ejecutar
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">CustomVisionAIProcessor&lt;/span> &lt;span class="n">processor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">L&lt;/span>&lt;span class="s">&amp;#34;models/yolov8n_quantized.onnx&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">// Datos de imagen de prueba para la inferencia (tamaño de lote 1 x 3 canales x 640 x 640)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">int64_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">inputShape&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">640&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">640&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">dummyImage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">640&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">640&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.5f&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">processor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">RunInference&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dummyImage&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">inputShape&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="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">exception&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;El programa terminó de forma anormal: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">what&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&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="k">return&lt;/span> &lt;span class="mi">0&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;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="61-la-importancia-de-la-gestión-de-la-memoria-y-la-inferencia-sin-copia-zero-copy-en-c">6.1 La importancia de la gestión de la memoria y la inferencia sin copia (Zero-Copy) en C++
&lt;/h3>&lt;p>La mayor ventaja de usar DirectML con C++ es que permite una estrecha integración con DirectX 12 (DX12). El código anterior incluye la copia de datos desde la memoria de la CPU estándar desde un punto de vista educativo, pero en los motores de juegos reales y las aplicaciones de procesamiento de video, hay muchos casos donde las imágenes (texturas) ya se mantienen en el espacio de memoria de la GPU o la NPU usando DX12.&lt;/p>
&lt;p>En este caso, al utilizar la función de vinculación avanzada de &lt;code>OrtDmlApi&lt;/code>, puede realizar &amp;ldquo;&lt;strong>Inferencia Sin Copia (Zero-Copy Inference)&lt;/strong>&amp;rdquo;, que mapea directamente los recursos de DX12 como tensores de ONNX Runtime. Como resultado, la sobrecarga de transferencia de datos entre el bus PCIe (el consumo del ancho de banda $BW$ descrito anteriormente) desaparece por completo, y la velocidad de fotogramas en el procesamiento de video en tiempo real mejora drásticamente.&lt;/p>
&lt;h2 id="7-optimización-del-rendimiento-y-mejores-prácticas">7. Optimización del rendimiento y mejores prácticas
&lt;/h2>&lt;p>A continuación se resumen las estrategias de optimización esenciales al desarrollar aplicaciones de IA de primer nivel utilizando la API de IA de Windows y DirectML.&lt;/p>
&lt;h3 id="71-cuantización-de-modelos-quantization-y-olive-toolkit">7.1 Cuantización de modelos (Quantization) y Olive Toolkit
&lt;/h3>&lt;p>Para liberar el verdadero poder de la NPU, es un requisito absoluto &lt;strong>cuantizar (Quantization)&lt;/strong> los pesos y las activaciones del modelo de IA de FP32 (punto flotante de precisión simple) a INT8 o INT4. La arquitectura de la NPU está especializada en operaciones de enteros y, en comparación con FP32, INT8 logra teóricamente 4 veces el rendimiento y un ahorro de energía significativo.&lt;/p>
&lt;p>Al utilizar la cadena de herramientas &lt;code>Olive (ONNX Live)&lt;/code> proporcionada por Microsoft, los modelos como PyTorch se pueden optimizar automáticamente para entornos Windows. Olive brinda un fuerte soporte para la optimización de atención especial para modelos Transformer y la compilación de gráficos por hardware.&lt;/p>
&lt;h3 id="72-la-compensación-entre-el-procesamiento-por-lotes-y-el-streaming-interactivo">7.2 La compensación entre el procesamiento por lotes y el streaming interactivo
&lt;/h3>&lt;p>En las llamadas a la API, al agrupar múltiples solicitudes de inferencia (procesamiento por lotes), se puede aumentar la eficiencia de utilización (Compute Utilization) de la NPU. Sin embargo, en el caso de las interfaces de usuario interactivas, como los chatbots, el tiempo hasta que se muestra el primer token (TTFT: Time To First Token) determina la experiencia del usuario (UX) más que el rendimiento (throughput).
Por lo tanto, en la interfaz de usuario interactiva, la mejor práctica es establecer el tamaño del lote (batch size) en 1 y priorizar el diseño de generación en streaming.&lt;/p>
&lt;h3 id="73-tareas-en-segundo-plano-y-coordinación-con-el-so">7.3 Tareas en segundo plano y coordinación con el SO
&lt;/h3>&lt;p>La inferencia de IA consume una gran cantidad de energía local y recursos del sistema. Se requiere coordinar con la &lt;code>App Lifecycle API&lt;/code> de Windows e implementar un mecanismo para suspender (Suspend) las tareas de inferencia de baja prioridad o reducir el consumo de recursos cuando la aplicación pasa a segundo plano.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant User as "Usuario"
participant App as "App de Windows (C#)"
participant API as "Microsoft.Windows.AI"
participant OS as "Windows Copilot Runtime"
participant ORT as "ONNX Runtime (DML)"
participant NPU as "Hardware NPU"
User->>App: "Ingresa el prompt"
App->>API: "GenerateResponseStreamAsync()"
API->>OS: "Despacho del trabajo de inferencia"
OS->>ORT: "Solicitud de ejecución del gráfico"
ORT->>NPU: "Ejecución de la lista de comandos a través de DirectML"
NPU-->>ORT: "Cálculo completado (1 token generado)"
ORT-->>OS: "Resultado del tensor"
OS-->>API: "Texto decodificado"
API-->>App: "Fragmentos de IAsyncEnumerable&lt;string>"
App-->>User: "Dibujo de texto en tiempo real en la UI"
Note over ORT,NPU: "Repetir rápidamente este bucle hasta completarlo"&lt;/div>
&lt;p>Este diagrama de secuencia ilustra la belleza del procesamiento asíncrono, donde los datos se transmiten de manera fluida desde el hardware de la NPU en la capa más baja hasta la capa de presentación de la aplicación, sin bloquear en absoluto el hilo de la interfaz de usuario.&lt;/p>
&lt;h2 id="8-perspectivas-futuras-y-la-evolución-de-windows-ai">8. Perspectivas futuras y la evolución de Windows AI
&lt;/h2>&lt;p>La API &lt;code>Microsoft.Windows.AI&lt;/code> y Copilot Runtime están experimentando una rápida evolución en curso. Se esperan los siguientes cambios de paradigma en futuras actualizaciones para desarrolladores:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Integración nativa del SO para API multimodales&lt;/strong>: Procesamiento simultáneo y sin problemas no solo de texto, sino también de audio, imágenes e incluso transmisiones de video en vivo, ofreciendo inferencia de IA intermodal de forma estándar a nivel de SO.&lt;/li>
&lt;li>&lt;strong>Soporte a nivel de sistema para RAG (Retrieval-Augmented Generation)&lt;/strong>: Vinculación de documentos personales en la PC local y el índice de Windows Search con el modelo de IA dentro de un entorno seguro del SO (sandbox), permitiendo la construcción de un asistente de IA personal súper avanzado con protección total de la privacidad del usuario.&lt;/li>
&lt;li>&lt;strong>Escalado dinámico de recursos de la NPU&lt;/strong>: Cuando múltiples aplicaciones de IA operan simultáneamente (por ejemplo, cancelación de ruido en segundo plano y generación de código en primer plano), el programador del kernel de Windows cambiará dinámicamente el contexto de ejecución de la NPU para garantizar la QoS (Calidad de Servicio).&lt;/li>
&lt;/ul>
&lt;h2 id="9-conclusión-el-futuro-de-las-aplicaciones-transformadas-por-la-ia-local">9. Conclusión: El futuro de las aplicaciones transformadas por la IA local
&lt;/h2>&lt;p>Copilot Runtime de Windows 11 y la API &lt;code>Microsoft.Windows.AI&lt;/code> han aportado un arma extremadamente poderosa llamada &amp;ldquo;IA local&amp;rdquo; a todos los desarrolladores de Windows. Ya no es necesario depender completamente de las API en la nube. Es posible eliminar la latencia, proteger firmemente la privacidad y ofrecer a los usuarios una experiencia de IA de próxima generación que funcione completamente incluso sin conexión.&lt;/p>
&lt;p>Aprovechando el conocimiento explicado en este artículo sobre la integración de modelos de lenguaje estándar del sistema utilizando C#, la evaluación matemática del rendimiento y la optimización extrema del hardware utilizando C++ y DirectML, cree usted mismo las aplicaciones de Windows &amp;ldquo;nativas de IA&amp;rdquo; de próxima generación. Las infinitas posibilidades que brinda la IA se extienden justo más allá del código que escriba.&lt;/p>
&lt;hr>
&lt;p>&lt;em>※ Nota: Este artículo ha sido escrito sobre la base de las versiones preliminares de la API y las especificaciones más recientes a partir de septiembre de 2026. Debido a las actualizaciones de Windows, las especificaciones de la API y los requisitos de hardware pueden cambiar, por lo que al momento de la implementación asegúrese de consultar también la documentación oficial de Microsoft Learn.&lt;/em>&lt;/p></description></item><item><title>¡No necesitas Python! Construí un motor de inferencia de IA solo con C++</title><link>http://kenji.blog/es/p/building-ai-inference-engine-cpp-only/</link><pubDate>Fri, 11 Sep 2026 17:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/building-ai-inference-engine-cpp-only/</guid><description>&lt;img src="http://kenji.blog/p/building-ai-inference-engine-cpp-only/img/eyecatch.jpg" alt="Featured image of post ¡No necesitas Python! Construí un motor de inferencia de IA solo con C++" />&lt;h2 id="1-introducción-por-qué-dejar-python-y-crear-un-motor-de-inferencia-de-ia-en-c">1. Introducción: ¿Por qué dejar Python y crear un motor de inferencia de IA en C++?
&lt;/h2>&lt;p>En el desarrollo de IA moderno, Python es el estándar de facto. Gracias a la ventaja de frameworks potentes como PyTorch o TensorFlow, puedes construir, entrenar e inferir redes neuronales complejas con unas pocas líneas de código. Sin embargo, detrás de estos frameworks, lenguajes de bajo nivel como C++ y CUDA se encargan del procesamiento computacional pesado. Python simplemente juega el papel de &amp;ldquo;pegamento&amp;rdquo; (glue).&lt;/p>
&lt;p>Entonces, ¿por qué molestarse en eliminar Python y crear un motor de inferencia de IA usando únicamente C++? Hay varias razones de peso para ello.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Rendimiento extremo y baja latencia&lt;/strong>: Puedes eliminar por completo la sobrecarga causada por el GIL (Global Interpreter Lock) de Python y su tipado dinámico. Especialmente en sistemas que requieren tiempo real, un retraso de milisegundos puede ser fatal.&lt;/li>
&lt;li>&lt;strong>Facilidad de implementación (Deployment)&lt;/strong>: Construir un entorno de Python (una enorme colección de bibliotecas, un infierno de dependencias) en el entorno del usuario final es extremadamente difícil. Con C++, solo necesitas distribuir un único binario ejecutable enlazado estáticamente (un &lt;code>.exe&lt;/code> o binario ELF).&lt;/li>
&lt;li>&lt;strong>Compatibilidad con dispositivos Edge&lt;/strong>: En entornos con recursos estrictamente limitados, como teléfonos inteligentes, dispositivos integrados o Raspberry Pi, no hay margen para ejecutar un entorno de ejecución de Python que consume varios gigabytes de memoria.&lt;/li>
&lt;li>&lt;strong>Control directo del hardware&lt;/strong>: C++ permite el control a bajo nivel, como la temporización de la asignación de memoria, el uso explícito de instrucciones SIMD y la optimización de las transferencias de memoria con la GPU.&lt;/li>
&lt;/ol>
&lt;p>En este artículo, basándome en gran medida en la arquitectura de la biblioteca &amp;ldquo;GGML&amp;rdquo; desarrollada por Georgi Gerganov, explicaré el proceso de construir desde cero un motor de inferencia para ejecutar modelos de lenguaje grande (LLM) usando solo C++, sumergiéndonos en las profundidades técnicas.&lt;/p>
&lt;hr>
&lt;h2 id="2-visión-general-de-la-arquitectura-del-motor-de-inferencia">2. Visión general de la arquitectura del motor de inferencia
&lt;/h2>&lt;p>El procesamiento de inferencia de IA es esencialmente una &amp;ldquo;sucesión de enormes cálculos matriciales&amp;rdquo;. Para ejecutar esto de manera eficiente, un motor de inferencia debe estar compuesto por los siguientes componentes.&lt;/p>
&lt;div class="mermaid">graph TD
A["Datos de Entrada (Tokens/Imágenes)"] --> B["Gestión de Tensores"]
B --> C["Grafo de Computación (DAG)"]
C --> D["Memory Arena y Asignador"]
C --> E["Planificador y Pool de Hilos"]
E --> F["Backend de CPU (AVX2/ARM NEON)"]
E --> G["Backend de GPU (CUDA/Metal)"]
F --> H["Resultados de Salida"]
G --> H&lt;/div>
&lt;ol>
&lt;li>&lt;strong>Gestión de Tensores (Tensor)&lt;/strong>: Gestiona las estructuras de datos de matrices multidimensionales y el stride de cada dimensión.&lt;/li>
&lt;li>&lt;strong>Grafo de Computación (Computation Graph)&lt;/strong>: Representa las operaciones de cada capa de la red neuronal como un Grafo Acíclico Dirigido (DAG).&lt;/li>
&lt;li>&lt;strong>Memory Arena (Arena de Memoria)&lt;/strong>: Un mecanismo de gestión de memoria preasignada para evitar la sobrecarga de la asignación de memoria dinámica (&lt;code>malloc&lt;/code> o &lt;code>new&lt;/code>).&lt;/li>
&lt;li>&lt;strong>Backend&lt;/strong>: Implementaciones de cálculos (kernels) optimizadas para hardware específico, como CPU o GPU.&lt;/li>
&lt;/ol>
&lt;p>Ensamblaremos estos componentes utilizando las poderosas características de C++ (plantillas, aritmética de punteros, RAII, etc.).&lt;/p>
&lt;hr>
&lt;h2 id="3-el-secreto-de-la-gestión-de-memoria-memory-arena-y-alineación-simd">3. El secreto de la gestión de memoria: Memory Arena y Alineación SIMD
&lt;/h2>&lt;p>La gestión de memoria en un motor de inferencia es uno de los factores más importantes directamente relacionados con el rendimiento. Durante la inferencia, especialmente al pasar a través de cada capa de un modelo Transformer, se genera una enorme cantidad de tensores intermedios. Si se asignan y liberan cada vez con el &lt;code>malloc&lt;/code> estándar, la fragmentación del montón (heap) y los cambios de contexto del sistema operativo causarán caídas drásticas y fatales en la velocidad.&lt;/p>
&lt;p>Por lo tanto, adoptamos el enfoque de un &amp;ldquo;&lt;strong>Memory Arena&lt;/strong>&amp;rdquo;. Este es un método en el que se calcula (o se fija) y se asigna de una sola vez la cantidad máxima de memoria necesaria al inicio de la inferencia, y luego la memoria se extrae simplemente incrementando un puntero.&lt;/p>
&lt;h3 id="31-la-importancia-de-la-alineación">3.1 La importancia de la alineación
&lt;/h3>&lt;p>Las CPU modernas admiten instrucciones SIMD (Single Instruction, Multiple Data). Ejemplos son AVX2/AVX-512 de Intel/AMD y NEON de ARM. Estas instrucciones procesan datos de 256 bits (32 bytes) o 512 bits (64 bytes) a la vez, pero la memoria de datos objetivo debe estar alineada (alineación) a un límite de bytes específico (generalmente 32 o 64 bytes).&lt;/p>
&lt;p>A continuación se muestra un ejemplo de implementación en C++ de un Memory Arena considerando la alineació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;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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstdint&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstddef&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdexcept&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">MemoryArena&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">size_t&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">size_t&lt;/span> &lt;span class="n">offset&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint8_t&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">data&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">MemoryArena&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">size_t&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">offset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Usa posix_memalign en sistemas POSIX, o _aligned_malloc en Windows
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#ifdef _WIN32
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">static_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint8_t&lt;/span>&lt;span class="o">*&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">_aligned_malloc&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">64&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#else
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">posix_memalign&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">reinterpret_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="o">**&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="mi">64&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">bad_alloc&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="cp">#endif
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&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="o">~&lt;/span>&lt;span class="n">MemoryArena&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#ifdef _WIN32
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="n">_aligned_free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#else
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="n">free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#endif
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&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="kt">void&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nf">allocate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">size_t&lt;/span> &lt;span class="n">bytes&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">size_t&lt;/span> &lt;span class="n">alignment&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">64&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Cálculo de alineación (buscar el padding)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">size_t&lt;/span> &lt;span class="n">pad&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">alignment&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">offset&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">alignment&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">alignment&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">offset&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">pad&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">bytes&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">runtime_error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;OOM: MemoryArena se quedó sin memoria&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">offset&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">pad&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">ptr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">offset&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">offset&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">bytes&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">ptr&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="kt">void&lt;/span> &lt;span class="nf">reset&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">offset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Liberar memoria es tan simple como reiniciar el puntero (O(1))
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&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;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>De esta manera, al crear un tensor, siempre se obtiene memoria a través de esta arena. Al final de cada paso de inferencia (por ejemplo, después de generar un token), simplemente llamando a &lt;code>reset()&lt;/code>, la memoria se puede reutilizar instantáneamente.&lt;/p>
&lt;hr>
&lt;h2 id="4-estructura-de-datos-de-tensores-y-la-magia-de-los-strides">4. Estructura de datos de tensores y la magia de los Strides
&lt;/h2>&lt;p>Un tensor es un concepto que generaliza escalares, vectores y matrices. Lo importante en la implementación es que, mientras los datos reales se ubican en la memoria como un &lt;strong>arreglo unidimensional contiguo&lt;/strong>, tiene el concepto de un &amp;ldquo;Stride&amp;rdquo; para interpretarlo como multidimensional.&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">enum&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">DataType&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">FP32&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="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">INT8&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Para cuantización
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">INT4&lt;/span> &lt;span class="c1">// Para cuantización
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&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">struct&lt;/span> &lt;span class="nc">Tensor&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_dims&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Número de dimensiones
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int64_t&lt;/span> &lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// Número de elementos en cada dimensión (Number of Elements)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">size_t&lt;/span> &lt;span class="n">nb&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// Stride en cada dimensión (Number of Bytes)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">DataType&lt;/span> &lt;span class="n">type&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Tipo de dato
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">void&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Puntero al payload
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Para el grafo de computación
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">enum&lt;/span> &lt;span class="nc">OpType&lt;/span> &lt;span class="n">op&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src1&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;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>El stride &lt;code>nb[i]&lt;/code> representa la distancia en bytes en la memoria entre elementos adyacentes en la dimensión &lt;code>i&lt;/code>.
Por ejemplo, si una matriz con un número de elementos $M \times N$ (FP32, 4 bytes por elemento) se almacena en orden Row-Major (prioridad de fila), los strides serían los siguientes:&lt;/p>
&lt;ul>
&lt;li>&lt;code>nb[0]&lt;/code> = 4 (bytes) : Movimiento en dirección de las columnas&lt;/li>
&lt;li>&lt;code>nb[1]&lt;/code> = $N \times 4$ (bytes) : Movimiento en dirección de las filas&lt;/li>
&lt;/ul>
&lt;p>Aprovechando esto, puedes lograr operaciones como &amp;ldquo;Transposición (Transpose)&amp;rdquo; y &amp;ldquo;Vista (View)&amp;rdquo; simplemente intercambiando los valores de los strides, sin implicar copias de memoria. Es sumamente elegante y rápido.&lt;/p>
&lt;hr>
&lt;h2 id="5-construcción-del-grafo-de-computación-dag-y-evaluación-perezosa">5. Construcción del Grafo de Computación (DAG) y Evaluación Perezosa
&lt;/h2>&lt;p>Al igual que PyTorch, nuestro motor de inferencia también adopta una Evaluación Perezosa (Lazy Evaluation), similar a &amp;ldquo;Define-by-Run&amp;rdquo;. Es decir, cuando se llama a una función de cálculo, no realiza el cálculo en ese momento, sino que solo construye un grafo (dependencias entre nodos).&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nf">tensor_add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">MemoryArena&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">arena&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">out&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">create_tensor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">arena&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">n_dims&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">op&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpType&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ADD&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">src0&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">src1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">out&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="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nf">tensor_mul_mat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">MemoryArena&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">arena&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// b a menudo está transpuesta
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int64_t&lt;/span> &lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">out&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">create_tensor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">arena&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ne&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">op&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpType&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">MUL_MAT&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">src0&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">src1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">out&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;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>El flujo del proceso de inferencia es el siguiente.&lt;/p>
&lt;div class="mermaid">graph LR
A["Definir Tensores"] --> B["Construir Grafo mediante Operaciones"]
B --> C["Ordenamiento Topológico"]
C --> D["Asignar Memoria para Salidas"]
D --> E["Ejecutar Nodos en Orden"]&lt;/div>
&lt;p>Al evaluar el grafo (forward pass), se utiliza el ordenamiento topológico para procesar los nodos en orden comenzando por aquellos sin dependencias. Si es solo inferencia, no es necesario mantener gradientes para backpropagation, por lo que la gestión de memoria es muy sencilla.&lt;/p>
&lt;hr>
&lt;h2 id="6-el-núcleo-de-las-matemáticas-y-la-optimización-producto-de-matrices-gemm">6. El núcleo de las matemáticas y la optimización: Producto de Matrices (GEMM)
&lt;/h2>&lt;p>Más del 90% de la carga computacional de la inferencia de IA se gasta en la multiplicación general de matrices (GEMM: General Matrix Multiply). Tanto el mecanismo de atención, que es el núcleo del modelo Transformer, como las redes feed-forward (FFN), son en última instancia enormes multiplicaciones de matrices.&lt;/p>
&lt;p>El producto $C = A B$ (de tamaño $M \times N$) de dos matrices $A$ (tamaño $M \times K$) y $B$ (tamaño $K \times N$) se expresa matemáticamente de la siguiente manera.&lt;/p>
$$
C_{i,j} = \sum_{k=0}^{K-1} A_{i,k} \cdot B_{k,j}
$$
&lt;p>Si se implementa ingenuamente usando un triple bucle, habrá frecuentes fallos de caché y no se logrará rendimiento.&lt;/p>
&lt;h3 id="61-bloqueo-de-caché-y-optimización-simd-en-la-cpu">6.1 Bloqueo de caché y optimización SIMD en la CPU
&lt;/h3>&lt;p>La estrategia básica para acelerar GEMM en la CPU es la siguiente:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Loop Tiling (Bloqueo de Caché)&lt;/strong>: Divide las matrices en pequeños bloques que caben en las cachés L1/L2 para el cálculo.&lt;/li>
&lt;li>&lt;strong>Empaquetado de Datos&lt;/strong>: Reorganiza internamente los datos para que el patrón de acceso a la memoria sea contiguo.&lt;/li>
&lt;li>&lt;strong>Uso de SIMD&lt;/strong>: Usa comandos FMA (Fused Multiply-Add) como &lt;code>_mm512_fmadd_ps&lt;/code> en AVX-512, manejando múltiples cálculos de multiplicación y suma en un solo ciclo de reloj.&lt;/li>
&lt;/ol>
&lt;p>A continuación se muestra un ejemplo de un Producto Punto (Dot Product) de vectores simplificado, utilizando C++ y SIMD Intrinsics.&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;immintrin.h&amp;gt;&lt;/span>&lt;span class="cp"> &lt;/span>&lt;span class="c1">// Para instrucciones AVX
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Producto punto rápido FP32 usando AVX2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">float&lt;/span> &lt;span class="nf">dot_product_avx2&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">__m256&lt;/span> &lt;span class="n">sum256&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">_mm256_setzero_ps&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&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">// Procesar 8 elementos a la vez (256 bits = 32 bytes = 8 * 4 bytes)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">__m256&lt;/span> &lt;span class="n">va&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">_mm256_loadu_ps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">__m256&lt;/span> &lt;span class="n">vb&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">_mm256_loadu_ps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Instrucción FMA: sum256 = va * vb + sum256
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">sum256&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">_mm256_fmadd_ps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">va&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vb&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sum256&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">// Suma horizontal de los valores en el registro SIMD
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">float&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_mm256_storeu_ps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sum256&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">float&lt;/span> &lt;span class="n">dot&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">6&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">7&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">// Procesamiento del resto
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">++&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dot&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&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="k">return&lt;/span> &lt;span class="n">dot&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;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Incluso con este pequeño ajuste, puedes obtener un aumento de velocidad de varias a decenas de veces en comparación con una implementación ingenua.&lt;/p>
&lt;hr>
&lt;h2 id="7-superando-la-barrera-del-hardware-integración-de-backends-cuda-y-metal">7. Superando la barrera del hardware: Integración de Backends CUDA y Metal
&lt;/h2>&lt;p>Aunque solo con la implementación pura en C++ funcionará de manera decente en la CPU, para ejecutar modelos enormes como LLMs a una velocidad práctica (por ejemplo: generar 20 tokens o más por segundo), la capacidad de cálculo en paralelo de la GPU es indispensable. Por lo tanto, introducimos una capa de abstracción de backend en nuestro motor.&lt;/p>
&lt;h3 id="71-abstracción-del-backend">7.1 Abstracción del Backend
&lt;/h3>&lt;p>Usamos el polimorfismo de C++ para permitir el intercambio del ejecutor (Executor) de las operaciones.&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Backend&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="o">~&lt;/span>&lt;span class="n">Backend&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">default&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">alloc_buffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">free_buffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">copy_to_device&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">copy_to_host&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&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">// Ejecución de diversas operaciones
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">compute_add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">dst&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">compute_mul_mat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">dst&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&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;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="72-implementación-del-backend-de-nvidia-cuda">7.2 Implementación del Backend de NVIDIA CUDA
&lt;/h3>&lt;p>Para aprovechar la GPU de NVIDIA, implementamos un backend usando la extensión C++ de CUDA. Aunque es posible escribir tus propios kernels, para la multiplicación de matrices, la mejor opción es usar &amp;ldquo;cuBLAS&amp;rdquo;, la mejor biblioteca proporcionada por NVIDIA.&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cublas_v2.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cuda_runtime.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">CUDABackend&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="k">public&lt;/span> &lt;span class="n">Backend&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cublasHandle_t&lt;/span> &lt;span class="n">handle&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">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CUDABackend&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cublasCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">handle&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="o">~&lt;/span>&lt;span class="n">CUDABackend&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cublasDestroy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">handle&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="kt">void&lt;/span> &lt;span class="nf">compute_mul_mat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">dst&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// En CUDA, el orden por defecto es Column-Major, por lo que hay que tener cuidado con los parámetros
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span> &lt;span class="n">alpha&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">1.0f&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span> &lt;span class="n">beta&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">0.0f&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="kt">int&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">src1&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// Se asume que src1 está transpuesta
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cublasSgemm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">handle&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">CUBLAS_OP_T&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">CUBLAS_OP_N&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">m&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">alpha&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="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">src0&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&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="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">src1&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">beta&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="kt">float&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">dst&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cudaDeviceSynchronize&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="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Como las transferencias de datos (&lt;code>cudaMemcpy&lt;/code>) entre la memoria de CUDA y la del host (CPU) son muy costosas, es vital diseñar un sistema que mantenga todos los pesos y tensores intermedios en la VRAM el mayor tiempo posible durante la inferencia.&lt;/p>
&lt;h3 id="73-backend-de-apple-silicon-metal">7.3 Backend de Apple Silicon (Metal)
&lt;/h3>&lt;p>Recientemente, los chips M1/M2/M3 (Apple Silicon) de Mac son excelentes máquinas de inferencia de IA. La razón radica en su &amp;ldquo;Memoria Unificada&amp;rdquo;. Como la CPU y la GPU comparten la misma área de memoria, las transferencias de memoria de alto costo entre el host y el dispositivo a través de un bus PCIe, como en el caso de CUDA mencionado antes, son completamente innecesarias.&lt;/p>
&lt;p>Para llamar a Metal desde C++, se usa Objective-C++ (archivos &lt;code>.mm&lt;/code>) como puente, o la biblioteca &lt;code>metal-cpp&lt;/code>.
Escribiremos el kernel usando Compute Shaders de Metal (descrito en un archivo &lt;code>.metal&lt;/code> de forma similar a C++).&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Shader de Metal (kernel.metal)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;metal_stdlib&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">metal&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">kernel&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">mul_mat_kernel&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&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">A&lt;/span> &lt;span class="p">[[&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&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&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">B&lt;/span> &lt;span class="na">[[buffer(1)]]&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&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">C&lt;/span> &lt;span class="na">[[buffer(2)]]&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">constant&lt;/span> &lt;span class="n">uint3&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">dims&lt;/span> &lt;span class="na">[[buffer(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">uint2&lt;/span> &lt;span class="n">gid&lt;/span> &lt;span class="na">[[thread_position_in_grid]]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">uint&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dims&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">uint&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dims&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">uint&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dims&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">z&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">uint&lt;/span> &lt;span class="n">row&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gid&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">uint&lt;/span> &lt;span class="n">col&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gid&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&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="p">(&lt;/span>&lt;span class="n">row&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">col&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">float&lt;/span> &lt;span class="n">sum&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">0.0&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="p">(&lt;/span>&lt;span class="n">uint&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">++&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sum&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">A&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">row&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">B&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">col&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// Simplificado
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">C&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">row&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">col&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sum&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="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>En el entorno de Apple Silicon, también se proporciona una biblioteca optimizada para multiplicación de matrices llamada MPS (Metal Performance Shaders), por lo que usarla en un entorno de producción puede lograr velocidades de inferencia asombrosas.&lt;/p>
&lt;hr>
&lt;h2 id="8-procesamiento-específico-del-modelo-transformer-attention-y-caché-kv">8. Procesamiento específico del modelo Transformer: Attention y Caché KV
&lt;/h2>&lt;p>Los LLM de vanguardia, como LLaMA 2/3 y GPT, se basan en la arquitectura Transformer. Para implementar esto en C++, la construcción del &amp;ldquo;Scaled Dot-Product Attention&amp;rdquo; expresado en la siguiente fórmula es indispensable.&lt;/p>
$$
\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V
$$
&lt;p>Además, en la generación de tokens autorregresiva (Autoregressive), es necesario mantener los resultados de cálculos de los tokens pasados (Key y Value). Esto se denomina &amp;ldquo;&lt;strong>Caché KV (Key-Value Cache)&lt;/strong>&amp;rdquo;.&lt;/p>
&lt;div class="mermaid">graph TD
T["Token Actual"] --> Q["Consulta (Query)"]
T --> K["Clave (Key)"]
T --> V["Valor (Value)"]
K --> KCache["Añadir a la Caché KV"]
V --> VCache["Añadir a la Caché KV"]
Q --> Dot1["Q * K_Cache^T"]
KCache --> Dot1
Dot1 --> Scale["Escalar (1/sqrt(d))"]
Scale --> Softmax["Softmax"]
Softmax --> Dot2["SoftmaxOut * V_Cache"]
VCache --> Dot2
Dot2 --> Out["Vector de Contexto"]&lt;/div>
&lt;p>Para asignar memoria de la caché KV, asignamos en la arena de memoria por adelantado espacio suficiente para la longitud máxima de contexto (por ejemplo, 4096 u 8192 tokens) y lo operamos como un búfer circular (ring buffer). Esto evita las reasignaciones en cada paso de generación.&lt;/p>
&lt;p>También, implementamos para la Codificación Posicional (Positional Encoding) el método &amp;ldquo;RoPE (Rotary Position Embedding)&amp;rdquo; que es el principal estándar reciente. Este es un método para incrustar información de posición como un vector de rotación en el espacio complejo, y la optimización de las llamadas a las funciones &lt;code>sin&lt;/code> y &lt;code>cos&lt;/code> en C++ (por ejemplo, mediante el uso de tablas de búsqueda) es clave para el rendimiento.&lt;/p>
&lt;hr>
&lt;h2 id="9-optimización-extrema-mediante-cuantización-de-modelos-quantization">9. Optimización extrema mediante Cuantización de Modelos (Quantization)
&lt;/h2>&lt;p>Si cargas un modelo de gran escala (por ejemplo, un modelo LLaMA de 7 mil millones de parámetros) en FP32 (punto flotante de 32 bits), consumirá aproximadamente 28 GB de memoria (VRAM) solo para sus pesos. Además, al incluir la caché KV y los buffers de inferencia, puede sobrepasar fácilmente los 30 GB, haciendo que sea imposible de ejecutar en una GPU convencional de consumo.&lt;/p>
&lt;p>Aquí es donde entra en juego la &amp;ldquo;&lt;strong>Cuantización (Quantization)&lt;/strong>&amp;rdquo;. También es el verdadero valor del formato GGML.&lt;/p>
&lt;p>La cuantización es una técnica que reduce intencionalmente la precisión de los pesos.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>FP16 (16-bit)&lt;/strong>: Reduce el tamaño a la mitad. Casi sin pérdida de precisión.&lt;/li>
&lt;li>&lt;strong>INT8 (8-bit)&lt;/strong>: Un cuarto del tamaño. Pérdida menor de precisión.&lt;/li>
&lt;li>&lt;strong>INT4 (4-bit)&lt;/strong>: Un octavo del tamaño. Usando técnicas propias de bloqueo y factores de escala, es posible hacer inferencias prácticas.&lt;/li>
&lt;/ul>
&lt;p>En el lado del motor de inferencia, se leen los pesos comprimidos como INT4 (o INT8) de la memoria, y &lt;strong>justo después de cargarlos en los registros de la CPU o GPU, se descomprimen (Dequantize) a FP16 o FP32 para realizar los cálculos&lt;/strong>.&lt;/p>
&lt;p>Sorprendentemente, resulta más rápido reducir la cantidad de datos que se leen desde la memoria, incluso si esto significa aumentar la cantidad de cálculos. Esto se debe a que, en el hardware moderno, el cuello de botella de la tarea de inferencia no es la &amp;ldquo;Capacidad de Cálculo (Compute Bound)&amp;rdquo; sino el &amp;ldquo;&lt;strong>Ancho de Banda de la Memoria (Memory Bandwidth Bound)&lt;/strong>&amp;rdquo;. Con un motor implementado en C++ utilizando la cuantización INT4, es posible ejecutar localmente LLMs de manera fluida incluso en dispositivos como un MacBook Air con 8 GB de RAM unificada.&lt;/p>
&lt;hr>
&lt;h2 id="10-ajuste-de-rendimiento-arquitectura-numa-y-pool-de-hilos">10. Ajuste de rendimiento: Arquitectura NUMA y Pool de Hilos
&lt;/h2>&lt;p>Cuando se realiza inferencia utilizando la CPU, el multiprocesamiento (multithreading) es obligatorio. Sin embargo, no es óptimo simplemente instanciar y lanzar muchas &lt;code>std::thread&lt;/code>.&lt;/p>
&lt;p>En los servidores multisocket modernos o en las CPU de gama alta como los Ryzen Threadripper, se adopta la arquitectura &lt;strong>NUMA (Non-Uniform Memory Access)&lt;/strong>. Acceder a la memoria que está físicamente cerca de un determinado núcleo de CPU (memoria local) es rápido, pero acceder a la memoria vinculada a otro procesador es extremadamente lento.&lt;/p>
&lt;p>En un motor de inferencia avanzado en C++, se utilizan las siguientes técnicas:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Thread Pinning&lt;/strong>: Se fija cada hilo a un núcleo de CPU específico (configuración de Affinity) para evitar la pérdida de caché debido a los cambios de contexto.&lt;/li>
&lt;li>&lt;strong>Asignación consciente de NUMA&lt;/strong>: Asegura la memoria en el mismo nodo NUMA donde se encuentra el hilo que procesará los datos.&lt;/li>
&lt;li>&lt;strong>Pool de Hilos con Work-Stealing&lt;/strong>: Implementa un planificador eficiente que divide cada nodo del grafo de computación en pequeñas tareas, de manera que los hilos inactivos roben automáticamente tareas de otros para ejecutarlas.&lt;/li>
&lt;/ol>
&lt;p>Al dominar esto, es posible mantener el uso de la CPU pegado a cerca del 100% y lograr un rendimiento cercano al máximo teórico.&lt;/p>
&lt;hr>
&lt;h2 id="11-conclusión-el-placer-de-impulsar-la-ia-con-el-músculo-de-c">11. Conclusión: El placer de impulsar la IA con el &amp;ldquo;músculo&amp;rdquo; de C++
&lt;/h2>&lt;p>Python, sin duda, es conveniente. En investigación, desarrollo o creación de prototipos, no hay lenguaje que se le iguale en productividad. Sin embargo, en el momento en que se pasa a la fase de &amp;ldquo;hacer que el modelo ya construido funcione en el mundo real, de manera eficiente, en cualquier dispositivo&amp;rdquo;, es ahí donde C++ brilla.&lt;/p>
&lt;p>Manipular los bytes de memoria directamente, presionar los registros al límite con instrucciones SIMD, luchar contra el ancho de banda de la VRAM de la GPU y ver cómo el motor de inferencia que has creado escupe textos (tokens) en un español natural en la consola, otorga un sentido de logro y una &amp;ldquo;alegría pura como ingeniero&amp;rdquo; que jamás obtendrías al solo llamar a &lt;code>model.generate()&lt;/code> en un framework de Python.&lt;/p>
&lt;p>Aunque la tecnología de la IA a menudo se considera una &amp;ldquo;Caja Negra&amp;rdquo; (Black Box), el simple hecho de programar cada detalle con tus propias manos en C++, desde los cálculos de tensores hasta la asignación de la memoria, te permite comprender profundamente los verdaderos mecanismos detrás de cómo los LLMs &amp;ldquo;piensan&amp;rdquo;.&lt;/p>
&lt;p>Si tienes conocimientos básicos de C++ y un fuerte interés en la tecnología de IA actual, definitivamente deberías intentar construir tu propio motor de inferencia desde cero. El código fuente de GGML o llama.cpp será el mejor libro de texto en vivo que puedas tener.&lt;/p>
&lt;p>&lt;strong>¡Vamos, deshazte del pesado entorno de ejecución de Python y ejecuta la IA de vanguardia con los músculos de C++!&lt;/strong>&lt;/p></description></item><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>¿Cómo deberían sobrevivir los programadores en la era de la IA?</title><link>http://kenji.blog/es/p/how-programmers-survive-in-ai-era/</link><pubDate>Fri, 11 Sep 2026 15:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/how-programmers-survive-in-ai-era/</guid><description>&lt;img src="http://kenji.blog/p/how-programmers-survive-in-ai-era/img/eyecatch.jpg" alt="Featured image of post ¿Cómo deberían sobrevivir los programadores en la era de la IA?" />&lt;h1 id="cómo-deberían-sobrevivir-los-programadores-en-la-era-de-la-ia-el-fin-de-la-codificación-y-el-amanecer-de-una-nueva-ingeniería">¿Cómo deberían sobrevivir los programadores en la era de la IA? El fin de la codificación y el amanecer de una nueva ingeniería
&lt;/h1>&lt;p>En la actualidad, en 2026, el campo del desarrollo de software se encuentra en un período de cambio sin precedentes. Hasta hace unos pocos años, el concepto de &amp;ldquo;la IA escribe código&amp;rdquo; se limitaba a su papel como &amp;ldquo;herramienta auxiliar&amp;rdquo; para los programadores, como la generación de código boilerplate (código repetitivo) y el autocompletado de funciones en el mejor de los casos. Sin embargo, con la asombrosa evolución de los grandes modelos de lenguaje (LLM), la situación ha dado un vuelco radical. La IA moderna no es simplemente una &amp;ldquo;máquina de escribir inteligente&amp;rdquo;, sino que se ha transformado en un &amp;ldquo;ingeniero junior autónomo&amp;rdquo; con la capacidad de ensamblar instantánea y autónomamente sistemas completos, desde el frontend hasta la lógica del backend, el diseño del esquema de la base de datos e incluso la construcción de pipelines de CI/CD, con solo proporcionarle un documento de definición de requisitos.&lt;/p>
&lt;p>En una era así, ¿cómo deberíamos sobrevivir nosotros los &amp;ldquo;programadores&amp;rdquo; e &amp;ldquo;ingenieros de software&amp;rdquo;? A medida que el valor económico del acto mismo de &amp;ldquo;escribir código&amp;rdquo; se deflacta rápidamente, los &amp;ldquo;codificadores&amp;rdquo; que simplemente conocen la sintaxis (gramática) de un lenguaje de programación específico y están familiarizados con las API de un framework en particular están siendo rápidamente eliminados del mercado.&lt;/p>
&lt;p>En este artículo, analizaremos con extremo detalle las estrategias de supervivencia para los programadores en la era de la IA desde perspectivas técnicas, matemáticas y filosóficas. Esto no es solo una teoría sobre la carrera profesional, sino una redefinición de la disciplina de la ingeniería de software en sí misma.&lt;/p>
&lt;hr>
&lt;h2 id="1-la-historia-de-la-abstracción-abstraction-y-la-redefinición-de-la-programación">1. La historia de la abstracción (Abstraction) y la redefinición de la &amp;ldquo;programación&amp;rdquo;
&lt;/h2>&lt;p>Al repasar la historia de la ingeniería de software, nos damos cuenta de que siempre ha sido una historia de &amp;ldquo;abstracción (Abstraction)&amp;rdquo;. Siempre hemos construido capas para describir sistemas más complejos en un lenguaje más cercano al humano.&lt;/p>
&lt;p>Los primeros científicos de la computación utilizaban tarjetas perforadas para manipular directamente los interruptores del hardware físico, dando instrucciones a las computadoras en código máquina (secuencias de 0 y 1). Más tarde, apareció el lenguaje ensamblador, que permitió operar el hardware con mnemónicos que los humanos podían entender fácilmente. A medida que avanzaba la época, surgieron lenguajes de alto nivel como C y Fortran, que lograron encapsular los detalles complejos del hardware, como la gestión de memoria y los registros de la CPU. Posteriormente, con los lenguajes modernos que aparecieron, como Java, Python, Ruby y TypeScript, los programadores pudieron concentrarse más en &amp;ldquo;qué queremos que haga la computadora (What)&amp;rdquo; en lugar de en &amp;ldquo;cómo hacer que la computadora funcione (How)&amp;rdquo;.&lt;/p>
&lt;p>La aparición de la IA (LLM) es el cambio de paradigma más reciente y más grande en esta historia de abstracción. Si la evolución de los lenguajes de programación fue la &amp;ldquo;ocultación del hardware&amp;rdquo;, la evolución de los LLM es la &amp;ldquo;ocultación de la sintaxis (gramática)&amp;rdquo;.&lt;/p>
&lt;div class="mermaid">graph TD
A["Código Máquina / Tarjetas Perforadas (1940s)"] --> B["Lenguaje Ensamblador (1950s)"]
B --> C["Lenguajes Compilados de Alto Nivel (1970s)"]
C --> D["Lenguajes Gestionados / de Scripting (1990s)"]
D --> E["Lenguaje Natural a través de LLMs (2020s)"]
E --> F["Agentes de IA Autónomos (2026-)"]
style E fill:#f9f,stroke:#333,stroke-width:2px
style F fill:#fbb,stroke:#333,stroke-width:2px&lt;/div>
&lt;p>La era en la que los desarrolladores manipulaban punteros preocupándose por las fugas de memoria o escribían cientos de líneas de código repetitivo para el procesamiento de análisis de JSON ha terminado. El estándar de &amp;ldquo;programación&amp;rdquo; en 2026 es definir sistemas utilizando el lenguaje natural (japonés, inglés, español, etc.), el lenguaje con el nivel más alto de abstracción para la humanidad.&lt;/p>
&lt;hr>
&lt;h2 id="2-modelo-matemático-de-la-productividad-surfeando-la-ola-del-crecimiento-exponencial">2. Modelo matemático de la productividad: Surfeando la ola del crecimiento exponencial
&lt;/h2>&lt;p>Evaluemos cuantitativamente el aumento de productividad que aporta la IA utilizando un modelo matemático.
La productividad individual $P_{traditional}$ en el desarrollo de software tradicional podría modelarse como una combinación lineal del nivel de habilidad individual $S$, la experiencia en el dominio $E$ y la eficiencia de las herramientas $T$.&lt;/p>
$$ P_{traditional} = c_1 \cdot S + c_2 \cdot E + c_3 \cdot T $$
&lt;p>Sin embargo, en el desarrollo moderno impulsado por IA, la capacidad de la IA $A(t)$ actúa como un &amp;ldquo;poderoso multiplicador (Multiplier)&amp;rdquo; que amplifica la capacidad humana. Dado que las capacidades de la IA crecen exponencialmente (la versión de IA de la Ley de Moore) a lo largo del tiempo $t$, la productividad en la era de la IA $P_{AI}(t)$ se puede expresar mediante la siguiente ecuación:&lt;/p>
$$ P_{AI}(t) = \alpha \cdot S_{core} \cdot e^{\beta \cdot A(t)} $$
&lt;p>Aquí, cada variable significa lo siguiente:&lt;/p>
&lt;ul>
&lt;li>$\alpha$: Coeficiente base de productividad humana&lt;/li>
&lt;li>$S_{core}$: Las &amp;ldquo;habilidades centrales humanas&amp;rdquo; no reemplazables por la IA (diseño de arquitectura, comprensión de requisitos de negocio, juicio ético, etc.)&lt;/li>
&lt;li>$A(t)$: Capacidad absoluta de los modelos de IA (recuento de parámetros, ventana de contexto, capacidad de razonamiento) en el momento $t$&lt;/li>
&lt;li>$\beta$: Coeficiente que indica la eficacia con la que se pueden aprovechar las herramientas de IA (calidad de la ingeniería de prompts o sofisticación de los flujos de trabajo colaborativos con la IA)&lt;/li>
&lt;/ul>
&lt;p>La visión clave derivada de esta fórmula es que &lt;strong>en un mundo donde $A(t)$ aumenta exponencialmente, el impacto de las habilidades tradicionales, como la velocidad de escritura pura o la memorización de un lenguaje específico, en la productividad general se vuelve minúsculo&lt;/strong>. En cambio, el coeficiente $\beta$ para aprovechar el crecimiento exponencial de la IA, y $S_{core}$, el área que la IA no puede cubrir, se convierten en los factores dominantes que determinan el valor de mercado de un ingeniero.&lt;/p>
&lt;hr>
&lt;h2 id="3-probabilidad-de-automatización-de-tareas-probability-of-automation">3. Probabilidad de automatización de tareas (Probability of Automation)
&lt;/h2>&lt;p>Entonces, ¿qué tipo de tareas se automatizarán y qué tipo de tareas quedarán en manos de los humanos?
La probabilidad $P_{auto}(T)$ de que una tarea determinada $T$ sea completamente automatizada por IA se puede formular de la siguiente manera:&lt;/p>
$$ P_{auto}(T) = 1 - \exp\left(-\lambda \cdot \frac{\text{Predictability}(T)}{\text{Complexity}(T) \times \text{Context Dependency}(T)}\right) $$
&lt;ul>
&lt;li>$\text{Predictability}(T)$: Previsibilidad de la tarea (cuántos patrones existen en los datos históricos)&lt;/li>
&lt;li>$\text{Complexity}(T)$: Complejidad de la tarea&lt;/li>
&lt;li>$\text{Context Dependency}(T)$: La fuerza del &amp;ldquo;contexto implícito (conocimiento específico del dominio o relaciones humanas)&amp;rdquo; del que depende la tarea&lt;/li>
&lt;li>$\lambda$: Tasa de progreso tecnológico de la IA&lt;/li>
&lt;/ul>
&lt;p>Las tareas que son altamente predecibles y poco dependientes del contexto, como escribir el procesamiento de enrutamiento de una API o crear una pantalla CRUD simple, tienen un $P_{auto} \approx 1$ y estarán casi completamente automatizadas. Por otro lado, las tareas con una dependencia del contexto extremadamente alta, como &amp;ldquo;¿cómo integramos de manera segura los sistemas heredados existentes con los nuevos microservicios?&amp;rdquo; o &amp;ldquo;¿cómo diseñamos un flujo de autenticación que satisfaga las demandas del departamento legal sin comprometer la experiencia del usuario?&amp;rdquo;, son difíciles de automatizar.&lt;/p>
&lt;hr>
&lt;h2 id="4-retorno-de-la-sintaxis-gramática-a-la-arquitectura-estructura">4. Retorno de la sintaxis (gramática) a la arquitectura (estructura)
&lt;/h2>&lt;p>Separar claramente en qué es buena la IA y en qué son buenos los humanos es un requisito absoluto para la supervivencia.&lt;/p>
&lt;div class="mermaid">graph LR
Sub1["Dominios de Excelencia de la IA"]
Sub2["Dominios de Excelencia Humanos"]
A["Generación de Código a partir de Especificaciones"] --> Sub1
B["Corrección de Errores de Sintaxis y Bugs"] --> Sub1
C["Generación de Boilerplate / Pruebas"] --> Sub1
D["Análisis de Logs y Reconocimiento de Patrones"] --> Sub1
E["Diseño de Arquitectura de Sistemas"] --> Sub2
F["Resolución de Requisitos Ambiguos"] --> Sub2
G["Negociación entre Equipos"] --> Sub2
H["Juicio Ético / Responsabilidad"] --> Sub2&lt;/div>
&lt;p>La IA supera a los humanos en la &amp;ldquo;optimización local&amp;rdquo;. Los humanos no tienen ninguna posibilidad contra la velocidad y precisión con la que se escribe una sola función, una sola clase o un solo módulo. Sin embargo, la IA es muy vulnerable a la &amp;ldquo;optimización global&amp;rdquo; y al &amp;ldquo;contexto faltante (Missing Context)&amp;rdquo;.&lt;/p>
&lt;p>Los programadores del futuro deben cambiar su papel de &amp;ldquo;trabajadores que escriben código&amp;rdquo; a &amp;ldquo;arquitectos que orquestan innumerables componentes generados por IA&amp;rdquo;. Tener una visión general del sistema completo, dónde trazar los límites de los microservicios, cómo resolver el compromiso entre disponibilidad y consistencia en el Teorema CAP para adaptarlo al contexto del negocio, y cómo controlar la deuda técnica. Estos son trabajos intelectuales de alto nivel que solo pueden ser realizados por humanos que comprenden el panorama general y los objetivos del negocio.&lt;/p>
&lt;hr>
&lt;h2 id="5-la-definición-de-requisitos-es-la-verdadera-ingeniería-de-prompts">5. La definición de requisitos es la &amp;ldquo;verdadera ingeniería de prompts&amp;rdquo;
&lt;/h2>&lt;p>El término &amp;ldquo;ingeniería de prompts&amp;rdquo;, que escuchamos a menudo últimamente, se malinterpreta con frecuencia como &amp;ldquo;un truco para engañar a la IA y obtener el resultado deseado&amp;rdquo;. Sin embargo, la esencia de la ingeniería de prompts en el desarrollo de software es, sin lugar a dudas, &lt;strong>la &amp;ldquo;ingeniería de requisitos avanzada (Requirements Engineering)&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;p>Para dar instrucciones a la IA en lenguaje natural y hacer que genere el software deseado, los siguientes elementos deben ser estrictamente articulados:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Propósito (Why)&lt;/strong>: ¿Por qué se necesita esta característica? ¿Cuál es el valor empresarial?&lt;/li>
&lt;li>&lt;strong>Restricciones (Constraints)&lt;/strong>: Requisitos de rendimiento (latencia, rendimiento), requisitos de seguridad, restricciones de costos.&lt;/li>
&lt;li>&lt;strong>Casos Extremos (Edge Cases)&lt;/strong>: Procesamiento alternativo (fallback) para cuando el usuario proporciona una entrada inesperada.&lt;/li>
&lt;li>&lt;strong>Interfaces (Interfaces)&lt;/strong>: Especificaciones de integración con los sistemas existentes.&lt;/li>
&lt;/ol>
&lt;p>Las instrucciones vagas (prompts) solo generarán sistemas vagos y frágiles. La capacidad de escuchar profundamente para descubrir &amp;ldquo;lo que el cliente realmente quería&amp;rdquo;, organizar requisitos contradictorios y crear especificaciones lógicamente consistentes (prompts). Esta es la &amp;ldquo;habilidad de codificación&amp;rdquo; más poderosa en la era de la IA. Los programadores pasarán cada vez más tiempo enfrentándose a archivos de Notion o Markdown en lugar de editores de código, describiendo con precisión cómo debería ser el sistema mediante texto.&lt;/p>
&lt;hr>
&lt;h2 id="6-la-ventaja-abrumadora-del-conocimiento-del-dominio-domain-knowledge">6. La ventaja abrumadora del conocimiento del dominio (Domain Knowledge)
&lt;/h2>&lt;p>Dado que la IA se ha entrenado en código fuente abierto y documentos públicos de todo el mundo, está bien versada en tecnologías y algoritmos web generales. Sin embargo, hay datos a los que la IA no puede acceder. Esas son las &amp;ldquo;reglas de negocio específicas de tu empresa&amp;rdquo; y el &amp;ldquo;conocimiento del dominio profundamente arraigado en una industria específica (médica, financiera, manufacturera, etc.)&amp;rdquo;.&lt;/p>
&lt;p>Por ejemplo, supongamos que estás desarrollando un sistema de historia clínica electrónica en una startup médica. La IA sabe &amp;ldquo;cómo crear una interfaz de usuario tabular en React&amp;rdquo; y &amp;ldquo;la estructura de datos general de HL7 FHIR&amp;rdquo;. Sin embargo, no ha aprendido el conocimiento tácito de &amp;ldquo;en un departamento médico específico del Hospital A, en qué orden ven los médicos los datos de los pacientes, y qué tipo de interfaz de usuario minimizaría el riesgo de errores médicos&amp;rdquo;.&lt;/p>
&lt;p>En un mundo donde la tecnología misma se mercantiliza (se convierte en un bien de consumo común), el verdadero valor de los ingenieros nace en la intersección de la &amp;ldquo;tecnología&amp;rdquo; y el &amp;ldquo;dominio de negocio&amp;rdquo;. En lugar de competir solo con la destreza técnica, los individuos que posean una profunda experiencia en un dominio específico, como la medicina, las finanzas, la logística o el entretenimiento, y que puedan resolver los problemas de ese dominio utilizando la poderosa herramienta de la IA, liderarán el mercado del futuro.&lt;/p>
&lt;hr>
&lt;h2 id="7-el-problema-del-tranvía-en-el-desarrollo-de-software-quién-asume-la-responsabilidad">7. El &amp;ldquo;Problema del Tranvía&amp;rdquo; en el desarrollo de software: ¿Quién asume la responsabilidad?
&lt;/h2>&lt;p>A medida que aumenta nuestra dependencia de la IA, nos enfrentamos a graves problemas filosóficos y éticos. Este es el problema de &amp;ldquo;dónde recae la responsabilidad&amp;rdquo; en la ingeniería de software.&lt;/p>
&lt;p>Si el código generado de forma autónoma por la IA causa un error grave en un entorno de producción, lo que resulta en millones de dólares en pérdidas para una empresa, o si causa un mal funcionamiento en un sistema médico que afecta vidas humanas, ¿quién asume la responsabilidad? ¿La empresa que desarrolló el modelo de IA? ¿O el ingeniero que ingresó el prompt? No se puede &amp;ldquo;despedir&amp;rdquo; ni &amp;ldquo;arrestar&amp;rdquo; a la IA.&lt;/p>
&lt;p>No importa cuánto avance la tecnología, el papel de los &amp;ldquo;humanos&amp;rdquo; como el sujeto que asume la &amp;ldquo;responsabilidad legal y ética (Accountability)&amp;rdquo; por el impacto que los sistemas tienen en la sociedad no desaparecerá. De hecho, cuanto más se convierta el proceso de generación de código en una caja negra, mayor será la pesada responsabilidad de los humanos como &amp;ldquo;aprobadores finales (Approvers)&amp;rdquo; y &amp;ldquo;supervisores (Supervisors)&amp;rdquo; de los sistemas.&lt;/p>
&lt;p>Auditar si la arquitectura y el código propuestos por la IA cumplen con los estándares de seguridad, no tienen problemas éticos (están libres de sesgos) y cumplen con las normativas, y luego dar la luz verde final. El acto de &amp;ldquo;asumir la responsabilidad&amp;rdquo; en sí mismo se convierte en una parte importante del trabajo del ingeniero.&lt;/p>
&lt;hr>
&lt;h2 id="8-programación-en-parejas-con-ia-y-gestión-de-la-carga-cognitiva-cognitive-load">8. Programación en parejas con IA y gestión de la carga cognitiva (Cognitive Load)
&lt;/h2>&lt;p>A medida que trabajamos junto a la IA, la naturaleza de la &amp;ldquo;carga cognitiva (Cognitive Load)&amp;rdquo; humana también está cambiando. La carga cognitiva al escribir código desde cero es completamente diferente de la carga cognitiva al &amp;ldquo;leer y revisar&amp;rdquo; cientos de líneas de código desconocido generado por la IA.&lt;/p>
&lt;p>Según la teoría de la carga cognitiva en psicología, cuando se procesa información compleja que no coincide con los esquemas existentes (estructuras de conocimiento en la mente), la memoria de trabajo humana se agota rápidamente. El código generado por IA a veces contiene optimizaciones avanzadas que a un humano no se le ocurrirían, mientras que otras veces contiene &amp;ldquo;alucinaciones (hallucinations)&amp;rdquo; que ignoran el contexto.&lt;/p>
&lt;p>Para evitar esto, es necesario sistematizar el proceso de revisión de la IA.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant H as "Ingeniero Humano (Arquitecto)"
participant A as "Agente de IA"
participant S as "CI/CD &amp; Pruebas"
H->>A: "Definir requisitos y restricciones estrictas"
A->>H: "Proponer Arquitectura y Código Inicial"
Note over H,A: Fase de Revisión: Alta Carga Cognitiva
H->>A: "Criticar opciones de diseño, solicitar refactorización"
A->>S: "Generar Código Final y Hacer Push"
S-->>H: "Resultados de Pruebas Automatizadas y Análisis Estático"
H->>H: "Aprobación Final y Asunción de Responsabilidad"&lt;/div>
&lt;p>Los humanos necesitan perfeccionar al máximo la habilidad de &amp;ldquo;lectura rápida y detección instantánea de fallas lógicas (Code Reading &amp;amp; Auditing)&amp;rdquo; incluso más que la habilidad de &amp;ldquo;escribir&amp;rdquo;. La importancia del Desarrollo Guiado por Pruebas (TDD) es aún mayor en la era de la IA. El enfoque principal será que un humano u otra IA escriba código de prueba estricto antes de dejar que la IA escriba el código, y luego haga que la IA corrija el código hasta que pase las pruebas.&lt;/p>
&lt;hr>
&lt;h2 id="9-estrategia-de-supervivencia-concreta-qué-aprender-a-partir-de-mañana">9. Estrategia de supervivencia concreta: Qué aprender a partir de mañana
&lt;/h2>&lt;p>Basado en el análisis realizado hasta ahora, presento un plan de acción concreto para que los programadores sobrevivan a la era de la IA.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Reaprender a fondo los &amp;ldquo;fundamentos&amp;rdquo; de la tecnología&lt;/strong>: Puedes dejar cómo usar frameworks a la IA. Sin embargo, un profundo conocimiento de cómo funcionan los sistemas operativos, los protocolos de red (TCP/IP, HTTP/3), la estructura interna de las bases de datos (B-Tree, niveles de aislamiento de transacciones), las estructuras de datos y algoritmos es absolutamente necesario. Para juzgar si el resultado de la IA es correcto, es indispensable una base sólida en ciencias de la computación.&lt;/li>
&lt;li>&lt;strong>Dominar la arquitectura en la nube y los sistemas distribuidos&lt;/strong>: Concéntrate en cómo combinar recursos en la nube como AWS, GCP y Azure para construir sistemas escalables, en lugar de código individual. Comprende el concepto de IaC (Infrastructure as Code) como Terraform y cultiva la capacidad de diseñar todo el sistema como código.&lt;/li>
&lt;li>&lt;strong>Conviértete en un experto en tu dominio de negocio&lt;/strong>: Aprende profundamente el modelo de negocio, las regulaciones legales y la psicología del comportamiento del usuario de la industria a la que perteneces. Ve más allá de los límites de un ingeniero y ten una perspectiva más cercana a la de un Product Manager (PM).&lt;/li>
&lt;li>&lt;strong>Perfecciona las habilidades de comunicación y facilitación&lt;/strong>: El proceso de resolver la &amp;ldquo;ambigüedad&amp;rdquo; entre humanos y lograr consensos no puede ser reemplazado por la IA. Las habilidades blandas de interactuar con las partes interesadas y descubrir los verdaderos problemas serán las habilidades más valiosas.&lt;/li>
&lt;li>&lt;strong>Usa la IA al máximo como un &amp;ldquo;colega&amp;rdquo;&lt;/strong>: En lugar de temer a la evolución de las herramientas de IA, utilízalas como tu arma más poderosa. Utiliza rutinariamente los últimos LLMs y agentes de codificación de IA para acumular el &amp;ldquo;conocimiento tácito&amp;rdquo; de dónde falla la IA y cómo ajustar los prompts para extraer el máximo rendimiento.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="conclusión-no-temas-surfea-la-ola">Conclusión: No temas, surfea la ola
&lt;/h2>&lt;p>La automatización de la programación por parte de la IA no significa la &amp;ldquo;muerte&amp;rdquo; de la profesión de programador. Más bien, es un &lt;strong>&amp;ldquo;Renacimiento&amp;rdquo;&lt;/strong> que nos libera del &amp;ldquo;trabajo no esencial&amp;rdquo; en el desarrollo de software, como la corrección de errores tipográficos, la solución de problemas de configuración del entorno y la escritura de aburrido código repetitivo (boilerplate).&lt;/p>
&lt;p>A lo largo de la historia, cuando apareció el telar automático o cuando apareció el software de hojas de cálculo (Excel), se extendió el pesimismo de que los trabajos desaparecerían. Sin embargo, la realidad es que a medida que la productividad mejoró drásticamente, se creó nueva demanda y nacieron trabajos más sofisticados. Lo mismo sucederá en el mundo del software. A medida que se vuelva posible &amp;ldquo;construir sistemas a bajo costo&amp;rdquo;, el software penetrará en todas las áreas que antes no estaban informatizadas debido a los altos costos, y los problemas (What) que los ingenieros tendrán que resolver se expandirán infinitamente.&lt;/p>
&lt;p>A nosotros, los programadores, se nos da ahora la oportunidad de evolucionar de artesanos que escriben código a &amp;ldquo;directores de orquesta&amp;rdquo; que comandan la poderosa inteligencia de la IA. En lugar de quedarnos en la orilla temerosos de la ola tecnológica, vamos a montarla rápidamente y zarpar hacia un viaje para crear sistemas más grandes y valiosos. La era de la IA es exactamente la era en la que comienza la &amp;ldquo;ingeniería&amp;rdquo; en su verdadero sentido.&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>¡Comparación exhaustiva de las API de ChatGPT, Gemini y Claude! ¿Cuál deberías elegir?</title><link>http://kenji.blog/es/p/chatgpt-gemini-claude-api-comparison/</link><pubDate>Fri, 11 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/es/p/chatgpt-gemini-claude-api-comparison/</guid><description>&lt;img src="http://kenji.blog/p/chatgpt-gemini-claude-api-comparison/img/eyecatch.jpg" alt="Featured image of post ¡Comparación exhaustiva de las API de ChatGPT, Gemini y Claude! ¿Cuál deberías elegir?" />&lt;h1 id="comparación-exhaustiva-de-las-api-de-chatgpt-gemini-y-claude-cuál-deberías-elegir">¡Comparación exhaustiva de las API de ChatGPT, Gemini y Claude! ¿Cuál deberías elegir?
&lt;/h1>&lt;p>La evolución de la tecnología de IA ha sido asombrosa y, especialmente en el campo de los modelos de lenguaje grande (LLM: Large Language Model), ChatGPT de OpenAI (serie GPT), Gemini de Google y Claude de Anthropic están en una intensa batalla a tres bandas por la supremacía. A partir de 2026, cada compañía está lanzando nuevos modelos y funciones de API en cuestión de meses, o incluso semanas. Para los desarrolladores y arquitectos de TI de las empresas, la pregunta de &amp;ldquo;¿qué API integrar en nuestro producto?&amp;rdquo; se ha convertido en una decisión extremadamente importante que determina el éxito de un proyecto.&lt;/p>
&lt;p>En este artículo, compararemos y explicaremos en detalle las API de estos tres grandes proveedores de IA desde la perspectiva del desarrollador. Iremos más allá de una simple enumeración de especificaciones y cubriremos desde el diseño de la arquitectura, la estructura detallada de precios y el análisis matemático de la latencia (retraso), hasta ejemplos de implementación específicos en Python y Node.js, así como las últimas técnicas de optimización de costos como el caché de prompts.&lt;/p>
&lt;p>Esperamos que esta guía sea completa para ayudar a los lectores a seleccionar la API de LLM más adecuada para sus casos de uso y construir aplicaciones de IA escalables y rentables.&lt;/p>
&lt;hr>
&lt;h2 id="1-filosofía-y-diseño-de-la-arquitectura-de-la-api-de-cada-llm">1. Filosofía y diseño de la arquitectura de la API de cada LLM
&lt;/h2>&lt;p>Al hacer una selección tecnológica, es fundamental entender primero qué tipo de filosofía impulsa la construcción de los modelos y las API de cada empresa.&lt;/p>
&lt;h3 id="11-openai-chatgpt">1.1 OpenAI (ChatGPT)
&lt;/h3>&lt;p>Con la misión de lograr la &amp;ldquo;Inteligencia Artificial General (AGI)&amp;rdquo;, OpenAI lidera constantemente los estándares de facto de la industria. Ofrece una variedad de modelos adaptados a diferentes casos de uso, como GPT-4o, GPT-4o-mini y el modelo especializado en inferencia o1. Su ecosistema es el más maduro, contando con la mayor cantidad de bibliotecas y documentación, tanto oficiales como no oficiales.&lt;/p>
&lt;h3 id="12-google-gemini">1.2 Google (Gemini)
&lt;/h3>&lt;p>Bajo la premisa de &amp;ldquo;AI First&amp;rdquo; (IA primero), Google aprovecha al máximo la escalabilidad de su propia infraestructura (redes de TPU). La mayor característica de Gemini 1.5 Pro/Flash es su abrumadora ventana de contexto (longitud de contexto) de hasta 2 millones de tokens, lo que le permite procesar simultáneamente documentos muy largos o varias horas de video y audio. La sólida integración con Google Cloud (Vertex AI) también resulta muy atractiva para las empresas.&lt;/p>
&lt;h3 id="13-anthropic-claude">1.3 Anthropic (Claude)
&lt;/h3>&lt;p>Anthropic es una empresa fundada por ex miembros de OpenAI y adopta un enfoque único de seguridad llamado &amp;ldquo;IA Constitucional&amp;rdquo; (Constitutional AI). Los modelos Claude 3.5 Sonnet y Opus han ganado un apoyo entusiasta de muchos desarrolladores por sus altas capacidades de razonamiento, generación de código y, sobre todo, por sus &amp;ldquo;diálogos naturales y humanos&amp;rdquo; y su &amp;ldquo;bajo nivel de alucinaciones&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="2-comparación-exhaustiva-de-las-especificaciones-de-las-familias-de-modelos">2. Comparación exhaustiva de las especificaciones de las familias de modelos
&lt;/h2>&lt;p>Comparamos las especificaciones de los principales modelos a partir de 2026.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Proveedor&lt;/th>
&lt;th>Modelo Principal&lt;/th>
&lt;th>Longitud de Contexto Máxima&lt;/th>
&lt;th>Principales Fortalezas&lt;/th>
&lt;th>Casos de Uso Recomendados&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>OpenAI&lt;/strong>&lt;/td>
&lt;td>GPT-4o&lt;/td>
&lt;td>128K&lt;/td>
&lt;td>Velocidad, reconocimiento visual, soporte de voz&lt;/td>
&lt;td>Aplicaciones interactivas, tareas de propósito general&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>OpenAI&lt;/strong>&lt;/td>
&lt;td>o1-preview&lt;/td>
&lt;td>128K&lt;/td>
&lt;td>Razonamiento lógico avanzado, matemáticas, programación&lt;/td>
&lt;td>Generación de algoritmos complejos, usos en investigación&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Google&lt;/strong>&lt;/td>
&lt;td>Gemini 1.5 Pro&lt;/td>
&lt;td>2,000K&lt;/td>
&lt;td>Procesamiento de textos ultralargos, multimodal (video/audio)&lt;/td>
&lt;td>Análisis de enormes bases de código, resumen de video&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Google&lt;/strong>&lt;/td>
&lt;td>Gemini 1.5 Flash&lt;/td>
&lt;td>2,000K&lt;/td>
&lt;td>Baja latencia, alto rendimiento (throughput), costo extremadamente bajo&lt;/td>
&lt;td>Procesamiento en tiempo real, procesamiento por lotes de grandes datos&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Anthropic&lt;/strong>&lt;/td>
&lt;td>Claude 3.5 Sonnet&lt;/td>
&lt;td>200K&lt;/td>
&lt;td>Capacidad de codificación, generación natural de texto&lt;/td>
&lt;td>Soporte en desarrollo de software, atención al cliente avanzada&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Anthropic&lt;/strong>&lt;/td>
&lt;td>Claude 3.5 Haiku&lt;/td>
&lt;td>200K&lt;/td>
&lt;td>Respuesta ultrarrápida, relación costo-rendimiento&lt;/td>
&lt;td>IA de borde (Edge AI), chatbots en tiempo real&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="3-profundización-en-la-arquitectura-detrás-de-las-peticiones-de-la-api">3. Profundización en la arquitectura: Detrás de las peticiones de la API
&lt;/h2>&lt;p>Cuando se hace una llamada a la API de un LLM, ¿qué tipo de procesamiento se lleva a cabo en el backend? Para optimizar el rendimiento, es necesario comprender esta arquitectura.&lt;/p>
&lt;p>El siguiente diagrama de Mermaid muestra el panorama general desde el momento en que la aplicación cliente envía una solicitud a la API hasta que los tokens se devuelven en streaming.&lt;/p>
&lt;div class="mermaid">graph TD
A["Aplicación Cliente"] -->|HTTP/REST or gRPC| B["Puerta de Enlace API"]
B --> C["Balanceador de Carga"]
C --> D["Clúster de Inferencia"]
D --> E["Tokenizador (BPE / SentencePiece)"]
E --> F["Caché KV y Mecanismo de Atención"]
F --> G["Bloques Transformer (Forward Pass)"]
G --> H["Capa de Salida (Logits)"]
H --> I["Muestreador (Temperature, Top-p, Top-k)"]
I --> J["Destokenizador"]
J -->|Respuesta en Streaming (Chunk)| A&lt;/div>
&lt;h3 id="31-algoritmo-de-tokenización">3.1 Algoritmo de Tokenización
&lt;/h3>&lt;p>El texto ingresado en la API se divide internamente en unidades llamadas &amp;ldquo;tokens&amp;rdquo;.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>OpenAI (tiktoken)&lt;/strong>: Utiliza Byte-Pair Encoding (BPE). Se comprime de manera extremadamente eficiente, especialmente en inglés, pero en lenguajes no alfabéticos, como el japonés, el número de tokens tiende a inflarse.&lt;/li>
&lt;li>&lt;strong>Google (Gemini)&lt;/strong>: Utiliza SentencePiece (Modelo de Lenguaje Unigrama). Es fuerte en multilingüismo y tiende a poder expresar textos, incluso en japonés, con un número relativamente menor de tokens.&lt;/li>
&lt;li>&lt;strong>Anthropic (Claude)&lt;/strong>: Utiliza una versión personalizada de BPE. El soporte multilingüe se ha fortalecido y, a partir de Claude 3, la eficiencia de tokens en lenguajes como el japonés ha mejorado significativamente.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="4-análisis-matemático-del-rendimiento-y-la-latencia">4. Análisis matemático del rendimiento y la latencia
&lt;/h2>&lt;p>En las aplicaciones en tiempo real, la latencia está directamente relacionada con la experiencia del usuario (UX). La latencia de la API de un LLM $T_{total}$ se puede modelar matemáticamente de la siguiente manera:&lt;/p>
$$ T_{total} = T_{network} + T_{TTFT} + (N \times T_{TPOT}) $$
&lt;p>Aquí, cada variable tiene el siguiente significado:&lt;/p>
&lt;ul>
&lt;li>$T_{network}$: Tiempo de ida y vuelta de la red (RTT).&lt;/li>
&lt;li>$T_{TTFT}$ (Time To First Token): El tiempo que tarda en generarse el primer carácter. Depende en gran medida del costo de cálculo de la atención, que es proporcional al cuadrado de la longitud del prompt (número de tokens de entrada).&lt;/li>
&lt;li>$N$: Número total de tokens de salida.&lt;/li>
&lt;li>$T_{TPOT}$ (Time Per Output Token): Tiempo de generación por token. Al ser un modelo autorregresivo, se calcula secuencialmente, dependiendo de las salidas anteriores.&lt;/li>
&lt;/ul>
&lt;h3 id="41-complejidad-computacional-del-mecanismo-de-autoatención">4.1 Complejidad computacional del mecanismo de autoatención
&lt;/h3>&lt;p>El costo computacional de la autoatención (Self-Attention) en la arquitectura Transformer aumenta de forma cuadrática en relación con la longitud de la secuencia de entrada $L$.&lt;/p>
$$ \text{Complexity} = O(L^2 \cdot d) $$
&lt;p>Donde $d$ es el número de dimensiones del vector de incrustación (embedding). Debido a esta restricción, normalmente, a medida que el prompt se hace más largo, $T_{TTFT}$ empeora drásticamente.
Sin embargo, Gemini 1.5 de Google emplea arquitecturas de optimización innovadoras como &amp;ldquo;Ring Attention&amp;rdquo; y &amp;ldquo;Block-wise Compute&amp;rdquo;, y logra generar el primer token en un tiempo razonable (de unos pocos segundos a unas decenas de segundos), incluso cuando se ingresa un texto largo de 2 millones de tokens.&lt;/p>
&lt;hr>
&lt;h2 id="5-estructura-de-precios-y-estrategias-de-optimización-de-costos">5. Estructura de precios y estrategias de optimización de costos
&lt;/h2>&lt;p>El costo de la API se calcula fundamentalmente en base al número de tokens de entrada y tokens de salida.&lt;/p>
$$ Cost = (Tokens_{in} \times Rate_{in}) + (Tokens_{out} \times Rate_{out}) $$
&lt;p>No obstante, en las API más recientes, se han introducido nuevos mecanismos para reducir drásticamente los costos.&lt;/p>
&lt;h3 id="51-caché-de-prompts-prompt-caching">5.1 Caché de Prompts (Prompt Caching)
&lt;/h3>&lt;p>Enviar un sistema de prompts (system prompts) extenso o grandes cantidades de documentos recuperados mediante RAG cada vez resulta en costos masivos. Para hacer frente a esto, cada empresa ofrece una función de caché.&lt;/p>
&lt;p>En Anthropic (Claude) y Google (Gemini), al almacenar en caché bloques de texto específicos, se puede reducir significativamente el costo de entrada (hasta en un 90%).&lt;/p>
&lt;p>El modelo de costo cuando se utiliza el caché sería el siguiente:&lt;/p>
$$ Cost_{cached} = (Tokens_{cache\_write} \times Rate_{cache\_write}) + (Tokens_{cache\_read} \times Rate_{cache\_read}) + (Tokens_{out} \times Rate_{out}) $$
&lt;p>Aquí, el $Rate_{cache\_read}$ se establece en alrededor del 10% al 25% del $Rate_{in}$ normal. Gracias a esto, es posible operar chatbots de bajo costo manteniendo como conocimiento de fondo bases de código de decenas de miles de líneas.&lt;/p>
&lt;h3 id="52-api-por-lotes-batch-api">5.2 API por Lotes (Batch API)
&lt;/h3>&lt;p>Para tareas que no requieren tiempo real (análisis de registros, clasificación de datos masivos, etc.), OpenAI y Anthropic ofrecen API por lotes. Es un mecanismo poderoso en el que, en lugar de recibir respuestas inmediatamente, se envían solicitudes agrupadas y se obtienen resultados dentro de las 24 horas, con un descuento del 50% de la tarifa normal de la API.&lt;/p>
&lt;hr>
&lt;h2 id="6-experiencia-del-desarrollador-dx-y-comparación-de-sdks">6. Experiencia del desarrollador (DX) y comparación de SDKs
&lt;/h2>&lt;p>Comparamos los SDKs (Kits de Desarrollo de Software) proporcionados por cada empresa desde la perspectiva de la eficiencia del desarrollo.&lt;/p>
&lt;h3 id="61-api-de-openai">6.1 API de OpenAI
&lt;/h3>&lt;p>Es el más utilizado en general y la compatibilidad con bibliotecas de terceros (LangChain, LlamaIndex, etc.) es la más rápida. Además, gracias a la función Structured Outputs (Salidas Estructuradas), se garantiza la devolución de respuestas que cumplen al 100% con un esquema JSON, lo que facilita enormemente la integración de sistemas.&lt;/p>
&lt;h3 id="62-api-de-anthropic-claude">6.2 API de Anthropic (Claude)
&lt;/h3>&lt;p>La interfaz de su SDK es muy refinada y su definición de tipos en TypeScript tiene fama de ser muy fácil de manejar. En particular, la estructura de la Message API es intuitiva, permitiendo redactar de forma sencilla solicitudes multimodales que incluyan múltiples imágenes.&lt;/p>
&lt;h3 id="63-api-de-google-gemini">6.3 API de Google Gemini
&lt;/h3>&lt;p>Existen dos vías de acceso: mediante Google Cloud Vertex AI y mediante AI Studio (Google Gen AI SDK), lo cual puede ser un poco confuso para los principiantes. Sin embargo, el SDK de Vertex AI, dirigido a empresas, se integra completamente con IAM (sistema de autenticación y autorización) de GCP, permitiendo construir un entorno de desarrollo seguro.&lt;/p>
&lt;hr>
&lt;h2 id="7-práctica-implementación-de-prueba-de-integración-de-múltiples-api-con-python">7. ¡Práctica! Implementación de prueba de integración de múltiples API con Python
&lt;/h2>&lt;p>Aquí, implementaremos un script en Python que envíe peticiones asíncronas simultáneamente a las tres APIs de OpenAI, Anthropic y Gemini para comparar su latencia.&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;/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">asyncio&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">time&lt;/span>
&lt;/span>&lt;/span>&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">openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AsyncOpenAI&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">anthropic&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AsyncAnthropic&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">google.generativeai&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">genai&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"># Inicialización de los clientes&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">openai_client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AsyncOpenAI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&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;OPENAI_API_KEY&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">anthropic_client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AsyncAnthropic&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&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;ANTHROPIC_API_KEY&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">genai&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">configure&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&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;GEMINI_API_KEY&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">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Por favor, explica los fundamentos de la computación cuántica y su impacto en la criptografía actual, de manera fácil de entender para un principiante.&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="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">fetch_openai&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">openai_client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">chat&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">completions&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&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="s2">&amp;#34;gpt-4o&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">messages&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&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="n">max_tokens&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1024&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">elapsed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">start_time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;OpenAI (GPT-4o)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">elapsed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">choices&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&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">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">fetch_anthropic&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">anthropic_client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">messages&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&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="s2">&amp;#34;claude-3-5-sonnet-20240620&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">messages&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&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="n">max_tokens&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1024&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">elapsed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">start_time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;Anthropic (Claude 3.5 Sonnet)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">elapsed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">text&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">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">fetch_gemini&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&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">genai&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">GenerativeModel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;gemini-1.5-pro&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="c1"># Uso del método asíncrono del SDK en Python de Gemini&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">generate_content_async&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="n">elapsed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">start_time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;Google (Gemini 1.5 Pro)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">elapsed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">text&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">async&lt;/span> &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="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Enviando peticiones a cada API de LLM...&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"># Ejecutar las 3 APIs en paralelo&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">asyncio&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">gather&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fetch_openai&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fetch_anthropic&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fetch_gemini&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="k">for&lt;/span> &lt;span class="n">provider&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">latency&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">results&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;--- &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">provider&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 class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Latencia: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">latency&lt;/span>&lt;span class="si">:&lt;/span>&lt;span class="s2">.2f&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> segundos&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="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Respuesta (Extracto): &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">...&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="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">asyncio&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&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;p>Al ejecutar este script, se puede medir fácilmente qué modelo responde más rápido (minimizando $T_{total}$) en un entorno de red real.&lt;/p>
&lt;hr>
&lt;h2 id="8-implementación-de-tool-calling-llamada-a-funciones-con-nodejs">8. Implementación de Tool Calling (Llamada a Funciones) con Node.js
&lt;/h2>&lt;p>Para hacer que un LLM funcione como un &amp;ldquo;agente de IA&amp;rdquo; que colabora con sistemas externos, y no solo como un chatbot, el Tool Calling (o Function Calling) es indispensable. A continuación, se muestra un ejemplo en el que se usa Node.js (TypeScript) para permitir que la API de OpenAI llame a una API del clima.&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;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">OpenAI&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s2">&amp;#34;openai&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="kr">const&lt;/span> &lt;span class="nx">openai&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">OpenAI&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">apiKey&lt;/span>: &lt;span class="kt">process.env.OPENAI_API_KEY&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="kr">async&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">runAgent() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">tools&lt;/span> &lt;span class="o">=&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="kr">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;function&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="kd">function&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;get_weather&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="nx">description&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Obtiene el clima actual de la ciudad especificada.&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="nx">parameters&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;object&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="nx">properties&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">location&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;string&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="nx">description&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Nombre de la ciudad (ej: Tokio, Nueva York)&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="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">required&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;location&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="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="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="kr">const&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="nx">openai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">chat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">completions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">create&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">model&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;gpt-4o&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="nx">messages&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[{&lt;/span> &lt;span class="nx">role&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;¿Cómo está el clima hoy en Tokio? ¿Necesito un paraguas?&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="nx">tools&lt;/span>: &lt;span class="kt">tools&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">tool_choice&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="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="kr">const&lt;/span> &lt;span class="nx">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">choices&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">message&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="p">(&lt;/span>&lt;span class="nx">message&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">tool_calls&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">toolCall&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">message&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">tool_calls&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`El LLM solicitó la llamada a una herramienta: Nombre de la función = &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">toolCall&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&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="kr">const&lt;/span> &lt;span class="nx">args&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">toolCall&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">arguments&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`Argumentos: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">location&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&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">// Aquí es donde se implementaría el código para llamar a la API meteorológica real (ej: OpenWeatherMap)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// const weather = await fetchWeatherFromAPI(args.location);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Luego se pasa el resultado obtenido de nuevo al LLM para generar la respuesta final
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&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="nx">runAgent&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&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;p>Claude 3.5 Sonnet y Gemini 1.5 Pro también tienen capacidades de Tool Calling equivalentes, y aunque hay diferencias menores en cómo se definen los esquemas, el flujo básico es común en todos.&lt;/p>
&lt;hr>
&lt;h2 id="9-rag-vs-ventana-de-contexto-larga-cuál-se-debe-adoptar">9. RAG vs Ventana de Contexto Larga: ¿Cuál se debe adoptar?
&lt;/h2>&lt;p>Actualmente, uno de los mayores debates en la arquitectura de IA empresarial es el problema de si &amp;ldquo;deberíamos usar RAG (Generación Aumentada por Recuperación) para incorporar conocimientos externos, o si deberíamos enviarle todo a una enorme ventana de contexto (Long Context)&amp;rdquo;.&lt;/p>
&lt;h3 id="ventajas-y-desafíos-del-rag-retrieval-augmented-generation">Ventajas y Desafíos del RAG (Retrieval-Augmented Generation)
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>Ventajas&lt;/strong>: Es de bajo costo (ya que solo se ingresan en el prompt los fragmentos de datos necesarios) y es más fácil identificar el origen (fuente) de la respuesta.&lt;/li>
&lt;li>&lt;strong>Desafíos&lt;/strong>: Como depende de la precisión de la búsqueda semántica, no es adecuado para tareas de razonamiento avanzado en las que el contexto esté disperso en varios documentos (por ejemplo, &amp;ldquo;Analiza cronológicamente la causa raíz del retraso del Proyecto A a partir de todas las actas de reuniones del año pasado&amp;rdquo;).&lt;/li>
&lt;/ul>
&lt;h3 id="contexto-largo-ej-los-2-millones-de-tokens-de-gemini-15-pro">Contexto Largo (ej. los 2 millones de tokens de Gemini 1.5 Pro)
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>Ventajas&lt;/strong>: No hay pérdida de información debido a la búsqueda. Incluso en la prueba de &amp;ldquo;Buscar una aguja en un pajar&amp;rdquo; (Needle In A Haystack: NIAH), Gemini 1.5 Pro y Claude 3.5 Sonnet pueden extraer información con una precisión de más del 99%.&lt;/li>
&lt;li>&lt;strong>Desafíos&lt;/strong>: El consumo de tokens es masivo, lo que aumenta los costos, y la latencia ($T_{TTFT}$) también aumenta.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Conclusión&lt;/strong>: La mejor práctica en 2026 es un &lt;strong>&amp;ldquo;Enfoque Híbrido&amp;rdquo;&lt;/strong>. El diseño predominante utiliza RAG con bases de datos vectoriales para preguntas y respuestas rutinarias, y aprovecha el contexto largo (Long Context) usando el caché de prompts para tareas especializadas que requieran análisis complejos o revisiones exhaustivas de código completo.&lt;/p>
&lt;hr>
&lt;h2 id="10-comparación-de-capacidades-de-procesamiento-multimodal">10. Comparación de capacidades de procesamiento multimodal
&lt;/h2>&lt;p>En las aplicaciones de IA de próxima generación, se necesita la capacidad de comprender directamente imágenes, audio y video, además del texto.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant User as "Usuario"
participant Client as "App Frontend"
participant API as "API LLM (Multimodal)"
User->>Client: Subir Video y Prompt de Texto
Client->>API: Enviar Bytes de Video/URI + Texto
Note over API: Fragmentación de video y separación de audio
Note over API: Modelo de Incrustación Multimodal
API-->>Client: Devolver Resumen de Texto y Marcas de Tiempo
Client-->>User: Mostrar Insights&lt;/div>
&lt;ul>
&lt;li>&lt;strong>OpenAI (GPT-4o)&lt;/strong>: Su precisión en el reconocimiento de imágenes es extremadamente alta y se destaca en la lectura de planos dibujados a mano o gráficos complejos. Además, las interacciones de voz nativas de latencia ultrabaja (cientos de milisegundos) que usan su Realtime API son muy poderosas.&lt;/li>
&lt;li>&lt;strong>Google (Gemini 1.5 Pro)&lt;/strong>: &lt;strong>Supera ampliamente a los demás en el análisis de video.&lt;/strong> Es posible ingresar directamente un archivo de video de 1 hora (fotogramas + audio) y responder preguntas muy específicas como: &amp;ldquo;¿Cuál es el título del documento que sostiene la persona que aparece en el extremo derecho de la pantalla en el minuto 12:45?&amp;rdquo;.&lt;/li>
&lt;li>&lt;strong>Anthropic (Claude 3.5 Sonnet)&lt;/strong>: Su capacidad de reconocimiento de imágenes (Visión) está al mismo nivel que GPT-4o y es excelente. Destaca excepcionalmente en el apoyo al desarrollo frontend, por ejemplo, entregándole una captura de pantalla de la interfaz de usuario con la instrucción &amp;ldquo;Genera el código del componente React de esta pantalla&amp;rdquo;.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="11-seguridad-y-cumplimiento-de-nivel-empresarial">11. Seguridad y cumplimiento de nivel empresarial
&lt;/h2>&lt;p>Al utilizar API de LLM en un entorno de producción, la mayor preocupación para las empresas es si &amp;ldquo;los datos de nuestra compañía se utilizarán para entrenar a la IA&amp;rdquo; y &amp;ldquo;si cumplen con los requisitos de cumplimiento corporativo&amp;rdquo;.&lt;/p>
&lt;p>Las tres empresas han declarado claramente que &lt;strong>los datos (prompts y respuestas) enviados a través de su API no se utilizarán para entrenar sus modelos (Zero Data Retention / No Training on Customer Data)&lt;/strong> (※ esto excluye a la UI del chat web gratuito dirigido a consumidores).&lt;/p>
&lt;p>Para casos donde se requieran niveles de seguridad aún más altos:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>OpenAI&lt;/strong>: A través del servicio de Azure OpenAI, se puede aprovechar la seguridad de nivel empresarial y los SLA de Microsoft, y realizar conexiones de red cerradas mediante Azure Private Link.&lt;/li>
&lt;li>&lt;strong>Google&lt;/strong>: Al usar Google Cloud Vertex AI, es posible realizar un aislamiento de red estricto utilizando VPC Service Controls, y proteger los datos mediante CMEK (Claves de Cifrado Gestionadas por el Cliente).&lt;/li>
&lt;li>&lt;strong>Anthropic&lt;/strong>: Al utilizarse a través de AWS Bedrock o Google Cloud Vertex AI, se puede aprovechar la robusta infraestructura de seguridad subyacente de estos proveedores de la nube.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="12-conclusión-guía-de-elección-definitiva-según-el-caso-de-uso">12. Conclusión: Guía de elección definitiva según el caso de uso
&lt;/h2>&lt;p>Hemos comparado estas opciones desde varios ángulos, pero, en última instancia, la conclusión a &amp;ldquo;¿cuál debería elegir?&amp;rdquo; variará según el caso de uso.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Desarrollo de software complejo, generación de código, razonamiento avanzado&lt;/strong>:
&lt;strong>👑 Ganador: Claude 3.5 Sonnet (Anthropic)&lt;/strong>
Actualmente ofrece el mejor rendimiento en la comprensión del contexto del código, refactorización y creación de textos naturales y similares a los humanos. Su API es muy fácil de usar y su rentabilidad mediante el caché de prompts es excelente.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Análisis de documentos ultralargos, procesamiento en lote de video/audio&lt;/strong>:
&lt;strong>👑 Ganador: Gemini 1.5 Pro (Google)&lt;/strong>
Su ventana de contexto de 2 millones de tokens es un arma inigualable. Para tareas en las que sea necesario tener una visión completa de los datos, como analizar un manual en PDF de cientos de páginas o resumir la grabación de una reunión prolongada, nada se le iguala.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Versatilidad, velocidad de ejecución, salida estructurada estable (JSON)&lt;/strong>:
&lt;strong>👑 Ganador: GPT-4o / GPT-4o-mini (OpenAI)&lt;/strong>
Maneja cualquier tarea sin problemas y ofrece el mayor soporte para herramientas de terceros. El ecosistema de OpenAI es indispensable si requiere de un análisis JSON confiable usando Structured Outputs o de un razonamiento lógico ultra avanzado a través de los modelos o1.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h3 id="recomendación-de-enrutamiento-multimodelo-multi-model-routing">Recomendación de enrutamiento multimodelo (Multi-model Routing)
&lt;/h3>&lt;p>En lugar de depender de una única API (fijación con un proveedor / vendor lock-in), la tendencia a futuro es usar una arquitectura de &lt;strong>&amp;ldquo;Enrutamiento LLM&amp;rdquo; (LLM Routing)&lt;/strong> que cambie dinámicamente el modelo según la dificultad e importancia de la tarea.
Por ejemplo, se puede lograr el equilibrio óptimo entre costo y rendimiento usando los modelos económicos y rápidos &lt;code>GPT-4o-mini&lt;/code> o &lt;code>Gemini 1.5 Flash&lt;/code> para respuestas simples de usuarios, y delegando en &lt;code>Claude 3.5 Sonnet&lt;/code> como respaldo solo cuando se determine que es necesario un procesamiento complejo.&lt;/p>
&lt;p>La evolución de la IA no se detiene. Comprenda profundamente las fortalezas, debilidades y las características de la arquitectura de cada API para construir aplicaciones de IA flexibles y escalables.&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>