<?xml version="1.0" encoding="UTF-8"?>
<rss  xmlns:atom="http://www.w3.org/2005/Atom" 
      xmlns:media="http://search.yahoo.com/mrss/" 
      xmlns:content="http://purl.org/rss/1.0/modules/content/" 
      xmlns:dc="http://purl.org/dc/elements/1.1/" 
      version="2.0">
<channel>
<title>Lucca Frachelle</title>
<link>https://luccafrachelle.github.io/porfolio_academia/blog.html</link>
<atom:link href="https://luccafrachelle.github.io/porfolio_academia/blog.xml" rel="self" type="application/rss+xml"/>
<description>Estadística, matemática y aprendizaje automático. Investigación, docencia, blog y proyectos de Lucca Frachelle.</description>
<image>
<url>https://luccafrachelle.github.io/porfolio_academia/assets/profile.jpg</url>
<title>Lucca Frachelle</title>
<link>https://luccafrachelle.github.io/porfolio_academia/blog.html</link>
</image>
<generator>quarto-1.10.18</generator>
<lastBuildDate>Fri, 11 Sep 2026 00:00:00 GMT</lastBuildDate>
<item>
  <title>Intro a RLHF</title>
  <dc:creator>Lucca Frachelle</dc:creator>
  <link>https://luccafrachelle.github.io/porfolio_academia/posts/rlhf-instructgpt.html</link>
  <description><![CDATA[ 





<div class="project-downloads">
<p><a href="../assets/pdfs/rlhf-instructgpt.pdf" class="text-link">Leer en PDF</a></p>
</div>
<p>Referencia principal: <a href="https://arxiv.org/abs/2203.02155">Ouyang et al.&nbsp;(2022)</a>. Lectura complementaria: <a href="https://rlhfbook.com/course">RLHF Book</a> (Nathan Lambert).</p>
<section id="de-language-model-a-policy-de-rl" class="level2" data-number="1">
<h2 data-number="1" class="anchored" data-anchor-id="de-language-model-a-policy-de-rl"><span class="header-section-number">1</span> De language model a policy de RL</h2>
<p>Un <em>large language model</em> no almacena respuestas prefabricadas. Parametriza una distribución de probabilidad sobre secuencias de tokens: dado un prompt <img src="https://latex.codecogs.com/png.latex?x">, asigna probabilidades a posibles respuestas <img src="https://latex.codecogs.com/png.latex?y"> mediante <img src="https://latex.codecogs.com/png.latex?%5Cpi_%5Ctheta(y%20%5Cmid%20x)">, con <img src="https://latex.codecogs.com/png.latex?%5Ctheta"> en el orden de miles de millones de parámetros distribuidos en un Transformer. Durante el <strong>pre-entrenamiento</strong>, esos pesos se ajustan para maximizar la verosimilitud de texto observado en corpus masivos. El resultado es un modelo con amplio conocimiento lingüístico y factual en sentido estadístico, pero cuyo objetivo original — predecir el siguiente token del internet — no coincide con el de un asistente que siga instrucciones de forma útil y segura.</p>
<p>Las respuestas se generan de forma <strong>autoregresiva</strong>. En post-entrenamiento conviene escribir prompt <img src="https://latex.codecogs.com/png.latex?x"> y respuesta <img src="https://latex.codecogs.com/png.latex?y%20=%20(y_1,%20%5Cldots,%20y_T)">, con</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cpi_%5Ctheta(y%20%5Cmid%20x)%20=%20%5Cprod_%7Bt=1%7D%5E%7BT%7D%20%5Cpi_%5Ctheta(y_t%20%5Cmid%20x,%20y_%7B%3Ct%7D)."></p>
<p>En <strong>pre-entrenamiento</strong>, en cambio, no hay split prompt/respuesta: sobre una secuencia cruda del corpus <img src="https://latex.codecogs.com/png.latex?w%20=%20(w_1,%20%5Cldots,%20w_N)"> se minimiza</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cmathcal%7BL%7D_%7B%5Cmathrm%7BLM%7D%7D(%5Ctheta)%20=%20-%20%5Csum_%7Bt=1%7D%5E%7BN%7D%20%5Clog%20%5Cpi_%5Ctheta(w_t%20%5Cmid%20w_%7B%3Ct%7D),"></p>
<p>equivalente a maximizar la verosimilitud de cada token condicionado a todo el contexto previo — que puede cruzar frases y documentos. En cada paso, el Transformer produce logits sobre el vocabulario y aplica un softmax; la arquitectura concreta (atención, capas, escala) importa menos, para nuestros propósitos, que el hecho de que <img src="https://latex.codecogs.com/png.latex?%5Cpi_%5Ctheta"> es una policy paramétrica diferenciable cuyas acciones son tokens discretos.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="img/rlhf/pretraining_next_token.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1" title="Predicción del siguiente token en pre-entrenamiento."><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/rlhf/pretraining_next_token.png" class="img-fluid figure-img" style="width:88.0%" alt="Esquema next-token prediction"></a></p>
<figcaption>Predicción del siguiente token en pre-entrenamiento.</figcaption>
</figure>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="img/rlhf/transformer.png" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="Arquitectura Transformer."><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/rlhf/transformer.png" class="img-fluid figure-img" style="width:65.0%" alt="Diagrama Transformer"></a></p>
<figcaption>Arquitectura Transformer.</figcaption>
</figure>
</div>
<p>Desde la perspectiva del TAR, conviene leer el LLM como una policy sobre un espacio de acciones enorme. El estado en el paso <img src="https://latex.codecogs.com/png.latex?t"> es el contexto <img src="https://latex.codecogs.com/png.latex?(x,%20y_%7B%3Ct%7D)">; la acción es el token <img src="https://latex.codecogs.com/png.latex?y_t">; la trayectoria es la respuesta completa <img src="https://latex.codecogs.com/png.latex?y">; un episodio consiste en un prompt y la secuencia generada hasta el fin. A diferencia de un MDP clásico, no hay una dinámica de entorno <img src="https://latex.codecogs.com/png.latex?P(s'%20%5Cmid%20s,%20a)"> independiente de la policy: el contexto solo crece porque el propio modelo va appendeando tokens. Tampoco hay, en la formulación de InstructGPT, un reward denso en cada paso intermedio: el Reward Model evalúa la respuesta terminada. La situación es la de un <strong>bandit contextual</strong>: se observa un contexto <img src="https://latex.codecogs.com/png.latex?x">, se elige una acción de alta dimensión <img src="https://latex.codecogs.com/png.latex?y">, y se recibe un escalar <img src="https://latex.codecogs.com/png.latex?r_%5Cphi(x,%20y)">.</p>
<p>El <strong>post-entrenamiento</strong> no reemplaza el pre-entrenamiento sino que parte de él. Donde el pre-training optimizaba <img src="https://latex.codecogs.com/png.latex?%5Clog%20%5Cpi_%5Ctheta(y%20%5Cmid%20x)"> sobre texto crudo, InstructGPT optimiza una combinación del score de un Reward Model y una penalización que mide cuánto se aleja la policy del checkpoint de supervised fine-tuning. El pre-entrenamiento aporta la capacidad lingüística base; el post-entrenamiento reorienta el comportamiento hacia el de un asistente.</p>
</section>
<section id="el-pipeline-de-instructgpt-y-sus-datos" class="level2" data-number="2">
<h2 data-number="2" class="anchored" data-anchor-id="el-pipeline-de-instructgpt-y-sus-datos"><span class="header-section-number">2</span> El pipeline de InstructGPT y sus datos</h2>
<p>Ouyang et al.&nbsp;entrenan variantes de GPT-3 (1.3B, 6B y 175B parámetros) en tres etapas encadenadas: supervised fine-tuning (SFT), entrenamiento de un Reward Model (RM) sobre preferencias humanas, y fine-tuning por RL con PPO sobre ese RM. Salvo indicación contraria, cuando el paper habla de <em>InstructGPT</em> se refiere a la variante <strong>PPO-ptx</strong>, que mezcla gradientes de pretraining con los de PPO; volveremos sobre eso en la sección correspondiente.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="img/rlhf/fig2_pipeline.png" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title="Pipeline InstructGPT (Fig. 2 del paper)."><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/rlhf/fig2_pipeline.png" class="img-fluid figure-img" style="width:82.0%" alt="SFT, RM, PPO"></a></p>
<figcaption>Pipeline InstructGPT (Fig. 2 del paper).</figcaption>
</figure>
</div>
<p>Los datos provienen de dos fuentes. Por un lado, unos cuarenta anotadores contratados escribieron instrucciones de distintos tipos — prompts simples, prompts con ejemplos few-shot, y casos de uso inspirados en la waitlist de la API de OpenAI. Por otro, se recolectaron prompts reales enviados por clientes a versiones tempranas de la API. La mayoría de las tareas son generativas (preguntas abiertas, diálogo, resumen, etc.); más del 96% del material está en inglés.</p>
<p>A partir de esos prompts se construyen tres conjuntos de entrenamiento, cada uno con un rol distinto en el pipeline. El dataset <strong>SFT</strong> contiene unas 13 mil demostraciones <img src="https://latex.codecogs.com/png.latex?(x,%20y%5E%5Cstar)">: el anotador escribe la respuesta que considera ideal. El dataset <strong>RM</strong> reúne unos 33 mil prompts para los cuales se generaron entre cuatro y nueve respuestas del modelo y los humanos produjeron un ranking completo. El dataset <strong>PPO</strong> aporta unos 31 mil prompts de la API sin etiquetas adicionales: solo sirven como contextos para rollouts durante el RL. Ninguno de estos conjuntos es público.</p>
<p>La recolección de preferencias merece detenerse un instante. Para cada prompt del dataset RM, el modelo genera <img src="https://latex.codecogs.com/png.latex?K"> completions distintas (con <img src="https://latex.codecogs.com/png.latex?K%20%5Cin%20%5B4,%209%5D">) y el anotador las ordena de mejor a peor, obteniendo <img src="https://latex.codecogs.com/png.latex?y_1%20%5Csucc%20y_2%20%5Csucc%20%5Ccdots%20%5Csucc%20y_K">. Ese ranking único induce hasta <img src="https://latex.codecogs.com/png.latex?%5Cbinom%7BK%7D%7B2%7D"> comparaciones pareadas consistentes — si <img src="https://latex.codecogs.com/png.latex?y_i"> está por encima de <img src="https://latex.codecogs.com/png.latex?y_j"> en el ranking, se registra <img src="https://latex.codecogs.com/png.latex?y_i%20%5Csucc%20y_j">. En la práctica hay muchos más pares de comparación que prompts, lo cual enriquece el entrenamiento del RM aun con un volumen moderado de anotación humana.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="img/rlhf/fig12_ranking.png" class="lightbox" data-gallery="quarto-lightbox-gallery-4" title="Interfaz de ranking (Fig. 12)."><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/rlhf/fig12_ranking.png" class="img-fluid figure-img" style="width:72.0%" alt="Ranking de respuestas"></a></p>
<figcaption>Interfaz de ranking (Fig. 12).</figcaption>
</figure>
</div>
</section>
<section id="supervised-fine-tuning" class="level2" data-number="3">
<h2 data-number="3" class="anchored" data-anchor-id="supervised-fine-tuning"><span class="header-section-number">3</span> Supervised fine-tuning</h2>
<p>La primera etapa es la más directa. Sobre las demostraciones humanas <img src="https://latex.codecogs.com/png.latex?%5C%7B(x,%20y%5E%5Cstar)%5C%7D"> se minimiza la negative log-likelihood de la respuesta demostrada, token a token:</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cmathcal%7BL%7D_%7B%5Cmathrm%7BSFT%7D%7D(%5Ctheta)%20=%20-%20%5Csum_%7B(x,%5C,%20y%5E%5Cstar)%7D%20%5Csum_%7Bt=1%7D%5E%7B%7Cy%5E%5Cstar%7C%7D%20%5Clog%20%5Cpi_%5Ctheta%5C!%5Cleft(y%5E%5Cstar_t%20%5Cmid%20x,%20y%5E%5Cstar_%7B%3Ct%7D%5Cright)."></p>
<p>Formalmente, es el mismo objetivo del language modeling aplicado a pares instrucción–respuesta. El gradiente incrementa la probabilidad de la trayectoria <img src="https://latex.codecogs.com/png.latex?y%5E%5Cstar"> bajo <img src="https://latex.codecogs.com/png.latex?%5Cpi_%5Ctheta">. No hay señal relativa entre dos respuestas alternativas: el modelo aprende a imitar un ejemplo dado, no a preferir una respuesta sobre otra cuando ambas serían plausibles.</p>
<p>Eso tiene consecuencias claras. El SFT enseña al base model a adoptar el <em>formato</em> de asistente — responder a instrucciones en lugar de continuar texto arbitrariamente — y condiciona la distribución hacia un subespacio de respuestas razonables. Pero no resuelve el problema de la alineación fina: dos completions distintas pueden ser gramaticalmente correctas y una ser claramente superior para el usuario. Esa distinción requiere preferencias y, más adelante, RL.</p>
<p>En el paper hay dos corridas de SFT con propósitos distintos. Para las evaluaciones de baseline, entrenan durante 16 épocas con dropout 0.2 y learning rate cosine; curiosamente, la validation loss empeora tras la primera época, pero el checkpoint se selecciona por el score del RM en validación, no por la loss de SFT. Para inicializar el RL, en cambio, usan una corrida más corta de dos épocas con un 10% de datos de pretraining mezclados. Ese checkpoint es <img src="https://latex.codecogs.com/png.latex?%5Cpi%5E%7B%5Cmathrm%7BSFT%7D%7D">: sirve como punto de partida de la policy <img src="https://latex.codecogs.com/png.latex?%5Cpi_%5Cphi%5E%7B%5Cmathrm%7BRL%7D%7D"> y, crucialmente, como ancla en la penalización KL de la etapa de PPO.</p>
</section>
<section id="el-reward-model-y-el-modelo-bradley-terry" class="level2" data-number="4">
<h2 data-number="4" class="anchored" data-anchor-id="el-reward-model-y-el-modelo-bradley-terry"><span class="header-section-number">4</span> El Reward Model y el modelo Bradley-Terry</h2>
<p>La segunda etapa traduce juicios humanos en una función de reward diferenciable <img src="https://latex.codecogs.com/png.latex?r_%5Cphi(x,%20y)%20%5Cin%20%5Cmathbb%7BR%7D">. El paper sigue la receta de Stiennon et al.&nbsp;(2020), aplicada antes al resumen de textos, y la extiende a instrucciones generales. La pieza conceptual clave es el <strong>modelo Bradley-Terry</strong> (Bradley y Terry, 1952).</p>
<p>La idea parte de postular que, fijado un prompt <img src="https://latex.codecogs.com/png.latex?x">, cada respuesta <img src="https://latex.codecogs.com/png.latex?y"> tiene una utilidad latente <img src="https://latex.codecogs.com/png.latex?u(x,%20y)"> que resume cuán deseable es para un anotador representativo. Esa utilidad no se observa; lo que se observa son comparaciones. Bradley y Terry modelan la probabilidad de que un anotador prefiera <img src="https://latex.codecogs.com/png.latex?y_a"> sobre <img src="https://latex.codecogs.com/png.latex?y_b"> como una función que depende únicamente de la diferencia de utilidades:</p>
<p><img src="https://latex.codecogs.com/png.latex?P(y_a%20%5Csucc%20y_b%20%5Cmid%20x)%20=%20%5Cfrac%7Be%5E%7Bu(x,y_a)%7D%7D%7Be%5E%7Bu(x,y_a)%7D%20+%20e%5E%7Bu(x,y_b)%7D%7D%20=%20%5Csigma%5C!%5Cbig(u(x,y_a)%20-%20u(x,y_b)%5Cbig),"></p>
<p>donde <img src="https://latex.codecogs.com/png.latex?%5Csigma(z)%20=%201/(1+e%5E%7B-z%7D)"> es la función logística. Si <img src="https://latex.codecogs.com/png.latex?u(x,%20y_a)"> supera ampliamente a <img src="https://latex.codecogs.com/png.latex?u(x,%20y_b)">, la preferencia por <img src="https://latex.codecogs.com/png.latex?y_a"> es casi segura; si las utilidades son similares, la elección se aproxima al azar. La forma logística no es arbitraria: emerge en modelos de elección discreta cuando las utilidades están perturbadas por ruido de tipo extreme value (la derivación clásica del logit).</p>
<p>InstructGPT parametriza <img src="https://latex.codecogs.com/png.latex?u(x,%20y)%20%5Capprox%20r_%5Cphi(x,%20y)"> con una red neuronal. Concretamente, toman un GPT-3 de 6B parámetros — un solo RM para todas las policies, porque la versión de 175B resultó inestable como función de valor durante el RL —, eliminan la capa de unembedding y la reemplazan por una proyección escalar. El modelo recibe el prompt y la respuesta concatenados y devuelve un número. Para una comparación observada <img src="https://latex.codecogs.com/png.latex?(y_w,%20y_l)">, con <img src="https://latex.codecogs.com/png.latex?y_w"> el <em>winner</em> y <img src="https://latex.codecogs.com/png.latex?y_l"> el <em>loser</em>, la probabilidad modelada de la preferencia registrada es</p>
<p><img src="https://latex.codecogs.com/png.latex?P(y_w%20%5Csucc%20y_l%20%5Cmid%20x)%20=%20%5Csigma%5C!%5Cbig(r_%5Cphi(x,%20y_w)%20-%20r_%5Cphi(x,%20y_l)%5Cbig)."></p>
<p>Una propiedad importante del modelo es la <strong>invarianza a traslación</strong>: si se suma una constante a todos los scores, las probabilidades no cambian, porque solo importan las diferencias. Por eso, antes de pasar al RL, el paper normaliza el RM de modo que las demostraciones SFT tengan score medio cero. Eso fija la escala que de otro modo sería indeterminada.</p>
<p>Los rankings de <img src="https://latex.codecogs.com/png.latex?K"> respuestas se convierten en datos de entrenamiento agrupando, para cada prompt, todas las comparaciones <img src="https://latex.codecogs.com/png.latex?y_i%20%5Csucc%20y_j"> con <img src="https://latex.codecogs.com/png.latex?i%20%3C%20j">. El paper advierte que mezclar al azor pares provenientes del mismo prompt en distintos batches provoca overfit severo en una sola época, porque las comparaciones dentro de un ranking están fuertemente correlacionadas. Por ello entrenan con un elemento de batch por prompt que contiene el conjunto completo de comparaciones de ese prompt — hasta <img src="https://latex.codecogs.com/png.latex?K%5E2%20%5Cleq%202304"> pares cuando el batch incluye 64 prompts.</p>
<p>La estimación es por máxima verosimilitud. Sea <img src="https://latex.codecogs.com/png.latex?%5Cmathcal%7BD%7D"> la distribución sobre tuplas <img src="https://latex.codecogs.com/png.latex?(x,%20y_w,%20y_l)"> inducida por los rankings. La log-verosimilitud del modelo Bradley-Terry es</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cell(%5Cphi)%20=%20%5Cmathbb%7BE%7D_%7B(x,y_w,y_l)%5Csim%5Cmathcal%7BD%7D%7D%5CBig%5B%5Clog%20%5Csigma%5C!%5Cbig(r_%5Cphi(x,y_w)%20-%20r_%5Cphi(x,y_l)%5Cbig)%5CBig%5D,"></p>
<p>y el entrenamiento minimiza su opuesto, que el paper escribe como la ecuación (1):</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cmathcal%7BL%7D_%7B%5Cmathrm%7BRM%7D%7D(%5Cphi)%20=%20-%5Cfrac%7B1%7D%7BK%5E2%7D%5C,%5Cmathbb%7BE%7D_%7B(x,%5C,%20y_w,%5C,%20y_l)%5C,%5Csim%5C,%5Cmathcal%7BD%7D%7D%5CBig%5B%5Clog%20%5Csigma%5C!%5Cbig(r_%5Cphi(x,%20y_w)%20-%20r_%5Cphi(x,%20y_l)%5Cbig)%5CBig%5D.%20%5Ctag%7B1%7D"></p>
<p>El factor <img src="https://latex.codecogs.com/png.latex?1/K%5E2"> promedia explícitamente sobre las comparaciones asociadas a cada tarea de anotación. Esta pérdida no es un clasificador binario genérico pegado sobre scores: es la verosimilitud de un modelo probabilístico de preferencias, y maximizarla ajusta <img src="https://latex.codecogs.com/png.latex?r_%5Cphi"> para que el orden inducido por los scores coincida, en promedio, con el orden humano.</p>
<p>La intuición del gradiente ayuda a leer la ecuación. Para un par <img src="https://latex.codecogs.com/png.latex?(y_w,%20y_l)">, sea <img src="https://latex.codecogs.com/png.latex?d%20=%20r_%5Cphi(x,%20y_w)%20-%20r_%5Cphi(x,%20y_l)">. La derivada de <img src="https://latex.codecogs.com/png.latex?-%5Clog%20%5Csigma(d)"> respecto de <img src="https://latex.codecogs.com/png.latex?d"> es <img src="https://latex.codecogs.com/png.latex?1%20-%20%5Csigma(d)">. Si el RM clasifica mal el par (<img src="https://latex.codecogs.com/png.latex?d%20%5Cll%200">), el gradiente es grande y empuja a separar los scores; si el par ya está bien clasificado (<img src="https://latex.codecogs.com/png.latex?d%20%5Cgg%200">), el gradiente es casi nulo y no hace falta seguir amplificando la diferencia. El RM se entrena una sola época sobre unos 33 mil prompts; más épocas degradan la validación.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="img/rlhf/pref_rm_training.png" class="lightbox" data-gallery="quarto-lightbox-gallery-5" title="Entrenamiento del reward model."><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/rlhf/pref_rm_training.png" class="img-fluid figure-img" style="width:72.0%" alt="Preferencias y RM"></a></p>
<figcaption>Entrenamiento del reward model.</figcaption>
</figure>
</div>
<p>Hay que insistir en que <img src="https://latex.codecogs.com/png.latex?r_%5Cphi"> es un <strong>proxy</strong>. Aprende a reproducir preferencias humanas en la distribución de entrenamiento, pero puede ser explotado por una policy de RL que optimice demasiado agresivamente su score. Respuestas verbosas, aduladoras o con patrones que el RM premia sin aportar utilidad real son un riesgo conocido (<em>reward hacking</em>). Eso motiva la tercera etapa: no basta con maximizar <img src="https://latex.codecogs.com/png.latex?r_%5Cphi">; hace falta regularizar.</p>
</section>
<section id="ppo-la-penalización-kl-y-ppo-ptx" class="level2" data-number="5">
<h2 data-number="5" class="anchored" data-anchor-id="ppo-la-penalización-kl-y-ppo-ptx"><span class="header-section-number">5</span> PPO, la penalización KL y PPO-ptx</h2>
<p>Con <img src="https://latex.codecogs.com/png.latex?r_%5Cphi"> y <img src="https://latex.codecogs.com/png.latex?%5Cpi%5E%7B%5Cmathrm%7BSFT%7D%7D"> congelados, la tercera etapa fine-tunea una policy <img src="https://latex.codecogs.com/png.latex?%5Cpi_%5Cphi%5E%7B%5Cmathrm%7BRL%7D%7D"> mediante PPO. Cada episodio del bandit consiste en muestrear un prompt <img src="https://latex.codecogs.com/png.latex?x"> del dataset PPO, generar una respuesta <img src="https://latex.codecogs.com/png.latex?y%20%5Csim%20%5Cpi_%5Cphi%5E%7B%5Cmathrm%7BRL%7D%7D(%5Ccdot%20%5Cmid%20x)">, evaluar el reward y terminar. El critic <img src="https://latex.codecogs.com/png.latex?V"> se inicializa desde el RM (misma arquitectura de 6B) y las ventajas se estiman con GAE, con clipping <img src="https://latex.codecogs.com/png.latex?%5Cvarepsilon%20=%200.2"> como en el PPO estándar del curso.</p>
<p>La variante <strong>PPO</strong> sin mezcla de pretraining (<img src="https://latex.codecogs.com/png.latex?%5Cgamma%20=%200">) maximiza</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cmathrm%7Bobjective%7D(%5Cphi)%20=%20%5Cmathbb%7BE%7D_%7B(x,y)%5Csim%20D_%7B%5Cpi_%5Cphi%5E%7B%5Cmathrm%7BRL%7D%7D%7D%7D%5CBig%5Br_%5Cphi(x,y)%20-%20%5Cbeta%20%5Clog%20%5Cfrac%7B%5Cpi_%5Cphi%5E%7B%5Cmathrm%7BRL%7D%7D(y%20%5Cmid%20x)%7D%7B%5Cpi%5E%7B%5Cmathrm%7BSFT%7D%7D(y%20%5Cmid%20x)%7D%5CBig%5D.%20%5Ctag%7B2a%7D"></p>
<p>El primer término empuja hacia respuestas con alto score del RM, es decir, hacia lo que los humanos habrían preferido en promedio. El segundo penaliza desviarse de <img src="https://latex.codecogs.com/png.latex?%5Cpi%5E%7B%5Cmathrm%7BSFT%7D%7D">. Como la probabilidad de una secuencia factoriza token a token, el log-ratio de la ecuación se descompone en una suma de contribuciones por paso; en la implementación del paper, esa penalización se aplica de forma <strong>token a token</strong> durante el rollout, lo que proporciona señal densa al critic aun cuando <img src="https://latex.codecogs.com/png.latex?r_%5Cphi"> solo evalúa la respuesta completa. El coeficiente reportado es <img src="https://latex.codecogs.com/png.latex?%5Cbeta%20=%200.02">.</p>
<p>Maximizar únicamente <img src="https://latex.codecogs.com/png.latex?r_%5Cphi(x,%20y)"> sería peligroso. La policy podría colapsar hacia completions que maximizan el proxy humano pero tienen probabilidad casi nula bajo el modelo pre-entrenado — lenguaje incoherente, fuera de distribución o explícitamente diseñado para engañar al RM. La penalización KL mantiene <img src="https://latex.codecogs.com/png.latex?%5Cpi_%5Cphi%5E%7B%5Cmathrm%7BRL%7D%7D"> cerca del manifold de lenguaje fluido aprendido en SFT. No es un detalle menor: sin ella, el RL sobre un RM imperfecto tiende a producir <em>reward hacking</em>.</p>
<p>Sin embargo, la KL por sí sola no resuelve otro problema. PPO puro mejora las preferencias humanas en la distribución de prompts de la API, pero <strong>empeora</strong> en benchmarks NLP estándar — SQuADv2, DROP, HellaSwag, traducción, entre otros. Ouyang et al.&nbsp;llaman a esta degradación <em>alignment tax</em>: al optimizar el proxy de alineación, la policy pierde parte de la capacidad lingüística general heredada del pretraining. Subir <img src="https://latex.codecogs.com/png.latex?%5Cbeta"> atenúa la regresión, pero también limita cuánto puede mejorar el score del RM; no hay un knob que resuelva ambos extremos a la vez.</p>
<p>La solución que adopta InstructGPT es <strong>PPO-ptx</strong> (<em>pretraining mix</em>). Además de los gradientes de PPO, mezclan en cada minibatch gradientes de language modeling sobre <img src="https://latex.codecogs.com/png.latex?D_%7B%5Cmathrm%7Bpretrain%7D%7D">, la distribución de texto crudo con la que se pre-entrenó GPT-3. El objetivo completo es</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cmathrm%7Bobjective%7D(%5Cphi)%20=%20%5Cmathbb%7BE%7D_%7B(x,y)%5Csim%20D_%7B%5Cpi_%5Cphi%5E%7B%5Cmathrm%7BRL%7D%7D%7D%7D%5CBig%5Br_%5Cphi(x,y)%20-%20%5Cbeta%20%5Clog%20%5Cfrac%7B%5Cpi_%5Cphi%5E%7B%5Cmathrm%7BRL%7D%7D(y%20%5Cmid%20x)%7D%7B%5Cpi%5E%7B%5Cmathrm%7BSFT%7D%7D(y%20%5Cmid%20x)%7D%5CBig%5D%20+%20%5Cgamma%5C,%5Cmathbb%7BE%7D_%7Bx%20%5Csim%20D_%7B%5Cmathrm%7Bpretrain%7D%7D%7D%5Cbig%5B%5Clog%20%5Cpi_%5Cphi%5E%7B%5Cmathrm%7BRL%7D%7D(x)%5Cbig%5D,%20%5Ctag%7B2b%7D"></p>
<p>con <img src="https://latex.codecogs.com/png.latex?%5Cgamma%20=%2027.8"> y aproximadamente ocho veces más ejemplos de pretraining que episodios de RL por minibatch. El término <img src="https://latex.codecogs.com/png.latex?%5Cgamma%5C,%5Cmathbb%7BE%7D%5B%5Clog%20%5Cpi(x)%5D"> obliga al modelo a seguir siendo un buen language model de texto general mientras el RL lo alinea como asistente. En la práctica, PPO y PPO-ptx obtienen preferencias humanas similares en la API, pero solo PPO-ptx mitiga la regresión en tareas de lenguaje más amplias. Por eso el paper reserva el nombre <em>InstructGPT</em> para la variante con pretraining mix.</p>
<p>El entrenamiento RL corre 256 mil episodios sobre unos 31 mil prompts únicos, con batch de 512 y minibatches de 64. El RM y el critic permanecen fijos en 6B parámetros aunque la policy alcance 175B. PPO implementa, en el fondo, una estimación robusta del policy gradient</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cnabla_%5Cphi%5C,%5Cmathrm%7Bobjective%7D%20%5Cpropto%20%5Cmathbb%7BE%7D%5CBigg%5B%20%5Csum_%7Bt=1%7D%5E%7BT%7D%20%5Cnabla_%5Cphi%20%5Clog%20%5Cpi_%5Cphi%5E%7B%5Cmathrm%7BRL%7D%7D(y_t%20%5Cmid%20x,%20y_%7B%3Ct%7D)%20%5Ccdot%20%5Chat%7BA%7D_t%20%5CBigg%5D,"></p>
<p>con ventajas <img src="https://latex.codecogs.com/png.latex?%5Chat%7BA%7D_t"> de GAE y ratio clipped <img src="https://latex.codecogs.com/png.latex?%5Crho_t%20=%20%5Cpi_%5Cphi%20/%20%5Cpi_%7B%5Cphi_%7B%5Cmathrm%7Bold%7D%7D%7D">. La correspondencia con el TAR es directa: la policy del LLM es <img src="https://latex.codecogs.com/png.latex?%5Cpi(a%20%5Cmid%20s)"> token a token; el reward del entorno es el score del RM (más la penalización KL por token); el critic sustituye al valor <img src="https://latex.codecogs.com/png.latex?V(s)"> del MDP, inicializado desde el RM.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="img/rlhf/ppo-viz-4x.png" class="lightbox" data-gallery="quarto-lightbox-gallery-6" title="Loop PPO."><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/rlhf/ppo-viz-4x.png" class="img-fluid figure-img" style="width:80.0%" alt="Visualización PPO"></a></p>
<figcaption>Loop PPO.</figcaption>
</figure>
</div>
</section>
<section id="resultados" class="level2" data-number="6">
<h2 data-number="6" class="anchored" data-anchor-id="resultados"><span class="header-section-number">6</span> Resultados</h2>
<p>Las evaluaciones humanas sobre la distribución de prompts de la API muestran una mejora progresiva en cada etapa del pipeline: GPT-3 base, GPT-3 con prompt cuidadoso, SFT, PPO y PPO-ptx. De forma llamativa, InstructGPT de 1.3B parámetros es preferido sobre GPT-3 de 175B en esa distribución; en comparaciones directas, la versión 175B de InstructGPT es elegida frente a GPT-3 175B en torno al 85% de las veces. Eso no implica que el modelo pequeño “sepa más”, sino que el post-entrenamiento optimizó directamente lo que los evaluadores valoran en un asistente.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="img/rlhf/fig1_results.png" class="lightbox" data-gallery="quarto-lightbox-gallery-7" title="Evaluaciones humanas (Fig. 1)."><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/rlhf/fig1_results.png" class="img-fluid figure-img" style="width:88.0%" alt="Win rate vs 175B SFT"></a></p>
<figcaption>Evaluaciones humanas (Fig. 1).</figcaption>
</figure>
</div>
<p>Más allá de la preferencia global, los modelos PPO superan a GPT-3 en metadata recogida por los anotadores: siguen mejor las instrucciones explícitas, alucinan con menos frecuencia en tareas de dominio cerrado y producen respuestas más apropiadas en el rol de asistente.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="img/rlhf/fig4_metadata.png" class="lightbox" data-gallery="quarto-lightbox-gallery-8" title="Metadata (Fig. 4)."><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/rlhf/fig4_metadata.png" class="img-fluid figure-img" style="width:88.0%" alt="Metadata API"></a></p>
<figcaption>Metadata (Fig. 4).</figcaption>
</figure>
</div>
</section>
<section id="cierre" class="level2" data-number="7">
<h2 data-number="7" class="anchored" data-anchor-id="cierre"><span class="header-section-number">7</span> Cierre</h2>
<p>InstructGPT condensa, en un pipeline de tres etapas, la lógica del RLHF aplicado a LLMs. El SFT enseña por imitación y fija la referencia <img src="https://latex.codecogs.com/png.latex?%5Cpi%5E%7B%5Cmathrm%7BSFT%7D%7D">. El Reward Model estima, vía Bradley-Terry, una función de utilidad cuyas diferencias reproducen las preferencias humanas — ecuación (1). PPO maximiza esa utilidad mientras penaliza la desviación de la referencia — ecuación (2a). PPO-ptx añade un término de language modeling sobre texto de pretraining para no sacrificar la capacidad lingüística general al pagar el <em>alignment tax</em> — ecuación (2b).</p>
<p>Desde el TAR, la lección operativa es que el LLM ya es la policy; el RM reemplaza la función de reward del entorno; PPO es el mismo algoritmo de trust region visto en el curso, adaptado a secuencias largas y rewards escasos. Lo específico de InstructGPT no es tanto el algoritmo base como la combinación de datos humanos moderados, un proxy de preferencias bien calibrado, regularización KL hacia el SFT, y la mezcla de pretraining que convierte un chatbot alineado en un asistente que sigue siendo, en buena medida, un language model general.</p>
</section>
<section id="referencias" class="level2" data-number="8">
<h2 data-number="8" class="anchored" data-anchor-id="referencias"><span class="header-section-number">8</span> Referencias</h2>
<ul>
<li>Ouyang et al.&nbsp;(2022). <a href="https://arxiv.org/abs/2203.02155">Training language models to follow instructions with human feedback</a></li>
<li>Stiennon et al.&nbsp;(2020). <a href="https://arxiv.org/abs/2009.01325">Learning to summarize with human feedback</a></li>
<li>Bradley, R. A.; Terry, M. E. (1952). Rank analysis of incomplete block designs.</li>
<li>Lambert, N. <a href="https://rlhfbook.com">RLHF Book</a> · <a href="https://rlhfbook.com/course">Curso</a></li>
</ul>


</section>

<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Volver arriba</a> ]]></description>
  <category>RL</category>
  <category>LLMs</category>
  <guid>https://luccafrachelle.github.io/porfolio_academia/posts/rlhf-instructgpt.html</guid>
  <pubDate>Fri, 11 Sep 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Tokenización con BPE</title>
  <dc:creator>Lucca Frachelle</dc:creator>
  <link>https://luccafrachelle.github.io/porfolio_academia/posts/tokenizacion-bpe.html</link>
  <description><![CDATA[ 





<div class="project-downloads">
<p><a href="../assets/pdfs/tokenizacion-bpe.pdf" class="text-link">Leer en PDF</a></p>
</div>
<section id="sec-por-que" class="level2" data-number="1">
<h2 data-number="1" class="anchored" data-anchor-id="sec-por-que"><span class="header-section-number">1</span> Por qué existe el tokenizador</h2>
<p>Un modelo de lenguaje no procesa texto. La primera capa de un Transformer es una <strong>tabla de embeddings</strong>: una matriz de <code>vocab_size</code> filas por <code>n_embd</code> columnas, donde cada fila es un vector aprendido. Lo que el modelo recibe es una secuencia de <strong>enteros</strong> que indexan esas filas.</p>
<p>Entonces, entre el texto que escribe una persona y el modelo hace falta una pieza que:</p>
<ol type="1">
<li>convierta un <code>str</code> arbitrario en una secuencia de enteros de un <strong>conjunto finito y fijo</strong> (el vocabulario);</li>
<li>pueda hacer el camino inverso sin pérdida, para que el modelo pueda <em>responder</em> en texto;</li>
<li>use <strong>pocos</strong> enteros por texto, porque la ventana de contexto es finita y el costo computacional de la atención crece con el cuadrado de la longitud de la secuencia.</li>
</ol>
<p>Esa pieza es el tokenizador. Es un módulo <strong>completamente separado</strong> del modelo: se entrena aparte, con su propio corpus y su propio algoritmo (nada de descenso por gradiente), y una vez congelado condiciona todo lo que el modelo puede o no puede hacer.</p>
<p>Antes de construir uno, veamos uno real trabajando. <code>tiktoken</code> es la biblioteca de OpenAI con los tokenizadores de sus modelos:</p>
<div id="c82dda60" class="cell" data-execution_count="1">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> tiktoken</span>
<span id="cb1-2"></span>
<span id="cb1-3">enc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tiktoken.get_encoding(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"o200k_base"</span>)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># el tokenizador de GPT-4o</span></span>
<span id="cb1-4"></span>
<span id="cb1-5">frase <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Los tokenizadores son la parte más subestimada de un LLM."</span></span>
<span id="cb1-6">ids <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> enc.encode(frase)</span>
<span id="cb1-7"></span>
<span id="cb1-8"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(frase)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> caracteres  -&gt;  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(ids)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> tokens"</span>)</span>
<span id="cb1-9"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(ids)</span>
<span id="cb1-10"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>([enc.decode([i]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> ids])</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>57 caracteres  -&gt;  15 tokens
[16593, 6602, 170058, 2391, 557, 7441, 3932, 1543, 125938, 1194, 334, 537, 451, 19641, 13]
['Los', ' token', 'izadores', ' son', ' la', ' parte', ' más', ' sub', 'estim', 'ada', ' de', ' un', ' L', 'LM', '.']</code></pre>
</div>
</div>
<p>Fijate en la última línea: los tokens no son palabras ni letras. Son fragmentos, y el espacio viaja <strong>pegado</strong> al inicio de la palabra que sigue. Eso tiene una consecuencia inmediata:</p>
<div id="f4be0a82" class="cell" data-execution_count="2">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"hola"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">" hola"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Hola"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">" Hola"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"HOLA"</span>]:</span>
<span id="cb3-2">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>s<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!r:&gt;8}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  -&gt;  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>enc<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>encode(s)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>  'hola'  -&gt;  [122845]
 ' hola'  -&gt;  [171847]
  'Hola'  -&gt;  [49864]
 ' Hola'  -&gt;  [157464]
  'HOLA'  -&gt;  [39, 87254]</code></pre>
</div>
</div>
<p>Cinco formas de escribir la misma palabra, cinco secuencias de ids distintas. Para el modelo son entradas diferentes, y tiene que aprender por separado que significan casi lo mismo.</p>
<p>Y comparemos idiomas, con la misma oración:</p>
<div id="5114e70e" class="cell" data-execution_count="3">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb5-1">oraciones <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [</span>
<span id="cb5-2">    (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"inglés"</span>,  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The tokenizer is the most underestimated part of a language model."</span>),</span>
<span id="cb5-3">    (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"español"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"El tokenizador es la parte más subestimada de un modelo de lenguaje."</span>),</span>
<span id="cb5-4">]</span>
<span id="cb5-5"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> idioma, texto <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> oraciones:</span>
<span id="cb5-6">    n <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(enc.encode(texto))</span>
<span id="cb5-7">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>idioma<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>n<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;3}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> tokens para </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(texto)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;3}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> caracteres "</span></span>
<span id="cb5-8">          <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"(</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(texto)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span>n<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.2f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> caracteres por token)"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>  inglés:  12 tokens para  66 caracteres (5.50 caracteres por token)
 español:  16 tokens para  68 caracteres (4.25 caracteres por token)</code></pre>
</div>
</div>
<p>El español entra menos texto en la misma cantidad de tokens. No es un accidente: es consecuencia directa del corpus con el que se entrenó el tokenizador, y lo vamos a ver aparecer una y otra vez.</p>
<div class="callout callout-style-default callout-important callout-titled" title="Preguntas que este apunte va a responder">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Importante</span>Preguntas que este apunte va a responder
</div>
</div>
<div class="callout-body-container callout-body">
<p>Todas estas conductas de los LLMs, que parecen fallas del modelo, son en realidad consecuencias del tokenizador. Volvemos a la lista en la sección 13, cuando tengamos las herramientas para explicarlas:</p>
<ul>
<li>¿Por qué a un LLM le cuesta deletrear palabras o contar letras?</li>
<li>¿Por qué falla al invertir un string, pero acierta si le pedís que lo haga letra por letra?</li>
<li>¿Por qué se equivoca en aritmética con números largos?</li>
<li>¿Por qué el español (y cualquier idioma que no sea inglés) “cuesta” más caro?</li>
<li>¿Por qué es peor escribiendo Python que JavaScript?</li>
<li>¿Por qué un espacio de más al final del prompt empeora la respuesta?</li>
<li>¿Por qué existen palabras como <code>SolidGoldMagikarp</code> que rompen al modelo?</li>
<li>¿Por qué conviene YAML antes que JSON para datos estructurados?</li>
</ul>
</div>
</div>
</section>
<section id="sec-unicode" class="level2" data-number="2">
<h2 data-number="2" class="anchored" data-anchor-id="sec-unicode"><span class="header-section-number">2</span> Primer intento: Unicode y code points</h2>
<p>La idea más obvia es: usemos los caracteres como vocabulario. En Python, un <code>str</code> es una secuencia de <strong>code points</strong> Unicode, y <code>ord</code> da el número de cada uno:</p>
<div id="6f57910a" class="cell" data-execution_count="4">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb7-1">saludo <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"안녕하세요 👋 (hola en coreano)"</span></span>
<span id="cb7-2"></span>
<span id="cb7-3"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(saludo)</span>
<span id="cb7-4"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(saludo), <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"code points"</span>)</span>
<span id="cb7-5"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>([<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">ord</span>(c) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> saludo])</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>안녕하세요 👋 (hola en coreano)
25 code points
[50504, 45397, 54616, 49464, 50836, 32, 128075, 32, 40, 104, 111, 108, 97, 32, 101, 110, 32, 99, 111, 114, 101, 97, 110, 111, 41]</code></pre>
</div>
</div>
<p>El camino inverso es <code>chr</code>:</p>
<div id="9a48ad41" class="cell" data-execution_count="5">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb9-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">chr</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50504</span>), <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">chr</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128075</span>), <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">chr</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">241</span>))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>안 👋 ñ</code></pre>
</div>
</div>
<p>Unicode es un <strong>estándar</strong> que asigna a cada carácter de (idealmente) todos los sistemas de escritura del mundo un número entero, llamado code point, que se escribe en hexadecimal con el prefijo <code>U+</code>. Cuántos hay:</p>
<div id="2545616d" class="cell" data-execution_count="6">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb11-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> sys</span>
<span id="cb11-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> unicodedata</span>
<span id="cb11-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> collections <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Counter</span>
<span id="cb11-4"></span>
<span id="cb11-5"></span>
<span id="cb11-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> mil(n):</span>
<span id="cb11-7">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Formatea con punto como separador de miles, como se escribe en español."""</span></span>
<span id="cb11-8">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>n<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:,}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>.replace(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">","</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"."</span>)</span>
<span id="cb11-9"></span>
<span id="cb11-10"></span>
<span id="cb11-11">posibles <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> sys.maxunicode <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb11-12">categorias <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Counter(unicodedata.category(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">chr</span>(cp)) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> cp <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(posibles))</span>
<span id="cb11-13"></span>
<span id="cb11-14">sin_asignar <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> categorias[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Cn"</span>]</span>
<span id="cb11-15">privados <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> categorias[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Co"</span>]      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># areas de uso privado, sin significado estandarizado</span></span>
<span id="cb11-16">surrogates <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> categorias[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Cs"</span>]    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># reservados para la mecánica de UTF-16</span></span>
<span id="cb11-17">caracteres <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> posibles <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> sin_asignar <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> privados <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> surrogates</span>
<span id="cb11-18"></span>
<span id="cb11-19"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"code points posibles:            </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>mil(posibles)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;9}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb11-20"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"  sin asignar:                   </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>mil(sin_asignar)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;9}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb11-21"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"  de uso privado:                </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>mil(privados)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;9}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb11-22"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"  surrogates (UTF-16):           </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>mil(surrogates)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;9}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb11-23"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"  caracteres de verdad:          </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>mil(caracteres)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;9}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>code points posibles:            1.114.112
  sin asignar:                     824.718
  de uso privado:                  137.468
  surrogates (UTF-16):               2.048
  caracteres de verdad:            149.878</code></pre>
</div>
</div>
<p>Y acá se cae el primer intento, por dos razones:</p>
<ul>
<li><strong>El vocabulario sería enorme.</strong> Casi 150.000 filas en la tabla de embeddings, y la enorme mayoría corresponden a caracteres que casi nunca aparecen en el corpus. Son parámetros que ocupan memoria y no se entrenan nunca.</li>
<li><strong>El vocabulario sería inestable.</strong> Unicode publica una versión nueva cada año, con caracteres y emoji nuevos. El vocabulario de un modelo tiene que ser un artefacto fijo, no algo que cambia con la versión del estándar.</li>
</ul>
<p>Hay un tercer problema, más de fondo: usar caracteres como tokens deja las secuencias muy largas. Un texto de 5.000 caracteres son 5.000 tokens, y la ventana de contexto se llena con muy poco.</p>
</section>
<section id="sec-utf8" class="level2" data-number="3">
<h2 data-number="3" class="anchored" data-anchor-id="sec-utf8"><span class="header-section-number">3</span> Segundo intento: UTF-8 y bytes</h2>
<p>Un code point es un número abstracto. Para guardarlo en un archivo o mandarlo por la red hay que <strong>codificarlo</strong> en bytes, y para eso existen UTF-8, UTF-16 y UTF-32.</p>
<div id="932c94a3" class="cell" data-execution_count="7">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb13-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>(saludo.encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)))</span>
<span id="cb13-2"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(saludo.encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)), <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bytes para"</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(saludo), <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"code points"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>[236, 149, 136, 235, 133, 149, 237, 149, 152, 236, 132, 184, 236, 154, 148, 32, 240, 159, 145, 139, 32, 40, 104, 111, 108, 97, 32, 101, 110, 32, 99, 111, 114, 101, 97, 110, 111, 41]
38 bytes para 25 code points</code></pre>
</div>
</div>
<p>UTF-8 usa <strong>1 a 4 bytes por code point</strong>, según el tamaño del número, con un sistema de prefijos binarios:</p>
<table class="caption-top table">
<colgroup>
<col style="width: 33%">
<col style="width: 33%">
<col style="width: 33%">
</colgroup>
<thead>
<tr class="header">
<th>Bytes UTF-8 (binario)</th>
<th>Bits del code point</th>
<th>Rango</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>0xxxxxxx</code></td>
<td><code>xxxxxxx</code></td>
<td>U+0000 – U+007F</td>
</tr>
<tr class="even">
<td><code>110xxxxx 10yyyyyy</code></td>
<td><code>xxxxxyyyyyy</code></td>
<td>U+0080 – U+07FF</td>
</tr>
<tr class="odd">
<td><code>1110xxxx 10yyyyyy 10zzzzzz</code></td>
<td><code>xxxxyyyyyyzzzzzz</code></td>
<td>U+0800 – U+FFFF</td>
</tr>
<tr class="even">
<td><code>11110xxx 10yyyyyy 10zzzzzz 10wwwwww</code></td>
<td><code>xxxyyyyyyzzzzzzwwwwww</code></td>
<td>U+10000 – U+10FFFF</td>
</tr>
</tbody>
</table>
<p>Los bits en negrita del principio de cada byte dicen qué papel cumple: <code>0</code> significa “byte ASCII suelto”, <code>110</code>/<code>1110</code>/<code>11110</code> significan “acá empieza una secuencia de 2, 3 o 4 bytes”, y <code>10</code> significa “soy continuación del byte anterior”. Se ve mejor en binario:</p>
<div id="0c5adeb3" class="cell" data-execution_count="8">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb15" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb15-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> mostrar_utf8(caracter):</span>
<span id="cb15-2">    bs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> caracter.encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)</span>
<span id="cb15-3">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>caracter<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!r}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  U+</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">ord</span>(caracter)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:04X}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  -&gt;  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(bs)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> byte(s)"</span>)</span>
<span id="cb15-4">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> b <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> bs:</span>
<span id="cb15-5">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"       </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>b<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;3}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">   0b</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>b<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:08b}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb15-6"></span>
<span id="cb15-7"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"a"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ñ"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"あ"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"👋"</span>]:</span>
<span id="cb15-8">    mostrar_utf8(c)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>'a'  U+0061  -&gt;  1 byte(s)
        97   0b01100001
'ñ'  U+00F1  -&gt;  2 byte(s)
       195   0b11000011
       177   0b10110001
'あ'  U+3042  -&gt;  3 byte(s)
       227   0b11100011
       129   0b10000001
       130   0b10000010
'👋'  U+1F44B  -&gt;  4 byte(s)
       240   0b11110000
       159   0b10011111
       145   0b10010001
       139   0b10001011</code></pre>
</div>
</div>
<p>Ese diseño explica por qué UTF-8 ganó, y por qué es la elección natural para un tokenizador:</p>
<ul>
<li><strong>Es retrocompatible con ASCII.</strong> Los primeros 128 code points se codifican en un byte, con el mismo valor que en ASCII. Un archivo ASCII ya es un archivo UTF-8 válido.</li>
<li><strong>Ningún byte de una secuencia multi-byte cae en el rango ASCII</strong> (todos son ≥ 128). Así que buscar un <code>\n</code>, una coma o un byte nulo byte a byte nunca da un falso positivo dentro de un carácter. Todo el software que ya trataba strings como bytes sigue funcionando.</li>
<li><strong>Es auto-sincronizante.</strong> Mirando un byte cualquiera sabés si es un comienzo o una continuación, así que podés retroceder hasta el límite del carácter sin leer desde el inicio.</li>
</ul>
<p>UTF-32, en cambio, gasta 4 bytes siempre (mucho desperdicio para texto en inglés), y UTF-16 gasta 2 como mínimo, introduce bytes nulos que rompen las convenciones de C y necesita el mecanismo de <em>surrogate pairs</em> para los code points altos.</p>
<div class="callout callout-style-default callout-tip callout-titled" title="Por qué esto importa para el tokenizador">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>Por qué esto importa para el tokenizador
</div>
</div>
<div class="callout-body-container callout-body">
<p>Si trabajamos sobre <strong>bytes</strong>, el vocabulario base tiene exactamente <strong>256</strong> entradas, un número chico y fijo para siempre. Y nunca existe el token “desconocido”: cualquier texto en cualquier idioma, presente o futuro, se puede representar, porque cualquier texto es una secuencia de bytes.</p>
</div>
</div>
<p>Carguemos los dos corpus con los que vamos a trabajar todo el apunte:</p>
<div id="a25661c8" class="cell" data-execution_count="9">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb17" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb17-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> pathlib <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> Path</span>
<span id="cb17-2"></span>
<span id="cb17-3">_datos <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"datos"</span>)</span>
<span id="cb17-4"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> (_datos <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"unicode_article.txt"</span>).exists():</span>
<span id="cb17-5">    _datos <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Path(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"posts/datos"</span>)</span>
<span id="cb17-6"></span>
<span id="cb17-7">texto_en <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (_datos <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"unicode_article.txt"</span>).read_text(encoding<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)</span>
<span id="cb17-8">texto_es <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (_datos <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"texto_es.txt"</span>).read_text(encoding<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)</span>
<span id="cb17-9"></span>
<span id="cb17-10"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> nombre, t <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> [(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"inglés"</span>, texto_en), (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"español"</span>, texto_es)]:</span>
<span id="cb17-11">    bs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> t.encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)</span>
<span id="cb17-12">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>nombre<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;8}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(t)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;6}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> caracteres   </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(bs)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;6}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> bytes   "</span></span>
<span id="cb17-13">          <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(bs)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(t)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.3f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> bytes por carácter"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>  inglés:  23328 caracteres    24597 bytes   1.054 bytes por carácter
 español:  23717 caracteres    24266 bytes   1.023 bytes por carácter</code></pre>
</div>
</div>
<p>Los dos archivos tienen casi los mismos bytes a propósito, para que las comparaciones que vengan después sean de igual a igual. Que el texto en inglés tenga <em>más</em> bytes por carácter que el español puede sorprender, pero tiene explicación: el corpus en inglés es un artículo sobre Unicode y está lleno de emoji, banderas y texto Zalgo, que gastan 3 y 4 bytes por carácter. El “impuesto” que paga el español no aparece a nivel de bytes, sino más adelante, a nivel de tokens.</p>
<p>Y acá está el problema del segundo intento:</p>
<div id="21849335" class="cell" data-execution_count="10">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb19" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb19-1">ids_en <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>(texto_en.encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>))</span>
<span id="cb19-2"></span>
<span id="cb19-3"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(texto_en)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> caracteres  -&gt;  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(ids_en)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> tokens (bytes)"</span>)</span>
<span id="cb19-4"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(ids_en[:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">40</span>], <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"..."</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>23328 caracteres  -&gt;  24597 tokens (bytes)
[65, 32, 80, 114, 111, 103, 114, 97, 109, 109, 101, 114, 226, 128, 153, 115, 32, 73, 110, 116, 114, 111, 100, 117, 99, 116, 105, 111, 110, 32, 116, 111, 32, 85, 110, 105, 99, 111, 100, 101] ...</code></pre>
</div>
</div>
<p>Vocabulario chiquito y sin excepciones, pero secuencias larguísimas: un token por byte. Con una ventana de 1.000 tokens no entra ni media página. <strong>Necesitamos comprimir</strong>, sin perder la propiedad de que todo se puede representar.</p>
</section>
<section id="sec-bpe" class="level2" data-number="4">
<h2 data-number="4" class="anchored" data-anchor-id="sec-bpe"><span class="header-section-number">4</span> Byte Pair Encoding</h2>
<p>La solución es un algoritmo de compresión viejo, de 1994, que Sennrich, Haddow y Birch trajeron a NLP en <a href="https://arxiv.org/abs/1508.07909">2016</a>. La idea, en una línea:</p>
<blockquote class="blockquote">
<p>Buscar el par de símbolos consecutivos más frecuente, reemplazarlo por un símbolo nuevo, y repetir.</p>
</blockquote>
<p>Cada repetición agrega una entrada al vocabulario y acorta la secuencia. Con 256 símbolos iniciales (los bytes) y unos miles de repeticiones se llega a un vocabulario que representa palabras enteras frecuentes con un solo token, y palabras raras con varios.</p>
<section id="paso-1-contar-pares" class="level3" data-number="4.1">
<h3 data-number="4.1" class="anchored" data-anchor-id="paso-1-contar-pares"><span class="header-section-number">4.1</span> Paso 1: contar pares</h3>
<div id="20a64c30" class="cell" data-execution_count="11">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb21" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb21-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> get_stats(ids, counts<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>):</span>
<span id="cb21-2">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Cuenta cuántas veces aparece cada par de ids consecutivos.</span></span>
<span id="cb21-3"></span>
<span id="cb21-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    El argumento `counts` permite acumular sobre un diccionario existente, que nos va a</span></span>
<span id="cb21-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    hacer falta en la sección 8 para contar sobre varios trozos de texto.</span></span>
<span id="cb21-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    """</span></span>
<span id="cb21-7">    counts <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {} <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> counts <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">is</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> counts</span>
<span id="cb21-8">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> par <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">zip</span>(ids, ids[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:]):</span>
<span id="cb21-9">        counts[par] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> counts.get(par, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb21-10">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> counts</span>
<span id="cb21-11"></span>
<span id="cb21-12"></span>
<span id="cb21-13">stats <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> get_stats(ids_en)</span>
<span id="cb21-14"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(stats), <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pares distintos</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb21-15"></span>
<span id="cb21-16"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (a, b), n <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sorted</span>(stats.items(), key<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">lambda</span> kv: <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span>kv[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])[:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>]:</span>
<span id="cb21-17">    texto <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">bytes</span>([a, b]).decode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>, errors<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"replace"</span>)</span>
<span id="cb21-18">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"(</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>a<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;3}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">, </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>b<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;3}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">) = </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>texto<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!r:&gt;6}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">   </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>n<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;4}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> veces"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>1381 pares distintos

(101,  32) =   'e '    646 veces
(105, 110) =   'in'    446 veces
(115,  32) =   's '    424 veces
( 32, 116) =   ' t'    405 veces
( 32,  97) =   ' a'    377 veces
(116, 104) =   'th'    337 veces
(101, 114) =   'er'    294 veces
( 99, 111) =   'co'    290 veces</code></pre>
</div>
</div>
<p><code>zip(ids, ids[1:])</code> es el truco de siempre para recorrer pares consecutivos: <code>ids[1:]</code> es la misma lista corrida un lugar, así que <code>zip</code> los va emparejando.</p>
<p>El par más frecuente es <code>(101, 32)</code>, que en ASCII es <code>'e'</code> seguido de un espacio. Tiene sentido en un texto en inglés: es el final de <em>the</em>, <em>more</em>, <em>code</em>, <em>are</em>.</p>
</section>
<section id="paso-2-fusionar" class="level3" data-number="4.2">
<h3 data-number="4.2" class="anchored" data-anchor-id="paso-2-fusionar"><span class="header-section-number">4.2</span> Paso 2: fusionar</h3>
<div id="baedd37f" class="cell" data-execution_count="12">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb23" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb23-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> merge(ids, par, nuevo_id):</span>
<span id="cb23-2">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Reemplaza toda aparición consecutiva de `par` en `ids` por `nuevo_id`."""</span></span>
<span id="cb23-3">    salida <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb23-4">    i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span></span>
<span id="cb23-5">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">while</span> i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(ids):</span>
<span id="cb23-6">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(ids) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> ids[i] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> par[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> ids[i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> par[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]:</span>
<span id="cb23-7">            salida.append(nuevo_id)</span>
<span id="cb23-8">            i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>          <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># consumimos los dos ids del par</span></span>
<span id="cb23-9">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span>:</span>
<span id="cb23-10">            salida.append(ids[i])</span>
<span id="cb23-11">            i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb23-12">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> salida</span>
<span id="cb23-13"></span>
<span id="cb23-14"></span>
<span id="cb23-15"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(merge([<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">99</span>))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>[5, 6, 99, 9, 1]</code></pre>
</div>
</div>
<p>La condición <code>i &lt; len(ids) - 1</code> evita mirar <code>ids[i + 1]</code> en la última posición. El recorrido es <strong>greedy de izquierda a derecha</strong>, lo que importa cuando el par se solapa consigo mismo:</p>
<div id="e6123ea1" class="cell" data-execution_count="13">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb25" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb25-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(merge([<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>], (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>), <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">99</span>))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>[99, 6]</code></pre>
</div>
</div>
<p>Hay dos ocurrencias de <code>(6, 6)</code> en <code>[6, 6, 6]</code> pero se puede fusionar solo una: la de la izquierda gana. No es un problema, es simplemente la convención, y lo importante es que <code>encode</code> y <code>decode</code> usen la misma.</p>
</section>
<section id="paso-3-repetir" class="level3" data-number="4.3">
<h3 data-number="4.3" class="anchored" data-anchor-id="paso-3-repetir"><span class="header-section-number">4.3</span> Paso 3: repetir</h3>
<p>Ahora el bucle de entrenamiento. Un parámetro: <code>vocab_size</code>, el tamaño final del vocabulario. Como arrancamos con 256 bytes, la cantidad de fusiones es <code>vocab_size - 256</code>:</p>
<div id="b547ae72" class="cell" data-execution_count="14">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb27" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb27-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> entrenar_bpe(ids, vocab_size, verbose<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>):</span>
<span id="cb27-2">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Entrena BPE sobre una lista de ids y devuelve (ids comprimidos, merges, vocab).</span></span>
<span id="cb27-3"></span>
<span id="cb27-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    `merges` mapea (id, id) -&gt; id nuevo, en el orden en que se aprendieron.</span></span>
<span id="cb27-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    `vocab`  mapea id -&gt; los bytes que representa.</span></span>
<span id="cb27-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    """</span></span>
<span id="cb27-7">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">assert</span> vocab_size <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"el vocabulario arranca en los 256 bytes"</span></span>
<span id="cb27-8"></span>
<span id="cb27-9">    merges <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {}</span>
<span id="cb27-10">    vocab <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {i: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">bytes</span>([i]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>)}</span>
<span id="cb27-11">    ids <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>(ids)                              <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># copia: no destruimos la entrada</span></span>
<span id="cb27-12"></span>
<span id="cb27-13">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(vocab_size <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>):</span>
<span id="cb27-14">        stats <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> get_stats(ids)</span>
<span id="cb27-15">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> stats:                            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># quedó un solo id: no hay más pares</span></span>
<span id="cb27-16">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">break</span></span>
<span id="cb27-17">        par <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>(stats, key<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>stats.get)          <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># el par más frecuente</span></span>
<span id="cb27-18">        nuevo_id <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> i                       <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># los ids nuevos arrancan después de los bytes</span></span>
<span id="cb27-19">        ids <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> merge(ids, par, nuevo_id)</span>
<span id="cb27-20">        merges[par] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nuevo_id</span>
<span id="cb27-21">        vocab[nuevo_id] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> vocab[par[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> vocab[par[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]</span>
<span id="cb27-22"></span>
<span id="cb27-23">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> verbose:</span>
<span id="cb27-24">            legible <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> vocab[nuevo_id].decode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>, errors<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"replace"</span>)</span>
<span id="cb27-25">            <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;3}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">. </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(par)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;12}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> -&gt; </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>nuevo_id<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">   </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>legible<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!r:&gt;8}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">   "</span></span>
<span id="cb27-26">                  <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"(</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>stats[par]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> apariciones)"</span>)</span>
<span id="cb27-27"></span>
<span id="cb27-28">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> ids, merges, vocab</span>
<span id="cb27-29"></span>
<span id="cb27-30"></span>
<span id="cb27-31">ids_bpe, merges, vocab <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> entrenar_bpe(ids_en, vocab_size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">276</span>, verbose<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>  1.    (101, 32) -&gt; 256       'e '   (646 apariciones)
  2.   (105, 110) -&gt; 257       'in'   (446 apariciones)
  3.    (115, 32) -&gt; 258       's '   (424 apariciones)
  4.   (116, 104) -&gt; 259       'th'   (337 apariciones)
  5.   (101, 114) -&gt; 260       'er'   (294 apariciones)
  6.    (99, 111) -&gt; 261       'co'   (290 apariciones)
  7.    (116, 32) -&gt; 262       't '   (285 apariciones)
  8.   (226, 128) -&gt; 263        '�'   (254 apariciones)
  9.     (44, 32) -&gt; 264       ', '   (243 apariciones)
 10.    (97, 110) -&gt; 265       'an'   (229 apariciones)
 11.   (111, 114) -&gt; 266       'or'   (214 apariciones)
 12.    (100, 32) -&gt; 267       'd '   (213 apariciones)
 13.    (97, 114) -&gt; 268       'ar'   (181 apariciones)
 14.   (101, 110) -&gt; 269       'en'   (174 apariciones)
 15.   (257, 103) -&gt; 270      'ing'   (166 apariciones)
 16.   (261, 100) -&gt; 271      'cod'   (165 apariciones)
 17.    (121, 32) -&gt; 272       'y '   (154 apariciones)
 18.     (46, 32) -&gt; 273       '. '   (154 apariciones)
 19.    (97, 108) -&gt; 274       'al'   (146 apariciones)
 20.   (259, 256) -&gt; 275     'the '   (144 apariciones)</code></pre>
</div>
</div>
<p>Vale la pena leer esa lista despacio, porque muestra el algoritmo pensando:</p>
<ul>
<li>Las primeras fusiones son las de la sección 4.1: <code>'e '</code>, <code>'in'</code>, <code>'s '</code>, <code>'th'</code>.</li>
<li>La fusión 8, <code>(226, 128)</code>, no es texto legible: son los dos primeros bytes de la secuencia UTF-8 de las comillas tipográficas <code>“ ” ’</code>, muy frecuentes en el artículo. BPE no sabe nada de caracteres; encuentra la regularidad igual.</li>
<li>La fusión 15 es <code>(257, 103)</code>: el token 257 (<code>'in'</code>) más <code>'g'</code>, o sea <code>'ing'</code>. <strong>Fusiona un token aprendido con otro.</strong> Es la primera vez que el algoritmo construye sobre sí mismo, y es la razón de que con pocos miles de fusiones se llegue a palabras enteras.</li>
<li>La última, <code>(259, 256)</code>, junta <code>'th'</code> con <code>'e '</code>: el token <code>'the '</code>, palabra y espacio en un solo id.</li>
</ul>
<p>Los ids nuevos arrancan en 256 porque los primeros 256 ya están tomados por los bytes. Y el vocabulario final es un <strong>árbol</strong>: cada token nuevo se define en términos de dos anteriores, hasta llegar a las hojas, que son bytes.</p>
<p>¿Cuánto comprimimos?</p>
<div id="b9f963ce" class="cell" data-execution_count="15">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb29" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb29-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"antes:    </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(ids_en)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;6}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> tokens"</span>)</span>
<span id="cb29-2"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"después:  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(ids_bpe)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;6}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> tokens"</span>)</span>
<span id="cb29-3"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"ratio:    </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(ids_en) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(ids_bpe)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;6.2f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">X"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>antes:     24597 tokens
después:   19438 tokens
ratio:      1.27X</code></pre>
</div>
</div>
<p>Un 1,27X con apenas 20 fusiones. La curva sigue subiendo con <code>vocab_size</code>, y la vamos a medir en la sección 12.</p>
<div class="callout callout-style-default callout-warning callout-titled" title="El corpus del tokenizador decide todo">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Advertencia</span>El corpus del tokenizador decide todo
</div>
</div>
<div class="callout-body-container callout-body">
<p><code>merges</code> sale de contar pares en un texto concreto. Entrenado en este artículo en inglés, el tokenizador va a ser eficiente en inglés y malo en español o en código. Los tokenizadores reales se entrenan sobre mezclas cuidadosamente elegidas de idiomas y lenguajes de programación, y esa mezcla —decidida antes de entrenar el modelo, y congelada para siempre— determina qué le va a salir barato y qué caro al LLM.</p>
</div>
</div>
</section>
</section>
<section id="sec-decode" class="level2" data-number="5">
<h2 data-number="5" class="anchored" data-anchor-id="sec-decode"><span class="header-section-number">5</span> Decodificar: de ids a texto</h2>
<p>Tenemos <code>merges</code>, que es lo que se aprende. Para decodificar hace falta <code>vocab</code>: qué bytes representa cada id. Se construye recorriendo <code>merges</code>:</p>
<div id="ca38bb01" class="cell" data-execution_count="16">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb31" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb31-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> construir_vocab(merges):</span>
<span id="cb31-2">    vocab <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {i: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">bytes</span>([i]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>)}</span>
<span id="cb31-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (p0, p1), idx <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> merges.items():</span>
<span id="cb31-4">        vocab[idx] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> vocab[p0] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> vocab[p1]</span>
<span id="cb31-5">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> vocab</span>
<span id="cb31-6"></span>
<span id="cb31-7"></span>
<span id="cb31-8">vocab <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> construir_vocab(merges)</span>
<span id="cb31-9"></span>
<span id="cb31-10"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> idx <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> [<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">270</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">275</span>]:</span>
<span id="cb31-11">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>idx<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>vocab[idx]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>256: b'e '
270: b'ing'
275: b'the '</code></pre>
</div>
</div>
<p>Ese <code>for</code> depende de un detalle importante de Python: <strong>los diccionarios preservan el orden de inserción</strong>. El token 275 se define como <code>vocab[259] + vocab[256]</code>, así que 259 y 256 ya tienen que estar en el diccionario cuando llegamos a él. Como <code>merges</code> se llenó en orden creciente de id, el recorrido funciona. Si iteráramos en otro orden, tendríamos un <code>KeyError</code>.</p>
<p>Con <code>vocab</code>, decodificar es concatenar bytes y decodificar UTF-8:</p>
<div id="0227c3be" class="cell" data-execution_count="17">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb33" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb33-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> decode(ids, vocab):</span>
<span id="cb33-2">    bs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">b""</span>.join(vocab[i] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> ids)</span>
<span id="cb33-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> bs.decode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>, errors<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"replace"</span>)</span>
<span id="cb33-4"></span>
<span id="cb33-5"></span>
<span id="cb33-6"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(decode([<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">275</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">271</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">270</span>], vocab))       <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 'the ' + 'cod' + 'ing'</span></span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>the coding</code></pre>
</div>
</div>
<section id="por-qué-errorsreplace-no-es-opcional" class="level3" data-number="5.1">
<h3 data-number="5.1" class="anchored" data-anchor-id="por-qué-errorsreplace-no-es-opcional"><span class="header-section-number">5.1</span> Por qué <code>errors="replace"</code> no es opcional</h3>
<p>Un detalle fácil de pasar por alto: <strong>no toda secuencia de ids corresponde a texto UTF-8 válido</strong>. El id 128 es el byte <code>0b10000000</code>, que según la tabla de la sección 3 es un byte de <em>continuación</em>. Suelto, sin un byte de comienzo antes, es inválido:</p>
<div id="f62e5db8" class="cell" data-execution_count="18">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb35" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb35-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">try</span>:</span>
<span id="cb35-2">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">bytes</span>([<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>]).decode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)</span>
<span id="cb35-3"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">except</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">UnicodeDecodeError</span> <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> error:</span>
<span id="cb35-4">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"UnicodeDecodeError:"</span>, error)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte</code></pre>
</div>
</div>
<p>Un tokenizador tiene que ser una función total: si el modelo genera el id 128, <code>decode</code> no puede explotar. Por eso <code>errors="replace"</code>, que sustituye los bytes inválidos por el carácter de reemplazo <code>U+FFFD</code>:</p>
<div id="7b35843c" class="cell" data-execution_count="19">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb37" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb37-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">repr</span>(decode([<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span>], vocab)))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>'�'</code></pre>
</div>
</div>
<p>Esto no es un caso hipotético. El LLM genera un token por vez, y si le pedís un emoji va a emitir varios tokens que juntos forman una secuencia UTF-8 de 4 bytes. Mientras la genera, los prefijos son inválidos. Por eso al usar un LLM en modo <em>streaming</em> a veces aparece un <code>�</code> que después se convierte en el carácter correcto: es exactamente este mecanismo.</p>
</section>
</section>
<section id="sec-encode" class="level2" data-number="6">
<h2 data-number="6" class="anchored" data-anchor-id="sec-encode"><span class="header-section-number">6</span> Codificar: de texto a ids</h2>
<p>Nos falta la dirección que más se usa. Podría parecer que basta con buscar en <code>vocab</code> los bytes más largos que coincidan, pero no: hay que <strong>reproducir exactamente el mismo proceso del entrenamiento</strong>, aplicando las fusiones en el orden en que se aprendieron.</p>
<div id="ee6f9159" class="cell" data-execution_count="20">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb39" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb39-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> encode(texto, merges):</span>
<span id="cb39-2">    ids <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>(texto.encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>))</span>
<span id="cb39-3"></span>
<span id="cb39-4">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">while</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(ids) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>:</span>
<span id="cb39-5">        stats <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> get_stats(ids)</span>
<span id="cb39-6">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># De todos los pares presentes, el que se aprendió PRIMERO (menor id nuevo).</span></span>
<span id="cb39-7">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Los pares que no están en `merges` reciben infinito, así nunca ganan.</span></span>
<span id="cb39-8">        par <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">min</span>(stats, key<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">lambda</span> p: merges.get(p, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"inf"</span>)))</span>
<span id="cb39-9">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> par <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> merges:</span>
<span id="cb39-10">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">break</span>                              <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># no queda nada por fusionar</span></span>
<span id="cb39-11">        ids <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> merge(ids, par, merges[par])</span>
<span id="cb39-12"></span>
<span id="cb39-13">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> ids</span>
<span id="cb39-14"></span>
<span id="cb39-15"></span>
<span id="cb39-16"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"the code"</span>, merges))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>[275, 271, 101]</code></pre>
</div>
</div>
<p>Hay tres decisiones ahí que merecen explicación.</p>
<p><strong>Por qué <code>min</code> y no <code>max</code>.</strong> En el entrenamiento usamos <code>max</code> sobre <em>frecuencias</em>. Acá usamos <code>min</code> sobre <em>ids nuevos</em>, que funcionan como un <strong>ranking</strong>: el id 256 fue la primera fusión aprendida, el 275 la última. Y hay que aplicarlas en ese orden porque las fusiones tardías se definen sobre las tempranas. El token 275 (<code>'the '</code>) es la fusión de 259 (<code>'th'</code>) con 256 (<code>'e '</code>). Si fusionáramos primero por otro criterio, podríamos llegar a un estado donde el par <code>(259, 256)</code> nunca aparece, y produciríamos una tokenización que el entrenamiento nunca genera. Codificar es <em>replay</em> del entrenamiento, no búsqueda del mejor recubrimiento.</p>
<p><strong>Por qué el <code>break</code>.</strong> <code>min</code> siempre devuelve algo, incluso si ningún par está en <code>merges</code>: en ese caso devuelve un par cualquiera con valor infinito. Sin el chequeo <code>par not in merges</code>, la línea siguiente daría <code>KeyError</code>, o peor, el bucle no terminaría nunca.</p>
<p><strong>Por qué <code>while len(ids) &gt;= 2</code>.</strong> Con menos de dos ids no hay pares, y <code>min</code> sobre un diccionario vacío levanta <code>ValueError</code>. Es el caso del string vacío y del carácter único:</p>
<div id="f2e4603c" class="cell" data-execution_count="21">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb41" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb41-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>, merges), encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"a"</span>, merges))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>[] [97]</code></pre>
</div>
</div>
<section id="verificar-que-cierra" class="level3" data-number="6.1">
<h3 data-number="6.1" class="anchored" data-anchor-id="verificar-que-cierra"><span class="header-section-number">6.1</span> Verificar que cierra</h3>
<p>La propiedad que tiene que valer es que decodificar lo codificado devuelva el texto original:</p>
<div id="45fbb2ce" class="cell" data-execution_count="22">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb43" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb43-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(decode(encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"hola mundo"</span>, merges), vocab) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"hola mundo"</span>)</span>
<span id="cb43-2"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(decode(encode(texto_en, merges), vocab) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> texto_en)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>True
True</code></pre>
</div>
</div>
<p>Y sobre texto que el tokenizador <strong>nunca vio</strong> al entrenar, que es el caso real:</p>
<div id="0eb8a285" class="cell" data-execution_count="23">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb45" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb45-1">validacion <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (</span>
<span id="cb45-2">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Muchos caracteres comunes, incluidos los numerales y la puntuación, no se tratan "</span></span>
<span id="cb45-3">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"como específicos de ningún sistema de escritura en particular. 안녕하세요 👋"</span></span>
<span id="cb45-4">)</span>
<span id="cb45-5"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(decode(encode(validacion, merges), vocab) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> validacion)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>True</code></pre>
</div>
</div>
<p>Funciona con cualquier texto, y eso es consecuencia directa de trabajar sobre bytes: no hay caracteres fuera del alcance, así que no hace falta un token <code>UNK</code>.</p>
<div class="callout callout-style-default callout-note callout-titled" title="La vuelta no vale al revés">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Nota</span>La vuelta no vale al revés
</div>
</div>
<div class="callout-body-container callout-body">
<p><code>decode(encode(t)) == t</code> siempre. Pero <code>encode(decode(ids)) == ids</code> <strong>no</strong>: hay secuencias de ids que decodifican bien y que <code>encode</code> nunca produciría, porque no están en forma canónica.</p>
</div>
</div>
<div id="547a6532" class="cell" data-execution_count="24">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb47" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb47-1">ids_raros <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">259</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>]                       <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 'th' + 'e ', sin fusionar</span></span>
<span id="cb47-2">texto_raro <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> decode(ids_raros, vocab)</span>
<span id="cb47-3"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>ids_raros<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> -&gt; </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>texto_raro<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!r}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> -&gt; </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>encode(texto_raro, merges)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>[259, 256] -&gt; 'the ' -&gt; [275]</code></pre>
</div>
</div>
<p>Los dos representan <code>'the '</code>, pero solo el segundo es lo que sale del entrenamiento. El modelo solo vio la forma canónica, así que si le pasás la otra estás mandando una entrada que no aparece nunca en sus datos de entrenamiento.</p>
</section>
</section>
<section id="sec-regex" class="level2" data-number="7">
<h2 data-number="7" class="anchored" data-anchor-id="sec-regex"><span class="header-section-number">7</span> El truco del regex: pre-tokenización</h2>
<p>Ya tenemos un tokenizador BPE completo y funcionando. El problema es que si lo dejamos correr con un vocabulario grande, empieza a aprender tokens que no queremos. Entrenemos con 512 y busquemos los casos raros:</p>
<div id="6048276a" class="cell" data-execution_count="25">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb49" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb49-1">ids_512, merges_512, vocab_512 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> entrenar_bpe(ids_en, vocab_size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">512</span>)</span>
<span id="cb49-2"></span>
<span id="cb49-3"></span>
<span id="cb49-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> mezcla_categorias(s):</span>
<span id="cb49-5">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""¿Este token junta cosas de distinta naturaleza (letras, símbolos, espacios)?"""</span></span>
<span id="cb49-6">    tiene_letra <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">any</span>(c.isalpha() <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> s)</span>
<span id="cb49-7">    tiene_simbolo <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">any</span>(<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> c.isalpha() <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> c.isspace() <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> s)</span>
<span id="cb49-8">    espacio_interno <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">" "</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> s.strip()</span>
<span id="cb49-9">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> (tiene_letra <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> tiene_simbolo) <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> espacio_interno</span>
<span id="cb49-10"></span>
<span id="cb49-11"></span>
<span id="cb49-12">sospechosos <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [</span>
<span id="cb49-13">    (idx, vocab_512[idx].decode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>, errors<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"replace"</span>))</span>
<span id="cb49-14">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> idx <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">512</span>)</span>
<span id="cb49-15">]</span>
<span id="cb49-16">sospechosos <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [(idx, s) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> idx, s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> sospechosos <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> mezcla_categorias(s)]</span>
<span id="cb49-17"></span>
<span id="cb49-18"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(sospechosos)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> de 256 tokens aprendidos mezclan categorías:</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb49-19"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">", "</span>.join(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">repr</span>(s) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _, s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> sospechosos[:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">24</span>]))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>26 de 256 tokens aprendidos mezclan categorías:

'code poin', 's, ', 'UTF-', 'U+', 'e, ', '’s ', 'code points ', 'es, ', '. Th', 'UTF-8', 'code point', '. I', '’t ', 'in the ', 'UTF-8 ', 'y, ', 's. ', 'UTF-16', '-b', 'as a ', ' U+', 'ing m', 'code point ', '-bit '</code></pre>
</div>
</div>
<p>Mirá esos tokens. Cosas como <code>'e. '</code>, <code>', '</code>, <code>'s, '</code>, <code>'ing '</code>: el algoritmo se comió la puntuación junto con las letras. Con un vocabulario de decenas de miles esto explota, y trae tres problemas concretos:</p>
<ul>
<li><strong>Duplicación del vocabulario.</strong> Van a existir tokens separados para <code>'dog'</code>, <code>'dog.'</code>, <code>'dog!'</code>, <code>'dog?'</code>, <code>' dog'</code>, <code>' Dog'</code>. Cada uno es una fila distinta de la tabla de embeddings, y el modelo tiene que aprender en cada una, por separado, que se trata de un perro.</li>
<li><strong>Se desperdicia capacidad.</strong> Cada token gastado en una variante de puntuación es un token que no se usó para algo útil, como un término técnico o una palabra en otro idioma.</li>
<li><strong>Se destruye la estructura del código.</strong> En Python la indentación es sintaxis. Si BPE puede fusionar espacios libremente, cuatro espacios seguidos, ocho, doce y dieciséis terminan siendo tokens distintos, y los niveles de anidación se representan de formas incompatibles entre sí.</li>
</ul>
<section id="la-solución-de-gpt-2-partir-antes-de-contar" class="level3" data-number="7.1">
<h3 data-number="7.1" class="anchored" data-anchor-id="la-solución-de-gpt-2-partir-antes-de-contar"><span class="header-section-number">7.1</span> La solución de GPT-2: partir antes de contar</h3>
<p>La solución que usa OpenAI desde GPT-2 es no correr BPE sobre el texto entero, sino:</p>
<ol type="1">
<li>partir el texto en trozos con una expresión regular;</li>
<li>correr BPE <strong>dentro de cada trozo por separado</strong>;</li>
<li>concatenar los resultados.</li>
</ol>
<p>La clave es el paso 2: si las estadísticas de pares nunca cruzan el límite entre trozos, entonces <strong>ningún token puede contener partes de dos trozos distintos</strong>. Es una restricción dura, impuesta a mano por el diseñador del tokenizador, no algo que el algoritmo aprenda.</p>
<p>Este es el patrón de GPT-2, tal como está en su código publicado:</p>
<div id="cb598835" class="cell" data-execution_count="26">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb51" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb51-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> regex <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> re</span>
<span id="cb51-2"></span>
<span id="cb51-3">GPT2_SPLIT_PATTERN <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (</span>
<span id="cb51-4">    <span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;">r"""'s</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;">'t</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;">'re</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;">'ve</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;">'m</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;">'ll</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;">'d</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">?</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\p{L}</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">?</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\p{N}</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">?</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">[^</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">\s</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\p{L}\p{N}</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">]</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">\s</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">(</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">?!</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">\S</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">)</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">\s</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span><span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb51-5">)</span>
<span id="cb51-6"></span>
<span id="cb51-7"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(re.findall(GPT2_SPLIT_PATTERN, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Hola mundo, ¿cómo andás? Son 42 grados."</span>))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>['Hola', ' mundo', ',', ' ¿', 'cómo', ' andás', '?', ' Son', ' 42', ' grados', '.']</code></pre>
</div>
</div>
<p>Primero, dos detalles de implementación:</p>
<ul>
<li>Se importa <code>regex</code>, <strong>no</strong> el módulo <code>re</code> de la biblioteca estándar. <code>regex</code> es una extensión compatible que agrega, entre otras cosas, las clases Unicode <code>\p{...}</code>. Se importa como <code>re</code> por costumbre, lo que confunde bastante al leer el código original.</li>
<li><code>\p{L}</code> significa “cualquier carácter que Unicode clasifique como letra”, <strong>en cualquier idioma</strong>: <code>a</code>, <code>ñ</code>, <code>é</code>, <code>안</code>, <code>α</code>. Y <code>\p{N}</code> es “cualquier dígito”. Con <code>[a-zA-Z]</code> habría que enumerar los alfabetos del mundo a mano.</li>
</ul>
<p>El patrón es una cadena de alternativas separadas por <code>|</code>. <code>findall</code> recorre el texto de izquierda a derecha y en cada posición prueba las alternativas <strong>en orden</strong>, quedándose con la primera que coincide:</p>
<table class="caption-top table">
<colgroup>
<col style="width: 33%">
<col style="width: 33%">
<col style="width: 33%">
</colgroup>
<thead>
<tr class="header">
<th>Alternativa</th>
<th>Qué captura</th>
<th>Ejemplo</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>'s</code>, <code>'t</code>, <code>'re</code>, <code>'ve</code>, <code>'m</code>, <code>'ll</code>, <code>'d</code></td>
<td>contracciones del inglés</td>
<td><code>it's</code> → <code>it</code>, <code>'s</code></td>
</tr>
<tr class="even">
<td><code>?\p{L}+</code></td>
<td>una o más letras, con un espacio opcional adelante</td>
<td><code>hola mundo</code> → <code>hola</code>, <code>mundo</code></td>
</tr>
<tr class="odd">
<td><code>?\p{N}+</code></td>
<td>uno o más dígitos, con espacio opcional</td>
<td><code>son 42</code> → <code>son</code>, <code>42</code></td>
</tr>
<tr class="even">
<td><code>?[^\s\p{L}\p{N}]+</code></td>
<td>símbolos: todo lo que no sea espacio, letra ni dígito</td>
<td><code>!!!</code> , <code>(</code></td>
</tr>
<tr class="odd">
<td><code>\s+(?!\S)</code></td>
<td>espacios en blanco que <strong>no</strong> son seguidos por texto</td>
<td>ver abajo</td>
</tr>
<tr class="even">
<td><code>\s+</code></td>
<td>cualquier resto de espacios</td>
<td>fin de string</td>
</tr>
</tbody>
</table>
<p>La alternativa clave, y la más difícil de leer, es <code>\s+(?!\S)</code>. <code>(?!\S)</code> es un <em>negative lookahead</em>: “siempre que lo que sigue <strong>no</strong> sea un carácter distinto de espacio”. Combinado con la codicia de <code>\s+</code>, el efecto es “tomá una corrida de espacios, pero dejá el último para la palabra que viene”:</p>
<div id="00605bc2" class="cell" data-execution_count="27">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb53" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb53-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"hola    mundo"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"hola    "</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"renglón</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">siguiente"</span>]:</span>
<span id="cb53-2">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>s<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!r:&gt;22}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  -&gt;  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>re<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>findall(GPT2_SPLIT_PATTERN, s)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>       'hola    mundo'  -&gt;  ['hola', '   ', ' mundo']
            'hola    '  -&gt;  ['hola', '    ']
'renglón\n\nsiguiente'  -&gt;  ['renglón', '\n', '\n', 'siguiente']</code></pre>
</div>
</div>
<p>En el primer caso, los cuatro espacios se parten en <code>'   '</code> (tres) más <code>' mundo'</code>. Ese último espacio se va con la palabra porque, como vimos en la sección 1, la convención es que el espacio viaja pegado al inicio de la palabra siguiente. En el segundo caso no hay palabra que siga, así que los cuatro espacios quedan juntos.</p>
<p>Por qué existe esa convención: <code>' mundo'</code> y <code>'mundo'</code> no son el mismo token, pero como casi siempre una palabra viene después de un espacio, meter el espacio adentro del token es prácticamente gratis y ahorra un token por palabra. El precio lo pagamos en la sección 13.</p>
</section>
<section id="los-agujeros-del-patrón-de-gpt-2" class="level3" data-number="7.2">
<h3 data-number="7.2" class="anchored" data-anchor-id="los-agujeros-del-patrón-de-gpt-2"><span class="header-section-number">7.2</span> Los agujeros del patrón de GPT-2</h3>
<p>El propio código de GPT-2 tiene un comentario admitiendo el primero: las contracciones están escritas en minúscula y sin <code>IGNORECASE</code>.</p>
<div id="b35716cd" class="cell" data-execution_count="28">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb55" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb55-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"it's"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"IT'S"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"It'S"</span>]:</span>
<span id="cb55-2">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>s<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!r:&gt;8}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  -&gt;  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>re<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>findall(GPT2_SPLIT_PATTERN, s)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>  "it's"  -&gt;  ['it', "'s"]
  "IT'S"  -&gt;  ['IT', "'", 'S']
  "It'S"  -&gt;  ['It', "'", 'S']</code></pre>
</div>
</div>
<p>En <code>IT'S</code>, el <code>'S</code> no coincide con la alternativa de contracciones (que espera <code>'s</code> en minúscula), así que cae en la rama de símbolos y se parte distinto que la versión en minúscula. El modelo ve dos estructuras diferentes para la misma contracción.</p>
<p>El segundo agujero son los números: <code>?\p{N}+</code> no tiene límite de largo, así que un número de diez dígitos es un solo trozo y BPE puede aprender tokens con cualquier cantidad de dígitos adentro.</p>
<div id="bb62aaf5" class="cell" data-execution_count="29">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb57" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb57-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(re.findall(GPT2_SPLIT_PATTERN, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"el resultado fue 1234567890 exactamente"</span>))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>['el', ' resultado', ' fue', ' 1234567890', ' exactamente']</code></pre>
</div>
</div>
</section>
<section id="el-patrón-de-gpt-4" class="level3" data-number="7.3">
<h3 data-number="7.3" class="anchored" data-anchor-id="el-patrón-de-gpt-4"><span class="header-section-number">7.3</span> El patrón de GPT-4</h3>
<p>GPT-4 (el encoding <code>cl100k_base</code>) corrige eso:</p>
<div id="1fcb8c80" class="cell" data-execution_count="30">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb59" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb59-1">GPT4_SPLIT_PATTERN <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (</span>
<span id="cb59-2">    <span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;">r"""'</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">(</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">?i:</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">[sdmt]</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;">ll</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;">ve</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;">re</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">)</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">[^</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\r\n\p{L}\p{N}</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">]</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">?+</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\p{L}</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\p{N}</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{1,3}</span><span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb59-3">    <span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;">r"""</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">?</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">[^</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">\s</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\p{L}\p{N}</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">]</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">++</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">[</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\r\n</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">]</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">\s</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">[</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\r\n</span><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">]</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">\s</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">(</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">?!</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">\S</span><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">)</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">|</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">\s</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span><span class="vs" style="color: #20794D;
background-color: null;
font-style: inherit;">"""</span></span>
<span id="cb59-4">)</span>
<span id="cb59-5"></span>
<span id="cb59-6">pruebas <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [</span>
<span id="cb59-7">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"IT'S"</span>,</span>
<span id="cb59-8">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"el resultado fue 1234567890 exactamente"</span>,</span>
<span id="cb59-9">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"¿cómo andás?"</span>,</span>
<span id="cb59-10">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"hola    mundo"</span>,</span>
<span id="cb59-11">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"def f():</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    return 1</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>,</span>
<span id="cb59-12">]</span>
<span id="cb59-13"></span>
<span id="cb59-14"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> pruebas:</span>
<span id="cb59-15">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>s<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!r}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb59-16">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"   GPT-2: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>re<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>findall(GPT2_SPLIT_PATTERN, s)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb59-17">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"   GPT-4: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>re<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>findall(GPT4_SPLIT_PATTERN, s)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>"IT'S"
   GPT-2: ['IT', "'", 'S']
   GPT-4: ['IT', "'S"]

'el resultado fue 1234567890 exactamente'
   GPT-2: ['el', ' resultado', ' fue', ' 1234567890', ' exactamente']
   GPT-4: ['el', ' resultado', ' fue', ' ', '123', '456', '789', '0', ' exactamente']

'¿cómo andás?'
   GPT-2: ['¿', 'cómo', ' andás', '?']
   GPT-4: ['¿cómo', ' andás', '?']

'hola    mundo'
   GPT-2: ['hola', '   ', ' mundo']
   GPT-4: ['hola', '   ', ' mundo']

'def f():\n    return 1\n'
   GPT-2: ['def', ' f', '():', '\n   ', ' return', ' 1', '\n']
   GPT-4: ['def', ' f', '():\n', '   ', ' return', ' ', '1', '\n']
</code></pre>
</div>
</div>
<p>Los cuatro cambios, en orden de aparición:</p>
<ul>
<li><code>'(?i:[sdmt]|ll|ve|re)</code> — el <code>(?i:...)</code> hace la comparación <strong>insensible a mayúsculas</strong> solo en ese grupo. <code>IT'S</code> ahora se parte igual que <code>it's</code>.</li>
<li><code>[^\r\n\p{L}\p{N}]?+\p{L}+</code> — en lugar de “espacio opcional más letras”, ahora es “<strong>cualquier carácter</strong> que no sea salto de línea, letra ni dígito, opcional, más letras”. Así <code>¿cómo</code> queda en un solo trozo, y también <code>(hola</code> o <code>"palabra</code>. El <code>?+</code> es <em>possessive</em>: una vez que consumió el carácter, no lo devuelve por <em>backtracking</em>.</li>
<li><code>\p{N}{1,3}</code> — los números se parten en grupos de <strong>a lo sumo tres dígitos</strong>, y sin espacio adelante (fijate que <code>' 1'</code> pasa a ser <code>' '</code> y <code>'1'</code>). Es la razón por la que <code>1234567890</code> se convierte en <code>123</code>, <code>456</code>, <code>789</code>, <code>0</code>, y tiene todo que ver con la aritmética de los LLMs (sección 13).</li>
<li><code>\s*[\r\n]</code> — los saltos de línea se tratan aparte. Fijate en la última prueba: GPT-2 mete el salto de línea junto con la indentación que le sigue en un mismo trozo (<code>'\n   '</code>), mientras GPT-4 lo pega a la puntuación anterior (<code>'():\n'</code>) y deja la indentación como trozo propio (<code>'   '</code>, <code>' return'</code>). En GPT-2, cada nivel de anidación produce un trozo distinto que combina salto de línea con espacios; en GPT-4, la indentación se agrupa aparte y de forma consistente. Esa diferencia, sola, explica buena parte de por qué GPT-4 escribe Python bastante mejor que GPT-2.</li>
</ul>
<div class="callout callout-style-default callout-note callout-titled" title="Qué implica para el español">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Nota</span>Qué implica para el español
</div>
</div>
<div class="callout-body-container callout-body">
<p>El patrón está diseñado alrededor del inglés. Las contracciones (<code>'s</code>, <code>'ll</code>, <code>'ve</code>) no existen en español, así que esas alternativas son peso muerto; y ningún patrón contempla que en español los signos de apertura <code>¿</code> y <code>¡</code> abren, ni que las palabras llevan tilde. GPT-4 al menos logra pegar <code>¿</code> a la palabra que sigue gracias a <code>[^\r\n\p{L}\p{N}]?+</code>, algo que GPT-2 no hacía.</p>
<p>Pero el gasto real no está en el regex sino en el corpus de entrenamiento del tokenizador, que es mayoritariamente inglés: las palabras en español simplemente no aparecen lo suficiente como para ganarse un token propio, y quedan partidas en pedazos. Lo medimos en la sección 9.</p>
</div>
</div>
</section>
</section>
<section id="sec-clase" class="level2" data-number="8">
<h2 data-number="8" class="anchored" data-anchor-id="sec-clase"><span class="header-section-number">8</span> Todo junto: <code>RegexTokenizer</code></h2>
<p>Ya tenemos todas las piezas. Falta empaquetarlas en algo reutilizable que aplique el regex antes de entrenar y antes de codificar:</p>
<div id="568c6d5b" class="cell" data-execution_count="31">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb61" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb61-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> RegexTokenizer:</span>
<span id="cb61-2">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Tokenizador BPE con pre-tokenización por expresión regular."""</span></span>
<span id="cb61-3"></span>
<span id="cb61-4">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, pattern<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>):</span>
<span id="cb61-5">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.pattern <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pattern <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> GPT4_SPLIT_PATTERN</span>
<span id="cb61-6">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>._re <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> re.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">compile</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.pattern)</span>
<span id="cb61-7">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.merges <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {}                                <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (id, id) -&gt; id nuevo</span></span>
<span id="cb61-8">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.vocab <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {i: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">bytes</span>([i]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>)}  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># id -&gt; bytes</span></span>
<span id="cb61-9"></span>
<span id="cb61-10">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> train(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, texto, vocab_size, verbose<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>):</span>
<span id="cb61-11">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Un trozo por coincidencia del regex; BPE nunca cruza estos límites.</span></span>
<span id="cb61-12">        trozos <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>(t.encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> t <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>._re.findall(texto)]</span>
<span id="cb61-13"></span>
<span id="cb61-14">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(vocab_size <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>):</span>
<span id="cb61-15">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Contamos pares en todos los trozos, acumulando en el mismo diccionario.</span></span>
<span id="cb61-16">            stats <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {}</span>
<span id="cb61-17">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> trozo <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> trozos:</span>
<span id="cb61-18">                get_stats(trozo, stats)</span>
<span id="cb61-19">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> stats:</span>
<span id="cb61-20">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">break</span></span>
<span id="cb61-21"></span>
<span id="cb61-22">            par <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>(stats, key<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>stats.get)</span>
<span id="cb61-23">            nuevo_id <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> i</span>
<span id="cb61-24">            trozos <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [merge(t, par, nuevo_id) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> t <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> trozos]</span>
<span id="cb61-25">            <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.merges[par] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> nuevo_id</span>
<span id="cb61-26">            <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.vocab[nuevo_id] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.vocab[par[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.vocab[par[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]</span>
<span id="cb61-27"></span>
<span id="cb61-28">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> verbose:</span>
<span id="cb61-29">                legible <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.vocab[nuevo_id].decode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>, errors<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"replace"</span>)</span>
<span id="cb61-30">                <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>nuevo_id<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>legible<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!r}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> (</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>stats[par]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> apariciones)"</span>)</span>
<span id="cb61-31"></span>
<span id="cb61-32">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> _encode_bytes(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, bs):</span>
<span id="cb61-33">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""BPE sobre los bytes de un solo trozo. Es el `encode` de la sección 6."""</span></span>
<span id="cb61-34">        ids <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>(bs)</span>
<span id="cb61-35">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">while</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(ids) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>:</span>
<span id="cb61-36">            stats <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> get_stats(ids)</span>
<span id="cb61-37">            par <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">min</span>(stats, key<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">lambda</span> p: <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.merges.get(p, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"inf"</span>)))</span>
<span id="cb61-38">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> par <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.merges:</span>
<span id="cb61-39">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">break</span></span>
<span id="cb61-40">            ids <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> merge(ids, par, <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.merges[par])</span>
<span id="cb61-41">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> ids</span>
<span id="cb61-42"></span>
<span id="cb61-43">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> encode(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, texto):</span>
<span id="cb61-44">        ids <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb61-45">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> trozo <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>._re.findall(texto):</span>
<span id="cb61-46">            ids.extend(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>._encode_bytes(trozo.encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)))</span>
<span id="cb61-47">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> ids</span>
<span id="cb61-48"></span>
<span id="cb61-49">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> decode(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, ids):</span>
<span id="cb61-50">        bs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">b""</span>.join(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.vocab[i] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> ids)</span>
<span id="cb61-51">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> bs.decode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>, errors<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"replace"</span>)</span></code></pre></div></div>
</details>
</div>
<p>Dos cosas a notar. En <code>train</code>, el diccionario <code>stats</code> se comparte entre todos los trozos: por eso <code>get_stats</code> recibía ese segundo argumento opcional. Y en <code>encode</code>, el bucle de fusiones corre por trozo, no sobre el texto completo, que es la mitad de codificación que le corresponde a la restricción impuesta en el entrenamiento.</p>
<p>Entrenemos las dos variantes de patrón y comparémoslas contra la versión sin regex, todas con el mismo vocabulario de 512:</p>
<div id="3175256f" class="cell" data-execution_count="32">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb62" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb62-1">tok_gpt2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> RegexTokenizer(GPT2_SPLIT_PATTERN)</span>
<span id="cb62-2">tok_gpt2.train(texto_en, vocab_size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">512</span>)</span>
<span id="cb62-3"></span>
<span id="cb62-4">tok <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> RegexTokenizer(GPT4_SPLIT_PATTERN)</span>
<span id="cb62-5">tok.train(texto_en, vocab_size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">512</span>)</span>
<span id="cb62-6"></span>
<span id="cb62-7"></span>
<span id="cb62-8"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> tokens_mezclados(vocab):</span>
<span id="cb62-9">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> [</span>
<span id="cb62-10">        vocab[idx].decode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>, errors<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"replace"</span>)</span>
<span id="cb62-11">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> idx <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">512</span>)</span>
<span id="cb62-12">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> mezcla_categorias(vocab[idx].decode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>, errors<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"replace"</span>))</span>
<span id="cb62-13">    ]</span>
<span id="cb62-14"></span>
<span id="cb62-15"></span>
<span id="cb62-16">bytes_totales <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(ids_en)</span>
<span id="cb62-17">variantes <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [</span>
<span id="cb62-18">    (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"sin regex"</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(ids_512), tokens_mezclados(vocab_512)),</span>
<span id="cb62-19">    (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"regex GPT-2"</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(tok_gpt2.encode(texto_en)), tokens_mezclados(tok_gpt2.vocab)),</span>
<span id="cb62-20">    (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"regex GPT-4"</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(tok.encode(texto_en)), tokens_mezclados(tok.vocab)),</span>
<span id="cb62-21">]</span>
<span id="cb62-22"></span>
<span id="cb62-23"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"bytes originales: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>bytes_totales<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb62-24"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> nombre, n_tokens, mezclados <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> variantes:</span>
<span id="cb62-25">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>nombre<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;12}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>n_tokens<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;6}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> tokens  (</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>bytes_totales <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> n_tokens<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.2f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">X)  "</span></span>
<span id="cb62-26">          <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(mezclados)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;2}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> tokens que mezclan categorías"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>bytes originales: 24597

   sin regex:  11442 tokens  (2.15X)  26 tokens que mezclan categorías
 regex GPT-2:  11699 tokens  (2.10X)   0 tokens que mezclan categorías
 regex GPT-4:  11694 tokens  (2.10X)   4 tokens que mezclan categorías</code></pre>
</div>
</div>
<p>Dos lecturas de esa tabla.</p>
<p><strong>La compresión baja.</strong> Con regex comprimimos <em>menos</em>, y era esperable: le prohibimos al algoritmo justamente las fusiones más rentables, las que cruzan categorías. Es el precio que se paga a cambio de un vocabulario más limpio, y es una decisión de diseño deliberada.</p>
<p><strong>El regex hace lo que promete, con una excepción interesante.</strong> Con el patrón de GPT-2 no queda ni un token mezclado. Con el de GPT-4 quedan unos pocos:</p>
<div id="b9ee9652" class="cell" data-execution_count="33">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb64" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb64-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(tokens_mezclados(tok.vocab))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>['’s', '-b', '’t', '-bit']</code></pre>
</div>
</div>
<p>No es un error: es exactamente la rama <code>[^\r\n\p{L}\p{N}]?+\p{L}+</code> que agregó GPT-4, la que permite pegar <strong>un</strong> símbolo delante de una palabra. Es la misma que hace que <code>¿cómo</code> quede entero, y trae de arrastre el guion de <code>-bit</code> y el apóstrofo tipográfico de <code>’s</code>. GPT-4 relajó a propósito la restricción de GPT-2 para manejar mejor los idiomas que no son inglés.</p>
<p>Y así se ven los tokens que aprendió el tokenizador con el patrón de GPT-4:</p>
<div id="83e156c4" class="cell" data-execution_count="34">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb66" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb66-1">aprendidos <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [tok.vocab[idx].decode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>, errors<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"replace"</span>) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> idx <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">512</span>)]</span>
<span id="cb66-2"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">", "</span>.join(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">repr</span>(s) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> aprendidos[:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">40</span>]))</span>
<span id="cb66-3"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"..."</span>)</span>
<span id="cb66-4"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">", "</span>.join(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">repr</span>(s) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> aprendidos[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">20</span>:]))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>'in', ' t', ' a', 'er', 'co', ' th', '�', ' s', ' o', 'de', 're', 'it', ' co', ' the', 'ing', 'en', ' p', 'at', 'or', 'an', 'le', ' U', ' in', ' w', 'ar', ' b', 'es', 'ac', ' of', 'on', ' m', 'al', ' f', 'ed', ' c', 'is', 'nd', ' to', 'ts', 'ic'
...
' r', 'ight', 'age', ' do', ' not', 'nder', 'pe', 'quen', ' E', ' j', ' li', 'am', ' characters', ' which', '-bit', '–', ' +', 'composed', '̰', '̳'</code></pre>
</div>
</div>
<p>Los primeros son bigramas del inglés; los últimos, palabras enteras con su espacio adelante. Se ve el algoritmo escalando de a poco de letras a palabras. Y el viaje redondo sigue valiendo:</p>
<div id="9871f0ee" class="cell" data-execution_count="35">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb68" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb68-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(tok.decode(tok.encode(texto_en)) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> texto_en)</span>
<span id="cb68-2"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(tok.decode(tok.encode(validacion)) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> validacion)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>True
True</code></pre>
</div>
</div>
</section>
<section id="sec-tiktoken" class="level2" data-number="9">
<h2 data-number="9" class="anchored" data-anchor-id="sec-tiktoken"><span class="header-section-number">9</span> Comparación contra el tokenizador de verdad</h2>
<p><code>tiktoken</code> trae los tokenizadores reales de los modelos de OpenAI. Son tres generaciones:</p>
<div id="b0fb249a" class="cell" data-execution_count="36">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb70" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb70-1">encodings <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {</span>
<span id="cb70-2">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"gpt2"</span>: tiktoken.get_encoding(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"gpt2"</span>),                 <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># GPT-2, GPT-3</span></span>
<span id="cb70-3">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cl100k_base"</span>: tiktoken.get_encoding(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cl100k_base"</span>),   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># GPT-3.5, GPT-4</span></span>
<span id="cb70-4">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"o200k_base"</span>: tiktoken.get_encoding(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"o200k_base"</span>),     <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># GPT-4o y posteriores</span></span>
<span id="cb70-5">}</span>
<span id="cb70-6"></span>
<span id="cb70-7"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> nombre, e <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> encodings.items():</span>
<span id="cb70-8">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>nombre<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;12}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">: vocabulario de </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>mil(e.n_vocab)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> tokens"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>        gpt2: vocabulario de 50.257 tokens
 cl100k_base: vocabulario de 100.277 tokens
  o200k_base: vocabulario de 200.019 tokens</code></pre>
</div>
</div>
<p>Y así rinden sobre nuestros dos corpus, que recordemos tienen prácticamente los mismos bytes:</p>
<div id="a34312a5" class="cell" data-execution_count="37">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb72" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb72-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'encoding'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;12}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'inglés'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;10}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'español'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;10}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'sobrecosto'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;12}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb72-2"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> nombre, e <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> encodings.items():</span>
<span id="cb72-3">    n_en <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(e.encode(texto_en))</span>
<span id="cb72-4">    n_es <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(e.encode(texto_es))</span>
<span id="cb72-5">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>nombre<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;12}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>n_en<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;10}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>n_es<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;10}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (n_es <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> n_en <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;11.0f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">%"</span>)</span>
<span id="cb72-6"></span>
<span id="cb72-7"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>()</span>
<span id="cb72-8"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'nuestro (512)'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;12}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(tok.encode(texto_en))<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;10}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> "</span></span>
<span id="cb72-9">      <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(tok.encode(texto_es))<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;10}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>    encoding     inglés    español   sobrecosto
        gpt2       7019       9104          30%
 cl100k_base       6564       7380          12%
  o200k_base       6447       6510           1%

nuestro (512)      11694      15583</code></pre>
</div>
</div>
<p>Ahí está, medido, el <strong>impuesto del español</strong>: con el tokenizador de GPT-2, el mismo contenido en español consume un 30% más de tokens que en inglés. Eso significa 30% más de costo por API, 30% menos de texto en la ventana de contexto y 30% más de cómputo por la misma información. Con <code>cl100k_base</code> la brecha baja a 12%, y con <code>o200k_base</code> a casi nada: cada generación de tokenizadores mejoró la cobertura multilingüe, agrandando el vocabulario y balanceando mejor el corpus de entrenamiento.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Nota
</div>
</div>
<div class="callout-body-container callout-body">
<p>Nuestro tokenizador de 512 tokens, entrenado sobre 24 KB de inglés, obviamente pierde por mucho. Lo interesante es que la diferencia es de escala, no de algoritmo: <code>cl100k_base</code> es el mismo código que escribimos, con 100.256 fusiones en lugar de 256 y un corpus de entrenamiento gigantesco.</p>
</div>
</div>
<section id="los-bytes-están-permutados" class="level3" data-number="9.1">
<h3 data-number="9.1" class="anchored" data-anchor-id="los-bytes-están-permutados"><span class="header-section-number">9.1</span> Los bytes están permutados</h3>
<p>Si intentamos leer las fusiones de GPT-4 nos topamos con dos sorpresas. La primera:</p>
<div id="8b2235c5" class="cell" data-execution_count="38">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb74" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb74-1">cl <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> encodings[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cl100k_base"</span>]</span>
<span id="cb74-2">byte_shuffle <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {i: cl._mergeable_ranks[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">bytes</span>([i])] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>)}</span>
<span id="cb74-3"></span>
<span id="cb74-4"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"¿el byte i tiene id i?"</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">all</span>(k <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> v <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> k, v <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> byte_shuffle.items()))</span>
<span id="cb74-5"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"primeros:"</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>(byte_shuffle.items())[:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>])</span>
<span id="cb74-6"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"el espacio (byte 32) tiene id"</span>, byte_shuffle[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">32</span>])</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>¿el byte i tiene id i? False
primeros: [(0, 188), (1, 189), (2, 190), (3, 191), (4, 192), (5, 193)]
el espacio (byte 32) tiene id 220</code></pre>
</div>
</div>
<p>Los 256 bytes iniciales <strong>no</strong> están en el orden obvio: el byte 0 es el id 188, el espacio es el 220. Es una permutación arbitraria, heredada de una decisión de implementación de GPT-2, sin ningún significado. Cualquier implementación que quiera producir los mismos ids que OpenAI tiene que aplicar esta permutación al codificar y la inversa al decodificar.</p>
</section>
<section id="recuperar-las-fusiones" class="level3" data-number="9.2">
<h3 data-number="9.2" class="anchored" data-anchor-id="recuperar-las-fusiones"><span class="header-section-number">9.2</span> Recuperar las fusiones</h3>
<p>La segunda sorpresa: <code>tiktoken</code> no guarda <code>merges</code>. Guarda <code>_mergeable_ranks</code>, que mapea <strong>bytes → rango</strong>, sin decir de qué dos padres salió cada token. Pero se puede reconstruir: para un token cualquiera, sus dos padres son los que resultan de aplicarle BPE usando solo las fusiones <strong>anteriores</strong> a él.</p>
<div id="6271abc3" class="cell" data-execution_count="39">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb76" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb76-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> bpe(ranks, token, max_rank):</span>
<span id="cb76-2">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Aplica BPE a `token` usando solo fusiones de rango menor a `max_rank`."""</span></span>
<span id="cb76-3">    partes <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">bytes</span>([b]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> b <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> token]</span>
<span id="cb76-4">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">while</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>:</span>
<span id="cb76-5">        mejor_i, mejor_rango <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>, <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span></span>
<span id="cb76-6">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i, (a, b) <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">enumerate</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">zip</span>(partes, partes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:])):</span>
<span id="cb76-7">            rango <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ranks.get(a <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> b)</span>
<span id="cb76-8">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> rango <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">is</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> (mejor_rango <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">is</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> rango <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> mejor_rango):</span>
<span id="cb76-9">                mejor_i, mejor_rango <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> i, rango</span>
<span id="cb76-10">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> mejor_rango <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">is</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> mejor_rango <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> max_rank:</span>
<span id="cb76-11">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">break</span></span>
<span id="cb76-12">        partes <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (partes[:mejor_i]</span>
<span id="cb76-13">                  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> [partes[mejor_i] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> partes[mejor_i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]</span>
<span id="cb76-14">                  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> partes[mejor_i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>:])</span>
<span id="cb76-15">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> partes</span>
<span id="cb76-16"></span>
<span id="cb76-17"></span>
<span id="cb76-18"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> recuperar_merges(ranks):</span>
<span id="cb76-19">    merges <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {}</span>
<span id="cb76-20">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> token, rango <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> ranks.items():</span>
<span id="cb76-21">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(token) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:</span>
<span id="cb76-22">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">continue</span>                      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># los bytes sueltos no son fusiones</span></span>
<span id="cb76-23">        p0, p1 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> bpe(ranks, token, max_rank<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>rango)</span>
<span id="cb76-24">        merges[(ranks[p0], ranks[p1])] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> rango</span>
<span id="cb76-25">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> merges</span>
<span id="cb76-26"></span>
<span id="cb76-27"></span>
<span id="cb76-28">merges_gpt4 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> recuperar_merges(cl._mergeable_ranks)</span>
<span id="cb76-29"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>mil(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(merges_gpt4))<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> fusiones recuperadas"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>100.000 fusiones recuperadas</code></pre>
</div>
</div>
<p>Y ahora sí podemos ver las <strong>primeras fusiones que aprendió GPT-4</strong>, que es una de las cosas más reveladoras de todo el apunte:</p>
<div id="e060b673" class="cell" data-execution_count="40">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb78" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb78-1">inverso <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {rango: token <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> token, rango <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> cl._mergeable_ranks.items()}</span>
<span id="cb78-2"></span>
<span id="cb78-3"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (a, b), rango <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sorted</span>(merges_gpt4.items(), key<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">lambda</span> kv: kv[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])[:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span>]:</span>
<span id="cb78-4">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>rango<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">:  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>inverso[a]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!r}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> + </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>inverso[b]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!r}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  =  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>inverso[rango]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!r}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>256:  b' ' + b' '  =  b'  '
257:  b'  ' + b'  '  =  b'    '
258:  b'i' + b'n'  =  b'in'
259:  b' ' + b't'  =  b' t'
260:  b'    ' + b'    '  =  b'        '
261:  b'e' + b'r'  =  b'er'
262:  b'  ' + b' '  =  b'   '
263:  b'o' + b'n'  =  b'on'</code></pre>
</div>
</div>
<p>La primera fusión de GPT-4 es <strong>dos espacios</strong>. La segunda, cuatro espacios. La quinta, ocho. El tokenizador de GPT-4 dedica sus primerísimas fusiones —las más valiosas, las que se aplican antes que todo lo demás— a comprimir corridas de espacios. Es la huella de un corpus con muchísimo código fuente, y la contracara de aquel <code>\s*[\r\n]</code> que agregaron al patrón. Comparado con GPT-2, que gastaba un token por cada espacio de indentación, es una mejora enorme para programar.</p>
</section>
</section>
<section id="sec-especiales" class="level2" data-number="10">
<h2 data-number="10" class="anchored" data-anchor-id="sec-especiales"><span class="header-section-number">10</span> Tokens especiales</h2>
<p>Un tokenizador que solo hace BPE alcanza para representar texto, pero un LLM necesita además marcar <strong>estructura</strong>: dónde termina un documento, dónde empieza el turno del usuario, dónde va a ir el relleno en un autocompletado. Para eso existen los <strong>tokens especiales</strong>: ids que no salen de ninguna fusión y que no corresponden a ningún texto.</p>
<div id="1960c0b7" class="cell" data-execution_count="41">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb80" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb80-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> nombre, e <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> encodings.items():</span>
<span id="cb80-2">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>nombre<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">:"</span>)</span>
<span id="cb80-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> token, idx <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> e._special_tokens.items():</span>
<span id="cb80-4">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"    </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>idx<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>token<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>gpt2:
    50256  &lt;|endoftext|&gt;
cl100k_base:
    100257  &lt;|endoftext|&gt;
    100258  &lt;|fim_prefix|&gt;
    100259  &lt;|fim_middle|&gt;
    100260  &lt;|fim_suffix|&gt;
    100276  &lt;|endofprompt|&gt;
o200k_base:
    199999  &lt;|endoftext|&gt;
    200018  &lt;|endofprompt|&gt;</code></pre>
</div>
</div>
<p><code>&lt;|endoftext|&gt;</code> es el más importante: es lo que se intercala entre documentos no relacionados al armar el corpus de entrenamiento. Sin él, el modelo aprendería que después de una receta de tortas viene el código fuente de Linux. Es la señal de “borrá el contexto, esto es otra cosa”.</p>
<p>Los <code>&lt;|fim_*|&gt;</code> son de <em>fill in the middle</em>: permiten pedirle al modelo que complete en el medio de un texto dándole el prefijo y el sufijo, que es exactamente lo que hace un autocompletado de código en un editor. Y los modelos de chat agregan más (<code>&lt;|im_start|&gt;</code>, <code>&lt;|im_end|&gt;</code>) para delimitar los turnos de la conversación: el “formato de chat” no es más que texto con tokens especiales adentro.</p>
<p>Dado que no salen de BPE, hay que manejarlos aparte: se busca el token especial en el texto, se lo saca, y a los pedazos que quedan se les aplica BPE normalmente. Agreguemos eso a nuestra clase:</p>
<div id="f5c18eac" class="cell" data-execution_count="42">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb82" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb82-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> TokenizerConEspeciales(RegexTokenizer):</span>
<span id="cb82-2"></span>
<span id="cb82-3">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, pattern<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>):</span>
<span id="cb82-4">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">__init__</span>(pattern)</span>
<span id="cb82-5">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.especiales <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {}</span>
<span id="cb82-6">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.especiales_inv <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {}</span>
<span id="cb82-7"></span>
<span id="cb82-8">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> registrar_especiales(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, especiales):</span>
<span id="cb82-9">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""`especiales` es un dict texto -&gt; id. Los ids van por encima del vocabulario."""</span></span>
<span id="cb82-10">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.especiales <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">dict</span>(especiales)</span>
<span id="cb82-11">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.especiales_inv <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {idx: txt <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> txt, idx <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> especiales.items()}</span>
<span id="cb82-12"></span>
<span id="cb82-13">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> encode(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, texto, permitir_especiales<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>):</span>
<span id="cb82-14">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> permitir_especiales <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.especiales:</span>
<span id="cb82-15">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().encode(texto)</span>
<span id="cb82-16"></span>
<span id="cb82-17">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Partimos por los tokens especiales, conservándolos (el grupo de captura hace</span></span>
<span id="cb82-18">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># que `split` los devuelva como elementos de la lista).</span></span>
<span id="cb82-19">        patron <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"("</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"|"</span>.join(re.escape(t) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> t <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.especiales) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">")"</span></span>
<span id="cb82-20"></span>
<span id="cb82-21">        ids <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb82-22">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> parte <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> re.split(patron, texto):</span>
<span id="cb82-23">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> parte <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.especiales:</span>
<span id="cb82-24">                ids.append(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.especiales[parte])</span>
<span id="cb82-25">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">elif</span> parte:</span>
<span id="cb82-26">                ids.extend(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">super</span>().encode(parte))</span>
<span id="cb82-27">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> ids</span>
<span id="cb82-28"></span>
<span id="cb82-29">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> decode(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>, ids):</span>
<span id="cb82-30">        piezas <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> []</span>
<span id="cb82-31">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> idx <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> ids:</span>
<span id="cb82-32">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> idx <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.vocab:</span>
<span id="cb82-33">                piezas.append(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.vocab[idx])</span>
<span id="cb82-34">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">elif</span> idx <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.especiales_inv:</span>
<span id="cb82-35">                piezas.append(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>.especiales_inv[idx].encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>))</span>
<span id="cb82-36">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span>:</span>
<span id="cb82-37">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">raise</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">ValueError</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"id fuera del vocabulario: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>idx<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span>
<span id="cb82-38">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">b""</span>.join(piezas).decode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>, errors<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"replace"</span>)</span>
<span id="cb82-39"></span>
<span id="cb82-40"></span>
<span id="cb82-41">tok_esp <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> TokenizerConEspeciales()</span>
<span id="cb82-42">tok_esp.merges <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tok.merges          <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># reusamos lo aprendido en la sección 8</span></span>
<span id="cb82-43">tok_esp.vocab <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tok.vocab</span>
<span id="cb82-44">tok_esp.registrar_especiales({<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;|endoftext|&gt;"</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">512</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;|usuario|&gt;"</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">513</span>})</span>
<span id="cb82-45"></span>
<span id="cb82-46">mensaje <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;|usuario|&gt;the code&lt;|endoftext|&gt;"</span></span>
<span id="cb82-47">ids <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tok_esp.encode(mensaje)</span>
<span id="cb82-48"></span>
<span id="cb82-49"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(ids)</span>
<span id="cb82-50"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(tok_esp.decode(ids))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>[513, 325, 101, 298, 512]
&lt;|usuario|&gt;the code&lt;|endoftext|&gt;</code></pre>
</div>
</div>
<p>Comparemos con lo que pasa si no los tratamos como especiales:</p>
<div id="54363192" class="cell" data-execution_count="43">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb84" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb84-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(tok_esp.encode(mensaje, permitir_especiales<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>[60, 124, 316, 117, 280, 105, 111, 124, 62, 325, 101, 298, 60, 124, 271, 100, 111, 102, 116, 364, 116, 124, 62]</code></pre>
</div>
</div>
<p>Sin el tratamiento especial, <code>&lt;|usuario|&gt;</code> se convierte en una docena de tokens comunes: es texto como cualquier otro. Y ahí está el problema de seguridad: si tu aplicación pega la entrada del usuario en un prompt y <strong>sí</strong> habilita los tokens especiales, un usuario puede escribir literalmente <code>&lt;|im_end|&gt;</code> y cerrar su propio turno, haciéndose pasar por el sistema. Por eso <code>tiktoken</code> obliga a pedirlo explícitamente:</p>
<div id="3e1a3889" class="cell" data-execution_count="44">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb86" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb86-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">try</span>:</span>
<span id="cb86-2">    cl.encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;|endoftext|&gt; hola"</span>)</span>
<span id="cb86-3"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">except</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">ValueError</span> <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> error:</span>
<span id="cb86-4">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(error)[:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">180</span>], <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"..."</span>)</span>
<span id="cb86-5"></span>
<span id="cb86-6"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(cl.encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&lt;|endoftext|&gt; hola"</span>, allowed_special<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"all"</span>))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>Encountered text corresponding to disallowed special token '&lt;|endoftext|&gt;'.
If you want this text to be encoded as a special token, pass it to `allowed_special`, e.g. `allowed_spec ...
[100257, 305, 8083]</code></pre>
</div>
</div>
<p>Por defecto levanta excepción; hay que decir <code>allowed_special</code> a propósito. La regla práctica es tratar el texto del usuario siempre con los especiales <strong>deshabilitados</strong>, y construir la estructura del prompt vos, desde el código.</p>
</section>
<section id="sec-sentencepiece" class="level2" data-number="11">
<h2 data-number="11" class="anchored" data-anchor-id="sec-sentencepiece"><span class="header-section-number">11</span> SentencePiece: lo que usan Llama y Mistral</h2>
<p>Fuera de OpenAI, la biblioteca más usada es <a href="https://github.com/google/sentencepiece">SentencePiece</a> (Llama, Mistral, Gemma). Hace BPE, igual que nosotros, pero con una diferencia de fondo:</p>
<blockquote class="blockquote">
<p><strong><code>tiktoken</code></strong> codifica el texto a UTF-8 y corre BPE <strong>sobre los bytes</strong>. <strong>SentencePiece</strong> corre BPE <strong>sobre los code points Unicode</strong>, y recién al final decide qué hacer con los caracteres raros.</p>
</blockquote>
<p>Ese orden invertido explica toda la lista de opciones raras que hay que configurar:</p>
<ul>
<li><strong><code>character_coverage</code></strong> (típicamente 0.9995): qué proporción de los caracteres del corpus entran al vocabulario base. Los que quedan afuera son “caracteres raros”, y hay que decidir qué hacer con ellos.</li>
<li><strong><code>byte_fallback</code></strong>: si está en <code>True</code>, los caracteres que quedaron afuera se descomponen en sus bytes UTF-8, que sí están en el vocabulario. Es el parche que hace que SentencePiece se comporte parecido a un tokenizador de bytes. Llama 2 lo usa.</li>
<li><strong><code>unk_id</code></strong>: si <code>byte_fallback</code> está en <code>False</code>, los caracteres raros se mapean todos al token <code>&lt;unk&gt;</code>, y ahí la información <strong>se pierde</strong>: <code>decode(encode(t)) != t</code>. Nada de eso puede pasar en un tokenizador de bytes.</li>
<li><strong><code>add_dummy_prefix</code></strong>: agrega un espacio al principio de todo texto, para que <code>"hola"</code> y <code>" hola"</code> se tokenicen igual. Es una decisión distinta a la de OpenAI, que prefiere que sean tokens diferentes.</li>
<li>La normalización Unicode (NFKC por defecto) puede <strong>modificar el texto</strong> antes de tokenizar.</li>
</ul>
<p>También cambia la notación: SentencePiece representa el espacio con el carácter <code>▁</code> (U+2581), así que los tokens se ven como <code>▁hola</code>, <code>▁mundo</code>.</p>
<div class="callout callout-style-default callout-warning callout-titled" title="Esta celda no se ejecuta">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Advertencia</span>Esta celda no se ejecuta
</div>
</div>
<div class="callout-body-container callout-body">
<p><code>sentencepiece</code> no tiene wheel para Python 3.13 y compilarlo requiere <code>cmake</code>, así que este bloque está marcado para no ejecutarse al renderizar. El código es correcto: si lo corrés en un entorno con la biblioteca instalada (Colab la trae), funciona tal cual.</p>
</div>
</div>
<div id="ddd6af30" class="cell" data-execution_count="45">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb88" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb88-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> sentencepiece <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> spm</span>
<span id="cb88-2"></span>
<span id="cb88-3">spm.SentencePieceTrainer.train(</span>
<span id="cb88-4">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">input</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"datos/texto_es.txt"</span>,</span>
<span id="cb88-5">    model_prefix<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tok_es"</span>,</span>
<span id="cb88-6">    model_type<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bpe"</span>,</span>
<span id="cb88-7">    vocab_size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">512</span>,</span>
<span id="cb88-8">    character_coverage<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.9995</span>,</span>
<span id="cb88-9">    byte_fallback<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>,        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># sin esto, los caracteres raros se pierden en &lt;unk&gt;</span></span>
<span id="cb88-10">    add_dummy_prefix<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>,</span>
<span id="cb88-11">    normalization_rule_name<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"identity"</span>,   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># sin normalización, para no alterar el texto</span></span>
<span id="cb88-12">)</span>
<span id="cb88-13"></span>
<span id="cb88-14">sp <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> spm.SentencePieceProcessor(model_file<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tok_es.model"</span>)</span>
<span id="cb88-15"></span>
<span id="cb88-16"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(sp.encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"hola mundo"</span>, out_type<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>))</span>
<span id="cb88-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># -&gt; ['▁hola', '▁mun', 'do']   (el ▁ es el espacio)</span></span>
<span id="cb88-18"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(sp.encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"안녕하세요"</span>, out_type<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>))</span>
<span id="cb88-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># con byte_fallback=True se parte en bytes UTF-8: ['&lt;0xEC&gt;', '&lt;0x95&gt;', ...]</span></span></code></pre></div></div>
</details>
</div>
<p>El resumen práctico: SentencePiece tiene muchas más perillas, arrastra decisiones históricas y puede perder información. Trabajar sobre bytes desde el principio, como hicimos nosotros, evita todos esos problemas de raíz.</p>
</section>
<section id="sec-vocab-size" class="level2" data-number="12">
<h2 data-number="12" class="anchored" data-anchor-id="sec-vocab-size"><span class="header-section-number">12</span> Cuánto vocabulario conviene</h2>
<p><code>vocab_size</code> es el único hiperparámetro real del tokenizador, y no se puede elegir mirando una sola cosa. Del lado de las ventajas, más vocabulario comprime más. Midámoslo: entrenemos hasta 2048 registrando cuántos tokens quedan después de cada fusión.</p>
<div id="eb456e2c" class="cell" data-execution_count="46">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb89" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb89-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> curva_compresion(texto, vocab_max, pattern<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>):</span>
<span id="cb89-2">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""Entrena BPE registrando el largo de la secuencia después de cada fusión."""</span></span>
<span id="cb89-3">    compilado <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> re.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">compile</span>(pattern <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> GPT4_SPLIT_PATTERN)</span>
<span id="cb89-4">    trozos <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>(t.encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>)) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> t <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> compilado.findall(texto)]</span>
<span id="cb89-5"></span>
<span id="cb89-6">    largo <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">lambda</span>: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(t) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> t <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> trozos)</span>
<span id="cb89-7">    curva <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>, largo())]</span>
<span id="cb89-8"></span>
<span id="cb89-9">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(vocab_max <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span>):</span>
<span id="cb89-10">        stats <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {}</span>
<span id="cb89-11">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> trozo <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> trozos:</span>
<span id="cb89-12">            get_stats(trozo, stats)</span>
<span id="cb89-13">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> stats:</span>
<span id="cb89-14">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">break</span></span>
<span id="cb89-15">        par <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>(stats, key<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>stats.get)</span>
<span id="cb89-16">        trozos <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [merge(t, par, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">256</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> i) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> t <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> trozos]</span>
<span id="cb89-17">        curva.append((<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">257</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> i, largo()))</span>
<span id="cb89-18"></span>
<span id="cb89-19">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> curva</span>
<span id="cb89-20"></span>
<span id="cb89-21"></span>
<span id="cb89-22">curva_en <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> curva_compresion(texto_en, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2048</span>)</span>
<span id="cb89-23">curva_es <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> curva_compresion(texto_es, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2048</span>)</span>
<span id="cb89-24"></span>
<span id="cb89-25"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> v, n <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> curva_en[::<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">384</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> [curva_en[<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]]:</span>
<span id="cb89-26">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"vocab </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>v<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;5}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">:  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>n<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;6}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> tokens  (</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(ids_en) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> n<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.2f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">X)"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>vocab   256:   24597 tokens  (1.00X)
vocab   640:   10382 tokens  (2.37X)
vocab  1024:    8357 tokens  (2.94X)
vocab  1408:    7367 tokens  (3.34X)
vocab  1792:    6662 tokens  (3.69X)
vocab  2048:    6406 tokens  (3.84X)</code></pre>
</div>
</div>
<div id="cell-fig-curva" class="cell" data-execution_count="47">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb91" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb91-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> matplotlib.pyplot <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> plt</span>
<span id="cb91-2"></span>
<span id="cb91-3">fig, ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> plt.subplots(figsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span>))</span>
<span id="cb91-4"></span>
<span id="cb91-5"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> curva, etiqueta, texto_fuente <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> [</span>
<span id="cb91-6">    (curva_en, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"inglés"</span>, texto_en),</span>
<span id="cb91-7">    (curva_es, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"español"</span>, texto_es),</span>
<span id="cb91-8">]:</span>
<span id="cb91-9">    bytes_totales <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(texto_fuente.encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"utf-8"</span>))</span>
<span id="cb91-10">    vs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [v <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> v, _ <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> curva]</span>
<span id="cb91-11">    ratios <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [bytes_totales <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> n <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> _, n <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> curva]</span>
<span id="cb91-12">    ax.plot(vs, ratios, label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>etiqueta)</span>
<span id="cb91-13"></span>
<span id="cb91-14">ax.set_xlabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tamaño del vocabulario"</span>)</span>
<span id="cb91-15">ax.set_ylabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"compresión (bytes por token)"</span>)</span>
<span id="cb91-16">ax.legend()</span>
<span id="cb91-17">ax.grid(alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.3</span>)</span>
<span id="cb91-18">plt.show()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-curva" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-curva-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<a href="tokenizacion-bpe_files/figure-html/fig-curva-output-1.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1" title="Figura&nbsp;1: Compresión en función del tamaño del vocabulario, para los dos corpus (24 KB cada uno). Las dos curvas se aplanan: cada fusión nueva rinde menos que la anterior."><img src="https://luccafrachelle.github.io/porfolio_academia/posts/tokenizacion-bpe_files/figure-html/fig-curva-output-1.png" class="img-fluid figure-img"></a>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-curva-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figura&nbsp;1: Compresión en función del tamaño del vocabulario, para los dos corpus (24 KB cada uno). Las dos curvas se aplanan: cada fusión nueva rinde menos que la anterior.
</figcaption>
</figure>
</div>
</div>
</div>
<p>La curva se aplana: las primeras fusiones capturan los pares realmente frecuentes y las últimas apenas rascan. <strong>Rendimientos decrecientes.</strong></p>
<p>Y hay algo más en ese gráfico que conviene señalar: las dos curvas van prácticamente pegadas. Un tokenizador entrenado <strong>en español</strong> comprime español igual de bien que uno entrenado en inglés comprime inglés. El idioma no tiene nada de intrínsecamente caro; lo caro es usar un tokenizador entrenado en otro idioma. Es exactamente el sobrecosto que medimos en la sección 9.</p>
<p>Del otro lado del balance hay tres costos.</p>
<p><strong>Cuesta parámetros.</strong> La tabla de embeddings tiene <code>vocab_size × n_embd</code> parámetros, y en la salida hace falta otra matriz del mismo tamaño para proyectar a logits:</p>
<div id="4f3637ec" class="cell" data-execution_count="48">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb92" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb92-1">n_embd <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">768</span>                       <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># el ancho de GPT-2 small</span></span>
<span id="cb92-2"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> v <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> [<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1_024</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50_257</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100_277</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">200_019</span>]:</span>
<span id="cb92-3">    tabla <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> v <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> n_embd</span>
<span id="cb92-4">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"vocab </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>mil(v)<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;9}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">:  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>tabla <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e6</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;5.1f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">M parámetros por tabla "</span></span>
<span id="cb92-5">          <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"(</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> tabla <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">124e6</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;4.1f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">% de los 124M de GPT-2 small)"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>vocab     1.024:    0.8M parámetros por tabla ( 0.6% de los 124M de GPT-2 small)
vocab    50.257:   38.6M parámetros por tabla (31.1% de los 124M de GPT-2 small)
vocab   100.277:   77.0M parámetros por tabla (62.1% de los 124M de GPT-2 small)
vocab   200.019:  153.6M parámetros por tabla (123.9% de los 124M de GPT-2 small)</code></pre>
</div>
</div>
<p>GPT-2 small tiene 124M parámetros en total, y con su vocabulario de 50.257 tokens son 38,6M —casi un tercio del modelo— los que se van en la tabla de embeddings. GPT-2 comparte esa matriz con la proyección de salida (<em>weight tying</em>), así que la paga una sola vez; los modelos que no lo hacen la pagan dos veces. En cualquier caso, duplicar el vocabulario no es gratis.</p>
<p><strong>Cuesta cómputo en la salida.</strong> Cada paso de generación calcula un softmax sobre todo el vocabulario. Con 200.000 tokens es una operación considerable, que además hay que hacer una vez por token generado.</p>
<p><strong>Los tokens raros quedan mal entrenados.</strong> Cuanto más grande el vocabulario, menos veces aparece cada token en el corpus. Un token que apareció tres veces en todo el entrenamiento tiene un embedding que es prácticamente su inicialización aleatoria. Volvemos a esto en la sección siguiente, con el caso de <code>SolidGoldMagikarp</code>.</p>
<div class="callout callout-style-default callout-tip callout-titled" title="Extender el vocabulario después">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>Extender el vocabulario después
</div>
</div>
<div class="callout-body-container callout-body">
<p>Se puede agregar tokens a un modelo ya entrenado: se agranda la tabla de embeddings, se inicializan las filas nuevas (idealmente con el promedio de los embeddings de los tokens que ese texto usaba antes) y se hace fine-tuning. Es lo habitual al adaptar un modelo a un idioma nuevo o a un dominio técnico, y también la base de técnicas de compresión de prompts, donde se entrenan tokens nuevos que resumen instrucciones enteras.</p>
</div>
</div>
</section>
<section id="sec-rarezas" class="level2" data-number="13">
<h2 data-number="13" class="anchored" data-anchor-id="sec-rarezas"><span class="header-section-number">13</span> Las rarezas, ya explicadas</h2>
<p>Volvamos a la lista de la sección 1. Ahora tenemos todo para responderla.</p>
<section id="por-qué-no-sabe-deletrear-ni-contar-letras" class="level3" data-number="13.1">
<h3 data-number="13.1" class="anchored" data-anchor-id="por-qué-no-sabe-deletrear-ni-contar-letras"><span class="header-section-number">13.1</span> Por qué no sabe deletrear ni contar letras</h3>
<div id="419adf6c" class="cell" data-execution_count="49">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb94" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb94-1">o200 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> encodings[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"o200k_base"</span>]</span>
<span id="cb94-2"></span>
<span id="cb94-3"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> palabra <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"constitución"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ubiquitous"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"DefaultCellStyle"</span>]:</span>
<span id="cb94-4">    trozos <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [o200.decode([i]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> o200.encode(palabra)]</span>
<span id="cb94-5">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>palabra<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;18}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  -&gt;  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>trozos<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>      constitución  -&gt;  ['constit', 'ución']
        ubiquitous  -&gt;  ['ub', 'iqu', 'it', 'ous']
  DefaultCellStyle  -&gt;  ['Default', 'Cell', 'Style']</code></pre>
</div>
</div>
<p>Si le preguntás cuántas letras <code>t</code> tiene <code>constitución</code>, el modelo no ve letras: ve dos tokens, <code>'constit'</code> y <code>'ución'</code>. Contar letras adentro de un token requiere que el modelo haya memorizado, durante el entrenamiento, la composición de cada token en caracteres. Algunos la aprenden parcialmente, y por eso a veces acierta. Pero no está <em>mirando</em> las letras: está recordando de memoria.</p>
<p>Por la misma razón, si le pedís que invierta un string falla, y si le pedís “primero separá las letras con guiones y después invertí la lista” acierta: al separarlas con guiones las convertís en tokens individuales, y recién ahí las puede manipular.</p>
</section>
<section id="por-qué-falla-en-aritmética" class="level3" data-number="13.2">
<h3 data-number="13.2" class="anchored" data-anchor-id="por-qué-falla-en-aritmética"><span class="header-section-number">13.2</span> Por qué falla en aritmética</h3>
<div id="659ec790" class="cell" data-execution_count="50">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb96" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb96-1">gpt2, o200 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> encodings[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"gpt2"</span>], encodings[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"o200k_base"</span>]</span>
<span id="cb96-2"></span>
<span id="cb96-3"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> numero <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1234"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1257"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"9999999"</span>]:</span>
<span id="cb96-4">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>numero<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;9}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">:  gpt2 </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>[gpt2.decode([i]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> gpt2.encode(numero)]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb96-5">          <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"   o200k </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>[o200.decode([i]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> o200.encode(numero)]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>     1234:  gpt2 ['12', '34']   o200k ['123', '4']
     1257:  gpt2 ['12', '57']   o200k ['125', '7']
  9999999:  gpt2 ['9999', '999']   o200k ['999', '999', '9']</code></pre>
</div>
</div>
<p><code>1234</code> se parte en <code>123</code> + <code>4</code>, y <code>1257</code> en <code>125</code> + <code>7</code>. La partición no tiene nada que ver con la estructura decimal del número: no separa unidades, decenas ni centenas, y dos números parecidos se parten distinto. Para sumar, el modelo tiene que aprender aritmética sobre representaciones fragmentadas de forma arbitraria. Que funcione tan bien como funciona es notable; que se equivoque con números largos, esperable.</p>
</section>
<section id="por-qué-el-español-cuesta-más" class="level3" data-number="13.3">
<h3 data-number="13.3" class="anchored" data-anchor-id="por-qué-el-español-cuesta-más"><span class="header-section-number">13.3</span> Por qué el español cuesta más</h3>
<p>Ya lo medimos en la sección 9: mismo contenido, 30% más de tokens con GPT-2. La causa es doble. Los caracteres con tilde ocupan dos bytes en UTF-8, así que arrancan costando el doble. Y sobre todo, las palabras en español aparecen mucho menos en el corpus del tokenizador, así que casi ninguna se ganó un token propio y quedan partidas en pedazos.</p>
</section>
<section id="por-qué-es-peor-escribiendo-python" class="level3" data-number="13.4">
<h3 data-number="13.4" class="anchored" data-anchor-id="por-qué-es-peor-escribiendo-python"><span class="header-section-number">13.4</span> Por qué es peor escribiendo Python</h3>
<div id="192d931c" class="cell" data-execution_count="51">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb98" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb98-1">codigo <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"def f(x):</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    if x &gt; 0:</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">        return x</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">    return -x</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb98-2"></span>
<span id="cb98-3"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> nombre, e <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> encodings.items():</span>
<span id="cb98-4">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>nombre<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;12}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(e.encode(codigo))<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;3}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> tokens"</span>)</span>
<span id="cb98-5"></span>
<span id="cb98-6"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>()</span>
<span id="cb98-7"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"gpt2  :"</span>, [gpt2.decode([i]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> gpt2.encode(codigo)][:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">14</span>])</span>
<span id="cb98-8"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cl100k:"</span>, [cl.decode([i]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> cl.encode(codigo)][:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">14</span>])</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>        gpt2:  32 tokens
 cl100k_base:  20 tokens
  o200k_base:  20 tokens

gpt2  : ['def', ' f', '(', 'x', '):', '\n', ' ', ' ', ' ', ' if', ' x', ' &gt;', ' 0', ':']
cl100k: ['def', ' f', '(x', '):\n', '   ', ' if', ' x', ' &gt;', ' ', '0', ':\n', '       ', ' return', ' x']</code></pre>
</div>
</div>
<p>El mismo código: 32 tokens con GPT-2, 20 con GPT-4. Mirá los trozos: GPT-2 gasta <strong>un token por cada espacio</strong> de indentación, mientras GPT-4 agrupa la indentación en un solo token. En Python la indentación es sintaxis, así que GPT-2 desperdiciaba una parte enorme de su contexto en espacios, y encima tenía que aprender la estructura del código a partir de secuencias larguísimas de tokens idénticos. En JavaScript, donde los espacios son opcionales, el problema era mucho menor. De ahí la fama.</p>
</section>
<section id="por-qué-molesta-un-espacio-al-final-del-prompt" class="level3" data-number="13.5">
<h3 data-number="13.5" class="anchored" data-anchor-id="por-qué-molesta-un-espacio-al-final-del-prompt"><span class="header-section-number">13.5</span> Por qué molesta un espacio al final del prompt</h3>
<div id="2c46dfd3" class="cell" data-execution_count="52">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb100" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb100-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> s <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"hola"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"hola "</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"hola  "</span>]:</span>
<span id="cb100-2">    ids <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> o200.encode(s)</span>
<span id="cb100-3">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>s<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!r:&gt;9}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  -&gt;  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>ids<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">  </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>[o200.decode([i]) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> ids]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>   'hola'  -&gt;  [122845]  ['hola']
  'hola '  -&gt;  [122845, 220]  ['hola', ' ']
 'hola  '  -&gt;  [122845, 256]  ['hola', '  ']</code></pre>
</div>
</div>
<p><code>'hola '</code> no es <code>'hola'</code> más un espacio en el mismo token: es <code>'hola'</code> seguido de un token de espacio suelto. Y como vimos en la sección 7, la convención es que los espacios viajan <strong>pegados a la palabra que sigue</strong>. Un espacio solo, huérfano, es una secuencia que casi no aparece en los datos de entrenamiento. El modelo queda parado en una parte rarísima de su distribución, y encima tiene que “adivinar” que la próxima palabra ya no debería empezar con espacio. De ahí que las APIs adviertan sobre los espacios finales en el prompt.</p>
</section>
<section id="por-qué-existen-tokens-que-rompen-el-modelo" class="level3" data-number="13.6">
<h3 data-number="13.6" class="anchored" data-anchor-id="por-qué-existen-tokens-que-rompen-el-modelo"><span class="header-section-number">13.6</span> Por qué existen tokens que rompen el modelo</h3>
<div id="795856b2" class="cell" data-execution_count="53">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb102" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb102-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"gpt2   ' SolidGoldMagikarp' -&gt;"</span>, gpt2.encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">" SolidGoldMagikarp"</span>))</span>
<span id="cb102-2"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cl100k ' SolidGoldMagikarp' -&gt;"</span>, cl.encode(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">" SolidGoldMagikarp"</span>))</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>gpt2   ' SolidGoldMagikarp' -&gt; [43453]
cl100k ' SolidGoldMagikarp' -&gt; [22925, 26509, 34015, 1609, 8035]</code></pre>
</div>
</div>
<p>En GPT-2, <code>' SolidGoldMagikarp'</code> es <strong>un solo token</strong>. Era el nombre de un usuario de Reddit muy activo, presente miles de veces en el corpus con el que se entrenó el <em>tokenizador</em>. Pero ese corpus no era el mismo con el que se entrenó el <em>modelo</em>: en los datos del modelo, ese token aparecía casi nunca. Resultado: una fila de la tabla de embeddings que quedó prácticamente en su inicialización aleatoria.</p>
<p>Cuando se le pedía a GPT-2 o GPT-3 repetir esa palabra, el modelo hacía cosas erráticas: insultaba, cambiaba de tema, alucinaba. Son los <strong>tokens no entrenados</strong> (<em>undertrained tokens</em>), y aparecen cuando el corpus del tokenizador y el del modelo no coinciden. En <code>cl100k_base</code> ya no existe: se parte en cinco tokens comunes.</p>
</section>
<section id="por-qué-conviene-yaml-antes-que-json" class="level3" data-number="13.7">
<h3 data-number="13.7" class="anchored" data-anchor-id="por-qué-conviene-yaml-antes-que-json"><span class="header-section-number">13.7</span> Por qué conviene YAML antes que JSON</h3>
<div id="52806d0a" class="cell" data-execution_count="54">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb104" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb104-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> json</span>
<span id="cb104-2"></span>
<span id="cb104-3">datos <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"nombre"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Ana"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"edad"</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">31</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ciudad"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Montevideo"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"activo"</span>: <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>}</span>
<span id="cb104-4"></span>
<span id="cb104-5">como_json <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> json.dumps(datos, indent<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, ensure_ascii<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>)</span>
<span id="cb104-6">como_yaml <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"nombre: Ana</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">edad: 31</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">ciudad: Montevideo</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">activo: true</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb104-7"></span>
<span id="cb104-8"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"JSON: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(o200.encode(como_json))<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;3}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> tokens"</span>)</span>
<span id="cb104-9"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"YAML: </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>(o200.encode(como_yaml))<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:&gt;3}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;"> tokens"</span>)</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-stdout">
<pre><code>JSON:  31 tokens
YAML:  19 tokens</code></pre>
</div>
</div>
<p>Los mismos datos, 60% más de tokens en JSON. Las llaves, los corchetes, las comillas y la indentación son todos tokens, y no aportan información. Cuando el contexto o el costo importan, la elección del formato es una decisión de ingeniería con impacto medible.</p>
</section>
</section>
<section id="sec-cierre" class="level2" data-number="14">
<h2 data-number="14" class="anchored" data-anchor-id="sec-cierre"><span class="header-section-number">14</span> Para seguir</h2>
<p>Lo que construimos son unas 60 líneas de Python, y es esencialmente el mismo algoritmo que corre adentro de GPT-4. Lo que cambia es la escala del corpus, el tamaño del vocabulario y una capa de ingeniería para que sea rápido.</p>
<p>Vale la pena quedarse con la idea de fondo: el tokenizador es una etapa <strong>separada del modelo, sin aprendizaje por gradiente, y congelada para siempre</strong> una vez que se empieza a entrenar. Todo lo que decida —qué idiomas comprime bien, cómo parte los números, si la indentación cuesta caro— se convierte en una restricción permanente del modelo. Es la parte menos glamorosa de un LLM y la que explica una fracción sorprendente de sus fallas.</p>
<p>Para practicar, el recorrido de Karpathy en <a href="https://github.com/karpathy/minbpe">minbpe</a> cubre lo mismo de forma ejecutable y termina con ejercicios: entrenar un tokenizador propio, barrer <code>vocab_size</code> sobre otro corpus, y reproducir los ids de un tokenizador de referencia a partir de las fusiones.</p>
<section id="recursos" class="level3" data-number="14.1">
<h3 data-number="14.1" class="anchored" data-anchor-id="recursos"><span class="header-section-number">14.1</span> Recursos</h3>
<ul>
<li>Karpathy, A. <a href="https://www.youtube.com/watch?v=zduSFxRajkE"><em>Let’s build the GPT Tokenizer</em></a> — la clase en la que se basa este apunte.</li>
<li>Karpathy, A. <a href="https://github.com/karpathy/minbpe">minbpe</a> — implementación de referencia y los ejercicios originales.</li>
<li><a href="https://tiktokenizer.vercel.app/">tiktokenizer</a> — pegás texto y ves los tokens de cada modelo. Insustituible para desarrollar intuición.</li>
<li><a href="https://github.com/openai/tiktoken">tiktoken</a> — la biblioteca de OpenAI. Vale la pena leer <code>tiktoken/_educational.py</code>.</li>
<li>Sennrich, R., Haddow, B. y Birch, A. (2016). <a href="https://arxiv.org/abs/1508.07909"><em>Neural Machine Translation of Rare Words with Subword Units</em></a> — el paper que trajo BPE a NLP.</li>
<li>Beta, R. <a href="https://www.reedbeta.com/blog/programmers-intro-to-unicode/"><em>A Programmer’s Introduction to Unicode</em></a> — para la parte de Unicode y UTF-8.</li>
<li><a href="https://github.com/google/sentencepiece">SentencePiece</a> — la alternativa que usan Llama, Mistral y Gemma.</li>
</ul>


</section>
</section>

<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Volver arriba</a> ]]></description>
  <category>LLMs</category>
  <guid>https://luccafrachelle.github.io/porfolio_academia/posts/tokenizacion-bpe.html</guid>
  <pubDate>Fri, 11 Sep 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Double descent</title>
  <dc:creator>Lucca Frachelle</dc:creator>
  <link>https://luccafrachelle.github.io/porfolio_academia/posts/double_desent.html</link>
  <description><![CDATA[ 





<div class="project-downloads">
<p><a href="../assets/pdfs/double-descent.pdf" class="text-link">Leer en PDF</a></p>
</div>
<p>Al aumentar la complejidad de un modelo, esperamos que el error de prueba primero baje y después suba: la conocida curva en U. El <strong>double descent</strong> introduce otro escenario. Cerca del umbral en el que el modelo puede ajustar exactamente los datos de entrenamiento, el error se dispara; con más parámetros, puede volver a bajar.</p>
<p>En un caso concreto — <strong>regresión polinómica en una dimensión</strong>, con una base de Legendre y soluciones de norma mínima — se puede conectar lo que aparece en las curvas con el álgebra del problema: la pseudoinversa y los valores singulares de la matriz de diseño.</p>
<section id="dos-objetivos-que-chocan" class="level2" data-number="1">
<h2 data-number="1" class="anchored" data-anchor-id="dos-objetivos-que-chocan"><span class="header-section-number">1</span> Dos objetivos que chocan</h2>
<p>Tenemos datos de entrenamiento <img src="https://latex.codecogs.com/png.latex?(x_i,%20y_i)_%7Bi=1%7D%5En">. En el ejemplo central, cada <img src="https://latex.codecogs.com/png.latex?x_i"> es un escalar en <img src="https://latex.codecogs.com/png.latex?%5B-1,1%5D">; en la discusión general conviene pensar <img src="https://latex.codecogs.com/png.latex?x_i%20%5Cin%20%5Cmathbb%7BR%7D%5Ep"> como vector de covariables y <img src="https://latex.codecogs.com/png.latex?y_i%20%5Cin%20%5Cmathbb%7BR%7D"> como respuesta. Disponemos además de un conjunto de prueba <img src="https://latex.codecogs.com/png.latex?(x_j%5E*,%20y_j%5E*)_%7Bj=1%7D%5E%7Bn%5E*%7D"> para medir lo que importa en aplicaciones. En muchos modelos aparece, de forma explícita o implícita, el objetivo de <strong>interpolar</strong> el entrenamiento,</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmin_%7B%5Ctheta%7D%20%5Csum_%7Bi=1%7D%5E%7Bn%7D%20%5Cbigl(f_%7B%5Ctheta%7D(x_i)%20-%20y_i%5Cbigr)%5E2,%0A"></p>
<p>mientras que lo que realmente importa en aplicaciones es la <strong>generalización</strong>: que <img src="https://latex.codecogs.com/png.latex?f_%7B%5Ctheta%7D(x_j%5E*)"> se parezca a <img src="https://latex.codecogs.com/png.latex?y_j%5E*"> en puntos nuevos. Esa tensión aparece en regresión lineal, en modelos basados en kernels, en redes neuronales cuando el entrenamiento minimiza el error cuadrático hasta casi cero, etc.</p>
<p>La historia clásica del sesgo–varianza sugiere que, al aumentar la complejidad, en algún momento el error de test empeora: la curva típica en la literatura pedagógica tiene forma de <strong>U</strong> o de tazón. El fenómeno de <strong>double descent</strong> muestra que, en ciertos regímenes (sobre todo con interpolación y reglas de elección de solución como la de <strong>norma mínima</strong>), la curva de error de test puede volver a <strong>bajar</strong> después de un pico dramático cerca del umbral donde el modelo pasa a poder interpolar. No es magia: es consecuencia de cómo se resuelve el problema lineal subyacente y de qué direcciones en el espacio de features quedan mal identificadas cuando la matriz de diseño está casi en rango deficiente.</p>
</section>
<section id="polinomios-de-legendre-como-features-1d" class="level2" data-number="2">
<h2 data-number="2" class="anchored" data-anchor-id="polinomios-de-legendre-como-features-1d"><span class="header-section-number">2</span> Polinomios de Legendre como features (1D)</h2>
<p>En el ejemplo numérico principal, <img src="https://latex.codecogs.com/png.latex?x"> vive en <img src="https://latex.codecogs.com/png.latex?%5B-1,1%5D"> y usamos la base de <strong>polinomios de Legendre</strong> <img src="https://latex.codecogs.com/png.latex?%5C%7BP_k%5C%7D_%7Bk%5Cge%200%7D">, ortogonal en <img src="https://latex.codecogs.com/png.latex?%5B-1,1%5D"> con peso 1:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cint_%7B-1%7D%5E%7B1%7D%20P_k(x)%5C,%20P_%5Cell(x)%5C,%20dx%20=%200,%20%5Cqquad%20k%5Cneq%20%5Cell.%0A"></p>
<p>Por ejemplo <img src="https://latex.codecogs.com/png.latex?P_0(x)=1">, <img src="https://latex.codecogs.com/png.latex?P_1(x)=x"> y <img src="https://latex.codecogs.com/png.latex?P_3(x)=%5Cfrac%7B1%7D%7B2%7D(5x%5E3-3x)">. En lugar de <img src="https://latex.codecogs.com/png.latex?(1,x,x%5E2,%5Cldots)"> tomamos</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Ctilde%7B%5Cphi%7D_P(x_i)=%5Cbig(P_0(x_i),%5Cldots,P_%7BP-1%7D(x_i)%5Cbig)%5ET%20%5Cin%20%5Cmathbb%7BR%7D%5EP%0A"></p>
<p>como vector de covariables. La matriz de diseño tiene una fila por observación de entrenamiento:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmathbf%7BX%7D_P%20=%0A%5Cbegin%7Bbmatrix%7D%0A%5Ctilde%7B%5Cphi%7D_P(x_1)%5E%7BT%7D%5C%5C%0A%5Cvdots%5C%5C%0A%5Ctilde%7B%5Cphi%7D_P(x_n)%5E%7BT%7D%0A%5Cend%7Bbmatrix%7D%0A%5Cin%20%5Cmathbb%7BR%7D%5E%7Bn%5Ctimes%20P%7D.%0A"></p>
<p>Eso reduce el mal condicionamiento típico de las potencias crudas <img src="https://latex.codecogs.com/png.latex?(1,x,x%5E2,%5Cldots)"> cuando <img src="https://latex.codecogs.com/png.latex?P"> crece: las columnas de monomios en una malla fija suelen volverse casi colineales, lo que encarece numéricamente la inversión de <img src="https://latex.codecogs.com/png.latex?X%5E%5Ctop%20X">. La base de Legendre no elimina por completo el mal condicionamiento en el <strong>límite</strong> <img src="https://latex.codecogs.com/png.latex?P%20%5Capprox%20n">, pero ordena el problema de un modo más estable para el experimento que mostramos abajo.</p>
</section>
<section id="experimento-función-verdadera-ruido-nodos-y-umbral" class="level2" data-number="3">
<h2 data-number="3" class="anchored" data-anchor-id="experimento-función-verdadera-ruido-nodos-y-umbral"><span class="header-section-number">3</span> Experimento: función verdadera, ruido, nodos y umbral</h2>
<p>La función “verdadera” del ejemplo es</p>
<p><img src="https://latex.codecogs.com/png.latex?%0Ay(x)%20=%202x%20+%20%5Ccos(25x),%0A"></p>
<p>y observamos <img src="https://latex.codecogs.com/png.latex?y_i%20=%20y(x_i)%20+%20%5Cepsilon_i"> con ruido gaussiano <img src="https://latex.codecogs.com/png.latex?%5Cepsilon_i%20%5Csim%20%5Cmathcal%7BN%7D(0,%200.2%5E2)">, independiente entre observaciones.</p>
<p><strong>Dónde se evalúa la función.</strong> Los valores de <img src="https://latex.codecogs.com/png.latex?x"> no se toman en una malla equiespaciada en <img src="https://latex.codecogs.com/png.latex?(-1,1)">. Con una malla uniforme, la matriz de Legendre evaluada en muchos puntos cercanos puede acercarse peligrosamente al rango deficiente de modo “accidental”, y el umbral <img src="https://latex.codecogs.com/png.latex?P=n"> deja de coincidir tan limpiamente con el fenómeno de interpolación <strong>en la práctica numérica</strong>. Por eso fijamos los <img src="https://latex.codecogs.com/png.latex?x"> en nodos tipo <strong>Chebyshev</strong> en <img src="https://latex.codecogs.com/png.latex?(-1,1)"> (transformación coseno de índices discretos), que reparten mejor los puntos hacia los extremos del intervalo y son el estándar en interpolación polinómica.</p>
<p><strong>Train y test.</strong> Partimos la muestra en mitad entrenamiento y mitad prueba, pero <strong>antes</strong> permutamos los índices al azar. Si usáramos siempre “la primera mitad” como train, en una malla ordenada los puntos de entrenamiento podrían concentrarse en una parte del intervalo (por ejemplo solo <img src="https://latex.codecogs.com/png.latex?x%3C0">), la matriz <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7BX%7D_P"> sería atípica y las conclusiones visuales se confundirían con un artefacto de muestreo. La permutación hace que train y test vean soporte comparable en <img src="https://latex.codecogs.com/png.latex?%5B-1,1%5D">.</p>
<p>El <strong>umbral de interpolación</strong> es <strong><img src="https://latex.codecogs.com/png.latex?P=n"></strong>: tantas columnas (features) como filas (puntos de train) en <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7BX%7D_P">. Para <img src="https://latex.codecogs.com/png.latex?P%3En">, en rango fila completo, existe una infinidad de <img src="https://latex.codecogs.com/png.latex?%5Cbeta"> que interpolan exactamente los <img src="https://latex.codecogs.com/png.latex?y_i"> del train; entre todas ellas se elige la de <strong>norma euclídea mínima</strong>, que es la que entrega la pseudo-inversa de Moore–Penrose en ese régimen.</p>
<p>El ajuste queda entonces:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Chat%7B%5Cbeta%7D_P%5E%7B%5Cmin%7D%20=%20%5Cmathbf%7BX%7D_P%5E%7B+%7D%5C,%5Cmathbf%7By%7D,%0A"></p>
<p>con la forma cerrada <img src="https://latex.codecogs.com/png.latex?(%5Cmathbf%7BX%7D_P%5ET%5Cmathbf%7BX%7D_P)%5E%7B-1%7D%5Cmathbf%7BX%7D_P%5ET"> si <img src="https://latex.codecogs.com/png.latex?P%5Cle%20n"> (rango columna completo) y <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7BX%7D_P%5ET(%5Cmathbf%7BX%7D_P%5Cmathbf%7BX%7D_P%5ET)%5E%7B-1%7D"> si <img src="https://latex.codecogs.com/png.latex?P%3En"> (rango fila completo, régimen interpolante).</p>
<p>La siguiente figura muestra el MSE en entrenamiento y en prueba frente a <img src="https://latex.codecogs.com/png.latex?P"> (escala logarítmica en ambos ejes). El patrón típico del double descent aparece de inmediato: error alto con pocos parámetros, un <strong>pico</strong> cerca de <img src="https://latex.codecogs.com/png.latex?P=n">, y luego descenso o estabilización cuando <img src="https://latex.codecogs.com/png.latex?P"> supera claramente a <img src="https://latex.codecogs.com/png.latex?n">. En el tramo <img src="https://latex.codecogs.com/png.latex?P%3Cn"> marcamos además un <img src="https://latex.codecogs.com/png.latex?P%5E%5Cstar"> que minimiza el MSE de test en <strong>esta</strong> realización fija del experimento (línea vertical punteada verde), para contrastarlo con el umbral <img src="https://latex.codecogs.com/png.latex?P=n"> (gris).</p>
<div id="cell-fig-mse-curve" class="cell" data-message="false" data-execution_count="1">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb1-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> matplotlib.pyplot <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> plt</span>
<span id="cb1-3"></span>
<span id="cb1-4">plt.rcParams.update({<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"figure.dpi"</span>: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">120</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"axes.grid"</span>: <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"grid.alpha"</span>: <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.2</span>})</span>
<span id="cb1-5"></span>
<span id="cb1-6">rng <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.random.default_rng(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2026</span>)</span>
<span id="cb1-7">N, P_max, sigma <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">200</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.2</span></span>
<span id="cb1-8">k <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.arange(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, N <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, dtype<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>)</span>
<span id="cb1-9">x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.cos((<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> k <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> N) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.pi)</span>
<span id="cb1-10">y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> np.cos(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">25</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> x) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> rng.normal(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, sigma, size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>N)</span>
<span id="cb1-11">perm <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> rng.permutation(N)</span>
<span id="cb1-12">n <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> N <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">//</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb1-13">train, test <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.sort(perm[:n]), np.sort(perm[n:])</span>
<span id="cb1-14"></span>
<span id="cb1-15"></span>
<span id="cb1-16"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> legendre_design(x, P):</span>
<span id="cb1-17">    x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.asarray(x)</span>
<span id="cb1-18">    m <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> x.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span>
<span id="cb1-19">    L <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.empty((m, P), dtype<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>)</span>
<span id="cb1-20">    L[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span></span>
<span id="cb1-21">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> P <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:</span>
<span id="cb1-22">        L[:, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> x</span>
<span id="cb1-23">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> kk <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, P):</span>
<span id="cb1-24">        n_ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> kk</span>
<span id="cb1-25">        L[:, kk] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ((<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> n_ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> n_) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> L[:, kk <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> ((n_ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> n_) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> L[:, kk <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>]</span>
<span id="cb1-26">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> L</span>
<span id="cb1-27"></span>
<span id="cb1-28"></span>
<span id="cb1-29"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> fit_min_norm(L_tr, y_tr):</span>
<span id="cb1-30">    beta, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>_ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.linalg.lstsq(L_tr, y_tr, rcond<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>)</span>
<span id="cb1-31">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> beta</span>
<span id="cb1-32"></span>
<span id="cb1-33"></span>
<span id="cb1-34"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> mse(a, b):</span>
<span id="cb1-35">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>(np.mean((a <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> b) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>))</span>
<span id="cb1-36"></span>
<span id="cb1-37"></span>
<span id="cb1-38">L <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> legendre_design(x, P_max)</span>
<span id="cb1-39">train_mse <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.empty(P_max)</span>
<span id="cb1-40">test_mse <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.empty(P_max)</span>
<span id="cb1-41"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> P <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, P_max <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>):</span>
<span id="cb1-42">    betaP <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> fit_min_norm(L[train, :P], y[train])</span>
<span id="cb1-43">    train_mse[P <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> mse(y[train], L[train, :P] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> betaP)</span>
<span id="cb1-44">    test_mse[P <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> mse(y[test], L[test, :P] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> betaP)</span>
<span id="cb1-45"></span>
<span id="cb1-46">P_pre <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.arange(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, n, dtype<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>)</span>
<span id="cb1-47">P_best_pre <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(P_pre[np.argmin(test_mse[P_pre <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])])</span>
<span id="cb1-48">P_under <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span></span>
<span id="cb1-49">P_thr <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> n</span>
<span id="cb1-50">P_over <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> P_max</span>
<span id="cb1-51">x_grid <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.linspace(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">800</span>)</span>
<span id="cb1-52">y_true <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> x_grid <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> np.cos(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">25</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> x_grid)</span>
<span id="cb1-53"></span>
<span id="cb1-54"></span>
<span id="cb1-55"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> predict_on_grid(P, xg):</span>
<span id="cb1-56">    Ltr <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> legendre_design(x[train], P)</span>
<span id="cb1-57">    betaP <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> fit_min_norm(Ltr, y[train])</span>
<span id="cb1-58">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> legendre_design(xg, P) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> betaP</span>
<span id="cb1-59"></span>
<span id="cb1-60"></span>
<span id="cb1-61">y_hat_under <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> predict_on_grid(P_under, x_grid)</span>
<span id="cb1-62">y_hat_best_pre <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> predict_on_grid(P_best_pre, x_grid)</span>
<span id="cb1-63">y_hat_thr <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> predict_on_grid(P_thr, x_grid)</span>
<span id="cb1-64">y_hat_over <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> predict_on_grid(P_over, x_grid)</span>
<span id="cb1-65"></span>
<span id="cb1-66">P_grid <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.arange(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, P_max <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb1-67">fig, ax <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> plt.subplots(figsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4.6</span>))</span>
<span id="cb1-68">ax.set_xscale(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"log"</span>)</span>
<span id="cb1-69">ax.set_yscale(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"log"</span>)</span>
<span id="cb1-70">ax.plot(P_grid, test_mse, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#1f77b4"</span>, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.2</span>, label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Test"</span>)</span>
<span id="cb1-71">ax.plot(P_grid, train_mse, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#ff7f0e"</span>, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.0</span>, label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Train"</span>)</span>
<span id="cb1-72">ax.axvline(n, linestyle<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--"</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"gray"</span>, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.2</span>, label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"$P=n=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>n<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$"</span>)</span>
<span id="cb1-73">ax.axvline(</span>
<span id="cb1-74">    P_best_pre,</span>
<span id="cb1-75">    linestyle<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">":"</span>,</span>
<span id="cb1-76">    color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#2ca02c"</span>,</span>
<span id="cb1-77">    linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.5</span>,</span>
<span id="cb1-78">    label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"$P^</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\\</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">star=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>P_best_pre<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$ (mín. test, $P&lt;n$)"</span>,</span>
<span id="cb1-79">)</span>
<span id="cb1-80">finite <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.isfinite(test_mse) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;</span> (test_mse <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;</span> np.isfinite(train_mse) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;</span> (train_mse <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb1-81">lo <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">min</span>(np.quantile(test_mse[finite], <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>), np.quantile(train_mse[finite], <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.02</span>))) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span></span>
<span id="cb1-82">hi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>(np.quantile(test_mse[finite], <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.98</span>), np.quantile(train_mse[finite], <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.98</span>))) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.2</span></span>
<span id="cb1-83">ax.set_ylim(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">max</span>(lo, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e-10</span>), hi)</span>
<span id="cb1-84">ax.set_xlabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Número de features $P$"</span>)</span>
<span id="cb1-85">ax.set_ylabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"MSE"</span>)</span>
<span id="cb1-86">ax.set_title(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Double descent (Legendre, norma mínima)"</span>)</span>
<span id="cb1-87">ax.legend(frameon<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>, loc<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"upper right"</span>)</span>
<span id="cb1-88">plt.tight_layout()</span>
<span id="cb1-89">plt.show()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-mse-curve" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-mse-curve-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<a href="double_desent_files/figure-html/fig-mse-curve-output-1.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1" title="Figura&nbsp;1: MSE en train y en test frente al número de features P. Línea gris: P=n; verde: P^\star que minimiza el MSE de test con P<n en esta muestra."><img src="https://luccafrachelle.github.io/porfolio_academia/posts/double_desent_files/figure-html/fig-mse-curve-output-1.png" class="img-fluid figure-img"></a>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-mse-curve-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figura&nbsp;1: MSE en train y en test frente al número de features <img src="https://latex.codecogs.com/png.latex?P">. Línea gris: <img src="https://latex.codecogs.com/png.latex?P=n">; verde: <img src="https://latex.codecogs.com/png.latex?P%5E%5Cstar"> que minimiza el MSE de test con <img src="https://latex.codecogs.com/png.latex?P%3Cn"> en esta muestra.
</figcaption>
</figure>
</div>
</div>
</div>
<p>El train baja monótonamente cuando el modelo gana flexibilidad; el test no: cerca de <img src="https://latex.codecogs.com/png.latex?P=n"> el sistema está mal condicionado y pequeñas perturbaciones (ruido, geometría de los datos) se amplifican. Más allá del umbral, la elección de la solución de norma mínima vuelve a imponer una forma “suave” al coeficiente y el error de test puede recuperarse.</p>
<section id="qué-muestran-las-curvas-ajustadas" class="level3" data-number="3.1">
<h3 data-number="3.1" class="anchored" data-anchor-id="qué-muestran-las-curvas-ajustadas"><span class="header-section-number">3.1</span> Qué muestran las curvas ajustadas</h3>
<p>El panel siguiente usa <strong>siempre los mismos</strong> puntos de entrenamiento y cambia solo <img src="https://latex.codecogs.com/png.latex?P">. Así se ve qué significa el gráfico anterior en términos de la función <img src="https://latex.codecogs.com/png.latex?%5Chat%7By%7D(x)"> frente a la verdadera <img src="https://latex.codecogs.com/png.latex?y(x)=2x+%5Ccos(25x)">.</p>
<ol type="1">
<li><strong>Pocos parámetros</strong> (<img src="https://latex.codecogs.com/png.latex?P"> pequeño): el modelo es rígido; la onda de alta frecuencia <img src="https://latex.codecogs.com/png.latex?%5Ccos(25x)"> casi no entra y el ajuste queda suave pero sesgado respecto de la verdad.</li>
<li><strong>Mejor <img src="https://latex.codecogs.com/png.latex?P"> antes del umbral</strong> (<img src="https://latex.codecogs.com/png.latex?P%5E%5Cstar%3Cn">): suele aparecer un compromiso razonable entre capturar estructura y no inflar varianza; la curva se acerca más a <img src="https://latex.codecogs.com/png.latex?y(x)">.</li>
<li><strong>Umbral</strong> (<img src="https://latex.codecogs.com/png.latex?P=n">): el sistema cuadrado es delicado; direcciones con valores singulares muy pequeños amplifican el ruido y el ajuste puede oscilar de forma dramática entre los puntos de entrenamiento.</li>
<li><strong>Muchos parámetros con min-norm</strong> (<img src="https://latex.codecogs.com/png.latex?P%5Cgg%20n">): el modelo interpola el train pero la regularización implícita de la norma mínima tiende a producir predicciones más suaves fuera de los datos de entrenamiento que en el caso “en el borde” <img src="https://latex.codecogs.com/png.latex?P=n">, lo que en este ejemplo puede <strong>reducir</strong> de nuevo el error de test.</li>
</ol>
<div id="cell-fig-four-panels" class="cell" data-message="false" data-execution_count="2">
<details class="code-fold">
<summary>Código</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1">fig, axes <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> plt.subplots(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>, figsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">7.5</span>), sharex<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, sharey<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb2-2">panels <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [</span>
<span id="cb2-3">    (axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], y_hat_under, <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Pocos parámetros</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$P=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>P_under<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$, $n=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>n<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$"</span>),</span>
<span id="cb2-4">    (</span>
<span id="cb2-5">        axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>],</span>
<span id="cb2-6">        y_hat_best_pre,</span>
<span id="cb2-7">        <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Mejor test ($P&lt;n$)</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$P^</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\\</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">star=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>P_best_pre<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$, MSE test = </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>test_mse[P_best_pre <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:.4f}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span>,</span>
<span id="cb2-8">    ),</span>
<span id="cb2-9">    (axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], y_hat_thr, <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Umbral interpolación</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$P=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>P_thr<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">=n$"</span>),</span>
<span id="cb2-10">    (axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>], y_hat_over, <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Sobre-parametrización (min-norm)</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$P=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>P_over<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$"</span>),</span>
<span id="cb2-11">]</span>
<span id="cb2-12"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> axp, yhat, title <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> panels:</span>
<span id="cb2-13">    axp.scatter(x[train], y[train], s<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>, alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.65</span>, label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Train"</span>)</span>
<span id="cb2-14">    axp.plot(x_grid, y_true, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#1f4e79"</span>, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.0</span>, label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Verdadera"</span>)</span>
<span id="cb2-15">    axp.plot(x_grid, yhat, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#D85A30"</span>, linewidth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.0</span>, label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Ajuste"</span>)</span>
<span id="cb2-16">    axp.set_xlim(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb2-17">    axp.set_ylim(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>)</span>
<span id="cb2-18">    axp.set_title(title, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)</span>
<span id="cb2-19">    axp.legend(frameon<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>, loc<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"upper right"</span>)</span>
<span id="cb2-20">axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].set_xlabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"$x$"</span>)</span>
<span id="cb2-21">axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>].set_xlabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"$x$"</span>)</span>
<span id="cb2-22">axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].set_ylabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"$y$"</span>)</span>
<span id="cb2-23">axes[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>].set_ylabel(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"$y$"</span>)</span>
<span id="cb2-24">plt.suptitle(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Intuición visual: misma muestra de entrenamiento"</span>, y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.02</span>, fontsize<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">12</span>)</span>
<span id="cb2-25">plt.tight_layout()</span>
<span id="cb2-26">plt.show()</span></code></pre></div></div>
</details>
<div class="cell-output cell-output-display">
<div id="fig-four-panels" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-four-panels-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<a href="double_desent_files/figure-html/fig-four-panels-output-1.png" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="Figura&nbsp;2: Misma muestra de entrenamiento; cuatro valores de P: pocos parámetros, óptimo P^\star<n, umbral P=n, y P grande con solución de norma mínima."><img src="https://luccafrachelle.github.io/porfolio_academia/posts/double_desent_files/figure-html/fig-four-panels-output-1.png" class="img-fluid figure-img"></a>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-four-panels-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figura&nbsp;2: Misma muestra de entrenamiento; cuatro valores de <img src="https://latex.codecogs.com/png.latex?P">: pocos parámetros, óptimo <img src="https://latex.codecogs.com/png.latex?P%5E%5Cstar%3Cn">, umbral <img src="https://latex.codecogs.com/png.latex?P=n">, y <img src="https://latex.codecogs.com/png.latex?P"> grande con solución de norma mínima.
</figcaption>
</figure>
</div>
</div>
</div>
<p>Esa secuencia es la narrativa visual que acompaña la curva en forma de doble descenso: el pico en <img src="https://latex.codecogs.com/png.latex?P%5Capprox%20n"> no es un artefacto del dibujo, sino que corresponde a ajustes casi singulares que explotan entre los datos, mientras que el régimen <img src="https://latex.codecogs.com/png.latex?P%5Cgg%20n"> con norma mínima puede verse mucho más benigno a ojo.</p>
</section>
</section>
<section id="pseudo-inversa-de-moorepenrose-lenguaje-unificado" class="level2" data-number="4">
<h2 data-number="4" class="anchored" data-anchor-id="pseudo-inversa-de-moorepenrose-lenguaje-unificado"><span class="header-section-number">4</span> Pseudo-inversa de Moore–Penrose (lenguaje unificado)</h2>
<p>Para una matriz de diseño <img src="https://latex.codecogs.com/png.latex?X%20%5Cin%20%5Cmathbb%7BR%7D%5E%7BN%5Ctimes%20P%7D"> (aquí <img src="https://latex.codecogs.com/png.latex?N"> es el número de puntos de entrenamiento) y el vector de respuestas <img src="https://latex.codecogs.com/png.latex?Y%20%5Cin%20%5Cmathbb%7BR%7D%5EN">, la solución que unifica los dos regímenes (cuando los rangos son completos en el sentido habitual) es</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Chat%7B%5Cbeta%7D%20=%20X%5E%7B+%7D%20Y,%0A"></p>
<p>donde <img src="https://latex.codecogs.com/png.latex?X%5E%7B+%7D"> es la <strong>pseudo-inversa de Moore–Penrose</strong>. En forma cerrada, fuera de los casos límite de rango deficiente, se recuerda como</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AX%5E%7B+%7D%20=%0A%5Cbegin%7Bcases%7D%0A(X%5E%5Ctop%20X)%5E%7B-1%7D%20X%5E%5Ctop%20&amp;%20%5Ctext%7Bsi%20%7D%20P%20%5Cle%20N%20%5Ctext%7B%20y%20%7D%20X%20%5Ctext%7B%20tiene%20rango%20columna%20completo,%7D%20%5C%5C%5B6pt%5D%0AX%5E%5Ctop%20(X%20X%5E%5Ctop)%5E%7B-1%7D%20&amp;%20%5Ctext%7Bsi%20%7D%20P%20%5Cge%20N%20%5Ctext%7B%20y%20%7D%20X%20%5Ctext%7B%20tiene%20rango%20fila%20completo.%7D%0A%5Cend%7Bcases%7D%0A"></p>
<p>En el régimen <strong>sobre-parametrizado interpolante</strong> (<img src="https://latex.codecogs.com/png.latex?P%3EN"> y rango fila completo), <img src="https://latex.codecogs.com/png.latex?X%5E%7B+%7DY"> es exactamente el vector <img src="https://latex.codecogs.com/png.latex?%5Chat%7B%5Cbeta%7D"> de <strong>norma euclídea mínima</strong> entre todos los que satisfacen <img src="https://latex.codecogs.com/png.latex?X%5Cbeta=Y">:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Chat%7B%5Cbeta%7D%20=%20%5Carg%5Cmin_%7B%5Cbeta%7D%5C%20%5C%7C%5Cbeta%5C%7C_2%5E2%20%5Cquad%20%5Ctext%7Bsujeto%20a%7D%20%5Cquad%20X%5Cbeta=Y.%0A"></p>
<section id="cómo-predecimos-en-un-punto-de-test" class="level3" data-number="4.1">
<h3 data-number="4.1" class="anchored" data-anchor-id="cómo-predecimos-en-un-punto-de-test"><span class="header-section-number">4.1</span> Cómo predecimos en un punto de test</h3>
<p>Sea <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%20%5Cin%20%5Cmathbb%7BR%7D%5EP"> el vector de features del mismo tipo que las filas de <img src="https://latex.codecogs.com/png.latex?X"> (en nuestro ejemplo, valores de Legendre hasta orden <img src="https://latex.codecogs.com/png.latex?P-1">). Entonces</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Chat%7By%7D_%7B%5Ctext%7Btest,under%7D%7D%20=%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%20(X%5E%5Ctop%20X)%5E%7B-1%7D%20X%5E%5Ctop%20Y%20%5Cqquad%20(P%3CN,%5C%20%5Ctext%7Brango%20columna%20completo%7D),%0A"></p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Chat%7By%7D_%7B%5Ctext%7Btest,over%7D%7D%20=%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%20X%5E%5Ctop%20(X%20X%5E%5Ctop)%5E%7B-1%7D%20Y%20%5Cqquad%20(P%3EN,%5C%20%5Ctext%7Brango%20fila%20completo%7D).%0A"></p>
<p>En la práctica, conviene obtener <img src="https://latex.codecogs.com/png.latex?%5Chat%7B%5Cbeta%7D"> y las predicciones con métodos basados en la <strong>descomposición en valores singulares</strong> u otras factorizaciones estables, en lugar de invertir matrices de Gram de forma directa cuando <img src="https://latex.codecogs.com/png.latex?P"> o <img src="https://latex.codecogs.com/png.latex?N"> son grandes. Las fórmulas anteriores son la referencia algebraica que conecta el experimento con el análisis del error.</p>
</section>
</section>
<section id="modelo-de-referencia-y-error-de-predicción" class="level2" data-number="5">
<h2 data-number="5" class="anchored" data-anchor-id="modelo-de-referencia-y-error-de-predicción"><span class="header-section-number">5</span> Modelo de referencia y error de predicción</h2>
<p>Para analizar el error sin asumir que el mundo es lineal, introducimos notación: escribimos</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AY%20=%20X%5Cbeta%5E%7B*%7D%20+%20E,%0A"></p>
<p>donde <img src="https://latex.codecogs.com/png.latex?%5Cbeta%5E%7B*%7D"> representa los coeficientes del <strong>mejor predictor lineal</strong> en un sentido ideal y <img src="https://latex.codecogs.com/png.latex?E"> recoge todo lo que esa clase lineal no puede explicar (ruido, o componentes no lineales). El valor “ideal” en test es <img src="https://latex.codecogs.com/png.latex?y%5E%7B*%7D_%7B%5Ctext%7Btest%7D%7D%20=%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5Ccdot%20%5Cbeta%5E%7B*%7D">. En general <img src="https://latex.codecogs.com/png.latex?%5Cbeta%5E%7B*%7D"> no tiene que ser los coeficientes de un “mundo lineal verdadero”; es una convención analítica que permite separar la parte del error que se parece a <strong>varianza</strong> (sensible al ruido <img src="https://latex.codecogs.com/png.latex?E">) de la parte que se parece a <strong>sesgo</strong> (sensible a la incapacidad del predictor lineal de representar la verdad).</p>
<section id="descomposición-en-valores-singulares" class="level3" data-number="5.1">
<h3 data-number="5.1" class="anchored" data-anchor-id="descomposición-en-valores-singulares"><span class="header-section-number">5.1</span> Descomposición en valores singulares</h3>
<p>Escribimos la SVD de <img src="https://latex.codecogs.com/png.latex?X"> en la forma reducida que conviene al rango <img src="https://latex.codecogs.com/png.latex?R">:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AX%20=%20U%20%5CSigma%20V%5E%5Ctop,%0A"></p>
<p>con <img src="https://latex.codecogs.com/png.latex?U%20%5Cin%20%5Cmathbb%7BR%7D%5E%7BN%5Ctimes%20R%7D">, <img src="https://latex.codecogs.com/png.latex?%5CSigma%20=%20%5Cmathrm%7Bdiag%7D(%5Csigma_1,%5Cldots,%5Csigma_R)">, <img src="https://latex.codecogs.com/png.latex?V%20%5Cin%20%5Cmathbb%7BR%7D%5E%7BP%5Ctimes%20R%7D"> y valores singulares <strong>estrictamente positivos</strong> <img src="https://latex.codecogs.com/png.latex?%5Csigma_1%20%5Cge%20%5Ccdots%20%5Cge%20%5Csigma_R%20%3E%200">. Las columnas <img src="https://latex.codecogs.com/png.latex?u_r"> de <img src="https://latex.codecogs.com/png.latex?U"> y <img src="https://latex.codecogs.com/png.latex?v_r"> de <img src="https://latex.codecogs.com/png.latex?V"> son ortonormales en sus espacios. La pseudo-inversa actúa sobre la parte de <img src="https://latex.codecogs.com/png.latex?Y"> que cae en el espacio columna de <img src="https://latex.codecogs.com/png.latex?X"> mediante factores <img src="https://latex.codecogs.com/png.latex?1/%5Csigma_r">; por eso los <strong>valores singulares pequeños</strong> son el corazón técnico del pico cerca de <img src="https://latex.codecogs.com/png.latex?P%5Capprox%20n">.</p>
</section>
<section id="caso-p-n-sub-parametrizado-derivación-del-error" class="level3" data-number="5.2">
<h3 data-number="5.2" class="anchored" data-anchor-id="caso-p-n-sub-parametrizado-derivación-del-error"><span class="header-section-number">5.2</span> Caso <img src="https://latex.codecogs.com/png.latex?P%20%3C%20N"> (sub-parametrizado): derivación del error</h3>
<p>Partimos de la fórmula del estimador de mínimos cuadrados (coincide con <img src="https://latex.codecogs.com/png.latex?X%5E+Y"> en rango columna completo). Sustituyendo <img src="https://latex.codecogs.com/png.latex?Y%20=%20X%5Cbeta%5E%7B*%7D%20+%20E">,</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Baligned%7D%0A%5Chat%7By%7D_%7B%5Ctext%7Btest%7D%7D%0A&amp;=%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%20(X%5E%5Ctop%20X)%5E%7B-1%7D%20X%5E%5Ctop%20Y%20%5C%5C%0A&amp;=%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%20(X%5E%5Ctop%20X)%5E%7B-1%7D%20X%5E%5Ctop%20(X%5Cbeta%5E%7B*%7D%20+%20E)%20%5C%5C%0A&amp;=%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%20(X%5E%5Ctop%20X)%5E%7B-1%7D%20X%5E%5Ctop%20X%5Cbeta%5E%7B*%7D%0A%20%20%20+%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%20(X%5E%5Ctop%20X)%5E%7B-1%7D%20X%5E%5Ctop%20E%20%5C%5C%0A&amp;=%20%5Cunderbrace%7B%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%20%5Cbeta%5E%7B*%7D%7D_%7B=%5C,%20y%5E%7B*%7D_%7B%5Ctext%7Btest%7D%7D%7D%0A%20%20%20+%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%20(X%5E%5Ctop%20X)%5E%7B-1%7D%20X%5E%5Ctop%20E.%0A%5Cend%7Baligned%7D%0A"></p>
<p>Por tanto el error en test respecto del mejor predictor lineal en la notación adoptada es</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Chat%7By%7D_%7B%5Ctext%7Btest%7D%7D%20-%20y%5E%7B*%7D_%7B%5Ctext%7Btest%7D%7D%20=%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%20(X%5E%5Ctop%20X)%5E%7B-1%7D%20X%5E%5Ctop%20E.%0A"></p>
<p>Sustituyendo <img src="https://latex.codecogs.com/png.latex?X%20=%20U%5CSigma%20V%5E%5Ctop"> y usando las identidades estándar para <img src="https://latex.codecogs.com/png.latex?(X%5E%5Ctop%20X)%5E%7B-1%7DX%5E%5Ctop">, se obtiene la forma modal</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Chat%7By%7D_%7B%5Ctext%7Btest%7D%7D%20-%20y%5E%7B*%7D_%7B%5Ctext%7Btest%7D%7D%0A=%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%20V%20%5CSigma%5E%7B+%7D%20U%5E%5Ctop%20E%0A=%20%5Csum_%7Br=1%7D%5E%7BR%7D%5Cfrac%7B1%7D%7B%5Csigma_r%7D%5C,(%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5Ccdot%20v_r)%5C,(u_r%5Ccdot%20E),%0A"></p>
<p>donde <img src="https://latex.codecogs.com/png.latex?%5CSigma%5E%7B+%7D=%5Cmathrm%7Bdiag%7D(1/%5Csigma_1,%5Cldots,1/%5Csigma_R)">. Es decir, el error es una <strong>suma sobre modos</strong> del operador lineal que mapea el ruido (y el residual mal modelado) del espacio de las observaciones al espacio de predicción.</p>
<p>En la descomposición algebraica anterior, para <img src="https://latex.codecogs.com/png.latex?P%3CN"> <strong>solo</strong> aparece el canal que pasa por <img src="https://latex.codecogs.com/png.latex?E"> en la expresión cerrada del error respecto de <img src="https://latex.codecogs.com/png.latex?y%5E%7B*%7D_%7B%5Ctext%7Btest%7D%7D">; el término de <strong>sesgo de parametrización</strong> <img src="https://latex.codecogs.com/png.latex?%5Cbig(X%5E%5Ctop%20(X%20X%5E%5Ctop)%5E%7B-1%7D%20X%20-%20I%5Cbig)%5Cbeta%5E%7B*%7D"> es propio del régimen <img src="https://latex.codecogs.com/png.latex?P%3EN">.</p>
</section>
<section id="caso-p-n-sobre-parametrizado-min-norm-derivación" class="level3" data-number="5.3">
<h3 data-number="5.3" class="anchored" data-anchor-id="caso-p-n-sobre-parametrizado-min-norm-derivación"><span class="header-section-number">5.3</span> Caso <img src="https://latex.codecogs.com/png.latex?P%20%3E%20N"> (sobre-parametrizado, min-norm): derivación</h3>
<p>Usamos la predicción <img src="https://latex.codecogs.com/png.latex?%5Chat%7By%7D_%7B%5Ctext%7Btest,over%7D%7D%20=%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%20X%5E%5Ctop%20(X%20X%5E%5Ctop)%5E%7B-1%7D%20Y"> y otra vez <img src="https://latex.codecogs.com/png.latex?Y%20=%20X%5Cbeta%5E%7B*%7D%20+%20E">:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Baligned%7D%0A%5Chat%7By%7D_%7B%5Ctext%7Btest,over%7D%7D%0A&amp;=%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%20X%5E%5Ctop%20(X%20X%5E%5Ctop)%5E%7B-1%7D%20(X%5Cbeta%5E%7B*%7D%20+%20E)%20%5C%5C%0A&amp;=%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%20X%5E%5Ctop%20(X%20X%5E%5Ctop)%5E%7B-1%7D%20X%5Cbeta%5E%7B*%7D%0A%20%20%20+%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%20X%5E%5Ctop%20(X%20X%5E%5Ctop)%5E%7B-1%7D%20E.%0A%5Cend%7Baligned%7D%0A"></p>
<p>Recordando <img src="https://latex.codecogs.com/png.latex?y%5E%7B*%7D_%7B%5Ctext%7Btest%7D%7D=%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%5Cbeta%5E%7B*%7D">, el error es</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Baligned%7D%0A%5Chat%7By%7D_%7B%5Ctext%7Btest,over%7D%7D%20-%20y%5E%7B*%7D_%7B%5Ctext%7Btest%7D%7D%0A&amp;=%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%5Cbig(X%5E%5Ctop%20(X%20X%5E%5Ctop)%5E%7B-1%7D%20X%20-%20I%5Cbig)%5Cbeta%5E%7B*%7D%20%5C%5C%0A&amp;%5Cquad%20+%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%20X%5E%5Ctop%20(X%20X%5E%5Ctop)%5E%7B-1%7D%20E.%0A%5Cend%7Baligned%7D%0A"></p>
<p>El segundo sumando admite la misma reexpresión modal que en <img src="https://latex.codecogs.com/png.latex?P%3CN">:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%20X%5E%5Ctop%20(X%20X%5E%5Ctop)%5E%7B-1%7D%20E%0A=%20%5Csum_%7Br=1%7D%5E%7BR%7D%5Cfrac%7B1%7D%7B%5Csigma_r%7D%5C,(%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5Ccdot%20v_r)%5C,(u_r%5Ccdot%20E).%0A"></p>
<p>Así</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Chat%7By%7D_%7B%5Ctext%7Btest,over%7D%7D%20-%20y%5E%7B*%7D_%7B%5Ctext%7Btest%7D%7D%0A=%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%5Cbig(X%5E%5Ctop%20(X%20X%5E%5Ctop)%5E%7B-1%7D%20X%20-%20I%5Cbig)%5Cbeta%5E%7B*%7D%0A+%20%5Csum_%7Br=1%7D%5E%7BR%7D%5Cfrac%7B1%7D%7B%5Csigma_r%7D%5C,(%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5Ccdot%20v_r)%5C,(u_r%5Ccdot%20E).%0A"></p>
<p>Aparece un <strong>término extra</strong> que no está en el caso <img src="https://latex.codecogs.com/png.latex?P%3CN">: el que lleva <img src="https://latex.codecogs.com/png.latex?%5Cbig(X%5E%5Ctop%20(X%20X%5E%5Ctop)%5E%7B-1%7D%20X%20-%20I%5Cbig)%5Cbeta%5E%7B*%7D">. En estadística se asocia al <strong>sesgo</strong> respecto de <img src="https://latex.codecogs.com/png.latex?%5Cbeta%5E%7B*%7D"> en el sentido siguiente: con <img src="https://latex.codecogs.com/png.latex?P%3EN">, los <img src="https://latex.codecogs.com/png.latex?N"> datos solo identifican un subespacio de dimensión a lo sumo <img src="https://latex.codecogs.com/png.latex?N"> dentro de <img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5EP">. La matriz <img src="https://latex.codecogs.com/png.latex?X%5E%5Ctop%20(X%20X%5E%5Ctop)%5E%7B-1%7D%20X"> es la <strong>proyección ortogonal</strong> sobre el subespacio fila de <img src="https://latex.codecogs.com/png.latex?X"> (equivalentemente, sobre el span de las columnas de <img src="https://latex.codecogs.com/png.latex?X%5E%5Ctop"> en <img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5EP">). La componente de <img src="https://latex.codecogs.com/png.latex?%5Cbeta%5E%7B*%7D"> <strong>ortogonal</strong> a ese subespacio no puede recuperarse del entrenamiento; al comparar con <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%5Cbeta%5E%7B*%7D"> completo, esa componente “faltante” genera error sistemático en test.</p>
<p>El <strong>segundo término</strong> (la suma en <img src="https://latex.codecogs.com/png.latex?r">) es análogo al de <img src="https://latex.codecogs.com/png.latex?P%3CN"> y es el que conecta con el <strong>pico</strong> del double descent cuando ciertos <img src="https://latex.codecogs.com/png.latex?%5Csigma_r"> son muy pequeños.</p>
</section>
<section id="resumen-compacto-de-las-dos-fórmulas" class="level3" data-number="5.4">
<h3 data-number="5.4" class="anchored" data-anchor-id="resumen-compacto-de-las-dos-fórmulas"><span class="header-section-number">5.4</span> Resumen compacto de las dos fórmulas</h3>
<p><strong><img src="https://latex.codecogs.com/png.latex?P%3CN">:</strong></p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Chat%7By%7D_%7B%5Ctext%7Btest%7D%7D%20-%20y%5E%7B*%7D_%7B%5Ctext%7Btest%7D%7D%0A=%20%5Csum_%7Br=1%7D%5E%7BR%7D%5Cfrac%7B1%7D%7B%5Csigma_r%7D%5C,(%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5Ccdot%20v_r)%5C,(u_r%5Ccdot%20E).%0A"></p>
<p><strong><img src="https://latex.codecogs.com/png.latex?P%3EN">:</strong></p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Chat%7By%7D_%7B%5Ctext%7Btest,over%7D%7D%20-%20y%5E%7B*%7D_%7B%5Ctext%7Btest%7D%7D%0A=%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%5Cbig(X%5E%5Ctop%20(X%20X%5E%5Ctop)%5E%7B-1%7D%20X%20-%20I%5Cbig)%5Cbeta%5E%7B*%7D%0A+%20%5Csum_%7Br=1%7D%5E%7BR%7D%5Cfrac%7B1%7D%7B%5Csigma_r%7D%5C,(%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5Ccdot%20v_r)%5C,(u_r%5Ccdot%20E).%0A"></p>
</section>
</section>
<section id="por-qué-explota-el-error" class="level2" data-number="6">
<h2 data-number="6" class="anchored" data-anchor-id="por-qué-explota-el-error"><span class="header-section-number">6</span> ¿Por qué explota el error?</h2>
<p>En el caso <img src="https://latex.codecogs.com/png.latex?P%3CN">, el error de predicción en test (y por tanto el <strong>error cuadrático</strong> en test) descompone la incertidumbre en modos singulares. Con la SVD <img src="https://latex.codecogs.com/png.latex?X=U%5CSigma%20V%5E%5Ctop"> y valores singulares no nulos <img src="https://latex.codecogs.com/png.latex?%5Csigma_1,%5Cldots,%5Csigma_R">, ya vimos</p>
<p><span id="eq-error-modes"><img src="https://latex.codecogs.com/png.latex?%0A%5Chat%7By%7D_%7B%5Ctext%7Btest%7D%7D%20-%20y%5E%7B*%7D_%7B%5Ctext%7Btest%7D%7D%0A=%20%5Csum_%7Br=1%7D%5E%7BR%7D%5Cfrac%7B1%7D%7B%5Csigma_r%7D%5C,(%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5Ccdot%20v_r)%5C,(u_r%5Ccdot%20E).%0A%5Ctag%7B1%7D"></span></p>
<p>La ecuación Ecuación&nbsp;1 es <strong>central</strong>: el error depende de una <strong>interacción</strong> entre tres cantidades (en el modo <img src="https://latex.codecogs.com/png.latex?r">-ésimo):</p>
<ol type="1">
<li><p><strong>Cuánto “varían” las columnas de entrenamiento de <img src="https://latex.codecogs.com/png.latex?X"> en cada dirección.</strong> Más formalmente: los <strong>inversos</strong> de los valores singulares <strong>no nulos</strong> de la matriz de diseño <img src="https://latex.codecogs.com/png.latex?X">, es decir <img src="https://latex.codecogs.com/png.latex?1/%5Csigma_r">.</p></li>
<li><p><strong>Cuánto y en qué direcciones varían las covariables de test</strong> <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D"> <strong>en relación con el entrenamiento.</strong> Más formalmente: cómo <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D"> se proyecta sobre los vectores singulares <strong>derechos</strong> <img src="https://latex.codecogs.com/png.latex?V"> de <img src="https://latex.codecogs.com/png.latex?X">, es decir <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5Ccdot%20v_r">.</p></li>
<li><p><strong>En qué medida el mejor modelo posible dentro de la clase puede alinear la variación en <img src="https://latex.codecogs.com/png.latex?X"> con los objetivos <img src="https://latex.codecogs.com/png.latex?Y">.</strong> Más formalmente: cómo los <strong>residuos</strong> <img src="https://latex.codecogs.com/png.latex?E"> del mejor predictor lineal en la clase (lo que, desde la clase, son errores “insalvables”) se proyectan sobre los vectores singulares <strong>izquierdos</strong> <img src="https://latex.codecogs.com/png.latex?U"> de <img src="https://latex.codecogs.com/png.latex?X">, es decir <img src="https://latex.codecogs.com/png.latex?u_r%5Ccdot%20E">.</p></li>
</ol>
<p>Usamos términos como “variar” y “varianza” para sugerir el vínculo con la noción estadística de varianza.<sup>1</sup> El <strong>producto</strong> de esas tres cantidades determina cuánto aporta el modo singular <img src="https://latex.codecogs.com/png.latex?r"> al error de predicción.</p>
<section id="cuándo-los-tres-factores-se-vuelven-extremos" class="level3" data-number="6.1">
<h3 data-number="6.1" class="anchored" data-anchor-id="cuándo-los-tres-factores-se-vuelven-extremos"><span class="header-section-number">6.1</span> Cuándo los tres factores se vuelven extremos</h3>
<p>El <strong>double descent</strong> aparece cuando esos tres factores son <strong>simultáneamente</strong> desfavorables:</p>
<ul>
<li><strong>(i)</strong> En el entrenamiento hay varianza <strong>pequeña pero no nula</strong> en alguna dirección singular (equivalente: <img src="https://latex.codecogs.com/png.latex?%5Csigma_r"> muy pequeño).</li>
<li><strong>(ii)</strong> Desde la perspectiva de la clase de modelos, el residuo <img src="https://latex.codecogs.com/png.latex?E"> tiene <strong>gran</strong> proyección sobre ese modo: la relación entre <strong>descriptores</strong> de entrenamiento y respuestas es frágil en esa dirección.</li>
<li><strong>(iii)</strong> El vector de test <strong>presenta variación importante</strong> a lo largo de ese mismo modo: <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5Ccdot%20v_r"> es grande.</li>
</ul>
<p>Si <strong>(i)</strong> y <strong>(ii)</strong> coinciden, los coeficientes efectivos a lo largo de ese modo suelen estar <strong>mal estimados</strong>. Si además <strong>(iii)</strong> entra por un <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D"> con gran proyección, el modelo se ve obligado a <strong>extrapolar</strong> fuerte en una dirección poco vista en entrenamiento, donde además la relación predicción–respuesta era ya propensa a error: el <strong>error cuadrático en test</strong> puede <strong>explotar</strong>.</p>
</section>
<section id="por-qué-la-explosión-ocurre-cerca-del-umbral-de-interpolación" class="level3" data-number="6.2">
<h3 data-number="6.2" class="anchored" data-anchor-id="por-qué-la-explosión-ocurre-cerca-del-umbral-de-interpolación"><span class="header-section-number">6.2</span> Por qué la explosión ocurre cerca del umbral de interpolación</h3>
<p>El factor <img src="https://latex.codecogs.com/png.latex?1/%5Csigma_r"> es el que se vuelve más <strong>peligroso</strong> al acercarse al umbral <strong>desde cualquier régimen</strong> de parametrización: los valores singulares casi nulos se vuelven más probables.</p>
<p>Una explicación <strong>probabilística</strong> profunda pasa por la ley de <strong>Marchenko–Pastur</strong> (matrices aleatorias): el menor valor singular no nulo tiende a situarse en un régimen crítico junto al borde de interpolación. Por ser técnica, aquí priorizamos la intuición geométrica: <strong>cuánta dispersión hemos observado en cada dirección ortogonal</strong> del espacio de covariables al ir añadiendo muestras (Figura&nbsp;3).</p>
<p>Supongamos un <strong>único</strong> dato de entrenamiento <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D_1">. Mientras no sea el vector cero, ese punto define <strong>una</strong> dirección de variación no trivial: ganamos información sobre la dispersión de la distribución <strong>en esa dirección</strong>; en todas las ortogonales la varianza empírica es <strong>exactamente cero</strong>, y el ajuste lineal no puede usarlas. Con un <strong>segundo</strong> dato <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D_2">, de nuevo hay variación, pero parte de <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D_2"> suele proyectarse sobre <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D_1">; la dirección <strong>compartida</strong> acumula más evidencia, pero la segunda dirección ortogonal de variación queda <strong>peor</strong> identificada. Por eso, con dos muestras, el <strong>menor</strong> valor singular no nulo de la matriz de datos es en <strong>probabilidad</strong> más pequeño que con una sola.</p>
<p>A medida que crece <img src="https://latex.codecogs.com/png.latex?N"> y nos acercamos al umbral <img src="https://latex.codecogs.com/png.latex?N=P=D"> (en el panel de Figura&nbsp;3, <img src="https://latex.codecogs.com/png.latex?D=3">), la probabilidad de que cada dato nuevo aporte varianza fuerte en una dirección <strong>nueva</strong> y ortogonal a todo lo observado <strong>cae</strong>. En el umbral, para que el <img src="https://latex.codecogs.com/png.latex?N">-ésimo dato <strong>no</strong> añada un valor singular pequeño-pero-no-nulo harían falta a la vez: (1) existir una dimensión en la que <strong>ninguno</strong> de los <img src="https://latex.codecogs.com/png.latex?N-1"> datos anteriores hubiera variado, y (2) que el <img src="https://latex.codecogs.com/png.latex?N">-ésimo dato varíe <strong>mucho</strong> solo en esa dimensión: es <strong>poco probable</strong>. Más allá del umbral, con más datos, la varianza en cada dimensión covariable se estima mejor y el menor valor singular no nulo se <strong>aleja</strong> de cero. Eso conecta el pico de error con el peor condicionamiento espectral de <img src="https://latex.codecogs.com/png.latex?X">.</p>
<div id="fig-4" class="quarto-float quarto-figure quarto-figure-center anchored" alt="Seis paneles 3D: distribución verdadera y muestras crecientes; flechas de varianza principal." data-fig-cap="**Figura 4.** Intuición geométrica de por qué el menor valor singular no nulo alcanza su valor más bajo al acercarse al umbral de interpolación. Al acercarse a ese umbral (aquí $D=P=N=3$), $N$ datos de entrenamiento tienen poca probabilidad de variar de forma sustancial en $P$ direcciones ortogonales: al menos una dirección ortogonal suele tener varianza pequeña. Con menos datos, las direcciones residuales tienen varianza exactamente nula; con más datos, las observaciones adicionales revelan varianza a lo largo de esas direcciones.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-4-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<a href="img/plot2.png" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title="Figura&nbsp;3: Figura 4: intuición geométrica (D=3). Distribución verdadera y muestras con N=1,2,3,8,100; ejes principales (azul, verde, rojo)."><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/plot2.png" class="img-fluid figure-img" style="width:100.0%" data-fig-cap="**Figura 4.** Intuición geométrica de por qué el menor valor singular no nulo alcanza su valor más bajo al acercarse al umbral de interpolación. Al acercarse a ese umbral (aquí $D=P=N=3$), $N$ datos de entrenamiento tienen poca probabilidad de variar de forma sustancial en $P$ direcciones ortogonales: al menos una dirección ortogonal suele tener varianza pequeña. Con menos datos, las direcciones residuales tienen varianza exactamente nula; con más datos, las observaciones adicionales revelan varianza a lo largo de esas direcciones." alt="Seis paneles 3D: distribución verdadera y muestras crecientes; flechas de varianza principal."></a>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-4-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figura&nbsp;3: Figura 4: intuición geométrica (<img src="https://latex.codecogs.com/png.latex?D=3">). Distribución verdadera y muestras con <img src="https://latex.codecogs.com/png.latex?N=1,2,3,8,100">; ejes principales (azul, verde, rojo).
</figcaption>
</figure>
</div>
<div id="fig-5" class="quarto-float quarto-figure quarto-figure-center anchored" alt="Dos paneles: MSE logarítmico y menor singular logarítmico vs número de muestras." data-fig-cap="**Figura 5.** Regresión lineal ordinaria sobre el conjunto California Housing (30 repeticiones). **Izquierda:** MSE medio en entrenamiento y en test frente al número de muestras de entrenamiento; la línea vertical discontinua marca el **umbral de interpolación** (donde el número de muestras iguala el de covariables). **Derecha:** menor valor singular distinto de cero de la matriz $X$ de entrenamiento; su mínimo coincide con ese umbral y enlaza el pico de error con el factor $1/\sigma_r$ de la ecuación @eq-error-modes.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-5-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<a href="img/plot3.png" class="lightbox" data-gallery="quarto-lightbox-gallery-4" title="Figura&nbsp;4: Figura 5: double descent en regresión lineal ordinaria (California Housing)."><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/plot3.png" class="img-fluid figure-img" style="width:100.0%" data-fig-cap="**Figura 5.** Regresión lineal ordinaria sobre el conjunto California Housing (30 repeticiones). **Izquierda:** MSE medio en entrenamiento y en test frente al número de muestras de entrenamiento; la línea vertical discontinua marca el **umbral de interpolación** (donde el número de muestras iguala el de covariables). **Derecha:** menor valor singular distinto de cero de la matriz $X$ de entrenamiento; su mínimo coincide con ese umbral y enlaza el pico de error con el factor $1/\sigma_r$ de la ecuación @eq-error-modes." alt="Dos paneles: MSE logarítmico y menor singular logarítmico vs número de muestras."></a>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-5-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figura&nbsp;4: Figura 5: double descent en regresión lineal ordinaria (California Housing).
</figcaption>
</figure>
</div>
<p>La Figura&nbsp;4 muestra en datos reales que el <strong>pico</strong> de MSE de test y el <strong>mínimo</strong> del menor valor singular no nulo de <img src="https://latex.codecogs.com/png.latex?X"> ocurren <strong>juntos</strong> en el umbral: evidencia empírica del vínculo entre <img src="https://latex.codecogs.com/png.latex?1/%5Csigma_r"> y el error.</p>
</section>
</section>
<section id="generalización-en-regresión-lineal-sobre-parametrizada" class="level2" data-number="7">
<h2 data-number="7" class="anchored" data-anchor-id="generalización-en-regresión-lineal-sobre-parametrizada"><span class="header-section-number">7</span> Generalización en regresión lineal sobre-parametrizada</h2>
<p>Puede sorprender que, en el régimen sobre-parametrizado, <strong>tres</strong> conjuntos de datos exhiban error cuadrático de test relativamente bajo (California Housing, Diabetes, Student–Teacher) mientras que <strong>uno</strong> (expectativa de vida de la OMS, <em>WHO Life Expectancy</em>) no. La clave es que, para <img src="https://latex.codecogs.com/png.latex?P%3EN">, el error de predicción incluye un <strong>término de sesgo</strong> que <strong>no aparece</strong> cuando <img src="https://latex.codecogs.com/png.latex?P%3CN">.</p>
<section id="el-término-de-sesgo-y-la-visibilidad-de-las-covariables" class="level3" data-number="7.1">
<h3 data-number="7.1" class="anchored" data-anchor-id="el-término-de-sesgo-y-la-visibilidad-de-las-covariables"><span class="header-section-number">7.1</span> El término de sesgo y la “visibilidad” de las covariables</h3>
<p>El objetivo del modelo lineal es <strong>correlacionar</strong> fluctuaciones en las covariables con fluctuaciones en la respuesta. Con <strong>más parámetros que datos</strong> (<img src="https://latex.codecogs.com/png.latex?P%3EN">), en cada punto de <img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5EP"> el ajuste solo puede “ver” fluctuaciones en un subespacio de dimensión a lo sumo <img src="https://latex.codecogs.com/png.latex?N">: las <strong><img src="https://latex.codecogs.com/png.latex?P-N"></strong> direcciones restantes son <strong>invisibles</strong> para el entrenamiento. Por eso se pierde información sobre la relación lineal óptima <img src="https://latex.codecogs.com/png.latex?%5Cbeta%5E%7B*%7D"> y crece el error de predicción en régimen sobre-parametrizado.</p>
<p>Sea <img src="https://latex.codecogs.com/png.latex?P%20=%20X%5E%5Ctop%20(X%20X%5E%5Ctop)%5E%7B-1%7D%20X"> la <strong>proyección ortogonal</strong> sobre el espacio fila de <img src="https://latex.codecogs.com/png.latex?X"> (en <img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5EP">, el span de las filas de la matriz de diseño). Para un covariable de test <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D"> definimos la <strong>representación interna</strong> inducida por el modelo lineal como la proyección</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cwidehat%7B%5Cmathbf%7Bx%7D%7D_%7B%5Ctext%7Btest%7D%7D%20:=%20P%5C,%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%20=%20X%5E%5Ctop%20(X%20X%5E%5Ctop)%5E%7B-1%7D%20X%5C,%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D.%0A"></p>
<p>La parte del error atribuible al sesgo respecto de <img src="https://latex.codecogs.com/png.latex?%5Cbeta%5E%7B*%7D"> (la componente que ya aparecía al descomponer <img src="https://latex.codecogs.com/png.latex?%5Chat%7By%7D_%7B%5Ctext%7Btest,over%7D%7D%20-%20y%5E%7B*%7D_%7B%5Ctext%7Btest%7D%7D">) puede escribirse como</p>
<p><span id="eq-bias-over"><img src="https://latex.codecogs.com/png.latex?%0A%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5E%5Ctop%5Cbig(X%5E%5Ctop%20(X%20X%5E%5Ctop)%5E%7B-1%7D%20X%20-%20I_P%5Cbig)%5Cbeta%5E%7B*%7D%0A=%20%5Cbigl(%5Cwidehat%7B%5Cmathbf%7Bx%7D%7D_%7B%5Ctext%7Btest%7D%7D%20-%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5Cbigr)%5E%5Ctop%20%5Cbeta%5E%7B*%7D.%0A%5Ctag%7B2%7D"></span></p>
<p>Es decir, el sesgo es el <strong>producto interno</strong> entre (1) el vector que va del dato de test a su proyección sobre el espacio “visible” por el entrenamiento, <img src="https://latex.codecogs.com/png.latex?%5Cwidehat%7B%5Cmathbf%7Bx%7D%7D_%7B%5Ctext%7Btest%7D%7D%20-%20%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D">, y (2) los coeficientes ideales <img src="https://latex.codecogs.com/png.latex?%5Cbeta%5E%7B*%7D"> de la clase. Si <img src="https://latex.codecogs.com/png.latex?%5Cbeta%5E%7B*%7D"> es casi <strong>ortogonal</strong> a la componente “no vista”, el sesgo es pequeño; si <img src="https://latex.codecogs.com/png.latex?%5Cbeta%5E%7B*%7D"> <strong>alinea</strong> con direcciones mal identificadas por <img src="https://latex.codecogs.com/png.latex?X">, el sesgo puede ser grande.</p>
<p>Lejos del umbral de interpolación, la <strong>varianza</strong> suele explicar poca parte de la discrepancia entre el modelo sobre-parametrizado y el ideal; buena parte de la discrepancia proviene entonces del <strong>sesgo</strong> (Ecuación&nbsp;2). Esa misma expresión admite una lectura geométrica sorprendente: la regresión lineal sobre-parametrizada hace <strong>aprendizaje de representaciones</strong> —reemplaza <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D"> por su proyección <img src="https://latex.codecogs.com/png.latex?%5Cwidehat%7B%5Cmathbf%7Bx%7D%7D_%7B%5Ctext%7Btest%7D%7D"> en el subespacio aprendido con los datos de entrenamiento.</p>
<div id="fig-7" class="quarto-float quarto-figure quarto-figure-center anchored" alt="Diagrama 3D: proyección del test al espacio fila y tres vectores beta estrella." data-fig-cap="Figura 7. Geometría de la generalización en regresión lineal ordinaria sobre-parametrizada: el espacio fila del entrenamiento es un subespacio del ambiente (p. ej. una recta en R³). El modelo proyecta ortogonalmente el vector de test sobre ese subespacio mediante la matriz de proyección; el sesgo crece con el producto interno entre (representación proyectada − dato original) y el vector β* ideal. Tres β* ilustran error de generalización bajo (azul), medio (verde) y alto (rojo).">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-7-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<a href="img/plot4.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-5" title="Figura&nbsp;5: Geometría de la generalización en OLR sobre-parametrizada."><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/plot4.jpg" class="img-fluid figure-img" style="width:100.0%" data-fig-cap="Figura 7. Geometría de la generalización en regresión lineal ordinaria sobre-parametrizada: el espacio fila del entrenamiento es un subespacio del ambiente (p. ej. una recta en R³). El modelo proyecta ortogonalmente el vector de test sobre ese subespacio mediante la matriz de proyección; el sesgo crece con el producto interno entre (representación proyectada − dato original) y el vector β* ideal. Tres β* ilustran error de generalización bajo (azul), medio (verde) y alto (rojo)." alt="Diagrama 3D: proyección del test al espacio fila y tres vectores beta estrella."></a>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-7-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figura&nbsp;5: Geometría de la generalización en OLR sobre-parametrizada.
</figcaption>
</figure>
</div>
</section>
<section id="sesgo-al-cuadrado-en-test-en-cuatro-conjuntos-de-datos" class="level3" data-number="7.2">
<h3 data-number="7.2" class="anchored" data-anchor-id="sesgo-al-cuadrado-en-test-en-cuatro-conjuntos-de-datos"><span class="header-section-number">7.2</span> Sesgo al cuadrado en test en cuatro conjuntos de datos</h3>
<div id="fig-8" class="quarto-float quarto-figure quarto-figure-center anchored" alt="Cuadrícula 2x2: sesgo al cuadrado vs ratio parámetros/muestras en cuatro datasets." data-fig-cap="Figura 8. Sesgo al cuadrado en test frente al cociente número de parámetros / número de muestras de entrenamiento (escala log-log). Línea vertical: umbral de interpolación. Un alineamiento fuerte entre β* y la componente &quot;no vista&quot; del test implica sesgo grande. California Housing, Diabetes y Student–Teacher muestran sesgo moderado; en datos de expectativa de vida (OMS) el sesgo puede crecer de forma extrema al sobre-parametrizar.">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-8-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<a href="img/plot5.png" class="lightbox" data-gallery="quarto-lightbox-gallery-6" title="Figura&nbsp;6: Sesgo al cuadrado en test para modelos sobre-parametrizados (cuatro conjuntos)."><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/plot5.png" class="img-fluid figure-img" style="width:100.0%" data-fig-cap="Figura 8. Sesgo al cuadrado en test frente al cociente número de parámetros / número de muestras de entrenamiento (escala log-log). Línea vertical: umbral de interpolación. Un alineamiento fuerte entre β* y la componente &quot;no vista&quot; del test implica sesgo grande. California Housing, Diabetes y Student–Teacher muestran sesgo moderado; en datos de expectativa de vida (OMS) el sesgo puede crecer de forma extrema al sobre-parametrizar." alt="Cuadrícula 2x2: sesgo al cuadrado vs ratio parámetros/muestras en cuatro datasets."></a>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-8-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figura&nbsp;6: Sesgo al cuadrado en test para modelos sobre-parametrizados (cuatro conjuntos).
</figcaption>
</figure>
</div>
<p>Intuitivamente, un modelo sobre-parametrizado <strong>generaliza bien</strong> cuando sus <strong>representaciones</strong> (las proyecciones <img src="https://latex.codecogs.com/png.latex?%5Cwidehat%7B%5Cmathbf%7Bx%7D%7D">) retienen la información que <img src="https://latex.codecogs.com/png.latex?%5Cbeta%5E%7B*%7D"> necesita para predecir (Figura&nbsp;5, Figura&nbsp;6). Donde <img src="https://latex.codecogs.com/png.latex?%5Cbeta%5E%7B*%7D"> tiene componente fuerte en direcciones que el entrenamiento no puede ver, el término Ecuación&nbsp;2 —y por tanto el error de test— se dispara aunque la varianza del estimador sea manejable.</p>
</section>
</section>
<section id="hacia-las-redes-lazy-training-ntk-y-el-prisma-de-la-interpolación" class="level2" data-number="8">
<h2 data-number="8" class="anchored" data-anchor-id="hacia-las-redes-lazy-training-ntk-y-el-prisma-de-la-interpolación"><span class="header-section-number">8</span> Hacia las redes: <em>lazy training</em>, NTK y el prisma de la interpolación</h2>
<p>Las cuentas anteriores son <strong>lineales</strong> a propósito: matriz de diseño <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7BX%7D_P"> (Legendre) y solución de norma mínima. Para trasladar el mismo lenguaje a redes no lineales se usa un régimen en que <img src="https://latex.codecogs.com/png.latex?w"> permanece en un entorno de la inicialización <img src="https://latex.codecogs.com/png.latex?w_0">, de modo que <img src="https://latex.codecogs.com/png.latex?f(w,x)"> admite una aproximación de <strong>orden uno</strong> en <img src="https://latex.codecogs.com/png.latex?w">; eso es el marco de <strong><em>lazy training</em></strong> / linealización. La exposición enlaza con la visión de interpolación y sobre-parametrización de Belkin <span class="citation" data-cites="belkin2021fit">(2021)</span>; las demostraciones que siguen son el caso <strong>linealizado finito</strong> (riguroso) y el puente al <strong>kernel tangente</strong> (límite de ancho y flujo de gradiente).</p>
<section id="linealización-de-taylor-y-feature-matrix" class="level3" data-number="8.1">
<h3 data-number="8.1" class="anchored" data-anchor-id="linealización-de-taylor-y-feature-matrix"><span class="header-section-number">8.1</span> Linealización de Taylor y <em>feature matrix</em></h3>
<p>Sea <img src="https://latex.codecogs.com/png.latex?f:%5Cmathbb%7BR%7D%5Ed%5Ctimes%20%5Cmathcal%7BX%7D%5Cto%5Cmathbb%7BR%7D"> de clase <img src="https://latex.codecogs.com/png.latex?%5Cmathcal%7BC%7D%5E1"> en <img src="https://latex.codecogs.com/png.latex?w">. Fijamos <img src="https://latex.codecogs.com/png.latex?w_0"> y escribimos el desarrollo de Taylor con resto integral de orden uno:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0Af(w,x)%20=%20f(w_0,x)%20+%20%5Cnabla_w%20f(w_0,x)%5E%5Ctop%20(w-w_0)%20+%20R(w,w_0,x),%0A"></p>
<p>con <img src="https://latex.codecogs.com/png.latex?%5C%7CR(w,w_0,x)%5C%7C%20=%20o(%5C%7Cw-w_0%5C%7C)"> cuando <img src="https://latex.codecogs.com/png.latex?w%5Cto%20w_0">. El modelo <strong>linealizado</strong> es</p>
<p><img src="https://latex.codecogs.com/png.latex?%0Af_%7B%5Cmathrm%7Blin%7D%7D(w,x)%20:=%20f(w_0,x)%20+%20%5Cphi(x)%5E%5Ctop%20(w-w_0),%20%5Cqquad%20%5Cphi(x)%20:=%20%5Cnabla_w%20f(w_0,x)%20%5Cin%20%5Cmathbb%7BR%7D%5Ed.%0A"></p>
<p>Definimos la <strong>matriz Jacobiana de entrenamiento</strong></p>
<p><img src="https://latex.codecogs.com/png.latex?J%20%5Cin%20%5Cmathbb%7BR%7D%5E%7Bn%5Ctimes%20d%7D"> por filas <img src="https://latex.codecogs.com/png.latex?J_%7Bi%5Ccdot%7D%20=%20%5Cphi(x_i)%5E%5Ctop">, y los vectores <img src="https://latex.codecogs.com/png.latex?f_0%20:=%20(f(w_0,x_1),%5Cldots,f(w_0,x_n))%5E%5Ctop">, <img src="https://latex.codecogs.com/png.latex?y%20:=%20(y_1,%5Cldots,y_n)%5E%5Ctop">. Entonces</p>
<p><img src="https://latex.codecogs.com/png.latex?%0Af_%7B%5Cmathrm%7Blin%7D%7D(w)%20:=%20%5Cbigl(f_%7B%5Cmathrm%7Blin%7D%7D(w,x_1),%5Cldots,f_%7B%5Cmathrm%7Blin%7D%7D(w,x_n)%5Cbigr)%5E%5Ctop%20=%20f_0%20+%20J(w-w_0).%0A"></p>
<p>Con <img src="https://latex.codecogs.com/png.latex?u%20:=%20w-w_0"> y <img src="https://latex.codecogs.com/png.latex?r%20:=%20y%20-%20f_0"> (vector de <strong>residuos al punto linealizado</strong>), interpolar los datos en el modelo linealizado es</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AJu%20=%20r.%20%5Ctag%7B$%5Cstar$%7D%0A"></p>
<p>La matriz <img src="https://latex.codecogs.com/png.latex?J"> es el análogo matricial de apilar features: en Legendre, las filas eran <img src="https://latex.codecogs.com/png.latex?%5Ctilde%7B%5Cphi%7D_P(x_i)%5E%5Ctop">; aquí las filas son gradientes <img src="https://latex.codecogs.com/png.latex?%5Cnabla_w%20f(w_0,x_i)%5E%5Ctop">. El régimen <em>lazy</em> es aquel en que <img src="https://latex.codecogs.com/png.latex?%5C%7Cw-w_0%5C%7C"> es tan pequeño (o el ancho tan grande) que <img src="https://latex.codecogs.com/png.latex?f%5Capprox%20f_%7B%5Cmathrm%7Blin%7D%7D"> a lo largo de la optimización; entonces el análisis de <img src="https://latex.codecogs.com/png.latex?(%5Cstar)"> es el sustituto riguroso del modelo polinómico finito.</p>
</section>
<section id="neural-tangent-kernel-definición-y-matriz-de-gram" class="level3" data-number="8.2">
<h3 data-number="8.2" class="anchored" data-anchor-id="neural-tangent-kernel-definición-y-matriz-de-gram"><span class="header-section-number">8.2</span> Neural Tangent Kernel: definición y matriz de Gram</h3>
<p><strong>Definición.</strong> El <strong>kernel tangente neuronal</strong> (NTK) en <img src="https://latex.codecogs.com/png.latex?w_0"> es la función positiva semidefinida</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5CTheta(x,x')%20:=%20%5Cbig%5Clangle%20%5Cnabla_w%20f(w_0,x),%5C,%20%5Cnabla_w%20f(w_0,x')%20%5Cbig%5Crangle%20=%20%5Cphi(x)%5E%5Ctop%20%5Cphi(x').%0A"></p>
<p>Sobre la muestra <img src="https://latex.codecogs.com/png.latex?%5C%7Bx_i%5C%7D_%7Bi=1%7D%5En">, la <strong>matriz</strong> <img src="https://latex.codecogs.com/png.latex?%5CTheta%20%5Cin%20%5Cmathbb%7BR%7D%5E%7Bn%5Ctimes%20n%7D"> dada por <img src="https://latex.codecogs.com/png.latex?%5CTheta_%7Bij%7D%20=%20%5CTheta(x_i,x_j)"> es la <strong>matriz de Gram</strong> de los vectores <img src="https://latex.codecogs.com/png.latex?%5Cphi(x_i)">:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5CTheta%20=%20J%20J%5E%5Ctop.%0A"></p>
<p><strong>Observación (flujo de gradiente en el límite ancho).</strong> Jacot et al. <span class="citation" data-cites="jacot2018neural">(2018)</span> prueban que, bajo inicialización aleatoria adecuada y ancho <img src="https://latex.codecogs.com/png.latex?%5Cto%5Cinfty">, <img src="https://latex.codecogs.com/png.latex?%5CTheta"> converge a un límite determinista y, en el entrenamiento continuo de GD sobre pérdida cuadrática, la evolución de las predicciones en los datos queda gobernada por un kernel que coincide con ese límite (<strong>NTK fijo</strong>). En la práctica, “NTK estático” significa que <img src="https://latex.codecogs.com/png.latex?%5CTheta"> no varía apreciablemente durante el entrenamiento, de modo que la dinámica es la de un modelo lineal en el espacio de funciones inducido por <img src="https://latex.codecogs.com/png.latex?%5CTheta">. El condicionamiento de <img src="https://latex.codecogs.com/png.latex?%5CTheta"> (valores propios pequeños) es el paralelo espectral al mal condicionamiento de <img src="https://latex.codecogs.com/png.latex?J"> o de <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7BX%7D_P"> en el ejemplo de Legendre.</p>
</section>
<section id="proposición-gd-lineal-y-norma-mínima" class="level3" data-number="8.3">
<h3 data-number="8.3" class="anchored" data-anchor-id="proposición-gd-lineal-y-norma-mínima"><span class="header-section-number">8.3</span> Proposición (GD lineal y norma mínima)</h3>
<p>Trabajamos con el modelo linealizado <img src="https://latex.codecogs.com/png.latex?f_%7B%5Cmathrm%7Blin%7D%7D"> y la pérdida cuadrática</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AL(w)%20=%20%5Cfrac%7B1%7D%7B2%7D%5C%7Cf_%7B%5Cmathrm%7Blin%7D%7D(w)%20-%20y%5C%7C_2%5E2%20=%20%5Cfrac%7B1%7D%7B2%7D%5C%7CJu%20-%20r%5C%7C_2%5E2,%20%5Cqquad%20u%20=%20w-w_0.%0A"></p>
<p><strong>Lema 1 (gradiente y subespacio).</strong> Se cumple <img src="https://latex.codecogs.com/png.latex?%5Cnabla_u%20L%20=%20J%5E%5Ctop(Ju-r)">. Por tanto, cada paso de GD,</p>
<p><img src="https://latex.codecogs.com/png.latex?%0Au_%7Bt+1%7D%20=%20u_t%20-%20%5Ceta%5C,%20J%5E%5Ctop(Ju_t%20-%20r),%0A"></p>
<p>satisface <img src="https://latex.codecogs.com/png.latex?u_%7Bt+1%7D-u_t%20%5Cin%20%5Cmathrm%7BIm%7D(J%5E%5Ctop)%20=%20%5Cmathrm%7Bspan%7D%5C%7B%5Cphi(x_1),%5Cldots,%5Cphi(x_n)%5C%7D">. Si <img src="https://latex.codecogs.com/png.latex?u_0=0">, entonces <img src="https://latex.codecogs.com/png.latex?u_t%20%5Cin%20%5Cmathrm%7BIm%7D(J%5E%5Ctop)"> para todo <img src="https://latex.codecogs.com/png.latex?t">.</p>
<p><em>Demostración.</em> La primera afirmación es la regla de la cadena; la segunda es definición de GD; la tercera, inducción sobre <img src="https://latex.codecogs.com/png.latex?t">. <img src="https://latex.codecogs.com/png.latex?%5Csquare"></p>
<p><strong>Lema 2 (complemento ortogonal fijo).</strong> Sea <img src="https://latex.codecogs.com/png.latex?P_%7B%5Cmathrm%7Brow%7D%7D%20=%20J%5E+%20J"> la proyección ortogonal sobre <img src="https://latex.codecogs.com/png.latex?%5Cmathrm%7BIm%7D(J%5E%5Ctop)"> y <img src="https://latex.codecogs.com/png.latex?P_%7B%5Cmathrm%7Bnull%7D%7D%20=%20I%20-%20P_%7B%5Cmathrm%7Brow%7D%7D"> sobre <img src="https://latex.codecogs.com/png.latex?%5Cker(J)">. Si <img src="https://latex.codecogs.com/png.latex?u_0=0">, entonces <img src="https://latex.codecogs.com/png.latex?P_%7B%5Cmathrm%7Bnull%7D%7D%20u_t%20=%200"> para todo <img src="https://latex.codecogs.com/png.latex?t"> (la componente en <img src="https://latex.codecogs.com/png.latex?%5Cker(J)"> permanece nula).</p>
<p><em>Demostración.</em> <img src="https://latex.codecogs.com/png.latex?J%5E%5Ctop%20v%20%5Cin%20%5Cmathrm%7BIm%7D(J%5E%5Ctop)"> para todo <img src="https://latex.codecogs.com/png.latex?v">, luego <img src="https://latex.codecogs.com/png.latex?P_%7B%5Cmathrm%7Bnull%7D%7D(u_%7Bt+1%7D-u_t)=0">. Con <img src="https://latex.codecogs.com/png.latex?u_0=0"> se obtiene <img src="https://latex.codecogs.com/png.latex?P_%7B%5Cmathrm%7Bnull%7D%7D%20u_t=0">. <img src="https://latex.codecogs.com/png.latex?%5Csquare"></p>
<p><strong>Proposición.</strong> Supongamos <img src="https://latex.codecogs.com/png.latex?%5Cmathrm%7Brango%7D(J)=n"> (interpolación posible y <img src="https://latex.codecogs.com/png.latex?J"> de rango fila completo). Entonces existe una única solución <img src="https://latex.codecogs.com/png.latex?u%5E%5Cstar"> de <strong>norma euclídea mínima</strong> tal que <img src="https://latex.codecogs.com/png.latex?Ju%5E%5Cstar=r">, dada por <img src="https://latex.codecogs.com/png.latex?u%5E%5Cstar%20=%20J%5E+%20r%20=%20J%5E%5Ctop%20(JJ%5E%5Ctop)%5E%7B-1%7D%20r">. Si el GD con paso <img src="https://latex.codecogs.com/png.latex?%5Ceta"> suficientemente pequeño converge desde <img src="https://latex.codecogs.com/png.latex?u_0=0">, su límite es <img src="https://latex.codecogs.com/png.latex?u%5E%5Cstar">.</p>
<p><em>Esquema de demostración.</em> El conjunto <img src="https://latex.codecogs.com/png.latex?%5C%7Bu:%20Ju=r%5C%7D"> es un subespacio afín paralelo a <img src="https://latex.codecogs.com/png.latex?%5Cker(J)">. Entre ellos, el de norma mínima es el único ortogonal a <img src="https://latex.codecogs.com/png.latex?%5Cker(J)">, es decir, en <img src="https://latex.codecogs.com/png.latex?%5Cmathrm%7BIm%7D(J%5E%5Ctop)">; la fórmula de <img src="https://latex.codecogs.com/png.latex?J%5E+"> es estándar. El flujo de GD en problemas cuadráticos convexos converge al único mínimo global <img src="https://latex.codecogs.com/png.latex?u%5E%5Cstar"> en <img src="https://latex.codecogs.com/png.latex?%5Cmathrm%7BIm%7D(J%5E%5Ctop)"> cuando <img src="https://latex.codecogs.com/png.latex?u_0=0"> y <img src="https://latex.codecogs.com/png.latex?%5Ceta"> está por debajo del inverso de la constante de Lipschitz de <img src="https://latex.codecogs.com/png.latex?%5Cnabla%20L"> (típicamente <img src="https://latex.codecogs.com/png.latex?%5Ceta%20%3C%202/%5Clambda_%7B%5Cmax%7D(J%5E%5Ctop%20J)">). Ese mínimo global coincide con el interpolador de norma mínima por Lema 2. <img src="https://latex.codecogs.com/png.latex?%5Csquare"></p>
<p><strong>Corolario (RKHS).</strong> Si identificamos el espacio de funciones lineales en las features <img src="https://latex.codecogs.com/png.latex?%5Cphi(x)"> con el RKHS asociado al kernel <img src="https://latex.codecogs.com/png.latex?%5CTheta">, la solución de norma mínima en parámetros <img src="https://latex.codecogs.com/png.latex?u"> corresponde a la función de <strong>norma de RKHS mínima</strong> que interpola en los puntos de entrenamiento (en el subespacio generado por <img src="https://latex.codecogs.com/png.latex?%5CTheta(%5Ccdot,x_i)">), en analogía directa con el caso Legendre y la pseudo-inversa.</p>
</section>
<section id="condición-de-polyakłojasiewicz-y-variantes" class="level3" data-number="8.4">
<h3 data-number="8.4" class="anchored" data-anchor-id="condición-de-polyakłojasiewicz-y-variantes"><span class="header-section-number">8.4</span> Condición de Polyak–Łojasiewicz y variantes</h3>
<p><strong>Definición (PL).</strong> Una función <img src="https://latex.codecogs.com/png.latex?L"> diferenciable satisface la <strong>condición PL</strong> con constante <img src="https://latex.codecogs.com/png.latex?%5Cmu%3E0"> en un conjunto <img src="https://latex.codecogs.com/png.latex?%5COmega"> si</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5C%7C%5Cnabla%20L(w)%5C%7C_2%5E2%20%5C;%5Cge%5C;%202%5Cmu%5C,%5Cbigl(%20L(w)%20-%20L%5E%5Cstar%20%5Cbigr)%20%5Cquad%20%5Cforall%20w%20%5Cin%20%5COmega,%0A"></p>
<p>donde <img src="https://latex.codecogs.com/png.latex?L%5E%5Cstar"> es el mínimo global de <img src="https://latex.codecogs.com/png.latex?L"> en <img src="https://latex.codecogs.com/png.latex?%5COmega"> (a veces se escribe la forma equivalente <img src="https://latex.codecogs.com/png.latex?%5C%7C%5Cnabla%20L%5C%7C%5E2%20%5Cge%20%5Cmu%20L"> cuando <img src="https://latex.codecogs.com/png.latex?L%5E%5Cstar=0">).</p>
<p><strong>Proposición (convergencia lineal bajo PL).</strong> Si <img src="https://latex.codecogs.com/png.latex?L"> es <img src="https://latex.codecogs.com/png.latex?%5Cbeta">-suave (gradiente Lipschitz) y satisface PL con constante <img src="https://latex.codecogs.com/png.latex?%5Cmu">, el GD con <img src="https://latex.codecogs.com/png.latex?%5Ceta%20%5Cle%201/%5Cbeta"> verifica</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AL(w_t)%20-%20L%5E%5Cstar%20%5Cle%20%5Cbigl(1%20-%20%5Ceta%5Cmu%5Cbigr)%5Et%20%5Cbigl(L(w_0)%20-%20L%5E%5Cstar%5Cbigr).%0A"></p>
<p><em>Demostración (esquema).</em> Descenso estándar usando PL y suavitud; ver Polyak (1963) o Karimi et al.&nbsp;para la constante exacta. <img src="https://latex.codecogs.com/png.latex?%5Csquare"></p>
<p>En redes, <img src="https://latex.codecogs.com/png.latex?L"> <strong>no</strong> es convexa en <img src="https://latex.codecogs.com/png.latex?w">; la variante <strong>PL*</strong> o hipótesis de tipo PL en el régimen interpolante (Belkin <span class="citation" data-cites="belkin2021fit">(2021)</span>, §4) postula que, con sobre-parametrización suficiente, en una región que contiene trayectorias razonables de GD se tiene control del tipo PL <strong>hacia el conjunto de mínimos globales</strong> (error nulo en entrenamiento). La consecuencia conceptual: no hace falta convexidad global para explicar convergencia a interpolación; basta una geometría que impida estancarse en valles con pérdida estrictamente positiva cuando existen mínimos con <img src="https://latex.codecogs.com/png.latex?L=0">.</p>
</section>
<section id="prisma-de-la-interpolación-y-benign-overfitting" class="level3" data-number="8.5">
<h3 data-number="8.5" class="anchored" data-anchor-id="prisma-de-la-interpolación-y-benign-overfitting"><span class="header-section-number">8.5</span> Prisma de la interpolación y <em>benign overfitting</em></h3>
<p>Formalmente, la <strong>navaja de Occam</strong> clásica penaliza la <strong>cardinalidad</strong> de parámetros o la dimensión del modelo. En el régimen interpolante moderno, la complejidad se mide en otra métrica: <strong>norma en el RKHS</strong> (o norma de los parámetros en la capa lineal final bajo NTK) en lugar del recuento de parámetros.</p>
<p>Belkin <span class="citation" data-cites="belkin2021fit">(2021)</span> enfatiza que, con muchos grados de libertad, un predictor puede interpolar datos ruidosos descomponiendo la solución en una parte “suave” (baja norma en el kernel efectivo) más correcciones de alta frecuencia que encajan el ruido pero tienen norma pequeña en la métrica adecuada; la <strong>señal</strong> puede preservarse en la componente suave. Por eso el error de test puede <strong>descender</strong> al aumentar la capacidad: hay más maneras de ser simultáneamente interpolante y de norma controlada en el espacio de funciones relevante —fenómeno distinto del sesgo–varianza con un solo mínimo.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Nota
</div>
</div>
<div class="callout-body-container callout-body">
<p><strong>Síntesis.</strong> Del mismo modo que en Legendre la pseudo-inversa selecciona, entre los <img src="https://latex.codecogs.com/png.latex?w"> con <img src="https://latex.codecogs.com/png.latex?Jw=y">, el de norma <img src="https://latex.codecogs.com/png.latex?%5C%7Cw-w_0%5C%7C"> mínima (con <img src="https://latex.codecogs.com/png.latex?w_0"> el origen del modelo linealizado), el <strong>NTK</strong> describe, en el límite de red ancha, un espacio de Hilbert de funciones donde el descenso de gradiente desde <img src="https://latex.codecogs.com/png.latex?w_0%5Capprox%200"> aproxima la interpolación de <strong>norma mínima</strong> en ese espacio: el puente entre el álgebra finita de <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7BX%7D_P"> y el comportamiento cualitativo del aprendizaje profundo.</p>
</div>
</div>
</section>
</section>
<section id="resumen" class="level2" data-number="9">
<h2 data-number="9" class="anchored" data-anchor-id="resumen"><span class="header-section-number">9</span> Resumen</h2>
<ul>
<li>El <strong>double descent</strong> aparece aquí en <strong>regresión polinómica 1D</strong> con base de <strong>Legendre</strong>, ruido gaussiano <img src="https://latex.codecogs.com/png.latex?%5Cmathcal%7BN%7D(0,0.2%5E2)">, nodos de Chebyshev, reparto train/test aleatorio y solución de <strong>norma mínima</strong> (pseudo-inversa de Moore–Penrose en el régimen interpolante).</li>
<li><strong><img src="https://latex.codecogs.com/png.latex?P=n"></strong> es el umbral donde el modelo lineal puede interpolar el entrenamiento; cerca de ese borde, valores singulares pequeños de <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7BX%7D_P"> <strong>amplifican</strong> componentes del ruido y del residual en la forma <img src="https://latex.codecogs.com/png.latex?%5Csum_r%20(%5Cmathbf%7Bx%7D_%7B%5Ctext%7Btest%7D%7D%5Ccdot%20v_r)(u_r%5Ccdot%20E)/%5Csigma_r">.</li>
<li>Para <strong><img src="https://latex.codecogs.com/png.latex?P%3EN"></strong>, además entra un término de <strong>sesgo</strong> respecto de <img src="https://latex.codecogs.com/png.latex?%5Cbeta%5E%7B*%7D"> asociado a la proyección <img src="https://latex.codecogs.com/png.latex?X%5E%5Ctop%20(X%20X%5E%5Ctop)%5E%7B-1%7DX">: solo una parte de <img src="https://latex.codecogs.com/png.latex?%5Cbeta%5E%7B*%7D"> es identificable desde <img src="https://latex.codecogs.com/png.latex?N"> ecuaciones en <img src="https://latex.codecogs.com/png.latex?P"> incógnitas.</li>
<li>El pico y la recuperación del error de test se entienden con mayor nitidez en el lenguaje de la <strong>SVD</strong> y la pseudo-inversa que solo con una curva genérica “sesgo vs.&nbsp;varianza”.</li>
</ul>



</section>


<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Volver arriba</a><div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">Referencias</h2><div id="refs" class="references csl-bib-body hanging-indent">
<div id="ref-belkin2021fit" class="csl-entry">
Belkin, Mikhail. 2021. <em>Fit without fear: remarkable mathematical phenomena of deep learning through the prism of interpolation</em>. <a href="https://arxiv.org/abs/2105.14368">https://arxiv.org/abs/2105.14368</a>.
</div>
<div id="ref-jacot2018neural" class="csl-entry">
Jacot, Arthur, Franck Gabriel, y Clément Hongler. 2018. <span>«Neural tangent kernel: Convergence and generalization in neural networks»</span>. <em>Advances in Neural Information Processing Systems</em> 31.
</div>
</div></section><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Notas</h2>

<ol>
<li id="fn1"><p>Si centráramos las columnas de <img src="https://latex.codecogs.com/png.latex?X"> para obtener <img src="https://latex.codecogs.com/png.latex?%5Cbar%7BX%7D"> y escribiéramos <img src="https://latex.codecogs.com/png.latex?%5Cbar%7BX%7D=%5Cbar%7BU%7D%5Cbar%7B%5CSigma%7D%5Cbar%7BV%7D%5E%5Ctop">, entonces <img src="https://latex.codecogs.com/png.latex?%5Cbar%7BX%7D%5E%5Ctop%20%5Cbar%7BX%7D"> sería la <strong>matriz de covarianza empírica</strong> y su descomposición espectral <img src="https://latex.codecogs.com/png.latex?%5Cbar%7BX%7D%5E%5Ctop%20%5Cbar%7BX%7D=%5Cbar%7BV%7D%5Cbar%7B%5CSigma%7D%5E2%5Cbar%7BV%7D%5E%5Ctop">: cada <img src="https://latex.codecogs.com/png.latex?v_r"> sería un eje ortogonal de variación y <img src="https://latex.codecogs.com/png.latex?%5Csigma_r%5E2"> la varianza empírica en esa dirección. <strong>Sin centrar</strong>, <img src="https://latex.codecogs.com/png.latex?X%5E%5Ctop%20X"> es el <strong>momento de segundo orden</strong> empírico, no la covarianza; por eso hablar de “varianza” es un ligero abuso terminológico, pero la intuición correcta es la misma: cuánto “se mueven” las entradas de <img src="https://latex.codecogs.com/png.latex?X"> en cada dirección y si ese movimiento se <strong>correlaciona</strong> con <img src="https://latex.codecogs.com/png.latex?Y"> a través del residuo. Centrar no es esencial para el fenómeno de double descent; omitir el centrado evita confundir al lector pedagógicamente.↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>ML</category>
  <category>STATS</category>
  <guid>https://luccafrachelle.github.io/porfolio_academia/posts/double_desent.html</guid>
  <pubDate>Wed, 01 Apr 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>UMAP y t-SNE</title>
  <dc:creator>Lucca Frachelle</dc:creator>
  <link>https://luccafrachelle.github.io/porfolio_academia/posts/umap-tsne.html</link>
  <description><![CDATA[ 





<div class="project-downloads">
<p><a href="../assets/pdfs/umap-tsne.pdf" class="text-link">Leer en PDF</a></p>
</div>
<section id="introducción" class="level2" data-number="1">
<h2 data-number="1" class="anchored" data-anchor-id="introducción"><span class="header-section-number">1</span> Introducción</h2>
<p>En la era digital, el crecimiento exponencial en volumen y complejidad de los datos ha consolidado a la <strong>reducción de dimensionalidad</strong> como una técnica indispensable en el análisis de datos. Su objetivo es transformar datos de un espacio de alta dimensión a una representación de menor dimensión, preservando al máximo la información relevante y la estructura inherente. Esta tarea es crucial no solo para la visualización y la comprensión de patrones complejos, sino también para mejorar la eficiencia computacional de algoritmos de aprendizaje automático y para mitigar la “maldición de la dimensionalidad”.</p>
<section id="enfoques-para-la-reducción-de-dimensionalidad" class="level3" data-number="1.1">
<h3 data-number="1.1" class="anchored" data-anchor-id="enfoques-para-la-reducción-de-dimensionalidad"><span class="header-section-number">1.1</span> Enfoques para la Reducción de Dimensionalidad</h3>
<p>Existen dos enfoques principales para la reducción de dimensionalidad:</p>
<ol type="1">
<li><strong>Factorización de Matrices:</strong>
<ul>
<li><strong>PCA (Análisis de Componentes Principales):</strong> Descompone la matriz de datos en componentes principales que maximizan la varianza.</li>
<li><strong>SVD (Descomposición en Valores Singulares):</strong> Factoriza la matriz en un producto de matrices ortogonales y una diagonal.</li>
<li><strong>NMF (Non-negative Matrix Factorization):</strong> Realiza una factorización con la restricción de que todos los elementos de las matrices deben ser no negativos.</li>
<li>Características:
<ul>
<li>Basado en álgebra lineal.</li>
<li>Optimiza criterios globales como la varianza o el error de reconstrucción.</li>
<li>Computacionalmente eficiente.</li>
<li>Menos sensible al ruido.</li>
</ul></li>
</ul></li>
<li><strong>Grafos de Vecindad:</strong>
<ul>
<li><strong>t-SNE:</strong> Construye un grafo de similitud basado en distribuciones de probabilidad.</li>
<li><strong>UMAP:</strong> Construye un grafo de vecindad difuso basado en fundamentos topológicos.</li>
<li><strong>LLE (Local Linear Embedding):</strong> Preserva las relaciones de vecindad locales asumiendo que la variedad es localmente lineal.</li>
<li>Características:
<ul>
<li>Basado en teoría de grafos y topología.</li>
<li>Preserva la estructura local de los datos.</li>
<li>Especialmente útil para la visualización y el descubrimiento de <em>clusters</em>.</li>
<li>Puede ser computacionalmente más costoso.</li>
</ul></li>
</ul></li>
</ol>
</section>
<section id="graph-based-dimensionality-reduction" class="level3" data-number="1.2">
<h3 data-number="1.2" class="anchored" data-anchor-id="graph-based-dimensionality-reduction"><span class="header-section-number">1.2</span> Graph-based dimensionality reduction</h3>
<p>La idea general de graph-based dimensionality reduction es la siguiente:</p>
<ol type="1">
<li>Se construye un métrica de similitud entre los datos en el espacio de alta dimensión.</li>
<li>Se busca reecontruir los datos en un espacio de baja dimensión , respetando la similitud entre los datos. Esto se hace mediante un algoritmo iteativo que busca optimizar la distancia entre los datos en el espacio de baja dimensión para que coincida con la métrica de similitud en el espacio de alta dimensión.</li>
</ol>
</section>
</section>
<section id="fundamentos-teóricos" class="level2" data-number="2">
<h2 data-number="2" class="anchored" data-anchor-id="fundamentos-teóricos"><span class="header-section-number">2</span> Fundamentos Teóricos</h2>
<section id="la-función-softmax" class="level3" data-number="2.1">
<h3 data-number="2.1" class="anchored" data-anchor-id="la-función-softmax"><span class="header-section-number">2.1</span> La Función Softmax</h3>
<p>La función softmax es una herramienta matemática fundamental, ampliamente utilizada en el campo del aprendizaje automático, especialmente en redes neuronales. Su propósito es transformar un vector de números reales en una distribución de probabilidad, es decir, un vector de valores entre 0 y 1 cuya suma es 1.</p>
<p>La fórmula de Softmax para un vector <img src="https://latex.codecogs.com/png.latex?z%20=%20%5Bz_1,%20z_2,%20%5Cdots,%20z_K%5D"> es:</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Ctext%7BSoftmax%7D(z_i)%20=%20%5Cfrac%7Be%5E%7Bz_i%7D%7D%7B%5Csum_%7Bj=1%7D%5E%7BK%7D%20e%5E%7Bz_j%7D%7D"></p>
<p>donde:</p>
<ul>
<li><p><img src="https://latex.codecogs.com/png.latex?z_i"> es el <img src="https://latex.codecogs.com/png.latex?i">-ésimo elemento del vector de entrada.</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?K"> es el número de elementos en el vector de entrada.</p></li>
</ul>
<p>En el contexto de t-SNE, la función softmax se utiliza para convertir las distancias euclidianas en probabilidades de similitud entre puntos.</p>
</section>
<section id="divergencia-de-kullback-leibler-kl" class="level3" data-number="2.2">
<h3 data-number="2.2" class="anchored" data-anchor-id="divergencia-de-kullback-leibler-kl"><span class="header-section-number">2.2</span> Divergencia de Kullback-Leibler (KL)</h3>
<p>La Divergencia de Kullback-Leibler (KL) es una medida de la diferencia entre dos distribuciones de probabilidad. Aunque a menudo se usa para cuantificar la “distancia” entre distribuciones, no es una métrica en el sentido matemático estricto, ya que no es simétrica (<img src="https://latex.codecogs.com/png.latex?KL(P%20%7C%7C%20Q)%20%5Cneq%20KL(Q%20%7C%7C%20P)">) y no satisface la desigualdad triangular.</p>
<p>En t-SNE, la divergencia KL es la <strong>función de costo</strong> que el algoritmo busca minimizar. Mide la discrepancia entre las distribuciones de probabilidad de similitud en el espacio de alta dimensión (<img src="https://latex.codecogs.com/png.latex?P">) y en el de baja dimensión (<img src="https://latex.codecogs.com/png.latex?Q">). El objetivo es ajustar <img src="https://latex.codecogs.com/png.latex?Q"> para que sea lo más parecida posible a <img src="https://latex.codecogs.com/png.latex?P">.</p>
<p>La fórmula de la divergencia KL de una distribución <img src="https://latex.codecogs.com/png.latex?Q"> con respecto a una distribución de referencia <img src="https://latex.codecogs.com/png.latex?P"> es:</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Ctext%7BKL%7D(P%20%7C%7C%20Q)%20=%20%5Csum_i%20P(i)%20%5Clog%20%5Cleft(%20%5Cfrac%7BP(i)%7D%7BQ(i)%7D%20%5Cright)"></p>
<p>donde:</p>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?P(i)"> es la probabilidad del evento <img src="https://latex.codecogs.com/png.latex?i"> en la distribución <img src="https://latex.codecogs.com/png.latex?P">.</li>
<li><img src="https://latex.codecogs.com/png.latex?Q(i)"> es la probabilidad del evento <img src="https://latex.codecogs.com/png.latex?i"> en la distribución <img src="https://latex.codecogs.com/png.latex?Q">.</li>
<li>La suma se realiza sobre todos los eventos posibles.</li>
</ul>
<p><strong>¿Cómo funciona?</strong></p>
<p>La divergencia KL puede interpretarse como la esperanza del logaritmo del cociente de probabilidades entre las dos distribuciones, donde la esperanza se toma sobre la distribución de referencia <img src="https://latex.codecogs.com/png.latex?P">.</p>
<ul>
<li><strong>Si <img src="https://latex.codecogs.com/png.latex?P(i)%20%5Capprox%20Q(i)">:</strong> El cociente <img src="https://latex.codecogs.com/png.latex?%5Cfrac%7BP(i)%7D%7BQ(i)%7D"> estará cerca de 1, y <img src="https://latex.codecogs.com/png.latex?%5Clog(1)%20=%200">. La contribución a la divergencia será mínima.</li>
<li><strong>Si <img src="https://latex.codecogs.com/png.latex?P(i)"> es grande y <img src="https://latex.codecogs.com/png.latex?Q(i)"> es pequeña:</strong> El cociente será grande, y su logaritmo será un número positivo grande. Esto penaliza fuertemente los casos en que <img src="https://latex.codecogs.com/png.latex?Q"> subestima la probabilidad de un evento importante en <img src="https://latex.codecogs.com/png.latex?P">.</li>
<li><strong>Si <img src="https://latex.codecogs.com/png.latex?P(i)"> es pequeña y <img src="https://latex.codecogs.com/png.latex?Q(i)"> es grande:</strong> El cociente será pequeño, y su logaritmo será un número negativo. Sin embargo, al estar ponderado por un <img src="https://latex.codecogs.com/png.latex?P(i)"> pequeño, la contribución total al costo será también pequeña.</li>
<li><strong>Si <img src="https://latex.codecogs.com/png.latex?P(i)%20%5Cneq%200"> y <img src="https://latex.codecogs.com/png.latex?Q(i)%20=%200">:</strong> La divergencia se vuelve infinita. Esta es una propiedad crucial en t-SNE, ya que impone una penalización infinita si la representación de baja dimensión asigna una probabilidad cero a una relación que era posible en el espacio original.</li>
</ul>
<p>Observar que KL no es simétrica, es decir, <img src="https://latex.codecogs.com/png.latex?KL(P%20%7C%7C%20Q)%20%5Cneq%20KL(Q%20%7C%7C%20P)">.</p>
</section>
</section>
<section id="t-sne-teoría-y-algoritmo" class="level2" data-number="3">
<h2 data-number="3" class="anchored" data-anchor-id="t-sne-teoría-y-algoritmo"><span class="header-section-number">3</span> t-SNE: Teoría y Algoritmo</h2>
<section id="de-sne-a-t-sne-evolución-del-algoritmo" class="level3" data-number="3.1">
<h3 data-number="3.1" class="anchored" data-anchor-id="de-sne-a-t-sne-evolución-del-algoritmo"><span class="header-section-number">3.1</span> De SNE a t-SNE: Evolución del Algoritmo</h3>
<section id="stochastic-neighbor-embedding-sne" class="level4" data-number="3.1.1">
<h4 data-number="3.1.1" class="anchored" data-anchor-id="stochastic-neighbor-embedding-sne"><span class="header-section-number">3.1.1</span> Stochastic Neighbor Embedding (SNE)</h4>
<p>SNE fue el predecesor de t-SNE, desarrollado por Hinton y Roweis en 2002. El algoritmo se basa en convertir distancias euclidianas entre puntos en probabilidades de similitud. El proceso es el siguiente:</p>
<p>La idea de SNE es poder representar un espacio de alta dimensionalidad en un espacio de baja dimensionalidad de manera que las relaciones de vecindad se preserven. Para ello se usa la función softmax para convertir las distancias euclidianas en probabilidades de similitud. El objetivo es que las probabilidades de similitud en el espacio de baja dimensionalidad sean similares a las del espacio de alta dimensionalidad.</p>
<ol type="1">
<li><p><strong>Probabilidades en el espacio de alta dimensión:</strong> Para cada par de puntos <img src="https://latex.codecogs.com/png.latex?x_i"> y <img src="https://latex.codecogs.com/png.latex?x_j"> en el espacio de alta dimensión, se calcula la probabilidad condicional <img src="https://latex.codecogs.com/png.latex?p_%7Bj%7Ci%7D"> de que <img src="https://latex.codecogs.com/png.latex?x_i"> elija a <img src="https://latex.codecogs.com/png.latex?x_j"> como su vecino:</p>
<p><img src="https://latex.codecogs.com/png.latex?p_%7Bj%7Ci%7D%20=%20%5Cfrac%7B%5Cexp(-%5C%7Cx_i%20-%20x_j%5C%7C%5E2%20/%202%5Csigma_i%5E2)%7D%7B%5Csum_%7Bk%20%5Cneq%20i%7D%20%5Cexp(-%5C%7Cx_i%20-%20x_k%5C%7C%5E2%20/%202%5Csigma_i%5E2)%7D"></p>
<p>donde <img src="https://latex.codecogs.com/png.latex?%5Csigma_i"> es la varianza de la distribución gaussiana centrada en <img src="https://latex.codecogs.com/png.latex?x_i">. Esta varianza se ajusta para cada punto <img src="https://latex.codecogs.com/png.latex?i"> de manera que la distribución de probabilidades <img src="https://latex.codecogs.com/png.latex?P_i"> tenga una perplejidad fija.</p></li>
<li><p><strong>Probabilidades en el espacio de baja dimensión:</strong> De manera similar, para los puntos <img src="https://latex.codecogs.com/png.latex?y_i"> y <img src="https://latex.codecogs.com/png.latex?y_j"> en el espacio de baja dimensión:</p>
<p><img src="https://latex.codecogs.com/png.latex?q_%7Bj%7Ci%7D%20=%20%5Cfrac%7B%5Cexp(-%5C%7Cy_i%20-%20y_j%5C%7C%5E2)%7D%7B%5Csum_%7Bk%20%5Cneq%20i%7D%20%5Cexp(-%5C%7Cy_i%20-%20y_k%5C%7C%5E2)%7D"></p>
<p>Aquí se usa una distribución gaussiana con varianza fija (1/2) para simplificar.</p></li>
<li><p><strong>Función de costo:</strong> SNE minimiza la suma de las divergencias KL entre las distribuciones <img src="https://latex.codecogs.com/png.latex?P_i"> y <img src="https://latex.codecogs.com/png.latex?Q_i">:</p>
<p><img src="https://latex.codecogs.com/png.latex?C%20=%20%5Csum_i%20%5Ctext%7BKL%7D(P_i%20%7C%7C%20Q_i)%20=%20%5Csum_i%20%5Csum_j%20p_%7Bj%7Ci%7D%20%5Clog%20%5Cfrac%7Bp_%7Bj%7Ci%7D%7D%7Bq_%7Bj%7Ci%7D%7D"></p></li>
</ol>
<p>La función de costo es lo que el algoritmo intenta minimizar, con la idea de que las probabilidades en el espacio de baja dimensionalidad sean similares a las del espacio de alta dimensionalidad.</p>
</section>
<section id="limitaciones-de-sne" class="level4" data-number="3.1.2">
<h4 data-number="3.1.2" class="anchored" data-anchor-id="limitaciones-de-sne"><span class="header-section-number">3.1.2</span> Limitaciones de SNE</h4>
<p>SNE presentaba dos problemas principales:</p>
<ol type="1">
<li><strong>Asimetría en las probabilidades:</strong>
<ul>
<li>Las probabilidades condicionales <img src="https://latex.codecogs.com/png.latex?p_%7Bj%7Ci%7D"> y <img src="https://latex.codecogs.com/png.latex?p_%7Bi%7Cj%7D"> no son iguales</li>
<li>Esto puede llevar a resultados inconsistentes</li>
<li>La función de costo es asimétrica respecto a <img src="https://latex.codecogs.com/png.latex?P"> y <img src="https://latex.codecogs.com/png.latex?Q"></li>
</ul></li>
<li><strong>El problema de hacinamiento (crowding problem):</strong>
<ul>
<li>En el espacio de baja dimensión, hay menos “espacio” disponible para que los puntos se separen y las probabilidades de vecindad sean similares a las del espacio de alta dimensionalidad.</li>
<li>Los puntos tienden a aglomerarse en el centro</li>
<li>La distribución gaussiana en baja dimensión no maneja bien este problema</li>
<li>Las colas de la gaussiana decaen muy rápido (<img src="https://latex.codecogs.com/png.latex?e%5E%7B-x%5E2%7D">)</li>
</ul></li>
</ol>
</section>
<section id="la-solución-t-sne" class="level4" data-number="3.1.3">
<h4 data-number="3.1.3" class="anchored" data-anchor-id="la-solución-t-sne"><span class="header-section-number">3.1.3</span> La Solución: t-SNE</h4>
<p>t-SNE resuelve estos problemas mediante :</p>
<ol type="1">
<li><strong>Probabilidades conjuntas simétricas:</strong>
<ul>
<li>Reemplaza las probabilidades condicionales por probabilidades conjuntas</li>
<li><img src="https://latex.codecogs.com/png.latex?p_%7Bij%7D%20=%20%5Cfrac%7Bp_%7Bj%7Ci%7D%20+%20p_%7Bi%7Cj%7D%7D%7B2N%7D"></li>
<li>Esto asegura que <img src="https://latex.codecogs.com/png.latex?p_%7Bij%7D%20=%20p_%7Bji%7D"></li>
<li>La función de costo se vuelve simétrica</li>
</ul></li>
<li><strong>Distribución t de Student en baja dimensión:</strong>
<ul>
<li>Reemplaza la distribución gaussiana por una t de Student con un grado de libertad</li>
<li>La fórmula para <img src="https://latex.codecogs.com/png.latex?q_%7Bij%7D"> se convierte en: <img src="https://latex.codecogs.com/png.latex?q_%7Bij%7D%20=%20%5Cfrac%7B(1%20+%20%5C%7Cy_i%20-%20y_j%5C%7C%5E2)%5E%7B-1%7D%7D%7B%5Csum_%7Bk%20%5Cneq%20l%7D%20(1%20+%20%5C%7Cy_k%20-%20y_l%5C%7C%5E2)%5E%7B-1%7D%7D"></li>
</ul></li>
</ol>
<p>La distribución t de Student tiene “colas pesadas” (heavy tails), lo que significa que: - Asigna una probabilidad significativa a valores lejos de la media - Permite que puntos moderadamente separados en alta dimensión se mapeen a distancias mayores en baja dimensión - Crea más “espacio” en el centro del mapa - Evita la aglomeración excesiva de puntos</p>
<p>La fórmula de la distribución t de Student con un grado de libertad es: <img src="https://latex.codecogs.com/png.latex?f(x)%20=%20%5Cfrac%7B1%7D%7B%5Cpi(1%20+%20x%5E2)%7D"></p>
<p>Esta función tiene colas que decaen como <img src="https://latex.codecogs.com/png.latex?1/x%5E2">, lo que es más lento que la distribución gaussiana que decae como <img src="https://latex.codecogs.com/png.latex?e%5E%7B-x%5E2%7D">. Esta propiedad es clave para resolver el problema de hacinamiento.</p>
<div id="79947026" class="cell" data-execution_count="2">
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><a href="umap-tsne_files/figure-html/cell-3-output-1.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://luccafrachelle.github.io/porfolio_academia/posts/umap-tsne_files/figure-html/cell-3-output-1.png" class="img-fluid figure-img"></a></p>
</figure>
</div>
</div>
</div>
</section>
<section id="función-de-costo-de-t-sne" class="level4" data-number="3.1.4">
<h4 data-number="3.1.4" class="anchored" data-anchor-id="función-de-costo-de-t-sne"><span class="header-section-number">3.1.4</span> Función de Costo de t-SNE</h4>
<p>La función de costo de t-SNE es una divergencia KL entre las probabilidades conjuntas <img src="https://latex.codecogs.com/png.latex?P"> y <img src="https://latex.codecogs.com/png.latex?Q">:</p>
<p><img src="https://latex.codecogs.com/png.latex?C%20=%20%5Ctext%7BKL%7D(P%20%7C%7C%20Q)%20=%20%5Csum_i%20%5Csum_j%20p_%7Bij%7D%20%5Clog%20%5Cfrac%7Bp_%7Bij%7D%7D%7Bq_%7Bij%7D%7D"></p>
</section>
<section id="gradiente-de-la-función-de-costo" class="level4" data-number="3.1.5">
<h4 data-number="3.1.5" class="anchored" data-anchor-id="gradiente-de-la-función-de-costo"><span class="header-section-number">3.1.5</span> Gradiente de la Función de Costo</h4>
<p>El gradiente de la función de costo con respecto a <img src="https://latex.codecogs.com/png.latex?y_i"> es:</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cfrac%7B%5Cpartial%20C%7D%7B%5Cpartial%20y_i%7D%20=%204%20%5Csum_j%20(p_%7Bij%7D%20-%20q_%7Bij%7D)(y_i%20-%20y_j)(1%20+%20%5C%7Cy_i%20-%20y_j%5C%7C%5E2)%5E%7B-1%7D"></p>
<p>Este gradiente tiene una interpretación física intuitiva intuitiva: - Si <img src="https://latex.codecogs.com/png.latex?p_%7Bij%7D%20%3E%20q_%7Bij%7D">: Los puntos <img src="https://latex.codecogs.com/png.latex?i"> y <img src="https://latex.codecogs.com/png.latex?j"> están más cerca en alta dimensión que en baja dimensión * Se crea una fuerza atractiva que los acerca - Si <img src="https://latex.codecogs.com/png.latex?p_%7Bij%7D%20%3C%20q_%7Bij%7D">: Los puntos <img src="https://latex.codecogs.com/png.latex?i"> y <img src="https://latex.codecogs.com/png.latex?j"> están más lejos en alta dimensión que en baja dimensión * Se crea una fuerza repulsiva que los aleja * La fuerza se modera por el término <img src="https://latex.codecogs.com/png.latex?(1%20+%20%5C%7Cy_i%20-%20y_j%5C%7C%5E2)%5E%7B-1%7D"></p>
<p>El término <img src="https://latex.codecogs.com/png.latex?(1%20+%20%5C%7Cy_i%20-%20y_j%5C%7C%5E2)%5E%7B-1%7D"> es crucial porque: - Modera la fuerza de repulsión entre puntos ya separados - Permite que los puntos se separen más fácilmente - Contribuye a la formación de clusters bien definidos</p>
</section>
</section>
<section id="algoritmo-de-optimización" class="level3" data-number="3.2">
<h3 data-number="3.2" class="anchored" data-anchor-id="algoritmo-de-optimización"><span class="header-section-number">3.2</span> Algoritmo de Optimización</h3>
<p>El algoritmo de t-SNE utiliza un algoritmo de descenso de gradiente para minimizar la función de costo. Se aplican técnicas como:</p>
<ul>
<li><strong>Momentum:</strong> Para acelerar la convergencia y evitar mínimos locales. La actualización de las coordenadas <img src="https://latex.codecogs.com/png.latex?y_i"> en cada iteración <img src="https://latex.codecogs.com/png.latex?t"> es: <img src="https://latex.codecogs.com/png.latex?%0A%20%20Y%5E%7B(t)%7D%20=%20Y%5E%7B(t-1)%7D%20+%20%5Ceta%20%5Cfrac%7B%5Cpartial%20C%7D%7B%5Cpartial%20Y%7D%20+%20%5Calpha(t)%20(Y%5E%7B(t-1)%7D%20-%20Y%5E%7B(t-2)%7D)%0A%20%20"> donde <img src="https://latex.codecogs.com/png.latex?%5Ceta"> es la tasa de aprendizaje y <img src="https://latex.codecogs.com/png.latex?%5Calpha(t)"> es el término de momentum.</li>
<li><strong>Ajuste del learning rate:</strong> Se suele usar un learning rate que se incrementa en las primeras iteraciones y luego se mantiene constante.</li>
<li><strong>Inicialización:</strong> Los puntos <img src="https://latex.codecogs.com/png.latex?y_i"> se inicializan aleatoriamente de una distribución normal con una pequeña varianza.</li>
</ul>
</section>
<section id="algoritmo-de-t-sne" class="level3" data-number="3.3">
<h3 data-number="3.3" class="anchored" data-anchor-id="algoritmo-de-t-sne"><span class="header-section-number">3.3</span> Algoritmo de t-SNE</h3>
<p>El algoritmo de t-SNE se puede describir de la siguiente manera:</p>
<p><strong>Algoritmo 1: Versión Simple de t-Distributed Stochastic Neighbor Embedding.</strong></p>
<p><strong>Datos:</strong> conjunto de datos <img src="https://latex.codecogs.com/png.latex?X%20=%20%5C%7Bx_1,%20x_2,...,%20x_n%5C%7D">, parámetros de la función de costo: perplejidad <img src="https://latex.codecogs.com/png.latex?Perp">, parámetros de optimización: número de iteraciones <img src="https://latex.codecogs.com/png.latex?T">, tasa de aprendizaje <img src="https://latex.codecogs.com/png.latex?%5Ceta">, momento <img src="https://latex.codecogs.com/png.latex?%5Calpha(t)">.</p>
<p><strong>Resultado:</strong> representación de baja dimensión <img src="https://latex.codecogs.com/png.latex?Y%5E%7B(T)%7D%20=%20%5C%7By_1,%20y_2,...,%20y_n%5C%7D">.</p>
<p><strong>Inicio:</strong></p>
<ol type="1">
<li><p>calcular afinidades por pares <img src="https://latex.codecogs.com/png.latex?p_%7Bj%7Ci%7D"> con perplejidad <img src="https://latex.codecogs.com/png.latex?Perp"> (usando Ecuación 1)</p></li>
<li><p>establecer <img src="https://latex.codecogs.com/png.latex?p_%7Bij%7D%20=%20%5Cfrac%7Bp_%7Bj%7Ci%7D%20+%20p_%7Bi%7Cj%7D%7D%7B2n%7D"></p></li>
<li><p>muestrear solución inicial <img src="https://latex.codecogs.com/png.latex?Y%5E%7B(0)%7D%20=%20%5C%7By_1,%20y_2,...,%20y_n%5C%7D"> desde <img src="https://latex.codecogs.com/png.latex?%5Cmathcal%7BN%7D(0,10%5E%7B-4%7DI)"></p></li>
<li><p>para <img src="https://latex.codecogs.com/png.latex?t=1"> hasta <img src="https://latex.codecogs.com/png.latex?T"> hacer</p>
<ol type="a">
<li>calcular afinidades de baja dimensión <img src="https://latex.codecogs.com/png.latex?q_%7Bij%7D"> (usando Ecuación 4)</li>
<li>calcular gradiente <img src="https://latex.codecogs.com/png.latex?%5Cfrac%7B%5Cpartial%20C%7D%7B%5Cpartial%20Y%7D"> (usando Ecuación 5)</li>
<li>establecer <img src="https://latex.codecogs.com/png.latex?Y%5E%7B(t)%7D%20=%20Y%5E%7B(t-1)%7D%20+%20%5Ceta%5Cfrac%7B%5Cpartial%20C%7D%7B%5Cpartial%20Y%7D%20+%20%5Calpha(t)(Y%5E%7B(t-1)%7D%20-%20Y%5E%7B(t-2)%7D)"></li>
</ol></li>
<li><p>Fin</p></li>
</ol>
<p>La actualización de los puntos en cada iteración <img src="https://latex.codecogs.com/png.latex?t"> se realiza mediante la ecuación:</p>
<p><img src="https://latex.codecogs.com/png.latex?Y%5E%7B(t)%7D%20=%20Y%5E%7B(t-1)%7D%20+%20%5Ceta%5Cfrac%7B%5Cpartial%20C%7D%7B%5Cpartial%20Y%7D%20+%20%5Calpha(t)(Y%5E%7B(t-1)%7D%20-%20Y%5E%7B(t-2)%7D)"></p>
<p>Donde:</p>
<ol type="1">
<li><img src="https://latex.codecogs.com/png.latex?Y%5E%7B(t)%7D">: Es la nueva posición de los puntos en la iteración <img src="https://latex.codecogs.com/png.latex?t"></li>
<li><img src="https://latex.codecogs.com/png.latex?Y%5E%7B(t-1)%7D">: Es la posición actual de los puntos</li>
<li><img src="https://latex.codecogs.com/png.latex?%5Ceta">: Es la tasa de aprendizaje (learning rate)</li>
<li><img src="https://latex.codecogs.com/png.latex?%5Cfrac%7B%5Cpartial%20C%7D%7B%5Cpartial%20Y%7D">: Es el gradiente de la función de costo</li>
<li><img src="https://latex.codecogs.com/png.latex?%5Calpha(t)">: Es el término de momento</li>
<li><img src="https://latex.codecogs.com/png.latex?(Y%5E%7B(t-1)%7D%20-%20Y%5E%7B(t-2)%7D)">: Es el cambio en la posición del paso anterior</li>
</ol>
<p>El movimiento de los puntos está determinado por dos componentes principales:</p>
<ol type="1">
<li><strong>Término del gradiente (</strong><img src="https://latex.codecogs.com/png.latex?%5Ceta%5Cfrac%7B%5Cpartial%20C%7D%7B%5Cpartial%20Y%7D">):
<ul>
<li>Mueve los puntos en la dirección que minimiza la divergencia KL</li>
<li>Los puntos se mueven para preservar la estructura de vecindad</li>
<li>Si dos puntos están cerca en el espacio original, tenderán a moverse uno hacia el otro</li>
<li>Si dos puntos están lejos en el espacio original, tenderán a alejarse</li>
</ul></li>
<li><strong>Término de momento (</strong><img src="https://latex.codecogs.com/png.latex?%5Calpha(t)(Y%5E%7B(t-1)%7D%20-%20Y%5E%7B(t-2)%7D)">):
<ul>
<li>Ayuda a evitar mínimos locales</li>
<li>Mantiene cierta “inercia” del movimiento anterior</li>
<li>Ayuda a que la optimización sea más estable</li>
<li>El factor <img src="https://latex.codecogs.com/png.latex?%5Calpha(t)"> controla cuánto del movimiento anterior se mantiene</li>
</ul></li>
</ol>
<section id="variante-con-inicialización-pca" class="level4" data-number="3.3.1">
<h4 data-number="3.3.1" class="anchored" data-anchor-id="variante-con-inicialización-pca"><span class="header-section-number">3.3.1</span> Variante con Inicialización PCA</h4>
<p>Una variante común del algoritmo t-SNE utiliza PCA (Análisis de Componentes Principales) para inicializar los puntos en lugar de usar una distribución normal aleatoria. Esta variante tiene las siguientes características:</p>
<ol type="1">
<li><strong>Inicialización con PCA</strong>:
<ul>
<li>En lugar de <img src="https://latex.codecogs.com/png.latex?Y%5E%7B(0)%7D%20%5Csim%20%5Cmathcal%7BN%7D(0,10%5E%7B-4%7DI)">, se usa <img src="https://latex.codecogs.com/png.latex?Y%5E%7B(0)%7D%20=%20%5Ctext%7BPCA%7D(X)"></li>
<li>Esto proporciona una mejor posición inicial que preserva la estructura global de los datos</li>
<li>Puede acelerar la convergencia del algoritmo</li>
<li>Ayuda a evitar mínimos locales pobres</li>
</ul></li>
</ol>
</section>
</section>
<section id="ejemplo-del-algoritmo" class="level3" data-number="3.4">
<h3 data-number="3.4" class="anchored" data-anchor-id="ejemplo-del-algoritmo"><span class="header-section-number">3.4</span> Ejemplo del algoritmo</h3>
<p>A continuación, implementaremos t-SNE paso a paso para proyectar datos de <img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5E2"> a <img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5E1">. Generaremos tres distribuciones normales con diferentes medias y varianzas, y seguiremos el proceso de optimización en diferentes iteraciones.</p>
<p><a href="umap-tsne_files/figure-html/tsne-example-output-1.png" class="lightbox" data-gallery="quarto-lightbox-gallery-2"><img src="https://luccafrachelle.github.io/porfolio_academia/posts/umap-tsne_files/figure-html/tsne-example-output-1.png" id="tsne-example" class="img-fluid"></a></p>
<p><a href="umap-tsne_files/figure-html/tsne-visualization-output-1.png" class="lightbox" data-gallery="quarto-lightbox-gallery-3"><img src="https://luccafrachelle.github.io/porfolio_academia/posts/umap-tsne_files/figure-html/tsne-visualization-output-1.png" id="tsne-visualization" class="img-fluid"></a></p>
<p>Este ejemplo muestra:</p>
<ul>
<li><strong>Generación de datos:</strong>
<ul>
<li>Tres clusters gaussianos en <img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5E2"></li>
<li>Cada cluster tiene 100 puntos</li>
<li>Diferentes medias y varianzas para cada cluster</li>
</ul></li>
</ul>
</section>
<section id="extensiones-y-variaciones" class="level3" data-number="3.5">
<h3 data-number="3.5" class="anchored" data-anchor-id="extensiones-y-variaciones"><span class="header-section-number">3.5</span> Extensiones y Variaciones</h3>
<p>El artículo también discute extensiones como:</p>
<ul>
<li><strong>t-SNE para conjuntos de datos grandes:</strong> Se propone una técnica de random walks en grafos de vecindad para manejar grandes volúmenes de datos, donde solo un subconjunto de puntos se visualiza directamente, pero la estructura global influye en el embedding.</li>
<li><strong>t-SNE con costos de incrustación tempranos (early exaggeration):</strong> Multiplicar los <img src="https://latex.codecogs.com/png.latex?p_%7Bij%7D"> por un factor (ej. 4 o 12) en las primeras iteraciones para crear clústeres más compactos y evitar que se formen “mini-clústeres” que no se pueden separar más tarde. <img src="https://latex.codecogs.com/png.latex?p'_%7Bij%7D%20=%20p_%7Bij%7D%20%5Ctimes%20%5Ctext%7Bexaggeration%5C_factor%7D"> para las primeras <img src="https://latex.codecogs.com/png.latex?T"> iteraciones.</li>
</ul>
</section>
</section>
<section id="umap-teoría-y-algoritmo" class="level2" data-number="4">
<h2 data-number="4" class="anchored" data-anchor-id="umap-teoría-y-algoritmo"><span class="header-section-number">4</span> UMAP: Teoría y Algoritmo</h2>
<p>UMAP (Uniform Manifold Approximation and Projection) es una técnica de reducción de dimensionalidad no lineal desarrollada por Leland McInnes, John Healy y James Melville. Su propósito central es <strong>proyectar datos de alta dimensión en un espacio de menor dimensión</strong> (comúnmente 2D o 3D) para <strong>visualización</strong> o como un paso de <strong>preprocesamiento</strong> en pipelines de aprendizaje automático.</p>
<p>A diferencia de técnicas como t-SNE, que se basan en probabilidades de similitud y optimización de la divergencia KL, UMAP se asienta sobre un <strong>marco teórico robusto derivado de la geometría riemanniana y la topología algebraica</strong>. Esta base le confiere propiedades distintivas en términos de <strong>velocidad, escalabilidad y una superior preservación de la estructura global</strong> de los datos.</p>
<section id="idea-general" class="level3" data-number="4.1">
<h3 data-number="4.1" class="anchored" data-anchor-id="idea-general"><span class="header-section-number">4.1</span> Idea general</h3>
<p>La potencia de UMAP radica en su suposición fundamental de que los datos de alta dimensión se encuentran aproximadamente sobre una <strong>variedad (manifold) riemanniana</strong> de baja dimensión. El objetivo principal del algoritmo es <strong>preservar la estructura topológica</strong> de esta variedad subyacente al proyectar los datos a un espacio de menor dimensionalidad. La teoría subyacente se expresa más fácilmente en el lenguaje de la topología y la teoría de categorías.</p>
<p>A un alto nivel, UMAP logra esto utilizando <strong>aproximaciones locales de la variedad</strong>. Estas aproximaciones se “parchean” o unen a través de representaciones de <strong>conjuntos simpliciales difusos</strong> (fuzzy simplicial sets) para construir una representación topológica coherente de los datos en alta dimensión. Posteriormente, se construye una representación topológica similar en el espacio de baja dimensión. El algoritmo optimiza entonces la disposición de los puntos en el espacio de baja dimensión para <strong>minimizar la entropía cruzada entre ambas representaciones topológicas</strong>.</p>
<p>La construcción de estas representaciones topológicas difusas se desglosa en dos problemas: 1. <strong>Aproximar la variedad</strong> sobre la que se asume que se encuentran los datos. 2. <strong>Construir una representación de conjunto simplicial difuso</strong> de la variedad aproximada.</p>
<p>En última instancia, UMAP se puede entender, desde una perspectiva computacional, como la <strong>construcción y operación sobre grafos ponderados</strong>, situándolo en la clase de algoritmos de aprendizaje basados en <img src="https://latex.codecogs.com/png.latex?k">-vecinos (como Laplacian Eigenmaps, Isomap y t-SNE). Las diferencias entre estos algoritmos residen en los detalles de cómo se construye el grafo y cómo se calcula su disposición en baja dimensión.</p>
<section id="distribución-uniforme-de-datos-en-una-variedad-y-aproximación-geodésica" class="level4" data-number="4.1.1">
<h4 data-number="4.1.1" class="anchored" data-anchor-id="distribución-uniforme-de-datos-en-una-variedad-y-aproximación-geodésica"><span class="header-section-number">4.1.1</span> Distribución Uniforme de Datos en una Variedad y Aproximación Geodésica</h4>
<p>El primer paso crucial en UMAP es aproximar la variedad subyacente. Se asume que los datos están <strong>distribuidos uniformemente</strong> en esta variedad con respecto a una <strong>métrica riemanniana</strong> <img src="https://latex.codecogs.com/png.latex?g"> intrínseca a la misma, no necesariamente la métrica euclidiana del espacio ambiente. Aunque los datos reales rara vez se comportan así, UMAP aborda esto de manera ingeniosa: <strong>adapta la métrica localmente</strong> para que los datos <em>parezcan</em> uniformemente distribuidos.</p>
<p>Normalmente, para cada punto <img src="https://latex.codecogs.com/png.latex?x_i">, UMAP normaliza las distancias a sus vecinos con respecto a la distancia a su <img src="https://latex.codecogs.com/png.latex?k">-ésimo vecino más cercano, denotada <img src="https://latex.codecogs.com/png.latex?%5Crho_i">. Esto se basa en el <strong>Lema 1</strong> (discutido en el paper), que establece que, bajo ciertas condiciones de un <em>manifold</em> Riemanniano <img src="https://latex.codecogs.com/png.latex?(M,g)">, en una bola <img src="https://latex.codecogs.com/png.latex?B"> con respecto a <img src="https://latex.codecogs.com/png.latex?g"> centrada en un punto <img src="https://latex.codecogs.com/png.latex?p%20%5Cin%20M">, la distancia geodésica de <img src="https://latex.codecogs.com/png.latex?p"> a cualquier <img src="https://latex.codecogs.com/png.latex?q%20%5Cin%20B"> es proporcional a la distancia euclidiana en el espacio ambiente <img src="https://latex.codecogs.com/png.latex?d_%7B%5Cmathbb%7BR%7D%5En%7D(p,q)">, escalada por el radio de la bola <img src="https://latex.codecogs.com/png.latex?r">:</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cfrac%7B1%7D%7Br%7D%20d_%7B%5Cmathbb%7BR%7D%5En%7D(p,q)"></p>
<p>La implicación práctica es que, al crear una <strong>distancia personalizada para cada</strong> <img src="https://latex.codecogs.com/png.latex?x_i"> (normalizando por <img src="https://latex.codecogs.com/png.latex?%5Crho_i">), se asegura que la suposición de distribución uniforme en la variedad sea válida localmente. El “costo” es que se generan <strong>nociones de distancia locales e independientes</strong> para cada punto, que pueden no ser directamente compatibles entre sí. La tarea siguiente es fusionar estos “espacios métricos discretos” locales en una estructura global consistente.</p>
</section>
<section id="representación-de-la-variedad-con-conjuntos-simpliciales-difusos" class="level4" data-number="4.1.2">
<h4 data-number="4.1.2" class="anchored" data-anchor-id="representación-de-la-variedad-con-conjuntos-simpliciales-difusos"><span class="header-section-number">4.1.2</span> Representación de la Variedad con Conjuntos Simpliciales Difusos</h4>
<p>La forma en que UMAP unifica estas nociones locales es a través de los <strong>conjuntos simpliciales difusos</strong>.</p>
<ul>
<li>Un <strong>simplex</strong> es una generalización de elementos geométricos básicos: un punto es un 0-simplex, una arista es un 1-simplex, un triángulo es un 2-simplex, etc. Un <strong>conjunto simplicial</strong> es una colección de estos simplices que se conectan entre sí para formar una representación discreta de una forma o espacio topológico.</li>
<li>El término “<strong>difuso</strong>” (fuzzy) significa que las conexiones entre los simplices no son binarias (existe/no existe), sino que tienen un <strong>grado de membresía o probabilidad</strong> (un valor en el intervalo <img src="https://latex.codecogs.com/png.latex?%5B0,%201%5D">). Esto permite capturar la incertidumbre y la gradualidad en las relaciones de vecindad de los datos, lo que es crucial para manejar la complejidad de la alta dimensionalidad y permite la combinación de las métricas locales inconsistentes en una estructura global.</li>
</ul>
<p>Computacionalmente, aunque la teoría habla de conjuntos simpliciales difusos completos, en la práctica UMAP trabaja con el <strong>“one skeleton”</strong> de estos conjuntos, lo que se traduce directamente en un <strong>grafo ponderado</strong>. En este grafo: * Cada punto de datos <img src="https://latex.codecogs.com/png.latex?x_i"> es un <strong>nodo</strong>. * Cada <strong>arista</strong> entre <img src="https://latex.codecogs.com/png.latex?x_i"> y <img src="https://latex.codecogs.com/png.latex?x_j"> tiene un <strong>peso</strong> que representa la “fuerza” o “probabilidad” de conexión entre ellos.</p>
</section>
<section id="tipos-de-símplex" class="level4" data-number="4.1.3">
<h4 data-number="4.1.3" class="anchored" data-anchor-id="tipos-de-símplex"><span class="header-section-number">4.1.3</span> Tipos de símplex</h4>
<p><a href="img/umap/img1.png" class="lightbox" data-gallery="quarto-lightbox-gallery-4"><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/umap/img1.png" class="img-fluid"></a></p>
</section>
</section>
<section id="de-los-datos-discretos-a-una-cubierta-adaptativa" class="level3" data-number="4.2">
<h3 data-number="4.2" class="anchored" data-anchor-id="de-los-datos-discretos-a-una-cubierta-adaptativa"><span class="header-section-number">4.2</span> De los Datos Discretos a una Cubierta Adaptativa</h3>
<p>Consideremos un conjunto de datos en un espacio de alta dimensión. La hipótesis fundamental de UMAP es que estos datos son un muestreo de una variedad (manifold) subyacente de menor dimensionalidad. El primer paso computacional es construir una “cubierta” de esta variedad mediante conjuntos abiertos, que pueden ser visualizados como “bolas” alrededor de cada punto.</p>
<p>Observemos un conjunto de datos bidimensional de ejemplo, una “onda sinusoidal ruidosa”:</p>
<p><a href="img/umap/img2.png" class="lightbox" data-gallery="quarto-lightbox-gallery-5"><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/umap/img2.png" class="img-fluid"></a></p>
<p>A diferencia de métodos que emplean un radio fijo para estos conjuntos abiertos, UMAP <strong>adapta el tamaño de cada conjunto abierto</strong> basándose en la densidad local de los datos.</p>
<section id="cubierta-con-radios-fijos" class="level4" data-number="4.2.1">
<h4 data-number="4.2.1" class="anchored" data-anchor-id="cubierta-con-radios-fijos"><span class="header-section-number">4.2.1</span> Cubierta con Radios Fijos</h4>
<p><a href="img/umap/img3.png" class="lightbox" data-gallery="quarto-lightbox-gallery-6"><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/umap/img3.png" class="img-fluid"></a></p>
<p>Una cubierta con un radio globalmente fijo puede llevar a una representación imprecisa de la topología local de la variedad.</p>
</section>
</section>
<section id="adaptación-de-la-métrica-densidad-y-uniformidad-local" class="level3" data-number="4.3">
<h3 data-number="4.3" class="anchored" data-anchor-id="adaptación-de-la-métrica-densidad-y-uniformidad-local"><span class="header-section-number">4.3</span> Adaptación de la Métrica: Densidad y Uniformidad Local</h3>
<p>Para garantizar que todas las vecindades locales muestren una <strong>uniformidad de muestreo aparente</strong>, UMAP implementa una re-escalación adaptativa de la métrica en cada vecindad. Esto se conceptualiza como un ajuste del “volumen” efectivo de la vecindad de <img src="https://latex.codecogs.com/png.latex?k"> puntos alrededor de cada dato.</p>
<p>Este ajuste métrico se logra calculando un valor <img src="https://latex.codecogs.com/png.latex?%5Csigma_i"> (sigma local) para cada punto <img src="https://latex.codecogs.com/png.latex?X_i">. Este <img src="https://latex.codecogs.com/png.latex?%5Csigma_i"> actúa como un factor de escala. Se determina iterativamente para satisfacer la siguiente condición:</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Csum_%7Bj=1%7D%5E%7Bk%7D%20%5Cexp%5Cleft(-%5Cfrac%7Bd(X_i,%20X_j)%20-%20%5Crho_i%7D%7B%5Csigma_i%7D%5Cright)%20=%20%5Clog_2(k)"></p>
<p>Desglosemos los términos:</p>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?X_i">: El <strong>punto de datos focal</strong> bajo consideración.</li>
<li><img src="https://latex.codecogs.com/png.latex?X_j">: Los <strong><img src="https://latex.codecogs.com/png.latex?k"> vecinos más cercanos</strong> a <img src="https://latex.codecogs.com/png.latex?X_i">.</li>
<li><img src="https://latex.codecogs.com/png.latex?d(X_i,%20X_j)">: La <strong>distancia euclidiana</strong> (o la métrica seleccionada) entre <img src="https://latex.codecogs.com/png.latex?X_i"> y cada vecino <img src="https://latex.codecogs.com/png.latex?X_j"> en el espacio de alta dimensión.</li>
<li><img src="https://latex.codecogs.com/png.latex?%5Crho_i"> (rho): La <strong>distancia al primer vecino no cero</strong> de <img src="https://latex.codecogs.com/png.latex?X_i">, es decir, la distancia más pequeña a cualquier otro punto.</li>
<li><img src="https://latex.codecogs.com/png.latex?%5Csigma_i"> (sigma): El <strong>parámetro de escala local</strong> calculado para cada <img src="https://latex.codecogs.com/png.latex?X_i">. Un <img src="https://latex.codecogs.com/png.latex?%5Csigma_i"> pequeño indica una compresión efectiva de las distancias (región densa), mientras que un <img src="https://latex.codecogs.com/png.latex?%5Csigma_i"> grande indica un estiramiento (región dispersa).</li>
<li><img src="https://latex.codecogs.com/png.latex?%5Clog_2(k)">: Un <strong>valor objetivo constante</strong>. Asegura que, en promedio, la suma de las probabilidades de conexión de los <img src="https://latex.codecogs.com/png.latex?k"> vecinos de <img src="https://latex.codecogs.com/png.latex?X_i"> sea <img src="https://latex.codecogs.com/png.latex?%5Clog_2(k)">, normalizando la “conectividad efectiva” de cada punto.</li>
</ul>
<p><strong>Impacto:</strong> Este procedimiento resulta en una <strong>distribución uniforme local simulada</strong>, garantizando que las vecindades de los puntos se comporten de manera consistente, independientemente de la densidad original de los datos.</p>
<section id="cubierta-adaptada-por-densidad" class="level4" data-number="4.3.1">
<h4 data-number="4.3.1" class="anchored" data-anchor-id="cubierta-adaptada-por-densidad"><span class="header-section-number">4.3.1</span> Cubierta Adaptada por Densidad</h4>
<p><a href="img/umap/img6.png" class="lightbox" data-gallery="quarto-lightbox-gallery-7"><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/umap/img6.png" class="img-fluid"></a></p>
</section>
</section>
<section id="la-métrica-adaptada-visualizando-la-uniformización-local" class="level3" data-number="4.4">
<h3 data-number="4.4" class="anchored" data-anchor-id="la-métrica-adaptada-visualizando-la-uniformización-local"><span class="header-section-number">4.4</span> La Métrica Adaptada: Visualizando la Uniformización Local</h3>
<p>El ajuste dinámico de <img src="https://latex.codecogs.com/png.latex?%5Csigma_i"> y <img src="https://latex.codecogs.com/png.latex?%5Crho_i"> produce una métrica localmente adaptada que hace que los puntos se “sientan” uniformemente distribuidos. Conceptualmente, esto puede visualizarse como una deformación del espacio alrededor de cada punto, donde las bolas de radios fijos se “estiran” o “comprimen” para lograr una densidad efectiva constante.</p>
<section id="espacio-métrico-adaptado" class="level4" data-number="4.4.1">
<h4 data-number="4.4.1" class="anchored" data-anchor-id="espacio-métrico-adaptado"><span class="header-section-number">4.4.1</span> Espacio Métrico Adaptado</h4>
<p><a href="img/umap/img8.png" class="lightbox" data-gallery="quarto-lightbox-gallery-8"><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/umap/img8.png" class="img-fluid"></a></p>
<p>Este ajuste es fundamental para que el “Fuzzy Simplicial Set” final refleje con precisión la topología subyacente, independientemente de las variaciones de densidad intrínsecas del dataset original.</p>
</section>
</section>
<section id="fuzzy-simplicial-set" class="level3" data-number="4.5">
<h3 data-number="4.5" class="anchored" data-anchor-id="fuzzy-simplicial-set"><span class="header-section-number">4.5</span> Fuzzy Simplicial Set</h3>
<p>Una vez que los parámetros de escala local (<img src="https://latex.codecogs.com/png.latex?%5Csigma_i"> y <img src="https://latex.codecogs.com/png.latex?%5Crho_i">) han sido determinados para cada punto, UMAP calcula los “scores de similaridad” entre cada punto <img src="https://latex.codecogs.com/png.latex?X_i"> y sus vecinos <img src="https://latex.codecogs.com/png.latex?X_j">. Estos scores representan la <strong>fuerza de conexión</strong> probabilística entre los puntos:</p>
<p><img src="https://latex.codecogs.com/png.latex?s_%7Bij%7D%20=%20%5Cexp%5Cleft(-%5Cfrac%7Bd(X_i,%20X_j)%20-%20%5Crho_i%7D%7B%5Csigma_i%7D%5Cright)"></p>
<p>Aquí, <img src="https://latex.codecogs.com/png.latex?s_%7Bij%7D"> es el <strong>score de similaridad</strong> (o “probabilidad de conexión”) de <img src="https://latex.codecogs.com/png.latex?X_i"> a <img src="https://latex.codecogs.com/png.latex?X_j">. Un valor cercano a 1 indica una conexión fuerte.</p>
<p>Estos scores se utilizan para construir un <strong>grafo de conectividad ponderado global</strong>. Cada punto de datos se convierte en un nodo del grafo, y las conexiones entre <img src="https://latex.codecogs.com/png.latex?X_i"> y <img src="https://latex.codecogs.com/png.latex?X_j"> son aristas con un peso <img src="https://latex.codecogs.com/png.latex?W_%7Bij%7D">. Este grafo se conceptualiza como un <strong>Conjunto Simplicial Difuso (Fuzzy Simplicial Set)</strong>.</p>
<section id="grafo-de-conectividad" class="level4" data-number="4.5.1">
<h4 data-number="4.5.1" class="anchored" data-anchor-id="grafo-de-conectividad"><span class="header-section-number">4.5.1</span> Grafo de Conectividad</h4>
<p><a href="img/umap/img4.png" class="lightbox" data-gallery="quarto-lightbox-gallery-9"><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/umap/img4.png" class="img-fluid"></a></p>
</section>
</section>
<section id="unión-difusa" class="level3" data-number="4.6">
<h3 data-number="4.6" class="anchored" data-anchor-id="unión-difusa"><span class="header-section-number">4.6</span> Unión Difusa</h3>
<p>La característica “difusa” (fuzzy) de los Conjuntos Simpliciales radica en que las conexiones no son binarias (sí/no), sino que poseen un <strong>grado de pertenencia</strong> (un valor continuo entre 0 y 1). Para consolidar los scores <img src="https://latex.codecogs.com/png.latex?s_%7Bij%7D"> y <img src="https://latex.codecogs.com/png.latex?s_%7Bji%7D"> (ya que la similaridad de <img src="https://latex.codecogs.com/png.latex?X_i"> a <img src="https://latex.codecogs.com/png.latex?X_j"> puede diferir de <img src="https://latex.codecogs.com/png.latex?X_j"> a <img src="https://latex.codecogs.com/png.latex?X_i"> debido a sus <img src="https://latex.codecogs.com/png.latex?%5Csigma"> y <img src="https://latex.codecogs.com/png.latex?%5Crho"> individuales), UMAP emplea la siguiente fórmula:</p>
<p><img src="https://latex.codecogs.com/png.latex?W_%7Bij%7D%20=%20s_%7Bij%7D%20+%20s_%7Bji%7D%20-%20(s_%7Bij%7D%20%5Ccdot%20s_%7Bji%7D)"></p>
<p>Análisis de la fórmula:</p>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?W_%7Bij%7D">: El <strong>peso final de la arista</strong> entre <img src="https://latex.codecogs.com/png.latex?X_i"> y <img src="https://latex.codecogs.com/png.latex?X_j"> en el grafo de conectividad global.</li>
<li><img src="https://latex.codecogs.com/png.latex?s_%7Bij%7D">: El score de similaridad calculado de <img src="https://latex.codecogs.com/png.latex?X_i"> a <img src="https://latex.codecogs.com/png.latex?X_j">.</li>
<li><img src="https://latex.codecogs.com/png.latex?s_%7Bji%7D">: El score de similaridad calculado de <img src="https://latex.codecogs.com/png.latex?X_j"> a <img src="https://latex.codecogs.com/png.latex?X_i">.</li>
</ul>
<p>Esta operación es una <strong>“unión difusa”</strong>. Es fundamental comprender que esta no es una operación de promediado. Promediar diluiría la información crucial inherente a una conexión fuerte. Si <img src="https://latex.codecogs.com/png.latex?X_i"> percibe a <img src="https://latex.codecogs.com/png.latex?X_j"> como un vecino muy cercano (alto <img src="https://latex.codecogs.com/png.latex?s_%7Bij%7D">), o si <img src="https://latex.codecogs.com/png.latex?X_j"> percibe a <img src="https://latex.codecogs.com/png.latex?X_i"> como un vecino muy cercano (alto <img src="https://latex.codecogs.com/png.latex?s_%7Bji%7D">), es deseable que esta fuerte conexión se mantenga en la representación topológica final. La unión difusa garantiza que la existencia de una conexión significativa en <strong>al menos una de las direcciones</strong> (ida o vuelta) contribuya de manera robusta al peso combinado. Conceptualmente, si <img src="https://latex.codecogs.com/png.latex?X_i"> está conectado a <img src="https://latex.codecogs.com/png.latex?X_j">, <strong>o</strong> <img src="https://latex.codecogs.com/png.latex?X_j"> está conectado a <img src="https://latex.codecogs.com/png.latex?X_i">, entonces <img src="https://latex.codecogs.com/png.latex?X_i"> y <img src="https://latex.codecogs.com/png.latex?X_j"> están considerados conectados en el grafo global.</p>
<p>Este grafo ponderado constituye la <strong>aproximación topológica de la variedad</strong> subyacente en el espacio de alta dimensión, capturando tanto las relaciones de proximidad como las características geométricas locales de una manera flexible y probabilística.</p>
</section>
<section id="grafo-de-conectividad-en-alta-dimensión" class="level3" data-number="4.7">
<h3 data-number="4.7" class="anchored" data-anchor-id="grafo-de-conectividad-en-alta-dimensión"><span class="header-section-number">4.7</span> Grafo de Conectividad en Alta Dimensión</h3>
<p>Como resultado de todos los pasos previos (adaptación métrica local, cálculo de scores de similaridad y unión difusa), el proceso de UMAP culmina en la construcción de un <strong>grafo de conectividad ponderado</strong> en el espacio de alta dimensión.</p>
<p>En este grafo:</p>
<ul>
<li>Cada <strong>nodo</strong> representa un punto de dato original.</li>
<li>Cada <strong>arista</strong> entre dos nodos indica una relación de similaridad o conectividad entre esos puntos.</li>
<li>El <strong>peso</strong> de cada arista (<img src="https://latex.codecogs.com/png.latex?W_%7Bij%7D">) cuantifica la fuerza de esa similaridad, calculada a través de la unión difusa.</li>
</ul>
<p>Este grafo ponderado puede ser convenientemente representado como una <strong>matriz de similaridad dispersa</strong>. En esta matriz, las filas y columnas corresponden a los puntos de datos, y cada entrada <img src="https://latex.codecogs.com/png.latex?(i,%20j)"> contiene el peso <img src="https://latex.codecogs.com/png.latex?W_%7Bij%7D"> de la arista que conecta el punto <img src="https://latex.codecogs.com/png.latex?i"> con el punto <img src="https://latex.codecogs.com/png.latex?j">. Dado que UMAP se enfoca en los <img src="https://latex.codecogs.com/png.latex?k"> vecinos más cercanos, la mayoría de las entradas de esta matriz serán cero, de ahí su naturaleza dispersa.</p>
<section id="ejemplo-conceptual-de-matriz-de-similaridad" class="level4" data-number="4.7.1">
<h4 data-number="4.7.1" class="anchored" data-anchor-id="ejemplo-conceptual-de-matriz-de-similaridad"><span class="header-section-number">4.7.1</span> Ejemplo Conceptual de Matriz de Similaridad</h4>
<table class="caption-top table">
<thead>
<tr class="header">
<th style="text-align: left;">Punto</th>
<th style="text-align: left;"><img src="https://latex.codecogs.com/png.latex?P_1"></th>
<th style="text-align: left;"><img src="https://latex.codecogs.com/png.latex?P_2"></th>
<th style="text-align: left;"><img src="https://latex.codecogs.com/png.latex?P_3"></th>
<th style="text-align: left;"><img src="https://latex.codecogs.com/png.latex?P_4"></th>
<th style="text-align: left;"><img src="https://latex.codecogs.com/png.latex?P_5"></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;"><img src="https://latex.codecogs.com/png.latex?P_1"></td>
<td style="text-align: left;">1.00</td>
<td style="text-align: left;">0.95</td>
<td style="text-align: left;">0.00</td>
<td style="text-align: left;">0.00</td>
<td style="text-align: left;">0.00</td>
</tr>
<tr class="even">
<td style="text-align: left;"><img src="https://latex.codecogs.com/png.latex?P_2"></td>
<td style="text-align: left;">0.95</td>
<td style="text-align: left;">1.00</td>
<td style="text-align: left;">0.82</td>
<td style="text-align: left;">0.00</td>
<td style="text-align: left;">0.00</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><img src="https://latex.codecogs.com/png.latex?P_3"></td>
<td style="text-align: left;">0.00</td>
<td style="text-align: left;">0.82</td>
<td style="text-align: left;">1.00</td>
<td style="text-align: left;">0.71</td>
<td style="text-align: left;">0.00</td>
</tr>
<tr class="even">
<td style="text-align: left;"><img src="https://latex.codecogs.com/png.latex?P_4"></td>
<td style="text-align: left;">0.00</td>
<td style="text-align: left;">0.00</td>
<td style="text-align: left;">0.71</td>
<td style="text-align: left;">1.00</td>
<td style="text-align: left;">0.90</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><img src="https://latex.codecogs.com/png.latex?P_5"></td>
<td style="text-align: left;">0.00</td>
<td style="text-align: left;">0.00</td>
<td style="text-align: left;">0.00</td>
<td style="text-align: left;">0.90</td>
<td style="text-align: left;">1.00</td>
</tr>
</tbody>
</table>
</section>
</section>
<section id="representación-en-baja-dimensión" class="level3" data-number="4.8">
<h3 data-number="4.8" class="anchored" data-anchor-id="representación-en-baja-dimensión"><span class="header-section-number">4.8</span> Representación en Baja Dimensión</h3>
<p>Una vez que UMAP ha construido el <strong>Conjunto Simplicial Difuso (Fuzzy Simplicial Set)</strong> en el espacio de alta dimensión, que encapsula la estructura topológica y las similaridades probabilísticas (<img src="https://latex.codecogs.com/png.latex?W_%7Bij%7D">), el siguiente paso es encontrar una representación de estos datos en un espacio de menor dimensionalidad (e.g., <img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5E2"> o <img src="https://latex.codecogs.com/png.latex?%5Cmathbb%7BR%7D%5E3">).</p>
<p>El objetivo es generar una incrustación <img src="https://latex.codecogs.com/png.latex?Y%20=%20%5C%7By_1,%20%5Cdots,%20y_N%5C%7D%20%5Csubset%20%5Cmathbb%7BR%7D%5Ed"> (donde <img src="https://latex.codecogs.com/png.latex?d"> es la dimensión objetivo, típicamente 2 o 3) que sea topológicamente similar al grafo de alta dimensión. Esto se logra formulando un problema de optimización para preservar las <strong>conectividades probabilísticas</strong> establecidas en el espacio original. La meta es que si dos puntos <img src="https://latex.codecogs.com/png.latex?X_i"> y <img src="https://latex.codecogs.com/png.latex?X_j"> tienen una alta probabilidad de conexión <img src="https://latex.codecogs.com/png.latex?W_%7Bij%7D"> en alta dimensión, sus correspondientes incrustaciones <img src="https://latex.codecogs.com/png.latex?y_i"> y <img src="https://latex.codecogs.com/png.latex?y_j"> deben estar cerca en baja dimensión, y viceversa.</p>
<p>Para cuantificar la “similaridad” o “conectividad” entre los puntos <img src="https://latex.codecogs.com/png.latex?y_i"> y <img src="https://latex.codecogs.com/png.latex?y_j"> en el espacio de baja dimensión, UMAP emplea una función de kernel específica, elegida por sus propiedades deseables para la optimización y la visualización:</p>
<p><img src="https://latex.codecogs.com/png.latex?w_%7Bij%7D%20=%20%5Cfrac%7B1%7D%7B1%20+%20a(d(y_i,%20y_j))%5E%20%7B2b%7D%7D"></p>
<p>Analicemos los componentes de esta fórmula y su propósito:</p>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?w_%7Bij%7D">: Representa la <strong>similaridad o probabilidad de conexión</strong> entre las proyecciones <img src="https://latex.codecogs.com/png.latex?y_i"> y <img src="https://latex.codecogs.com/png.latex?y_j"> en el espacio de baja dimensión. Este es el homólogo de <img src="https://latex.codecogs.com/png.latex?W_%7Bij%7D"> pero definido en el espacio de salida.</li>
<li><img src="https://latex.codecogs.com/png.latex?d(y_i,%20y_j)">: Es la <strong>distancia euclidiana</strong> entre <img src="https://latex.codecogs.com/png.latex?y_i"> y <img src="https://latex.codecogs.com/png.latex?y_j"> en el espacio de baja dimensión.</li>
<li><img src="https://latex.codecogs.com/png.latex?a"> y <img src="https://latex.codecogs.com/png.latex?b">: Son <strong>parámetros de UMAP</strong> derivados de los hiperparámetros <code>min_dist</code> y <code>spread</code>.
<ul>
<li><code>min_dist</code>: Este parámetro controla la distancia mínima que los puntos pueden tener en el espacio de baja dimensión. Un <code>min_dist</code> pequeño (cercano a 0) permite que los puntos se agrupen densamente, mientras que un <code>min_dist</code> mayor los fuerza a separarse más, resultando en agrupaciones más difusas.</li>
<li><code>spread</code>: Este parámetro controla la dispersión de los puntos, influenciando la escala a la que se interpretan las distancias. Un <code>spread</code> más alto permite que los puntos se dispersen más ampliamente.</li>
<li>Los valores de <img src="https://latex.codecogs.com/png.latex?a"> y <img src="https://latex.codecogs.com/png.latex?b"> se ajustan para que la curva de decaimiento de la conectividad en baja dimensión (<code>min_dist</code> y <code>spread</code>) se alinee lo mejor posible con la curva de decaimiento del espacio de alta dimensión (<img src="https://latex.codecogs.com/png.latex?%5Csigma_i"> y <img src="https://latex.codecogs.com/png.latex?%5Crho_i">), creando una correspondencia métrica.</li>
</ul></li>
</ul>
</section>
<section id="minimización-de-la-divergencia-de-entropía-cruzada" class="level3" data-number="4.9">
<h3 data-number="4.9" class="anchored" data-anchor-id="minimización-de-la-divergencia-de-entropía-cruzada"><span class="header-section-number">4.9</span> Minimización de la Divergencia de Entropía Cruzada</h3>
<p>El corazón de la incrustación de UMAP reside en la minimización de la “divergencia” entre el grafo de conectividad en alta dimensión (<img src="https://latex.codecogs.com/png.latex?W_%7Bij%7D">) y el grafo en baja dimensión (<img src="https://latex.codecogs.com/png.latex?w_%7Bij%7D">). Esto se formula como la minimización de la <strong>divergencia de la sección cruzada de la entropía (Cross-Entropy)</strong>, una medida estándar para la diferencia entre dos distribuciones de probabilidad.</p>
<p>La función objetivo que UMAP busca minimizar es:</p>
<p><img src="https://latex.codecogs.com/png.latex?L(Y)%20=%20%5Csum_%7Bi%20%5Cneq%20j%7D%20%5Cleft%5B%20W_%7Bij%7D%20%5Clog%5Cleft(%5Cfrac%7BW_%7Bij%7D%7D%7Bw_%7Bij%7D%7D%5Cright)%20+%20(1%20-%20W_%7Bij%7D)%20%5Clog%5Cleft(%5Cfrac%7B1%20-%20W_%7Bij%7D%7D%7B1%20-%20w_%7Bij%7D%7D%5Cright)%20%5Cright%5D"></p>
<p>Donde:</p>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?W_%7Bij%7D">: La <strong>probabilidad de conexión</strong> entre <img src="https://latex.codecogs.com/png.latex?X_i"> y <img src="https://latex.codecogs.com/png.latex?X_j"> en el espacio de <strong>alta dimensión</strong> (un valor entre 0 y 1).</li>
<li><img src="https://latex.codecogs.com/png.latex?w_%7Bij%7D">: La <strong>probabilidad de conexión</strong> entre <img src="https://latex.codecogs.com/png.latex?y_i"> y <img src="https://latex.codecogs.com/png.latex?y_j"> en el espacio de <strong>baja dimensión</strong> (calculada con el kernel de Cauchy).</li>
</ul>
<p><strong>Interpretación de los Términos de la Función de Pérdida y sus Gradientes:</strong></p>
<ol type="1">
<li><strong>Término de Atracción (primer término):</strong> <img src="https://latex.codecogs.com/png.latex?W_%7Bij%7D%20%5Clog%5Cleft(%5Cfrac%7BW_%7Bij%7D%7D%7Bw_%7Bij%7D%7D%5Cright)">
<ul>
<li>Cuando <img src="https://latex.codecogs.com/png.latex?W_%7Bij%7D"> es alto (los puntos están conectados en alta dimensión) y <img src="https://latex.codecogs.com/png.latex?w_%7Bij%7D"> es bajo (están lejos en baja dimensión), este término se vuelve grande y positivo, generando una penalización significativa.</li>
<li>El gradiente de este término tiende a <strong>acercar</strong> los puntos <img src="https://latex.codecogs.com/png.latex?y_i"> y <img src="https://latex.codecogs.com/png.latex?y_j">.</li>
</ul></li>
<li><strong>Término de Repulsión (segundo término):</strong> <img src="https://latex.codecogs.com/png.latex?(1%20-%20W_%7Bij%7D)%20%5Clog%5Cleft(%5Cfrac%7B1%20-%20W_%7Bij%7D%7D%7B1%20-%20w_%7Bij%7D%7D%5Cright)">
<ul>
<li>Cuando <img src="https://latex.codecogs.com/png.latex?W_%7Bij%7D"> es bajo (los puntos no están conectados en alta dimensión) y <img src="https://latex.codecogs.com/png.latex?w_%7Bij%7D"> es alto (están demasiado cerca en baja dimensión), este término también se vuelve grande, penalizando la proximidad.</li>
<li>El gradiente de este término tiende a <strong>alejar</strong> los puntos <img src="https://latex.codecogs.com/png.latex?y_i"> y <img src="https://latex.codecogs.com/png.latex?y_j">.</li>
</ul></li>
</ol>
<p>La minimización de esta función de costo guía el proceso de ajuste de las posiciones de los puntos en el espacio de baja dimensión, buscando una configuración óptima que preserve la topología probabilística del espacio original.</p>
</section>
<section id="descenso-de-gradiente-estocástico" class="level3" data-number="4.10">
<h3 data-number="4.10" class="anchored" data-anchor-id="descenso-de-gradiente-estocástico"><span class="header-section-number">4.10</span> Descenso de Gradiente Estocástico</h3>
<p>La minimización de la función de pérdida <img src="https://latex.codecogs.com/png.latex?L(Y)"> se realiza mediante un proceso iterativo de <strong>Descenso de Gradiente Estocástico (Stochastic Gradient Descent - SGD)</strong>.</p>
<p>Las etapas clave son:</p>
<ol type="1">
<li><strong>Inicialización del Embedding:</strong>
<ul>
<li>Para acelerar la convergencia y proporcionar un punto de partida razonable, los puntos <img src="https://latex.codecogs.com/png.latex?y_i"> en el espacio de baja dimensión se inicializan comúnmente utilizando una <strong>incrustación espectral</strong> (e.g., basada en los vectores propios del Laplaciano normalizado del grafo de conectividad de alta dimensión). Alternativamente, se puede usar una inicialización aleatoria.</li>
</ul></li>
<li><strong>Muestreo de Aristas y No-Aristas:</strong>
<ul>
<li>En cada iteración del SGD, se selecciona aleatoriamente una arista (un par <img src="https://latex.codecogs.com/png.latex?(i,%20j)"> con <img src="https://latex.codecogs.com/png.latex?W_%7Bij%7D%20%3E%200">) de la matriz de similaridad en alta dimensión. Para este par, se aplica una <strong>fuerza de atracción</strong> a <img src="https://latex.codecogs.com/png.latex?y_i"> y <img src="https://latex.codecogs.com/png.latex?y_j">.</li>
<li>Para la repulsión, UMAP emplea <strong>muestreo negativo</strong>. En lugar de considerar todos los pares no conectados (lo que sería computacionalmente inviable para grandes datasets, <img src="https://latex.codecogs.com/png.latex?O(N%5E2)">), se muestrean aleatoriamente varios puntos <img src="https://latex.codecogs.com/png.latex?y_k"> que no están conectados a <img src="https://latex.codecogs.com/png.latex?y_i">. Para estos pares <img src="https://latex.codecogs.com/png.latex?(i,%20k)">, se aplica una <strong>fuerza de repulsión</strong>. La proporción de muestras negativas por muestra positiva es un parámetro configurable.</li>
</ul></li>
<li><strong>Cálculo de Gradientes y Actualización de Posiciones:</strong>
<ul>
<li>Se calculan las derivadas parciales de la función de pérdida <img src="https://latex.codecogs.com/png.latex?L"> con respecto a las coordenadas de los puntos <img src="https://latex.codecogs.com/png.latex?y_i"> y <img src="https://latex.codecogs.com/png.latex?y_j">.</li>
<li>Estas gradientes determinan la dirección y magnitud del ajuste de las posiciones de los puntos.</li>
<li>Las posiciones de los puntos se actualizan iterativamente: <img src="https://latex.codecogs.com/png.latex?y_k%20%5Cleftarrow%20y_k%20-%20%5Ceta%20%5Cnabla_%7By_k%7D%20L">, donde <img src="https://latex.codecogs.com/png.latex?%5Ceta"> es la tasa de aprendizaje.</li>
</ul></li>
<li><strong>Annealing y Convergencia:</strong>
<ul>
<li>El proceso de optimización se ejecuta durante un número predefinido de épocas (<code>n_epochs</code>).</li>
<li>A menudo, las fuerzas repulsivas se ponderan más fuertemente al inicio de la optimización para asegurar que los puntos se dispersen adecuadamente y no queden atrapados en mínimos locales apretados. A medida que avanza la optimización, el equilibrio entre atracción y repulsión se ajusta para permitir que las agrupaciones se refinen.</li>
</ul></li>
</ol>
</section>
<section id="impacto-de-parámetros-clave-en-la-embedificación" class="level3" data-number="4.11">
<h3 data-number="4.11" class="anchored" data-anchor-id="impacto-de-parámetros-clave-en-la-embedificación"><span class="header-section-number">4.11</span> Impacto de Parámetros Clave en la Embedificación</h3>
<p>La calidad y la interpretación de la incrustación de UMAP están significativamente influenciadas por varios hiperparámetros, que afectan directamente la función de pérdida y el proceso de optimización:</p>
<ul>
<li><strong><code>n_neighbors</code> (Número de vecinos):</strong> Este parámetro (usado en la fase de alta dimensión para construir el k-NN graph) tiene un impacto crucial en el balance entre la preservación de la estructura local y global en la incrustación final.
<ul>
<li><strong>Valores pequeños:</strong> Enfatizan la estructura local, lo que puede llevar a una fragmentación de la incrustación y a la aparición de muchos clústeres pequeños y separados.</li>
<li><strong>Valores grandes:</strong> Permiten que UMAP considere más la estructura global de los datos, lo que puede resultar en una vista más cohesiva, pero quizás menos detallada de las relaciones locales.</li>
</ul></li>
<li><strong><code>min_dist</code> (Distancia Mínima):</strong> Directamente relacionado con el parámetro <code>a</code> del kernel de Cauchy.
<ul>
<li>Controla cuán cerca se pueden agrupar los puntos en la incrustación de baja dimensión.</li>
<li><strong><code>min_dist</code> pequeño (cercano a 0):</strong> Los puntos pueden agruparse de forma muy densa, lo que es útil para visualizar clústeres bien separados y compactos.</li>
<li><strong><code>min_dist</code> grande:</strong> Los puntos son forzados a estar más dispersos, lo que puede revelar una estructura más continua y fluida, o separar clústeres que de otra manera se solaparían visualmente.</li>
</ul></li>
<li><strong><code>spread</code> (Dispersión):</strong> Directamente relacionado con el parámetro <code>b</code> del kernel de Cauchy.
<ul>
<li>Controla la dispersión general del embedding.</li>
<li><strong><code>spread</code> pequeño:</strong> Resulta en una incrustación más compacta.</li>
<li><strong><code>spread</code> grande:</strong> Permite que la incrustación se extienda más, útil para visualizar la jerarquía global.</li>
</ul></li>
<li><strong><code>n_epochs</code> (Número de Épocas):</strong>
<ul>
<li>Define el número de iteraciones del algoritmo de optimización SGD.</li>
<li>Más épocas (<code>n_epochs</code> más alto) generalmente conducen a una incrustación más optimizada y estable, pero a costa de un mayor tiempo de cómputo.</li>
<li>Para datasets grandes o embeddings de alta calidad, un <code>n_epochs</code> elevado es recomendable.</li>
</ul></li>
</ul>
<p>Estos parámetros permiten al usuario ajustar el equilibrio entre la preservación de la estructura local y global, y la densidad visual de la incrustación resultante, adaptando UMAP a diversas necesidades de análisis y visualización.</p>
</section>
<section id="ventajas-clave-de-umap" class="level3" data-number="4.12">
<h3 data-number="4.12" class="anchored" data-anchor-id="ventajas-clave-de-umap"><span class="header-section-number">4.12</span> Ventajas Clave de UMAP</h3>
<p>UMAP se compara favorablemente con t-SNE y otros algoritmos de la misma clase (basados en grafos de <img src="https://latex.codecogs.com/png.latex?k">-vecinos). Su principal ventaja es que sus elecciones algorítmicas, tanto en la construcción del grafo como en el diseño de la incrustación, tienen una <strong>justificación teórica clara</strong> derivada de sus axiomas sobre la variedad y la topología, lo que le permite ser:</p>
<ul>
<li><strong>Más Rápido y Escalable:</strong> Su optimización en grafos dispersos y el muestreo eficiente de no-vecinos lo hacen significativamente más rápido que t-SNE, siendo aplicable a datasets de millones de puntos con una complejidad computacional que se aproxima a <img src="https://latex.codecogs.com/png.latex?O(N%20%5Clog%20N)">.</li>
<li><strong>Mejor Preservación de la Estructura Global:</strong> La naturaleza de su función objetivo y la forma en que maneja las distancias adaptativas le permiten mantener de manera más consistente las relaciones a gran escala entre los clusters, además de la estructura local.</li>
<li><strong>Flexible y Controlable:</strong> Los parámetros <code>min_dist</code> y <code>spread</code> ofrecen un control intuitivo sobre la densidades y separación de los <em>clusters</em> en la visualización final.</li>
<li><strong>Capacidad de Transformación:</strong> UMAP puede aprender una transformación desde el espacio de alta a baja dimensión, lo que permite proyectar nuevos datos en un <em>embedding</em> existente sin recalcularlo todo.</li>
</ul>
</section>
</section>
<section id="comparativa-t-sne-vs-umap" class="level2" data-number="5">
<h2 data-number="5" class="anchored" data-anchor-id="comparativa-t-sne-vs-umap"><span class="header-section-number">5</span> Comparativa t-SNE vs UMAP</h2>
<p>Ambas t-SNE y UMAP son herramientas poderosas para la reducción de dimensionalidad no lineal y la visualización, buscando revelar la estructura intrínseca de los datos. Sin embargo, sus fundamentos matemáticos y sus enfoques computacionales les otorgan características y rendimientos distintivos.</p>
<table class="caption-top table">
<colgroup>
<col style="width: 25%">
<col style="width: 36%">
<col style="width: 38%">
</colgroup>
<thead>
<tr class="header">
<th style="text-align: left;">Característica</th>
<th style="text-align: left;">t-SNE</th>
<th style="text-align: left;">UMAP</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;"><strong>Fundamentos Teóricos</strong></td>
<td style="text-align: left;">Teoría de probabilidades, divergencia Kullback-Leibler. Busca preservar las probabilidades de similitud (vecindad).</td>
<td style="text-align: left;">Topología algebraica, teoría de la geometría riemanniana, conjuntos simpliciales difusos. Busca preservar la estructura topológica (conectividad).</td>
</tr>
<tr class="even">
<td style="text-align: left;"><strong>Concepto de Similitud</strong></td>
<td style="text-align: left;">Probabilidades de similitud <img src="https://latex.codecogs.com/png.latex?p_%7Bij%7D"> (basadas en Gaussianas) en alta dimensión y <img src="https://latex.codecogs.com/png.latex?q_%7Bij%7D"> (basadas en t-Student) en baja dimensión.</td>
<td style="text-align: left;">Conectividad en un grafo ponderado (fuzzy simplicial set) que representa la proximidad. La noción de “vecino” es localmente adaptable.</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><strong>Preservación de Estructura</strong></td>
<td style="text-align: left;"><strong>Excelente para la estructura local (clusters).</strong> Tiende a aglomerar puntos cercanos y separarlos bien. A menudo <strong>distorsiona la estructura global</strong> (las distancias entre clusters no son fiables).</td>
<td style="text-align: left;"><strong>Excelente para estructura local Y global.</strong> Busca preservar tanto los clusters como las relaciones entre ellos. Es más probable que las distancias entre clusters tengan significado.</td>
</tr>
<tr class="even">
<td style="text-align: left;"><strong>Función de Costo</strong></td>
<td style="text-align: left;">Divergencia Kullback-Leibler (KL) asimétrica: <img src="https://latex.codecogs.com/png.latex?%5Ctext%7BKL%7D(P%20%7C%7C%20Q)">. Penaliza fuertemente la pérdida de vecinos cercanos.</td>
<td style="text-align: left;">Inspirada en la entropía cruzada binaria, optimizada para la correspondencia de grafos: <img src="https://latex.codecogs.com/png.latex?%5Csum%20%5Bw%20%5Clog(w/q)%20+%20(1-w)%20%5Clog((1-w)/(1-q))%5D">.</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><strong>Distribución en Baja Dimensión</strong></td>
<td style="text-align: left;">Distribución t-Student con 1 grado de libertad (Cauchy). Sus colas pesadas resuelven el “problema de hacinamiento”.</td>
<td style="text-align: left;">Función de conectividad personalizada $&nbsp;(1 + a|y_i - y_j|<sup>{2b})</sup>{-1} $. Parametros <code>min_dist</code> y <code>spread</code> controlan su forma para influir en la dispersión.</td>
</tr>
<tr class="even">
<td style="text-align: left;"><strong>Velocidad y Escalabilidad</strong></td>
<td style="text-align: left;"><strong>Generalmente lento.</strong> Su complejidad es <img src="https://latex.codecogs.com/png.latex?O(N%5E2)"> (o <img src="https://latex.codecogs.com/png.latex?O(N%20%5Clog%20N)"> con optimizaciones como la del árbol de Barnes-Hut). No es ideal para datasets con más de ~50,000 puntos.</td>
<td style="text-align: left;"><strong>Significativamente más rápido.</strong> Su complejidad es <img src="https://latex.codecogs.com/png.latex?O(N%20%5Clog%20N)"> para la construcción del grafo y luego <img src="https://latex.codecogs.com/png.latex?O(N)"> para la optimización. Puede manejar datasets de millones de puntos.</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><strong>Reproductibilidad</strong></td>
<td style="text-align: left;">Muy sensible a los parámetros (<code>perplexity</code>, <code>learning_rate</code>, <code>init</code>). Ejecuciones repetidas con diferentes <code>random_state</code> pueden producir visualizaciones notablemente diferentes.</td>
<td style="text-align: left;">Generalmente más robusto a los cambios de parámetros y más consistente en la estructura global resultante entre ejecuciones.</td>
</tr>
<tr class="even">
<td style="text-align: left;"><strong>Parámetros Clave</strong></td>
<td style="text-align: left;"><code>perplexity</code> (número de “vecinos efectivos”), <code>learning_rate</code> (tasa de aprendizaje).</td>
<td style="text-align: left;"><code>n_neighbors</code> (número de vecinos para construir el grafo inicial), <code>min_dist</code> (separación mínima entre puntos proyectados), <code>spread</code> (escala de los clusters), <code>metric</code> (métrica de distancia).</td>
</tr>
<tr class="odd">
<td style="text-align: left;"><strong>Forma de los Clusters</strong></td>
<td style="text-align: left;">Tiende a producir clusters más densos y circulares, a veces con puntos aglomerados en el centro del mapa.</td>
<td style="text-align: left;">Puede producir clusters con formas más diversas y una mejor separación visual, reflejando mejor la estructura intrínseca de los datos.</td>
</tr>
<tr class="even">
<td style="text-align: left;"><strong>Aplicaciones Típicas</strong></td>
<td style="text-align: left;">Excelente para la visualización de datos de transcriptómica (ej. single-cell RNA-seq), imágenes, texto. Muy bueno para identificar subpoblaciones distintas.</td>
<td style="text-align: left;">Ampliamente utilizado en biología (ej. single-cell), visualización de embeddings de procesamiento del lenguaje natural (NLP), análisis de imágenes grandes. A menudo es la opción preferida por su equilibrio entre velocidad y calidad del embedding.</td>
</tr>
</tbody>
</table>
</section>
<section id="implementación-en-el-dataset-mnist" class="level2" data-number="6">
<h2 data-number="6" class="anchored" data-anchor-id="implementación-en-el-dataset-mnist"><span class="header-section-number">6</span> Implementación en el dataset MNIST</h2>
<p>En esta sección, aplicaremos tanto t-SNE como UMAP al dataset de dígitos de Scikit-learn y analizaremos los resultados, comparando las visualizaciones y las métricas de calidad de las proyecciones.</p>
<section id="representación-de-imágenes-como-matrices" class="level3" data-number="6.1">
<h3 data-number="6.1" class="anchored" data-anchor-id="representación-de-imágenes-como-matrices"><span class="header-section-number">6.1</span> Representación de Imágenes como Matrices</h3>
<p>Para procesar imágenes con algoritmos de machine learning, es fundamental entender cómo se representan numéricamente. Las imágenes, en esencia, son rejillas de píxeles, donde cada píxel tiene un valor numérico que representa su intensidad o color.</p>
<section id="imágenes-en-escala-de-grises" class="level4" data-number="6.1.1">
<h4 data-number="6.1.1" class="anchored" data-anchor-id="imágenes-en-escala-de-grises"><span class="header-section-number">6.1.1</span> Imágenes en Escala de Grises</h4>
<p>Una imagen en escala de grises se representa como una matriz 2D, donde cada elemento de la matriz corresponde a un píxel y su valor representa la intensidad del gris (típicamente entre 0 para negro y 255 para blanco, u otros rangos dependiendo del formato).</p>
<p>En nuestro ejemplo, el dataset de dígitos de Scikit-learn consta de imágenes de 8x8 píxeles en escala de grises. Cada imagen individual se puede visualizar como una matriz de 8x8:</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cbegin%7Bpmatrix%7D%0Avalor_%7B0,0%7D%20&amp;%20valor_%7B0,1%7D%20&amp;%20%5Cdots%20&amp;%20valor_%7B0,7%7D%20%5C%5C%0Avalor_%7B1,0%7D%20&amp;%20valor_%7B1,1%7D%20&amp;%20%5Cdots%20&amp;%20valor_%7B1,7%7D%20%5C%5C%0A%5Cvdots%20&amp;%20%5Cvdots%20&amp;%20%5Cddots%20&amp;%20%5Cvdots%20%5C%5C%0Avalor_%7B7,0%7D%20&amp;%20valor_%7B7,1%7D%20&amp;%20%5Cdots%20&amp;%20valor_%7B7,7%7D%0A%5Cend%7Bpmatrix%7D"></p>
</section>
<section id="imágenes-a-color-rgb" class="level4" data-number="6.1.2">
<h4 data-number="6.1.2" class="anchored" data-anchor-id="imágenes-a-color-rgb"><span class="header-section-number">6.1.2</span> Imágenes a Color (RGB)</h4>
<p>Las imágenes a color, comúnmente en formato RGB (Rojo, Verde, Azul), se representan como tensores 3D. Tienen dimensiones de altura, ancho y canales de color. Para una imagen RGB, hay 3 canales:</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Ctext%7BImagen%20RGB%7D%20%5Cin%20%5Cmathbb%7BR%7D%5E%7B%5Ctext%7Baltura%7D%20%5Ctimes%20%5Ctext%7Bancho%7D%20%5Ctimes%203%7D"></p>
<p>Cada canal es una matriz 2D que representa la intensidad de ese color específico para cada píxel. La combinación de los valores en los tres canales para un píxel dado determina su color final.</p>
</section>
<section id="aplanamiento-flattening-para-algoritmos-lineales" class="level4" data-number="6.1.3">
<h4 data-number="6.1.3" class="anchored" data-anchor-id="aplanamiento-flattening-para-algoritmos-lineales"><span class="header-section-number">6.1.3</span> Aplanamiento (Flattening) para Algoritmos Lineales</h4>
<p>Algoritmos como t-SNE o PCA trabajan con vectores de características de una sola dimensión. Por lo tanto, es necesario “aplanar” la representación matricial o tensorial de cada imagen en un único vector largo.</p>
<p>Para una imagen de 8x8 como en el dataset de dígitos, la matriz 2D de 8x8 (64 píxeles) se aplana en un vector 1D de 64 elementos:</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Cbegin%7Bpmatrix%7D%0Avalor_%7B0,0%7D%20%5C%5C%0Avalor_%7B0,1%7D%20%5C%5C%0A%5Cvdots%20%5C%5C%0Avalor_%7B7,7%7D%0A%5Cend%7Bpmatrix%7D_%7B64%20%5Ctimes%201%7D"></p>
<p>Para una imagen RGB de altura x ancho x 3 canales, el tensor 3D se aplana en un vector 1D de altura <img src="https://latex.codecogs.com/png.latex?%5Ctimes"> ancho <img src="https://latex.codecogs.com/png.latex?%5Ctimes"> 3 elementos. Por ejemplo, una imagen de 100x100 píxeles RGB se convertiría en un vector de <img src="https://latex.codecogs.com/png.latex?100%20%5Ctimes%20100%20%5Ctimes%203%20=%2030,000"> elementos.</p>
<p>Este vector aplanado se convierte en la “muestra” o “instancia” de datos en el conjunto de datos de entrada para t-SNE, donde cada elemento del vector es una “característica” (un valor de píxel).</p>
</section>
</section>
<section id="visualización-tabular-de-los-datos-dataframe" class="level3" data-number="6.2">
<h3 data-number="6.2" class="anchored" data-anchor-id="visualización-tabular-de-los-datos-dataframe"><span class="header-section-number">6.2</span> Visualización Tabular de los Datos (DataFrame)</h3>
<p>Para comprender mejor la estructura de los datos aplanados antes de aplicar los algoritmos de reducción de dimensionalidad, podemos visualizarlos en un formato tabular, como un DataFrame de Pandas. Cada fila representará una imagen aplanada (una muestra), y cada columna representará un píxel específico (una característica).</p>
<div>


<table class="dataframe caption-top table table-sm table-striped small" data-border="1">
<thead>
<tr class="header">
<th data-quarto-table-cell-role="th"></th>
<th data-quarto-table-cell-role="th">0</th>
<th data-quarto-table-cell-role="th">1</th>
<th data-quarto-table-cell-role="th">2</th>
<th data-quarto-table-cell-role="th">3</th>
<th data-quarto-table-cell-role="th">4</th>
<th data-quarto-table-cell-role="th">5</th>
<th data-quarto-table-cell-role="th">6</th>
<th data-quarto-table-cell-role="th">7</th>
<th data-quarto-table-cell-role="th">8</th>
<th data-quarto-table-cell-role="th">9</th>
<th data-quarto-table-cell-role="th">...</th>
<th data-quarto-table-cell-role="th">55</th>
<th data-quarto-table-cell-role="th">56</th>
<th data-quarto-table-cell-role="th">57</th>
<th data-quarto-table-cell-role="th">58</th>
<th data-quarto-table-cell-role="th">59</th>
<th data-quarto-table-cell-role="th">60</th>
<th data-quarto-table-cell-role="th">61</th>
<th data-quarto-table-cell-role="th">62</th>
<th data-quarto-table-cell-role="th">63</th>
<th data-quarto-table-cell-role="th">digito</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<th data-quarto-table-cell-role="th">0</th>
<td>0.0</td>
<td>0.0</td>
<td>5.0</td>
<td>13.0</td>
<td>9.0</td>
<td>1.0</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>...</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>6.0</td>
<td>13.0</td>
<td>10.0</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>0</td>
</tr>
<tr class="even">
<th data-quarto-table-cell-role="th">1</th>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>12.0</td>
<td>13.0</td>
<td>5.0</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>...</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>11.0</td>
<td>16.0</td>
<td>10.0</td>
<td>0.0</td>
<td>0.0</td>
<td>1</td>
</tr>
<tr class="odd">
<th data-quarto-table-cell-role="th">2</th>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>4.0</td>
<td>15.0</td>
<td>12.0</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>...</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>3.0</td>
<td>11.0</td>
<td>16.0</td>
<td>9.0</td>
<td>0.0</td>
<td>2</td>
</tr>
<tr class="even">
<th data-quarto-table-cell-role="th">3</th>
<td>0.0</td>
<td>0.0</td>
<td>7.0</td>
<td>15.0</td>
<td>13.0</td>
<td>1.0</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>8.0</td>
<td>...</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>7.0</td>
<td>13.0</td>
<td>13.0</td>
<td>9.0</td>
<td>0.0</td>
<td>0.0</td>
<td>3</td>
</tr>
<tr class="odd">
<th data-quarto-table-cell-role="th">4</th>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>1.0</td>
<td>11.0</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>...</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>0.0</td>
<td>2.0</td>
<td>16.0</td>
<td>4.0</td>
<td>0.0</td>
<td>0.0</td>
<td>4</td>
</tr>
</tbody>
</table>

<p>5 rows × 65 columns</p>
</div>
<p>En este DataFrame, cada una de las primeras 64 columnas (<code>pixel_0</code> a <code>pixel_63</code>) representan la intensidad aplanada de un píxel de la imagen de 8x8. La última columna (<code>digito</code>) contiene la etiqueta de la clase (el dígito que representa la imagen).</p>
<p>Esta vista nos permite ver los valores numéricos exactos que alimentan los algoritmos.</p>
</section>
<section id="visualización-de-datos-originales" class="level3" data-number="6.3">
<h3 data-number="6.3" class="anchored" data-anchor-id="visualización-de-datos-originales"><span class="header-section-number">6.3</span> Visualización de Datos Originales</h3>
<p><a href="umap-tsne_files/figure-html/plot-original-output-1.png" class="lightbox" data-gallery="quarto-lightbox-gallery-10"><img src="https://luccafrachelle.github.io/porfolio_academia/posts/umap-tsne_files/figure-html/plot-original-output-1.png" id="plot-original" class="img-fluid"></a></p>
</section>
<section id="implementación-y-análisis-de-t-sne" class="level3" data-number="6.4">
<h3 data-number="6.4" class="anchored" data-anchor-id="implementación-y-análisis-de-t-sne"><span class="header-section-number">6.4</span> Implementación y Análisis de t-SNE</h3>
<p>En esta subsección, aplicaremos t-SNE al dataset de dígitos y exploraremos el proceso de optimización y el efecto de sus hiperparámetros.</p>
<section id="proceso-de-optimización-de-t-sne" class="level4" data-number="6.4.1">
<h4 data-number="6.4.1" class="anchored" data-anchor-id="proceso-de-optimización-de-t-sne"><span class="header-section-number">6.4.1</span> Proceso de Optimización de t-SNE</h4>
<p><a href="umap-tsne_files/figure-html/optimization-visualization-output-1.png" class="lightbox" data-gallery="quarto-lightbox-gallery-11"><img src="https://luccafrachelle.github.io/porfolio_academia/posts/umap-tsne_files/figure-html/optimization-visualization-output-1.png" id="optimization-visualization" class="img-fluid"></a></p>
</section>
<section id="análisis-de-la-convergencia-de-t-sne" class="level4" data-number="6.4.2">
<h4 data-number="6.4.2" class="anchored" data-anchor-id="análisis-de-la-convergencia-de-t-sne"><span class="header-section-number">6.4.2</span> Análisis de la Convergencia de t-SNE</h4>
<p><a href="umap-tsne_files/figure-html/convergence-analysis-output-1.png" class="lightbox" data-gallery="quarto-lightbox-gallery-12"><img src="https://luccafrachelle.github.io/porfolio_academia/posts/umap-tsne_files/figure-html/convergence-analysis-output-1.png" id="convergence-analysis" class="img-fluid"></a></p>
</section>
<section id="efecto-de-los-hiperparámetros-de-t-sne" class="level4" data-number="6.4.3">
<h4 data-number="6.4.3" class="anchored" data-anchor-id="efecto-de-los-hiperparámetros-de-t-sne"><span class="header-section-number">6.4.3</span> Efecto de los Hiperparámetros de t-SNE</h4>
<div id="hyperparameter-effects" class="cell" data-execution_count="13">
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><a href="umap-tsne_files/figure-html/hyperparameter-effects-output-1.png" class="lightbox" data-gallery="quarto-lightbox-gallery-13"><img src="https://luccafrachelle.github.io/porfolio_academia/posts/umap-tsne_files/figure-html/hyperparameter-effects-output-1.png" id="hyperparameter-effects-1" class="img-fluid figure-img"></a></p>
</figure>
</div>
</div>
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><a href="umap-tsne_files/figure-html/hyperparameter-effects-output-2.png" class="lightbox" data-gallery="quarto-lightbox-gallery-14"><img src="https://luccafrachelle.github.io/porfolio_academia/posts/umap-tsne_files/figure-html/hyperparameter-effects-output-2.png" id="hyperparameter-effects-2" class="img-fluid figure-img"></a></p>
</figure>
</div>
</div>
</div>
</section>
</section>
<section id="implementación-y-análisis-de-umap-en-mnist" class="level3" data-number="6.5">
<h3 data-number="6.5" class="anchored" data-anchor-id="implementación-y-análisis-de-umap-en-mnist"><span class="header-section-number">6.5</span> Implementación y Análisis de UMAP en MNIST</h3>
<p>En esta sección, aplicaremos UMAP al dataset de dígitos MNIST usando scikit-learn y analizaremos su comportamiento y parámetros.</p>
<div id="charts-umap" class="cell" data-execution_count="15">
<p><img src="https://luccafrachelle.github.io/porfolio_academia/posts/umap-tsne_files/figure-html/charts-umap-output-1.png" id="charts-umap-1" class="img-fluid"> <img src="https://luccafrachelle.github.io/porfolio_academia/posts/umap-tsne_files/figure-html/charts-umap-output-2.png" id="charts-umap-2" class="img-fluid"> <img src="https://luccafrachelle.github.io/porfolio_academia/posts/umap-tsne_files/figure-html/charts-umap-output-3.png" id="charts-umap-3" class="img-fluid"> <img src="https://luccafrachelle.github.io/porfolio_academia/posts/umap-tsne_files/figure-html/charts-umap-output-4.png" id="charts-umap-4" class="img-fluid"></p>
</div>
</section>
</section>
<section id="predicción-con-umap" class="level2" data-number="7">
<h2 data-number="7" class="anchored" data-anchor-id="predicción-con-umap"><span class="header-section-number">7</span> Predicción con UMAP</h2>
<p>Una de las ventajas significativas de UMAP, en contraste con t-SNE, es su capacidad para <strong>aprender una función de mapeo</strong> desde el espacio de alta dimensión al espacio de baja dimensión. Esto significa que, una vez que un modelo UMAP ha sido entrenado con un conjunto de datos, se puede utilizar para <strong>transformar nuevos datos</strong> (es decir, datos no vistos durante el entrenamiento) en el mismo espacio de baja dimensión sin necesidad de reentrenar el modelo completo.</p>
<p>Esta capacidad permite:</p>
<ul>
<li><strong>Evaluación realista:</strong> Permite evaluar el rendimiento de los modelos de aprendizaje automático en el espacio de baja dimensión utilizando datos no vistos, lo cual es fundamental para una evaluación imparcial.</li>
<li><strong>Implementación en producción:</strong> Una vez entrenado, el modelo UMAP puede ser guardado y reutilizado para proyectar nuevos datos en tiempo real, lo que lo hace adecuado para sistemas de producción.</li>
</ul>
<section id="proyección-de-datos-no-vistos-en-umap" class="level3" data-number="7.1">
<h3 data-number="7.1" class="anchored" data-anchor-id="proyección-de-datos-no-vistos-en-umap"><span class="header-section-number">7.1</span> Proyección de Datos No Vistos en UMAP</h3>
<p>La capacidad de UMAP para proyectar nuevos datos se deriva de su proceso de entrenamiento, donde no solo se reduce la dimensionalidad de los datos de entrada, sino que también se construye un <strong>grafo de vecindad</strong> y se aprende una <strong>función de transformación implícita</strong>. Cuando se invoca el método <code>transform()</code> en un nuevo conjunto de datos <img src="https://latex.codecogs.com/png.latex?X_%7B%5Ctext%7Bnew%7D%7D">, UMAP realiza los siguientes pasos:</p>
<ol type="1">
<li><p><strong>Cálculo de Distancias a los Vecinos más Cercanos Aprendidos:</strong> Para cada nuevo punto <img src="https://latex.codecogs.com/png.latex?x_%7B%5Ctext%7Bnew%7D,i%7D%20%5Cin%20X_%7B%5Ctext%7Bnew%7D%7D">, UMAP identifica sus <img src="https://latex.codecogs.com/png.latex?k"> vecinos más cercanos dentro del conjunto de datos de entrenamiento original <img src="https://latex.codecogs.com/png.latex?X_%7B%5Ctext%7Btrain%7D%7D">. Se calculan las distancias <img src="https://latex.codecogs.com/png.latex?d(x_%7B%5Ctext%7Bnew%7D,i%7D,%20x_j)"> a estos vecinos.</p></li>
<li><p><strong>Inferencia de Probabilidades de Conexión:</strong> Utilizando los parámetros <img src="https://latex.codecogs.com/png.latex?%5Csigma_j"> y <img src="https://latex.codecogs.com/png.latex?%5Crho_j"> aprendidos de los vecinos del conjunto de entrenamiento, se infieren las probabilidades de conexión para el nuevo punto con sus vecinos más cercanos. Esto se asemeja al cálculo de <img src="https://latex.codecogs.com/png.latex?s_%7Bij%7D"> en la fase de construcción del grafo: <img src="https://latex.codecogs.com/png.latex?s_%7B%5Ctext%7Bnew%7D,ij%7D%20=%20%5Cexp%5Cleft(-%5Cfrac%7Bd(x_%7B%5Ctext%7Bnew%7D,i%7D,%20x_j)%20-%20%5Crho_j%7D%7B%5Csigma_j%7D%5Cright)"> donde <img src="https://latex.codecogs.com/png.latex?x_j"> son los vecinos de <img src="https://latex.codecogs.com/png.latex?x_%7B%5Ctext%7Bnew%7D,i%7D"> en <img src="https://latex.codecogs.com/png.latex?X_%7B%5Ctext%7Btrain%7D%7D">.</p></li>
<li><p><strong>Localización en el Espacio de Baja Dimensión:</strong> El nuevo punto <img src="https://latex.codecogs.com/png.latex?y_%7B%5Ctext%7Bnew%7D,i%7D"> se inserta en el espacio de baja dimensión de tal manera que sus probabilidades de conexión con los puntos de entrenamiento cercanos <img src="https://latex.codecogs.com/png.latex?y_j"> (ya incrustados) sean lo más similares posible a las probabilidades inferidas en alta dimensión. Esto se logra minimizando una función de costo similar a la utilizada en la fase de entrenamiento, pero manteniendo fijos los puntos del conjunto de entrenamiento <img src="https://latex.codecogs.com/png.latex?y_j">. <img src="https://latex.codecogs.com/png.latex?L(y_%7B%5Ctext%7Bnew%7D,i%7D)%20=%20%5Csum_%7Bj%20%5Cin%20%5Ctext%7Bvecinos%7D(x_%7B%5Ctext%7Bnew%7D,i%7D)%7D%20%5Cleft%5B%20s_%7B%5Ctext%7Bnew%7D,ij%7D%20%5Clog%5Cleft(%5Cfrac%7Bs_%7B%5Ctext%7Bnew%7D,ij%7D%7D%7Bw_%7B%5Ctext%7Bnew%7D,ij%7D%7D%5Cright)%20+%20(1%20-%20s_%7B%5Ctext%7Bnew%7D,ij%7D)%20%5Clog%5Cleft(%5Cfrac%7B1%20-%20s_%7B%5Ctext%7Bnew%7D,ij%7D%7D%7B1%20-%20w_%7B%5Ctext%7Bnew%7D,ij%7D%7D%5Cright)%20%5Cright%5D"> donde <img src="https://latex.codecogs.com/png.latex?w_%7B%5Ctext%7Bnew%7D,ij%7D"> es la probabilidad de conexión en baja dimensión entre <img src="https://latex.codecogs.com/png.latex?y_%7B%5Ctext%7Bnew%7D,i%7D"> y <img src="https://latex.codecogs.com/png.latex?y_j">, calculada con el kernel de Cauchy: <img src="https://latex.codecogs.com/png.latex?w_%7B%5Ctext%7Bnew%7D,ij%7D%20=%20%5Cfrac%7B1%7D%7B1%20+%20a(d(y_%7B%5Ctext%7Bnew%7D,i%7D,%20y_j))%5E%20%7B2b%7D%7D"></p></li>
</ol>
<p>Este proceso es computacionalmente eficiente porque solo se optimiza la posición de los nuevos puntos, sin alterar el embedding ya existente del conjunto de entrenamiento. Esto permite una proyección consistente y escalable de datos no vistos.</p>
<p><a href="umap-tsne_files/figure-html/umap-prediction-output-1.png" class="lightbox" data-gallery="quarto-lightbox-gallery-15"><img src="https://luccafrachelle.github.io/porfolio_academia/posts/umap-tsne_files/figure-html/umap-prediction-output-1.png" id="umap-prediction" class="img-fluid"></a></p>
</section>
<section id="predicción-de-dígitos-con-árboles-de-decisión" class="level3" data-number="7.2">
<h3 data-number="7.2" class="anchored" data-anchor-id="predicción-de-dígitos-con-árboles-de-decisión"><span class="header-section-number">7.2</span> Predicción de Dígitos con Árboles de Decisión</h3>
<p>Para complementar el análisis de reducción de dimensionalidad, es útil demostrar cómo los datos proyectados o el dataset original pueden ser utilizados en tareas de clasificación.</p>
<section id="resultados" class="level4" data-number="7.2.1">
<h4 data-number="7.2.1" class="anchored" data-anchor-id="resultados"><span class="header-section-number">7.2.1</span> Resultados</h4>
<div id="cell-decision-tree-prediction-umap-results" class="cell" data-execution_count="17">
<div class="cell-output cell-output-display">
<div>
<figure class="figure">
<p><a href="umap-tsne_files/figure-html/decision-tree-prediction-umap-results-output-1.png" class="lightbox" data-gallery="quarto-lightbox-gallery-16"><img src="https://luccafrachelle.github.io/porfolio_academia/posts/umap-tsne_files/figure-html/decision-tree-prediction-umap-results-output-1.png" id="decision-tree-prediction-umap-results" class="img-fluid figure-img"></a></p>
</figure>
</div>
</div>
</div>
<p>Se obverva que el modelo con UMAP tiene un mejor rendimiento que el modelo simple. Esto se debe a que UMAP al ser un algoritmo de reducción de dimensionalidad no lineal, es capaz de capturar las relaciones no lineales entre los datos, lo que es útil para la clasificación.</p>


</section>
</section>
</section>

<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Volver arriba</a> ]]></description>
  <category>Dimensionality reduction</category>
  <category>Geometry</category>
  <guid>https://luccafrachelle.github.io/porfolio_academia/posts/umap-tsne.html</guid>
  <pubDate>Mon, 01 Dec 2025 00:00:00 GMT</pubDate>
</item>
<item>
  <title>GANs desde la teoría de juegos</title>
  <dc:creator>Lucca Frachelle</dc:creator>
  <link>https://luccafrachelle.github.io/porfolio_academia/posts/gans-teoria-juegos.html</link>
  <description><![CDATA[ 





<div class="project-downloads">
<p><a href="../assets/pdfs/gans-teoria-juegos.pdf" class="text-link">Leer en PDF</a></p>
</div>
<section id="introducción" class="level2" data-number="1">
<h2 data-number="1" class="anchored" data-anchor-id="introducción"><span class="header-section-number">1</span> Introducción</h2>
<p>Las Redes Generativas Antagónicas (GANs) representan una clase de modelos generativos propuesta originalmente por <span class="citation" data-cites="goodfellow2014generative">Goodfellow et&nbsp;al. (2014)</span>. En años recientes, estos modelos han recibido una amplia atención debido a su potencial para modelar datos del mundo real complejos y de alta dimensión.</p>
<p>A diferencia de los enfoques tradicionales, las GANs no minimizan un único criterio de entrenamiento. Su objetivo es estimar la distribución de probabilidad de los datos reales mediante una técnica de aprendizaje adversarial que involucra dos redes neuronales entrenadas simultáneamente. Esta capacidad permite a las GANs generar nuevas muestras infinitamente realistas desde un espacio latente sin depender de suposiciones fuertes sobre la distribución de los datos <span class="citation" data-cites="hong2019generative">Hong et&nbsp;al. (2019)</span>.</p>
<section id="arquitectura-básica-y-dinámica-del-juego" class="level3" data-number="1.1">
<h3 data-number="1.1" class="anchored" data-anchor-id="arquitectura-básica-y-dinámica-del-juego"><span class="header-section-number">1.1</span> Arquitectura Básica y Dinámica del Juego</h3>
<p>Una GAN se compone usualmente de dos agentes principales:</p>
<ol type="1">
<li><strong>El Generador (<img src="https://latex.codecogs.com/png.latex?G">):</strong> Intenta crear muestras realistas que no puedan ser distinguidas de los datos genuinos.</li>
<li><strong>El Discriminador (<img src="https://latex.codecogs.com/png.latex?D">):</strong> Intenta diferenciar entre muestras de datos reales y muestras falsas fabricadas por el generador.</li>
</ol>
<p>La idea central se inspira en un <strong>juego minimax de suma cero de dos jugadores</strong>. En este juego, la utilidad total es constante (cero); la ganancia o pérdida de un jugador se equilibra exactamente con la pérdida o ganancia del otro. El diseño de las GANs busca alcanzar un <strong>Equilibrio de Nash</strong>, un estado donde ningún jugador puede incrementar su ganancia cambiando unilateralmente su estrategia <span class="citation" data-cites="wang2017generative">Wang et&nbsp;al. (2017)</span>.</p>
<div id="fig-arq-gan" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-arq-gan-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<a href="img/gans/arquitectura_gan.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1" title="Figura&nbsp;1: Arquitectura básica de una GAN y el flujo de entrenamiento adversarial."><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/gans/arquitectura_gan.png" class="img-fluid figure-img" style="width:80.0%"></a>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-arq-gan-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figura&nbsp;1: Arquitectura básica de una GAN y el flujo de entrenamiento adversarial.
</figcaption>
</figure>
</div>
</section>
<section id="analogía-el-juego-maestro-estudiante" class="level3" data-number="1.2">
<h3 data-number="1.2" class="anchored" data-anchor-id="analogía-el-juego-maestro-estudiante"><span class="header-section-number">1.2</span> Analogía: El Juego Maestro-Estudiante</h3>
<p>Para comprender mejor este concepto abstracto, podemos visualizar este juego de dos jugadores como una interacción entre un estudiante de arte y su maestro:</p>
<ul>
<li><strong>El Estudiante (Generador):</strong> Juega el rol de un falsificador o aprendiz que intenta pintar cuadros. Su objetivo es crear obras tan indistinguibles que pasen por auténticas.</li>
<li><strong>El Maestro (Discriminador):</strong> Actúa como un crítico de arte o experto. Su objetivo es enseñar al estudiante discriminando correctamente entre pinturas reales (obras maestras genuinas) y las falsificaciones del alumno.</li>
</ul>
<p>Para tener éxito en este juego, el estudiante debe aprender a generar pinturas indistinguibles de las reales, y el maestro debe refinar su capacidad de detectar fraudes.</p>
<p>En términos más formales, las “estrategias” de los jugadores corresponden a los pesos de las redes neuronales:</p>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?S_1">: Elegir los mejores pesos <img src="https://latex.codecogs.com/png.latex?%5Ctheta_D"> para el Discriminador.</li>
<li><img src="https://latex.codecogs.com/png.latex?S_2">: Elegir los mejores pesos <img src="https://latex.codecogs.com/png.latex?%5Ctheta_G"> para el Generador.</li>
</ul>
<p>Ambos jugadores intentan maximizar sus respectivos pagos (<img src="https://latex.codecogs.com/png.latex?u_1"> y <img src="https://latex.codecogs.com/png.latex?u_2">) para alcanzar el punto de equilibrio.</p>
<div id="fig-game-theory" class="quarto-float quarto-figure quarto-figure-center anchored" data-align="center">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-game-theory-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<a href="img/gans/juego_gan.png" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="Figura&nbsp;2: Juego Estudiante - Maestro"><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/gans/juego_gan.png" class="img-fluid figure-img" style="width:70.0%" data-align="center"></a>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-game-theory-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figura&nbsp;2: Juego Estudiante - Maestro
</figcaption>
</figure>
</div>
</section>
<section id="desafíos-y-motivación-del-enfoque-teórico" class="level3" data-number="1.3">
<h3 data-number="1.3" class="anchored" data-anchor-id="desafíos-y-motivación-del-enfoque-teórico"><span class="header-section-number">1.3</span> Desafíos y Motivación del Enfoque Teórico</h3>
<p>A pesar de su éxito significativo en aplicaciones que van desde la síntesis de imágenes hasta la ciberseguridad <span class="citation" data-cites="alqahtani2021applications">Alqahtani et&nbsp;al. (2021)</span>, la aplicación de GANs a ciertos problemas del mundo real se ha visto obstaculizada por desafíos fundamentales.</p>
<p>El problema más significativo es que las GANs son notoriamente difíciles de entrenar y sufren de problemas de inestabilidad, tales como:</p>
<ul>
<li><strong>Colapso de Modo (Mode Collapse):</strong> Donde el generador produce una variedad muy limitada de muestras.</li>
<li><strong>No convergencia:</strong> El juego entra en ciclos y no se estabiliza.</li>
<li><strong>Desvanecimiento de gradientes:</strong> El discriminador se vuelve demasiado bueno demasiado rápido, dejando de proveer información útil al generador.</li>
</ul>
<p>Teóricamente, una GAN necesita converger a un Equilibrio de Nash durante el proceso de entrenamiento, pero tal convergencia ha demostrado ser un desafío algorítmico mayor <span class="citation" data-cites="wang2019generative">Wang et&nbsp;al. (2019)</span>. Dado una GANs se puede ver como un juego de suma cero, explotar técnicas de teoria de juegos es esencial para mejorar la teoría subyacente y entender estos modelos.</p>
<p><em>Esta sección esta basada en: <span class="citation" data-cites="moghadam2023games">Mohebbi Moghaddam et&nbsp;al. (2023)</span>, <span class="citation" data-cites="goodfellow2014generative">Goodfellow et&nbsp;al. (2014)</span> y <span class="citation" data-cites="hong2019generative">Hong et&nbsp;al. (2019)</span>.</em></p>
</section>
</section>
<section id="juegos-de-suma-cero-y-equilibrio-de-nash" class="level2" data-number="2">
<h2 data-number="2" class="anchored" data-anchor-id="juegos-de-suma-cero-y-equilibrio-de-nash"><span class="header-section-number">2</span> Juegos de Suma Cero y Equilibrio de Nash</h2>
<section id="definición-general-de-un-juego" class="level3" data-number="2.1">
<h3 data-number="2.1" class="anchored" data-anchor-id="definición-general-de-un-juego"><span class="header-section-number">2.1</span> Definición General de un Juego</h3>
<p>Un juego en forma normal o estratégica se define formalmente como una tupla <img src="https://latex.codecogs.com/png.latex?%5CGamma%20=%20%5Clangle%20N,%20(S_i)_%7Bi%20%5Cin%20N%7D,%20(u_i)_%7Bi%20%5Cin%20N%7D%20%5Crangle">, donde:</p>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?N%20=%20%5C%7B1,%20%5Cdots,%20n%5C%7D"> es el conjunto finito de jugadores.</li>
<li><img src="https://latex.codecogs.com/png.latex?S_i"> representa el espacio de estrategias disponibles para el jugador <img src="https://latex.codecogs.com/png.latex?i">.</li>
<li><img src="https://latex.codecogs.com/png.latex?u_i:%20S%20%5Cto%20%5Cmathbb%7BR%7D"> es la función de utilidad (o <em>payoff</em>) del jugador <img src="https://latex.codecogs.com/png.latex?i">, que asigna un valor escalar a cada perfil de estrategia resultante <span class="citation" data-cites="nisan2007algorithmic">Nisan et&nbsp;al. (2007)</span>.</li>
</ul>
<p>Para el propósito de este trabajo, nos centraremos en juegos donde los espacios de estrategia pueden ser continuos (como en redes neuronales), pero fundamentaremos la teoría en juegos matriciales finitos.</p>
</section>
<section id="definiciones-y-configuración-del-juego-matricial" class="level3" data-number="2.2">
<h3 data-number="2.2" class="anchored" data-anchor-id="definiciones-y-configuración-del-juego-matricial"><span class="header-section-number">2.2</span> Definiciones y Configuración del Juego Matricial</h3>
<p>Para establecer la notación que utilizaremos en las demostraciones (ver Apéndices), consideramos un juego de dos jugadores, I y II.</p>
<section id="matrices-de-pago" class="level4" data-number="2.2.1">
<h4 data-number="2.2.1" class="anchored" data-anchor-id="matrices-de-pago"><span class="header-section-number">2.2.1</span> Matrices de Pago</h4>
<p>Definimos las interacciones mediante matrices que contienen las recompensas para cada combinación de acciones puras:</p>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?A%20%5Cin%20%5Cmathbb%7BR%7D%5E%7Bm%20%5Ctimes%20n%7D">: Matriz de pagos para el Jugador I.</li>
<li><img src="https://latex.codecogs.com/png.latex?B%20%5Cin%20%5Cmathbb%7BR%7D%5E%7Bm%20%5Ctimes%20n%7D">: Matriz de pagos para el Jugador II.</li>
</ul>
</section>
<section id="estrategias-puras-y-mixtas" class="level4" data-number="2.2.2">
<h4 data-number="2.2.2" class="anchored" data-anchor-id="estrategias-puras-y-mixtas"><span class="header-section-number">2.2.2</span> Estrategias: Puras y Mixtas</h4>
<p>Distinguimos entre dos tipos de decisiones:</p>
<ol type="1">
<li><strong>Estrategias Puras:</strong> La elección determinista de una acción específica del conjunto finito disponible.</li>
<li><strong>Estrategias Mixtas:</strong> Una distribución de probabilidad sobre las acciones puras. Esto permite modelar la incertidumbre o la aleatorización en la toma de decisiones.
<ul>
<li>Para el Jugador I: <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D%20=%20(x_1,%20%5Cldots,%20x_m)%5ET%20%5Cin%20%5CDelta_m">.</li>
<li>Para el Jugador II: <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7By%7D%20=%20(y_1,%20%5Cldots,%20y_n)%5ET%20%5Cin%20%5CDelta_n">.</li>
</ul></li>
</ol>
<p>Donde <img src="https://latex.codecogs.com/png.latex?%5CDelta_k"> denota el símplex estándar de dimensión <img src="https://latex.codecogs.com/png.latex?k">, definido como el conjunto de vectores no negativos cuya suma es 1: <img src="https://latex.codecogs.com/png.latex?%0A%5CDelta_k%20=%20%5C%7B%20z%20%5Cin%20%5Cmathbb%7BR%7D%5Ek%20%5Cmid%20z_i%20%5Cge%200,%20%5Csum%20z_i%20=%201%20%5C%7D%0A"></p>
</section>
<section id="espacio-de-estrategias-conjuntas-k" class="level4" data-number="2.2.3">
<h4 data-number="2.2.3" class="anchored" data-anchor-id="espacio-de-estrategias-conjuntas-k"><span class="header-section-number">2.2.3</span> Espacio de Estrategias Conjuntas (<img src="https://latex.codecogs.com/png.latex?K">)</h4>
<p>El estado del juego está determinado por el par <img src="https://latex.codecogs.com/png.latex?(%5Cmathbf%7Bx%7D,%20%5Cmathbf%7By%7D)">. El conjunto de todos los perfiles posibles es el producto cartesiano: <img src="https://latex.codecogs.com/png.latex?%0AK%20=%20%5CDelta_m%20%5Ctimes%20%5CDelta_n%0A"></p>
<p>Es importante notar que, dado que cada símplex <img src="https://latex.codecogs.com/png.latex?%5CDelta"> es un conjunto cerrado y acotado (compacto) y convexo, su producto <img src="https://latex.codecogs.com/png.latex?K"> hereda estas propiedades. <img src="https://latex.codecogs.com/png.latex?K"> es un conjunto <strong>cerrado, convexo y compacto</strong> en el espacio euclidiano(esta propiedad es clave para la demostración de la existencia de Equilibrios de Nash en el Apéndice A).</p>
</section>
<section id="pago-esperado" class="level4" data-number="2.2.4">
<h4 data-number="2.2.4" class="anchored" data-anchor-id="pago-esperado"><span class="header-section-number">2.2.4</span> Pago Esperado</h4>
<p>La utilidad en estrategias mixtas no es un valor determinista, sino una esperanza. Dada una estrategia conjunta <img src="https://latex.codecogs.com/png.latex?(%5Cmathbf%7Bx%7D,%20%5Cmathbf%7By%7D)">, los pagos esperados se calculan mediante formas bilineales:</p>
<ul>
<li><strong>Pago a I:</strong> <img src="https://latex.codecogs.com/png.latex?u_I(%5Cmathbf%7Bx%7D,%20%5Cmathbf%7By%7D)%20=%20%5Cmathbf%7Bx%7D%5ET%20A%5Cmathbf%7By%7D%20=%20%5Csum_%7Bi,j%7D%20x_i%20a_%7Bij%7D%20y_j">.</li>
<li><strong>Pago a II:</strong> <img src="https://latex.codecogs.com/png.latex?u_%7BII%7D(%5Cmathbf%7Bx%7D,%20%5Cmathbf%7By%7D)%20=%20%5Cmathbf%7Bx%7D%5ET%20B%5Cmathbf%7By%7D%20=%20%5Csum_%7Bi,j%7D%20x_i%20b_%7Bij%7D%20y_j">.</li>
</ul>
<p>Esto representa el promedio ponderado de los pagos, donde cada resultado <img src="https://latex.codecogs.com/png.latex?a_%7Bij%7D"> se multiplica por la probabilidad conjunta de que ocurra.</p>
</section>
</section>
<section id="juegos-de-suma-cero" class="level3" data-number="2.3">
<h3 data-number="2.3" class="anchored" data-anchor-id="juegos-de-suma-cero"><span class="header-section-number">2.3</span> Juegos de Suma Cero</h3>
<p>Un caso especial y fundamental para las GANs son los juegos estrictamente de suma cero. Aquí, los intereses son diametralmente opuestos: lo que gana uno es exactamente lo que pierde el otro. <img src="https://latex.codecogs.com/png.latex?%0A%5Cforall%20(%5Cmathbf%7Bx%7D,%20%5Cmathbf%7By%7D)%20%5Cin%20K:%20%5Cquad%20u_I(%5Cmathbf%7Bx%7D,%20%5Cmathbf%7By%7D)%20+%20u_%7BII%7D(%5Cmathbf%7Bx%7D,%20%5Cmathbf%7By%7D)%20=%200%0A"></p>
<p>Esto implica que <img src="https://latex.codecogs.com/png.latex?B%20=%20-A">. Por tanto, podemos describir el juego completo usando solo la matriz <img src="https://latex.codecogs.com/png.latex?A"> y una única <strong>Función de Valor</strong> <img src="https://latex.codecogs.com/png.latex?V">:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AV(%5Cmathbf%7Bx%7D,%20%5Cmathbf%7By%7D)%20=%20%5Cmathbf%7Bx%7D%5ET%20A%20%5Cmathbf%7By%7D%0A"></p>
<p>Los objetivos de optimización se vuelven antagónicos sobre esta misma función:</p>
<ol type="1">
<li><p><strong>Jugador I (Maximizador):</strong> Busca <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D"> para maximizar <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D%5ET%20A%20%5Cmathbf%7By%7D">.</p></li>
<li><p><strong>Jugador II (Minimizador):</strong> Busca <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7By%7D"> para maximizar <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D%5ET%20(-A)%20%5Cmathbf%7By%7D">, lo que equivale a minimizar <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D%5ET%20A%20%5Cmathbf%7By%7D">.</p></li>
</ol>
<p>El valor del juego en equilibrio se formaliza como el problema minimax: <img src="https://latex.codecogs.com/png.latex?%0A%5Cmin_%7B%5Cmathbf%7By%7D%20%5Cin%20%5CDelta_n%7D%20%5Cmax_%7B%5Cmathbf%7Bx%7D%20%5Cin%20%5CDelta_m%7D%20%5Cmathbf%7Bx%7D%5ET%20A%20%5Cmathbf%7By%7D%0A"></p>
</section>
<section id="el-equilibrio-de-nash" class="level3" data-number="2.4">
<h3 data-number="2.4" class="anchored" data-anchor-id="el-equilibrio-de-nash"><span class="header-section-number">2.4</span> El Equilibrio de Nash</h3>
<p>Un perfil de estrategias <img src="https://latex.codecogs.com/png.latex?(%5Cmathbf%7Bx%7D%5E*,%20%5Cmathbf%7By%7D%5E*)"> constituye un Equilibrio de Nash (NE) si ningún jugador puede mejorar su utilidad desviándose unilateralmente.</p>
<p>Formalmente, <img src="https://latex.codecogs.com/png.latex?(%5Cmathbf%7Bx%7D%5E*,%20%5Cmathbf%7By%7D%5E*)"> es un NE si satisface simultáneamente:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmathbf%7Bx%7D%5E%7B*T%7D%20A%20%5Cmathbf%7By%7D%5E*%20%5Cgeq%20%5Cmathbf%7Bx%7D%5ET%20A%20%5Cmathbf%7By%7D%5E*%20%5Cquad%20%5Cforall%20%5Cmathbf%7Bx%7D%20%5Cin%20%5CDelta_m%0A"></p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmathbf%7Bx%7D%5E%7B*T%7D%20A%20%5Cmathbf%7By%7D%5E*%20%5Cleq%20%5Cmathbf%7Bx%7D%5E%7B*T%7D%20A%20%5Cmathbf%7By%7D%20%5Cquad%20%5Cforall%20%5Cmathbf%7By%7D%20%5Cin%20%5CDelta_n%0A"></p>
<p><em>(Nota: La segunda desigualdad es <img src="https://latex.codecogs.com/png.latex?%5Cleq"> porque el Jugador II minimiza <img src="https://latex.codecogs.com/png.latex?V">).</em></p>
<p>La demostración de existencia de este equilibrio se detalla en el <strong>Apéndice A</strong>. Sin embargo, encontrarlo en espacios no convexos de alta dimensión (como en aprendizaje profundo) es un desafío algorítmico mayor <span class="citation" data-cites="daskalakis2018training">Daskalakis et&nbsp;al. (2018)</span>.</p>
<section id="ejemplo-1-dilema-del-prisionero-suma-no-cero" class="level4" data-number="2.4.1">
<h4 data-number="2.4.1" class="anchored" data-anchor-id="ejemplo-1-dilema-del-prisionero-suma-no-cero"><span class="header-section-number">2.4.1</span> Ejemplo 1: Dilema del Prisionero (Suma NO Cero)</h4>
<p>Para ilustrar la diferencia, consideremos un juego donde <img src="https://latex.codecogs.com/png.latex?B%20%5Cneq%20-A">. Dos prisioneros deciden si cooperar (<img src="https://latex.codecogs.com/png.latex?C">) o traicionar (<img src="https://latex.codecogs.com/png.latex?D">). Matriz de pagos <img src="https://latex.codecogs.com/png.latex?(A,%20B)">:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Barray%7D%7Bc%7Ccc%7D%0A&amp;%20%5Ctext%7BP2:%20C%7D%20&amp;%20%5Ctext%7BP2:%20D%7D%20%5C%5C%0A%5Chline%0A%5Ctext%7BP1:%20C%7D%20&amp;%20(-1,%20-1)%20&amp;%20(-3,%200)%20%5C%5C%0A%5Ctext%7BP1:%20D%7D%20&amp;%20(0,%20-3)%20&amp;%20(-2,%20-2)%0A%5Cend%7Barray%7D%0A"></p>
<p>Aquí, la estrategia pura <strong>(D, D)</strong> es el único Equilibrio de Nash, demostrando que el equilibrio individual no siempre lleva al óptimo global cooperativo (C, C).</p>
</section>
<section id="ejemplo-2-matching-pennies-suma-cero" class="level4" data-number="2.4.2">
<h4 data-number="2.4.2" class="anchored" data-anchor-id="ejemplo-2-matching-pennies-suma-cero"><span class="header-section-number">2.4.2</span> Ejemplo 2: Matching Pennies (Suma Cero)</h4>
<p>Este ejemplo es análogo a la dinámica de las GANs. P1 gana si coinciden, P2 gana si difieren. <img src="https://latex.codecogs.com/png.latex?A"> para el Jugador 1 (<img src="https://latex.codecogs.com/png.latex?B=-A">):</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AA%20=%20%5Cbegin%7Barray%7D%7Bc%7Ccc%7D%0A&amp;%20%5Ctext%7BP2:%20Cara%7D%20&amp;%20%5Ctext%7BP2:%20Cruz%7D%20%5C%5C%0A%5Chline%0A%5Ctext%7BP1:%20Cara%7D%20&amp;%20+1%20&amp;%20-1%20%5C%5C%0A%5Ctext%7BP1:%20Cruz%7D%20&amp;%20-1%20&amp;%20+1%0A%5Cend%7Barray%7D%0A"></p>
<p>Aquí no existe equilibrio en estrategias puras. Si P1 juega Cara, P2 cambia a Cruz, etc. El equilibrio solo existe en el interior del símplex (estrategias mixtas): <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D%5E*%20=%20(0.5,%200.5),%20%5Cmathbf%7By%7D%5E*%20=%20(0.5,%200.5)">.</p>
</section>
</section>
<section id="el-teorema-minimax-y-puntos-silla" class="level3" data-number="2.5">
<h3 data-number="2.5" class="anchored" data-anchor-id="el-teorema-minimax-y-puntos-silla"><span class="header-section-number">2.5</span> El Teorema Minimax y Puntos Silla</h3>
<p>En juegos de suma cero, el Equilibrio de Nash tiene una conexión profunda con el <strong>Teorema Minimax de von Neumann</strong> (ver <strong>Apéndice B</strong> para la demostración). Este teorema establece que el valor que el maximizador puede asegurar es igual al valor que el minimizador puede forzar.</p>
<p>Gracias a las propiedades de convexidad y compacidad de los espacios de estrategias mixtas (<img src="https://latex.codecogs.com/png.latex?%5CDelta_m,%20%5CDelta_n">), el orden de los operadores es intercambiable:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmax_%7B%5Cmathbf%7Bx%7D%20%5Cin%20%5CDelta_m%7D%20%5Cmin_%7B%5Cmathbf%7By%7D%20%5Cin%20%5CDelta_n%7D%20%5Cmathbf%7Bx%7D%5ET%20A%20%5Cmathbf%7By%7D%20=%20%5Cmin_%7B%5Cmathbf%7By%7D%20%5Cin%20%5CDelta_n%7D%20%5Cmax_%7B%5Cmathbf%7Bx%7D%20%5Cin%20%5CDelta_m%7D%20%5Cmathbf%7Bx%7D%5ET%20A%20%5Cmathbf%7By%7D%20=%20V%5E*%0A"></p>
<section id="el-punto-silla-como-equilibrio" class="level4" data-number="2.5.1">
<h4 data-number="2.5.1" class="anchored" data-anchor-id="el-punto-silla-como-equilibrio"><span class="header-section-number">2.5.1</span> El Punto Silla como Equilibrio</h4>
<p>Geométricamente, extendiendo esto a funciones continuas generales <img src="https://latex.codecogs.com/png.latex?V(%5Ctheta_G,%20%5Ctheta_D)"> (como en las GANs), un Equilibrio de Nash equivale a encontrar un <strong>punto silla</strong>.</p>
<p>Un perfil <img src="https://latex.codecogs.com/png.latex?(%5Ctheta_G%5E*,%20%5Ctheta_D%5E*)"> es un punto silla si es un máximo local para <img src="https://latex.codecogs.com/png.latex?G"> y un mínimo local para <img src="https://latex.codecogs.com/png.latex?D">: <img src="https://latex.codecogs.com/png.latex?%0AV(%5Ctheta_G,%20%5Ctheta_D%5E*)%20%5Cleq%20V(%5Ctheta_G%5E*,%20%5Ctheta_D%5E*)%20%5Cleq%20V(%5Ctheta_G%5E*,%20%5Ctheta_D)%0A"></p>
<p><strong>Implicación para GANs:</strong> El entrenamiento de GANs busca este punto donde la distribución generada <img src="https://latex.codecogs.com/png.latex?p_g"> coincide con <img src="https://latex.codecogs.com/png.latex?p_%7Bdata%7D">. Sin embargo, los métodos de gradiente estándar (SGD) suelen fallar en converger a puntos silla en paisajes no convexos, presentando oscilaciones o ciclos <span class="citation" data-cites="mescheder2017numerics">Mescheder et&nbsp;al. (2017)</span>.</p>
<p><em>Esta sección esta basada en: <span class="citation" data-cites="KarlinPeres2017GameTheoryAlive">Karlin y Peres (2017)</span> y <span class="citation" data-cites="nisan2007algorithmic">Nisan et&nbsp;al. (2007)</span></em></p>
</section>
</section>
</section>
<section id="gans" class="level2" data-number="3">
<h2 data-number="3" class="anchored" data-anchor-id="gans"><span class="header-section-number">3</span> GANs</h2>
<p>En este capítulo, formalizamos las Redes Generativas Antagónicas como un problema de optimización en un espacio de funciones de densidad de probabilidad, analizando las propiedades de convergencia teórica del juego minimax inducido.</p>
<section id="definición-formal-del-modelo" class="level3" data-number="3.1">
<h3 data-number="3.1" class="anchored" data-anchor-id="definición-formal-del-modelo"><span class="header-section-number">3.1</span> Definición Formal del Modelo</h3>
<p>Una Red Generativa Antagónica (GAN) puede definirse formalmente como una tupla <img src="https://latex.codecogs.com/png.latex?%5COmega%20=%20%5Clangle%20p_%7Bdata%7D,%20(G,%20p_z),%20D,%20%5Cphi%20%5Crangle">, donde:</p>
<ol type="1">
<li><strong>Distribución de Datos (<img src="https://latex.codecogs.com/png.latex?p_%7Bdata%7D">):</strong> Es la densidad de probabilidad desconocida definida sobre el espacio de datos <img src="https://latex.codecogs.com/png.latex?x%20%5Cin%20%5Cmathbb%7BR%7D%5Ed"> que deseamos modelar.</li>
<li><strong>Generador (<img src="https://latex.codecogs.com/png.latex?G,%20p_z">):</strong>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?z%20%5Cin%20%5Cmathbb%7BR%7D%5Ek"> es un vector aleatorio latente muestreado de una distribución a priori fija <img src="https://latex.codecogs.com/png.latex?p_z"> (e.g., <img src="https://latex.codecogs.com/png.latex?%5Cmathcal%7BN%7D(0,%20I)">).</li>
<li><img src="https://latex.codecogs.com/png.latex?G:%20%5Cmathbb%7BR%7D%5Ek%20%5Ctimes%20%5CTheta_G%20%5Cto%20%5Cmathbb%7BR%7D%5Ed"> es una función diferenciable (red neuronal) parametrizada por <img src="https://latex.codecogs.com/png.latex?%5Ctheta_G">.</li>
<li><img src="https://latex.codecogs.com/png.latex?G"> induce una distribución de probabilidad implícita <img src="https://latex.codecogs.com/png.latex?p_g"> en el espacio de datos (formalmente, <img src="https://latex.codecogs.com/png.latex?p_g"> es la medida <em>pushforward</em> de <img src="https://latex.codecogs.com/png.latex?p_z"> bajo <img src="https://latex.codecogs.com/png.latex?G">, denotada como <img src="https://latex.codecogs.com/png.latex?G_%7B%5C#%7Dp_z">).</li>
</ul></li>
<li><strong>Discriminador (<img src="https://latex.codecogs.com/png.latex?D">):</strong>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?D:%20%5Cmathbb%7BR%7D%5Ed%20%5Ctimes%20%5CTheta_D%20%5Cto%20%5B0,%201%5D"> es una función diferenciable parametrizada por <img src="https://latex.codecogs.com/png.latex?%5Ctheta_D">.</li>
<li><img src="https://latex.codecogs.com/png.latex?D(x)"> representa la probabilidad estimada de que <img src="https://latex.codecogs.com/png.latex?x"> provenga de <img src="https://latex.codecogs.com/png.latex?p_%7Bdata%7D"> en lugar de <img src="https://latex.codecogs.com/png.latex?p_g">.</li>
</ul></li>
<li><strong>Función de Medición (<img src="https://latex.codecogs.com/png.latex?%5Cphi">):</strong> Una función convexa <img src="https://latex.codecogs.com/png.latex?%5Cphi:%20%5B0,%201%5D%20%5Cto%20%5Cmathbb%7BR%7D"> que define la métrica de divergencia.</li>
</ol>
</section>
<section id="el-juego-inducido-de-suma-cero" class="level3" data-number="3.2">
<h3 data-number="3.2" class="anchored" data-anchor-id="el-juego-inducido-de-suma-cero"><span class="header-section-number">3.2</span> El Juego Inducido de Suma Cero</h3>
<p>El entrenamiento de una GAN se modela como un juego de suma cero donde las estrategias puras son los vectores de parámetros <img src="https://latex.codecogs.com/png.latex?%5Ctheta_G%20%5Cin%20%5CTheta_G"> y <img src="https://latex.codecogs.com/png.latex?%5Ctheta_D%20%5Cin%20%5CTheta_D">.</p>
<p>La función de utilidad (valor) del juego, <img src="https://latex.codecogs.com/png.latex?V(G,%20D)">, se define como la esperanza de la evaluación correcta del discriminador sobre ambas distribuciones:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AV(G,%20D)%20=%20%5Cmathbb%7BE%7D_%7Bx%20%5Csim%20p_%7Bdata%7D%7D%20%5B%5Cphi(D(x))%5D%20+%20%5Cmathbb%7BE%7D_%7Bz%20%5Csim%20p_z%7D%20%5B%5Cphi(1%20-%20D(G(z)))%5D%0A"></p>
<p>En la formulación original de <span class="citation" data-cites="goodfellow2014generative">Goodfellow et&nbsp;al. (2014)</span> (Vanilla GAN), utilizamos <img src="https://latex.codecogs.com/png.latex?%5Cphi(t)%20=%20%5Cln(t)">, lo que resulta en la función de pérdida de entropía cruzada binaria:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AV(G,%20D)%20=%20%5Cmathbb%7BE%7D_%7Bx%20%5Csim%20p_%7Bdata%7D%7D%20%5B%5Cln%20D(x)%5D%20+%20%5Cmathbb%7BE%7D_%7Bz%20%5Csim%20p_z%7D%20%5B%5Cln(1%20-%20D(G(z)))%5D%0A"></p>
<p><strong>Nota:</strong> Variaciones como la Wasserstein GAN (WGAN) modifican <img src="https://latex.codecogs.com/png.latex?%5Cphi"> (e.g., <img src="https://latex.codecogs.com/png.latex?%5Cphi(t)%20=%20t">) y eliminan la restricción sigmoide en <img src="https://latex.codecogs.com/png.latex?D">, cambiando la naturaleza de la convergencia, aunque aquí nos centraremos en la formulación logarítmica clásica.</p>
<p>El objetivo global es encontrar el equilibrio minimax (Nash) dado por:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmin_%7BG%7D%20%5Cmax_%7BD%7D%20V(G,%20D)%0A"></p>
</section>
<section id="análisis-del-óptimo" class="level3" data-number="3.3">
<h3 data-number="3.3" class="anchored" data-anchor-id="análisis-del-óptimo"><span class="header-section-number">3.3</span> Análisis del Óptimo</h3>
<p>Para analizar la convergencia teórica, asumimos temporalmente que <img src="https://latex.codecogs.com/png.latex?G"> y <img src="https://latex.codecogs.com/png.latex?D"> tienen capacidad infinita (espacio de funciones no paramétrico) y analizamos el óptimo en el espacio de funciones de densidad.</p>
<section id="el-discriminador-óptimo" class="level4" data-number="3.3.1">
<h4 data-number="3.3.1" class="anchored" data-anchor-id="el-discriminador-óptimo"><span class="header-section-number">3.3.1</span> El Discriminador Óptimo</h4>
<p>Consideremos el generador <img src="https://latex.codecogs.com/png.latex?G"> fijo. Queremos encontrar la función <img src="https://latex.codecogs.com/png.latex?D%5E*"> que maximiza <img src="https://latex.codecogs.com/png.latex?V(G,%20D)">. Reescribimos la función de valor en términos de integrales sobre el espacio de datos <img src="https://latex.codecogs.com/png.latex?%5Cmathcal%7BX%7D">:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AV(G,%20D)%20=%20%5Cint_%7B%5Cmathcal%7BX%7D%7D%20p_%7Bdata%7D(x)%20%5Cln(D(x))%20%5C,%20dx%20+%20%5Cint_%7B%5Cmathcal%7BZ%7D%7D%20p_z(z)%20%5Cln(1%20-%20D(G(z)))%20%5C,%20dz%0A"></p>
<p>Haciendo un cambio de variable para la segunda integral, expresamos todo sobre el soporte de <img src="https://latex.codecogs.com/png.latex?x">. Sea <img src="https://latex.codecogs.com/png.latex?p_g(x)"> la densidad inducida por <img src="https://latex.codecogs.com/png.latex?G(z)">:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AV(G,%20D)%20=%20%5Cint_%7B%5Cmathcal%7BX%7D%7D%20%5Cleft(%20p_%7Bdata%7D(x)%20%5Cln(D(x))%20+%20p_g(x)%20%5Cln(1%20-%20D(x))%20%5Cright)%20%5C,%20dx%0A"></p>
<p><strong>Proposición 1.</strong> <em>Para un generador <img src="https://latex.codecogs.com/png.latex?G"> fijo, el discriminador óptimo <img src="https://latex.codecogs.com/png.latex?D%5E*_G(x)"> está dado por la razón:</em> <img src="https://latex.codecogs.com/png.latex?%0AD%5E*_G(x)%20=%20%5Cfrac%7Bp_%7Bdata%7D(x)%7D%7Bp_%7Bdata%7D(x)%20+%20p_g(x)%7D%0A"></p>
<p><em>Demostración.</em> El problema de maximización se puede resolver puntualmente para cada <img src="https://latex.codecogs.com/png.latex?x">. Definimos la función <img src="https://latex.codecogs.com/png.latex?J(y)"> para <img src="https://latex.codecogs.com/png.latex?y%20%5Cin%20%5B0,%201%5D"> dados escalares <img src="https://latex.codecogs.com/png.latex?(a,%20b)%20%5Cin%20%5Cmathbb%7BR%7D%5E2%20%5Csetminus%20%5C%7B(0,0)%5C%7D">: <img src="https://latex.codecogs.com/png.latex?J(y)%20=%20a%20%5Cln(y)%20+%20b%20%5Cln(1%20-%20y)"> Derivando respecto a <img src="https://latex.codecogs.com/png.latex?y"> e igualando a cero: <img src="https://latex.codecogs.com/png.latex?%0A%5Cfrac%7B%5Cpartial%20J%7D%7B%5Cpartial%20y%7D%20=%20%5Cfrac%7Ba%7D%7By%7D%20-%20%5Cfrac%7Bb%7D%7B1-y%7D%20=%200%20%5Cimplies%20a(1-y)%20=%20by%20%5Cimplies%20y%20=%20%5Cfrac%7Ba%7D%7Ba+b%7D%0A"> Sustituyendo <img src="https://latex.codecogs.com/png.latex?a%20=%20p_%7Bdata%7D(x)"> y <img src="https://latex.codecogs.com/png.latex?b%20=%20p_g(x)">, y observando que la segunda derivada es negativa (máximo), obtenemos el resultado. <img src="https://latex.codecogs.com/png.latex?%5Cblacksquare"></p>
<p>OBS: El discriminador óptimo no está definido fuera del soporte <img src="https://latex.codecogs.com/png.latex?%5Ctext%7BSupp%7D(p_%7Bdata%7D)%20%5Ccup%20%5Ctext%7BSupp%7D(p_g)">.</p>
</section>
<section id="la-función-de-costo-del-generador" class="level4" data-number="3.3.2">
<h4 data-number="3.3.2" class="anchored" data-anchor-id="la-función-de-costo-del-generador"><span class="header-section-number">3.3.2</span> La función de costo del Generador</h4>
<p>Sustituyendo el discriminador óptimo <img src="https://latex.codecogs.com/png.latex?D%5E*_G"> en la función de valor, reducimos el juego minimax a una minimización pura sobre <img src="https://latex.codecogs.com/png.latex?G">. Llamamos a esta función reformulada <img src="https://latex.codecogs.com/png.latex?C(G)">:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Baligned%7D%0AC(G)%20&amp;=%20%5Cmax_%7BD%7D%20V(G,%20D)%20=%20V(G,%20D%5E*_G)%20%5C%5C%0A&amp;=%20%5Cmathbb%7BE%7D_%7Bx%20%5Csim%20p_%7Bdata%7D%7D%20%5Cleft%5B%20%5Cln%20%5Cfrac%7Bp_%7Bdata%7D(x)%7D%7Bp_%7Bdata%7D(x)%20+%20p_g(x)%7D%20%5Cright%5D%20+%20%5Cmathbb%7BE%7D_%7Bx%20%5Csim%20p_g%7D%20%5Cleft%5B%20%5Cln%20%5Cfrac%7Bp_g(x)%7D%7Bp_%7Bdata%7D(x)%20+%20p_g(x)%7D%20%5Cright%5D%0A%5Cend%7Baligned%7D%0A"></p>
<p>Podemos relacionar esta expresión con la Divergencia de Kullback-Leibler (<img src="https://latex.codecogs.com/png.latex?KL">) y la Divergencia de Jensen-Shannon (<img src="https://latex.codecogs.com/png.latex?JSD">). Recordando que <img src="https://latex.codecogs.com/png.latex?KL(P%20%5C%7C%20Q)%20=%20%5Cint%20p(x)%20%5Cln%20%5Cfrac%7Bp(x)%7D%7Bq(x)%7D%20dx">:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AC(G)%20=%20-%5Cln(4)%20+%20KL%5Cleft(p_%7Bdata%7D%20%5CBig%5C%7C%20%5Cfrac%7Bp_%7Bdata%7D%20+%20p_g%7D%7B2%7D%5Cright)%20+%20KL%5Cleft(p_g%20%5CBig%5C%7C%20%5Cfrac%7Bp_%7Bdata%7D%20+%20p_g%7D%7B2%7D%5Cright)%0A"></p>
<p>Esto es exactamente igual a: <img src="https://latex.codecogs.com/png.latex?%0AC(G)%20=%20-%5Cln(4)%20+%202%20%5Ccdot%20JSD(p_%7Bdata%7D%20%5C%7C%20p_g)%0A"></p>
<p><strong>Teorema (Mínimo Global).</strong> <em>El mínimo global de la función de costo del generador <img src="https://latex.codecogs.com/png.latex?C(G)"> se alcanza si y solo si la distribución generada coincide con la real (<img src="https://latex.codecogs.com/png.latex?p_g%20=%20p_%7Bdata%7D">). En este punto, el valor del juego es <img src="https://latex.codecogs.com/png.latex?-%5Cln(4)">.</em></p>
<p><strong>Demostración:</strong></p>
<p>La función objetivo derivada es: <img src="https://latex.codecogs.com/png.latex?C(G)%20=%20-%5Cln(4)%20+%202%20%5Ccdot%20JSD(p_%7Bdata%7D%20%5C%7C%20p_g)"></p>
<ol type="1">
<li><p><strong>Cota Inferior:</strong> Por definición, la Divergencia de Jensen-Shannon es no negativa (<img src="https://latex.codecogs.com/png.latex?JSD%20%5Cgeq%200">). Por lo tanto, el valor mínimo posible de <img src="https://latex.codecogs.com/png.latex?C(G)"> está acotado inferiormente por <img src="https://latex.codecogs.com/png.latex?-%5Cln(4)">.</p></li>
<li><p><strong>Condición de Igualdad:</strong> La propiedad fundamental de la divergencia establece que <img src="https://latex.codecogs.com/png.latex?JSD(P%20%5C%7C%20Q)%20=%200"> si y solo si <img src="https://latex.codecogs.com/png.latex?P%20=%20Q"> en casi todo punto.</p>
<p>Esto implica que para minimizar <img src="https://latex.codecogs.com/png.latex?C(G)">, el generador debe satisfacer <img src="https://latex.codecogs.com/png.latex?p_g%20=%20p_%7Bdata%7D">.</p></li>
<li><p><strong>Interpretación del Valor <img src="https://latex.codecogs.com/png.latex?-%5Cln(4)">:</strong> Si <img src="https://latex.codecogs.com/png.latex?p_g%20=%20p_%7Bdata%7D">, el discriminador óptimo es incapaz de distinguir las muestras, colapsando a la incertidumbre máxima: <img src="https://latex.codecogs.com/png.latex?D%5E*(x)%20=%20%5Cfrac%7B1%7D%7B2%7D">. Evaluando la función de costo original bajo esta condición: <img src="https://latex.codecogs.com/png.latex?%0A%5Cmathbb%7BE%7D%5B%5Cln(1/2)%5D%20+%20%5Cmathbb%7BE%7D%5B%5Cln(1-1/2)%5D%20=%20%5Cln(1/4)%20=%20-%5Cln(4)%0A"></p></li>
</ol>
<p>Esto confirma que el equilibrio de Nash ocurre cuando el generador recupera perfectamente la distribución de datos y el discriminador opera como un clasificador aleatorio. <img src="https://latex.codecogs.com/png.latex?%5Cblacksquare"></p>
</section>
</section>
<section id="algoritmo-de-entrenamiento" class="level3" data-number="3.4">
<h3 data-number="3.4" class="anchored" data-anchor-id="algoritmo-de-entrenamiento"><span class="header-section-number">3.4</span> Algoritmo de Entrenamiento</h3>
<p>En la práctica, no optimizamos en el espacio de funciones, sino mediante Descenso de Gradiente Estocástico en el espacio de parámetros <img src="https://latex.codecogs.com/png.latex?%5CTheta_D,%20%5CTheta_G">.</p>
<p>Para un minibatch de tamaño <img src="https://latex.codecogs.com/png.latex?m"> y <img src="https://latex.codecogs.com/png.latex?T"> épocas, el procedimiento de <span class="citation" data-cites="goodfellow2014generative">Goodfellow et&nbsp;al. (2014)</span> es:</p>
<ol type="1">
<li>Muestrear <img src="https://latex.codecogs.com/png.latex?m"> ruidos <img src="https://latex.codecogs.com/png.latex?z%5E%7B(i)%7D"> de <img src="https://latex.codecogs.com/png.latex?p_z"> y <img src="https://latex.codecogs.com/png.latex?m"> datos <img src="https://latex.codecogs.com/png.latex?x%5E%7B(i)%7D"> de <img src="https://latex.codecogs.com/png.latex?p_%7B%5Cmathrm%7Bdata%7D%7D">.</li>
<li>Actualizar el discriminador por ascenso de gradiente: <img src="https://latex.codecogs.com/png.latex?%5Cnabla_%5Cphi%20%5Cfrac%7B1%7D%7Bm%7D%5Csum_%7Bi=1%7D%5E%7Bm%7D%5Cbigl%5B%5Clog%20D_%5Cphi(x%5E%7B(i)%7D)+%5Clog%5Cbigl(1-D_%5Cphi(G_%5Ctheta(z%5E%7B(i)%7D))%5Cbigr)%5Cbigr%5D."></li>
<li>Actualizar el generador por descenso de gradiente: <img src="https://latex.codecogs.com/png.latex?%5Cnabla_%5Ctheta%20%5Cfrac%7B1%7D%7Bm%7D%5Csum_%7Bi=1%7D%5E%7Bm%7D%5Clog%5Cbigl(1-D_%5Cphi(G_%5Ctheta(z%5E%7B(i)%7D))%5Cbigr)."></li>
</ol>
<div id="fig-convergence" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-convergence-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<a href="img/gans/convergencia_distribucion.png" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title="Figura&nbsp;3: Evolución de las distribuciones durante el entrenamiento."><img src="https://luccafrachelle.github.io/porfolio_academia/posts/img/gans/convergencia_distribucion.png" class="img-fluid figure-img" style="width:90.0%"></a>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-convergence-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figura&nbsp;3: Evolución de las distribuciones durante el entrenamiento.
</figcaption>
</figure>
</div>
</section>
<section id="convergencia-teórica" class="level3" data-number="3.5">
<h3 data-number="3.5" class="anchored" data-anchor-id="convergencia-teórica"><span class="header-section-number">3.5</span> Convergencia Teórica</h3>
<p>Formalmente, bajo el análisis clásico de <span class="citation" data-cites="goodfellow2014generative">Goodfellow et&nbsp;al. (2014)</span>, la convergencia está garantizada si asumimos condiciones ideales:</p>
<ol type="1">
<li><p><img src="https://latex.codecogs.com/png.latex?G"> y <img src="https://latex.codecogs.com/png.latex?D"> tienen capacidad infinita (espacio de funciones no paramétrico).</p></li>
<li><p>En cada paso iterativo del generador, se permite a <img src="https://latex.codecogs.com/png.latex?D"> alcanzar su óptimo global <img src="https://latex.codecogs.com/png.latex?D%5E*_G">.</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?p_g"> se actualiza siguiendo el gradiente para minimizar el criterio <img src="https://latex.codecogs.com/png.latex?C(G)">.</p></li>
</ol>
<p>Bajo estas hipótesis, se demuestra que la distribución generada <img src="https://latex.codecogs.com/png.latex?p_g"> converge a la distribución real <img src="https://latex.codecogs.com/png.latex?p_%7Bdata%7D">, alcanzando el Equilibrio de Nash donde <img src="https://latex.codecogs.com/png.latex?D(x)%20=%201/2"> en todo el dominio.</p>
<p>Sin embargo, en la práctica, estas condiciones no se cumplen. Un problema crítico es el desvanecimiento de gradientes. A lo largo de los años, se han propuesto numerosas variantes de GANs (e.g., WGAN, LSGAN, etc.) para mitigar estos problemas y mejorar la estabilidad del entrenamiento <span class="citation" data-cites="arjovsky2017wasserstein">Arjovsky et&nbsp;al. (2017)</span> <span class="citation" data-cites="mao2017least">Mao et&nbsp;al. (2017)</span>. Sin embargo no van a ser cubiertas en este trabajo por motivos de extensión.</p>
<p><em>Esta sección esta basada en: <span class="citation" data-cites="goodfellow2014generative">Goodfellow et&nbsp;al. (2014)</span>, <span class="citation" data-cites="hong2019generative">Hong et&nbsp;al. (2019)</span>, <span class="citation" data-cites="farnia2020gans">Farnia y Ozdaglar (2020)</span> y <span class="citation" data-cites="ermon2023deepgenerative"><span class="nocase">Ermon et&nbsp;al.</span> (2023)</span></em></p>
</section>
</section>
<section id="apéndices" class="level2" data-number="4">
<h2 data-number="4" class="anchored" data-anchor-id="apéndices"><span class="header-section-number">4</span> Apéndices</h2>
<section id="apéndice-a-demostración-del-teorema-de-nash-existencia" class="level3" data-number="4.1">
<h3 data-number="4.1" class="anchored" data-anchor-id="apéndice-a-demostración-del-teorema-de-nash-existencia"><span class="header-section-number">4.1</span> Apéndice A: Demostración del Teorema de Nash (Existencia)</h3>
<p>En este apéndice presentamos la demostración de la existencia de un Equilibrio de Nash en estrategias mixtas para juegos finitos de 2 jugadores. Ya que las GANs hay 2 jugadores,aunque el argumento se puede extender a <img src="https://latex.codecogs.com/png.latex?n"> jugadores facilmente,</p>
<section id="a.-definiciones-y-configuración-del-juego" class="level4" data-number="4.1.1">
<h4 data-number="4.1.1" class="anchored" data-anchor-id="a.-definiciones-y-configuración-del-juego"><span class="header-section-number">4.1.1</span> A. Definiciones y Configuración del Juego</h4>
<p>Consideramos un juego de suma general con dos jugadores, I (con <img src="https://latex.codecogs.com/png.latex?m"> acciones puras) y II (con <img src="https://latex.codecogs.com/png.latex?n"> acciones puras).</p>
<p><strong>Matrices de Pago:</strong></p>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?A_%7Bm%20%5Ctimes%20n%7D">: Matriz de pagos para el Jugador I.</li>
<li><img src="https://latex.codecogs.com/png.latex?B_%7Bm%20%5Ctimes%20n%7D">: Matriz de pagos para el Jugador II.</li>
</ul>
<p><strong>Estrategias Mixtas:</strong></p>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D%20=%20(x_1,%20%5Cldots,%20x_m)%5ET%20%5Cin%20%5CDelta_m"> (Estrategia de I, donde <img src="https://latex.codecogs.com/png.latex?%5CDelta_m"> es el símplex <img src="https://latex.codecogs.com/png.latex?m">-dimensional estándar).</li>
<li><img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7By%7D%20=%20(y_1,%20%5Cldots,%20y_n)%5ET%20%5Cin%20%5CDelta_n"> (Estrategia de II).</li>
</ul>
<p><strong>Espacio de Estrategias Conjuntas (<img src="https://latex.codecogs.com/png.latex?K">):</strong></p>
<p>El conjunto de todos los perfiles de estrategias posibles es el producto cartesiano de los espacios de estrategias individuales: <img src="https://latex.codecogs.com/png.latex?%0AK%20=%20%5CDelta_m%20%5Ctimes%20%5CDelta_n%0A"> <strong>Propiedad topológica clave:</strong> Dado que cada símplex es un conjunto cerrado, convexo y acotado, su producto <img src="https://latex.codecogs.com/png.latex?K"> también es un conjunto <strong>cerrado, convexo y acotado (compacto)</strong> en el espacio euclidiano.</p>
<p><strong>Pago Esperado:</strong></p>
<p>Los pagos esperados dada una estrategia conjunta <img src="https://latex.codecogs.com/png.latex?(%5Cmathbf%7Bx%7D,%20%5Cmathbf%7By%7D)"> son:</p>
<ul>
<li>Pago a I: <img src="https://latex.codecogs.com/png.latex?u_I(%5Cmathbf%7Bx%7D,%20%5Cmathbf%7By%7D)%20=%20%5Cmathbf%7Bx%7D%5ET%20A%5Cmathbf%7By%7D">.</li>
<li>Pago a II: <img src="https://latex.codecogs.com/png.latex?u_%7BII%7D(%5Cmathbf%7Bx%7D,%20%5Cmathbf%7By%7D)%20=%20%5Cmathbf%7Bx%7D%5ET%20B%5Cmathbf%7By%7D">.</li>
</ul>
</section>
<section id="b.-definición-de-equilibrio-de-nash" class="level4" data-number="4.1.2">
<h4 data-number="4.1.2" class="anchored" data-anchor-id="b.-definición-de-equilibrio-de-nash"><span class="header-section-number">4.1.2</span> B. Definición de Equilibrio de Nash</h4>
<p>Antes de proceder a la demostración, definimos formalmente el objeto de estudio. Un par de estrategias <img src="https://latex.codecogs.com/png.latex?(%5Cmathbf%7Bx%7D%5E*,%20%5Cmathbf%7By%7D%5E*)%20%5Cin%20K"> es un Equilibrio de Nash si ninguna desviación unilateral es rentable para ningún jugador.</p>
<p>Formalmente, <img src="https://latex.codecogs.com/png.latex?(%5Cmathbf%7Bx%7D%5E*,%20%5Cmathbf%7By%7D%5E*)"> es un equilibrio si y solo si:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Baligned%7D%0A%5Cmathbf%7Bx%7D%5E%7B*T%7D%20A%20%5Cmathbf%7By%7D%5E*%20&amp;%5Cge%20%5Cmathbf%7Bx%7D%5ET%20A%20%5Cmathbf%7By%7D%5E*%20%5Cquad%20%5Cforall%20%5Cmathbf%7Bx%7D%20%5Cin%20%5CDelta_m%20%5C%5C%0A(%5Cmathbf%7Bx%7D%5E*)%5ET%20B%20%5Cmathbf%7By%7D%5E*%20&amp;%5Cge%20(%5Cmathbf%7Bx%7D%5E*)%5ET%20B%20%5Cmathbf%7By%7D%20%5Cquad%20%5Cforall%20%5Cmathbf%7By%7D%20%5Cin%20%5CDelta_n%0A%5Cend%7Baligned%7D%0A"></p>
<p>Esto es equivalente a decir que <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bx%7D%5E*"> es una mejor respuesta a <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7By%7D%5E*"> y viceversa.</p>
</section>
<section id="c.-construcción-de-la-función-de-mapeo-t" class="level4" data-number="4.1.3">
<h4 data-number="4.1.3" class="anchored" data-anchor-id="c.-construcción-de-la-función-de-mapeo-t"><span class="header-section-number">4.1.3</span> C. Construcción de la Función de Mapeo <img src="https://latex.codecogs.com/png.latex?T"></h4>
<p>Para probar la existencia de dicho equilibrio, utilizamos el Teorema del Punto Fijo de Brouwer:</p>
<blockquote class="blockquote">
<p><strong>Teorema (Brouwer):</strong> Si <img src="https://latex.codecogs.com/png.latex?K"> es un conjunto compacto y convexo, y <img src="https://latex.codecogs.com/png.latex?T:%20K%20%5Cto%20K"> es una función continua, entonces existe al menos un punto fijo <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bz%7D%5E*%20%5Cin%20K"> tal que <img src="https://latex.codecogs.com/png.latex?T(%5Cmathbf%7Bz%7D%5E*)%20=%20%5Cmathbf%7Bz%7D%5E*">.</p>
</blockquote>
<p>El objetivo es construir una función <img src="https://latex.codecogs.com/png.latex?T"> tal que sus puntos fijos coincidan con los Equilibrios de Nash.</p>
<section id="incentivos-de-desviación" class="level5" data-number="4.1.3.1">
<h5 data-number="4.1.3.1" class="anchored" data-anchor-id="incentivos-de-desviación"><span class="header-section-number">4.1.3.1</span> Incentivos de Desviación</h5>
<p>Para cada acción pura <img src="https://latex.codecogs.com/png.latex?i"> del Jugador I, definimos <img src="https://latex.codecogs.com/png.latex?c_i"> como la ganancia marginal positiva que obtendría el jugador al cambiar toda su masa de probabilidad a la acción pura <img src="https://latex.codecogs.com/png.latex?i"> frente a la estrategia actual <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7By%7D"> del oponente:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0Ac_i(%5Cmathbf%7Bx%7D,%20%5Cmathbf%7By%7D)%20:=%20%5Cmax%20%5Cleft%5C%7B%20A_i%20%5Cmathbf%7By%7D%20-%20%5Cmathbf%7Bx%7D%5ET%20A%5Cmathbf%7By%7D,%20%5C%200%20%5Cright%5C%7D%0A"></p>
<p>Donde <img src="https://latex.codecogs.com/png.latex?A_i%20%5Cmathbf%7By%7D"> es el pago de jugar la acción pura <img src="https://latex.codecogs.com/png.latex?i">. <em>Nota:</em> Si la acción <img src="https://latex.codecogs.com/png.latex?i"> da menos pago que el promedio actual, <img src="https://latex.codecogs.com/png.latex?c_i%20=%200">.</p>
<p>De manera simétrica, para el Jugador II y cada acción pura <img src="https://latex.codecogs.com/png.latex?j">:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0Ad_j(%5Cmathbf%7Bx%7D,%20%5Cmathbf%7By%7D)%20:=%20%5Cmax%20%5Cleft%5C%7B%20%5Cmathbf%7Bx%7D%5ET%20B_j%20-%20%5Cmathbf%7Bx%7D%5ET%20B%5Cmathbf%7By%7D,%20%5C%200%20%5Cright%5C%7D%0A"></p>
</section>
<section id="definición-del-mapeo-tmathbfx-mathbfy" class="level5" data-number="4.1.3.2">
<h5 data-number="4.1.3.2" class="anchored" data-anchor-id="definición-del-mapeo-tmathbfx-mathbfy"><span class="header-section-number">4.1.3.2</span> Definición del Mapeo <img src="https://latex.codecogs.com/png.latex?T(%5Cmathbf%7Bx%7D,%20%5Cmathbf%7By%7D)"></h5>
<p>Definimos la función <img src="https://latex.codecogs.com/png.latex?T:%20K%20%5Cto%20K"> que transforma <img src="https://latex.codecogs.com/png.latex?(%5Cmathbf%7Bx%7D,%20%5Cmathbf%7By%7D)"> en un nuevo par <img src="https://latex.codecogs.com/png.latex?(%5Chat%7B%5Cmathbf%7Bx%7D%7D,%20%5Chat%7B%5Cmathbf%7By%7D%7D)">. La intuición es que <img src="https://latex.codecogs.com/png.latex?T"> “empuja” la probabilidad hacia las acciones que tienen un incentivo de desviación positivo (<img src="https://latex.codecogs.com/png.latex?c_i%20%3E%200"> o <img src="https://latex.codecogs.com/png.latex?d_j%20%3E%200">).</p>
<p>Definimos los factores de normalización <img src="https://latex.codecogs.com/png.latex?S%20=%20%5Csum_%7Bk=1%7D%5Em%20c_k"> y <img src="https://latex.codecogs.com/png.latex?D%20=%20%5Csum_%7Bk=1%7D%5En%20d_k">.</p>
<p>Las nuevas estrategias se calculan como:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Chat%7Bx%7D_i%20=%20%5Cfrac%7Bx_i%20+%20c_i%7D%7B1%20+%20S%7D,%20%5Cquad%20%5Chat%7By%7D_j%20=%20%5Cfrac%7By_j%20+%20d_j%7D%7B1%20+%20D%7D%0A"></p>
<p>Es fácil verificar que <img src="https://latex.codecogs.com/png.latex?%5Csum%20%5Chat%7Bx%7D_i%20=%201"> y <img src="https://latex.codecogs.com/png.latex?%5Csum%20%5Chat%7By%7D_j%20=%201">, por lo que el mapeo está bien definido dentro de <img src="https://latex.codecogs.com/png.latex?K">.</p>
</section>
</section>
<section id="d.-propiedades-y-demostración" class="level4" data-number="4.1.4">
<h4 data-number="4.1.4" class="anchored" data-anchor-id="d.-propiedades-y-demostración"><span class="header-section-number">4.1.4</span> D. Propiedades y Demostración</h4>
<section id="continuidad-de-t" class="level5" data-number="4.1.4.1">
<h5 data-number="4.1.4.1" class="anchored" data-anchor-id="continuidad-de-t"><span class="header-section-number">4.1.4.1</span> Continuidad de <img src="https://latex.codecogs.com/png.latex?T"></h5>
<p>Las funciones de pago esperado son multilineales y, por tanto, continuas. La función <img src="https://latex.codecogs.com/png.latex?%5Cmax%5C%7B%5Ccdot,%200%5C%7D"> es continua. Dado que el denominador <img src="https://latex.codecogs.com/png.latex?(1+S)"> es siempre <img src="https://latex.codecogs.com/png.latex?%5Cge%201"> (nunca cero), la composición resultante <img src="https://latex.codecogs.com/png.latex?T(%5Cmathbf%7Bx%7D,%20%5Cmathbf%7By%7D)"> es una función continua.</p>
</section>
<section id="existencia-del-punto-fijo" class="level5" data-number="4.1.4.2">
<h5 data-number="4.1.4.2" class="anchored" data-anchor-id="existencia-del-punto-fijo"><span class="header-section-number">4.1.4.2</span> Existencia del Punto Fijo</h5>
<p>Dado que <img src="https://latex.codecogs.com/png.latex?K"> es compacto y convexo, y <img src="https://latex.codecogs.com/png.latex?T"> es continua, por el Teorema de Brouwer existe un punto <img src="https://latex.codecogs.com/png.latex?(%5Cmathbf%7Bx%7D%5E*,%20%5Cmathbf%7By%7D%5E*)%20%5Cin%20K"> tal que: <img src="https://latex.codecogs.com/png.latex?%0AT(%5Cmathbf%7Bx%7D%5E*,%20%5Cmathbf%7By%7D%5E*)%20=%20(%5Cmathbf%7Bx%7D%5E*,%20%5Cmathbf%7By%7D%5E*)%0A"> Esto implica que <img src="https://latex.codecogs.com/png.latex?%5Chat%7B%5Cmathbf%7Bx%7D%7D%20=%20%5Cmathbf%7Bx%7D%5E*"> y <img src="https://latex.codecogs.com/png.latex?%5Chat%7B%5Cmathbf%7By%7D%7D%20=%20%5Cmathbf%7By%7D%5E*">.</p>
</section>
<section id="verificación-el-punto-fijo-es-un-equilibrio-de-nash" class="level5" data-number="4.1.4.3">
<h5 data-number="4.1.4.3" class="anchored" data-anchor-id="verificación-el-punto-fijo-es-un-equilibrio-de-nash"><span class="header-section-number">4.1.4.3</span> Verificación: El Punto Fijo es un Equilibrio de Nash</h5>
<p>Debemos demostrar que en el punto fijo, los incentivos de desviación desaparecen (<img src="https://latex.codecogs.com/png.latex?S=0"> y <img src="https://latex.codecogs.com/png.latex?D=0">).</p>
<p>Analicemos al Jugador I. En el punto fijo se cumple: <img src="https://latex.codecogs.com/png.latex?%0Ax%5E*_i%20=%20%5Cfrac%7Bx%5E*_i%20+%20c_i%7D%7B1%20+%20S%7D%20%5Cquad%20%5Ciff%20%5Cquad%20x%5E*_i(1%20+%20S)%20=%20x%5E*_i%20+%20c_i%20%5Cquad%20%5Ciff%20%5Cquad%20x%5E*_i%20S%20=%20c_i%0A"> Esto implica que <img src="https://latex.codecogs.com/png.latex?c_i"> es proporcional a <img src="https://latex.codecogs.com/png.latex?x%5E*_i"> escalado por <img src="https://latex.codecogs.com/png.latex?S">.</p>
<p>Supongamos por contradicción que la estrategia actual <strong>no</strong> es un equilibrio, es decir, existe alguna mejora posible, lo que implica <img src="https://latex.codecogs.com/png.latex?S%20%3E%200">. Si <img src="https://latex.codecogs.com/png.latex?S%20%3E%200">, entonces debe existir al menos un <img src="https://latex.codecogs.com/png.latex?k"> tal que <img src="https://latex.codecogs.com/png.latex?c_k%20%3E%200">.</p>
<p>Consideremos la ganancia ponderada por la mejora. Sabemos que: <img src="https://latex.codecogs.com/png.latex?%0A%5Csum_%7Bi=1%7D%5Em%20c_i%20(A_i%20%5Cmathbf%7By%7D%5E*%20-%20(%5Cmathbf%7Bx%7D%5E*)%5ET%20A%5Cmathbf%7By%7D%5E*)%20=%20%5Csum_%7Bi=1%7D%5Em%20c_i%5E2%0A"> Esto se debe a que si <img src="https://latex.codecogs.com/png.latex?A_i%20%5Cmathbf%7By%7D%5E*%20-%20(%5Cmathbf%7Bx%7D%5E*)%5ET%20A%5Cmathbf%7By%7D%5E*%20%3C%200">, entonces <img src="https://latex.codecogs.com/png.latex?c_i%20=%200">, anulando el término.</p>
<p>Sin embargo, usando la propiedad del punto fijo <img src="https://latex.codecogs.com/png.latex?c_i%20=%20x%5E*_i%20S">: <img src="https://latex.codecogs.com/png.latex?%0A%5Csum_%7Bi=1%7D%5Em%20x%5E*_i%20S%20(A_i%20%5Cmathbf%7By%7D%5E*%20-%20(%5Cmathbf%7Bx%7D%5E*)%5ET%20A%5Cmathbf%7By%7D%5E*)%20=%20S%20%5Cleft%5B%20%5Cunderbrace%7B%5Csum_%7Bi=1%7D%5Em%20x%5E*_i%20A_i%20%5Cmathbf%7By%7D%5E*%7D_%7B(%5Cmathbf%7Bx%7D%5E*)%5ET%20A%20%5Cmathbf%7By%7D%5E*%7D%20-%20%5Cunderbrace%7B%5Csum_%7Bi=1%7D%5Em%20x%5E*_i%20(%5Cmathbf%7Bx%7D%5E*)%5ET%20A%5Cmathbf%7By%7D%5E*%7D_%7B(%5Cmathbf%7Bx%7D%5E*)%5ET%20A%20%5Cmathbf%7By%7D%5E*%7D%20%5Cright%5D%20=%20S%20%5Ccdot%200%20=%200%0A"></p>
<p>Llegamos a la contradicción: <img src="https://latex.codecogs.com/png.latex?%5Csum%20c_i%5E2%20=%200">, lo cual implica que <img src="https://latex.codecogs.com/png.latex?c_i%20=%200"> para todo <img src="https://latex.codecogs.com/png.latex?i">.</p>
<p><strong>Conclusión:</strong> Si <img src="https://latex.codecogs.com/png.latex?c_i%20=%200"> para todo <img src="https://latex.codecogs.com/png.latex?i">, entonces por definición: <img src="https://latex.codecogs.com/png.latex?%0AA_i%20%5Cmathbf%7By%7D%5E*%20-%20(%5Cmathbf%7Bx%7D%5E*)%5ET%20A%5Cmathbf%7By%7D%5E*%20%5Cle%200%20%5Cquad%20%5Cimplies%20%5Cquad%20A_i%20%5Cmathbf%7By%7D%5E*%20%5Cle%20(%5Cmathbf%7Bx%7D%5E*)%5ET%20A%5Cmathbf%7By%7D%5E*%0A"> Esto significa que ninguna acción pura da un pago mayor al de la estrategia mixta actual. Por linealidad, ninguna estrategia mixta alternativa dará un pago mayor.</p>
<p>El mismo argumento aplica para el Jugador II (<img src="https://latex.codecogs.com/png.latex?D=0">). Por lo tanto, el punto fijo <img src="https://latex.codecogs.com/png.latex?(%5Cmathbf%7Bx%7D%5E*,%20%5Cmathbf%7By%7D%5E*)"> satisface las condiciones del Equilibrio de Nash.</p>
<p><em>La prueba es una adaptación de la demostración que se hace en <span class="citation" data-cites="KarlinPeres2017GameTheoryAlive">Karlin y Peres (2017)</span>. Gracias a Diego por prestarnos el libro :)</em></p>
</section>
</section>
</section>
<section id="apéndice-b-demostración-del-teorema-minimax-von-neumann" class="level3" data-number="4.2">
<h3 data-number="4.2" class="anchored" data-anchor-id="apéndice-b-demostración-del-teorema-minimax-von-neumann"><span class="header-section-number">4.2</span> Apéndice B: Demostración del Teorema Minimax (Von Neumann)</h3>
<p>El Teorema Minimax de Von Neumann es fundamental en la Teoría de Juegos de suma cero. Este teorema establece que el valor Maximin del Jugador I es igual al valor Minimax del Jugador II.</p>
<section id="teorema-minimax-de-von-neumann" class="level4" data-number="4.2.1">
<h4 data-number="4.2.1" class="anchored" data-anchor-id="teorema-minimax-de-von-neumann"><span class="header-section-number">4.2.1</span> Teorema Minimax de Von Neumann</h4>
<p>Sea <img src="https://latex.codecogs.com/png.latex?A"> una matriz de pagos de <img src="https://latex.codecogs.com/png.latex?m%20%5Ctimes%20n">. El valor del juego <img src="https://latex.codecogs.com/png.latex?V"> satisface:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmax_%7Bx%5Cin%5CDelta_m%7D%20%5Cmin_%7By%5Cin%5CDelta_n%7D%20x%5ET%20Ay%20=%20V%20=%20%5Cmin_%7By%5Cin%5CDelta_n%7D%20%5Cmax_%7Bx%5Cin%5CDelta_m%7D%20x%5ET%20Ay%0A"></p>
<p>La demostración se basa en el Teorema del Hiperplano Separador de la geometría convexa, procediendo en dos partes.</p>
</section>
<section id="parte-1-la-desigualdad-maximin-leq-minimax" class="level4" data-number="4.2.2">
<h4 data-number="4.2.2" class="anchored" data-anchor-id="parte-1-la-desigualdad-maximin-leq-minimax"><span class="header-section-number">4.2.2</span> Parte 1: La Desigualdad (Maximin <img src="https://latex.codecogs.com/png.latex?%5Cleq"> Minimax)</h4>
<p>La primera parte de la demostración es inmediata y se basa en la propiedad de que el máximo de un mínimo es siempre menor o igual al mínimo de un máximo para cualquier función continua <img src="https://latex.codecogs.com/png.latex?f(x,%20y)"> sobre conjuntos compactos.</p>
<p>Formalmente, demostramos: <img src="https://latex.codecogs.com/png.latex?%0A%5Cmax_%7Bx%5Cin%5CDelta_m%7D%20%5Cmin_%7By%5Cin%5CDelta_n%7D%20x%5ET%20Ay%20%5Cleq%20%5Cmin_%7By%5Cin%5CDelta_n%7D%20%5Cmax_%7Bx%5Cin%5CDelta_m%7D%20x%5ET%20Ay%0A"></p>
<ol type="1">
<li>Para cualquier par de estrategias <img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bx%7D%20%5Cin%20%5CDelta_m"> y <img src="https://latex.codecogs.com/png.latex?%5Ctilde%7By%7D%20%5Cin%20%5CDelta_n">, se tiene trivialmente: <img src="https://latex.codecogs.com/png.latex?%0A%5Cmin_%7By%5Cin%5CDelta_n%7D%20%5Ctilde%7Bx%7D%5ET%20Ay%20%5Cleq%20%5Ctilde%7Bx%7D%5ET%20A%5Ctilde%7By%7D%20%5Cleq%20%5Cmax_%7Bx%5Cin%5CDelta_m%7D%20x%5ET%20A%5Ctilde%7By%7D%0A"></li>
<li>Dado que la desigualdad <img src="https://latex.codecogs.com/png.latex?%5Cmin_%7By%7D%20%5Ctilde%7Bx%7D%5ET%20Ay%20%5Cleq%20%5Cmax_%7Bx%7D%20x%5ET%20A%5Ctilde%7By%7D"> se cumple para <strong>cualquier</strong> <img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bx%7D"> y <img src="https://latex.codecogs.com/png.latex?%5Ctilde%7By%7D">, podemos tomar el máximo sobre <img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bx%7D"> en el lado izquierdo y el mínimo sobre <img src="https://latex.codecogs.com/png.latex?%5Ctilde%7By%7D"> en el lado derecho, manteniendo la desigualdad. <img src="https://latex.codecogs.com/png.latex?%0A%5Cmax_%7Bx%5Cin%5CDelta_m%7D%20%5Cmin_%7By%5Cin%5CDelta_n%7D%20x%5ET%20Ay%20%5Cleq%20%5Cmin_%7By%5Cin%5CDelta_n%7D%20%5Cmax_%7Bx%5Cin%5CDelta_m%7D%20x%5ET%20Ay%0A"></li>
</ol>
</section>
<section id="parte-2-la-igualdad-minimax-leq-maximin" class="level4" data-number="4.2.3">
<h4 data-number="4.2.3" class="anchored" data-anchor-id="parte-2-la-igualdad-minimax-leq-maximin"><span class="header-section-number">4.2.3</span> Parte 2: La Igualdad (Minimax <img src="https://latex.codecogs.com/png.latex?%5Cleq"> Maximin)</h4>
<p>El objetivo ahora es demostrar que el lado izquierdo debe ser al menos tan grande como el lado derecho, lo que fuerza la igualdad.</p>
<p>Se prueba por contradicción usando el Teorema del Hiperplano Separador.</p>
<section id="a.-hipótesis-de-contradicción" class="level5" data-number="4.2.3.1">
<h5 data-number="4.2.3.1" class="anchored" data-anchor-id="a.-hipótesis-de-contradicción"><span class="header-section-number">4.2.3.1</span> A. Hipótesis de Contradicción</h5>
<p>Asumimos que la igualdad no se cumple y que existe una brecha, es decir, que existe un número <img src="https://latex.codecogs.com/png.latex?%5Clambda"> tal que:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Clambda%20%3C%20%5Cmin_%7By%5Cin%5CDelta_n%7D%20%5Cmax_%7Bx%5Cin%5CDelta_m%7D%20x%5ET%20Ay%0A"></p>
<p>Esto implica que el valor Minimax (<img src="https://latex.codecogs.com/png.latex?V_%7BII%7D">) es estrictamente mayor que <img src="https://latex.codecogs.com/png.latex?%5Clambda">.</p>
</section>
<section id="b.-transformación-del-juego" class="level5" data-number="4.2.3.2">
<h5 data-number="4.2.3.2" class="anchored" data-anchor-id="b.-transformación-del-juego"><span class="header-section-number">4.2.3.2</span> B. Transformación del Juego</h5>
<p>Definimos una nueva matriz de pagos <img src="https://latex.codecogs.com/png.latex?%7B%5Chat%7BA%7D%7D"> con <img src="https://latex.codecogs.com/png.latex?%5Chat%7Ba%7D_%7Bi,j%7D%20=%20a_%7Bi,j%7D%20-%20%5Clambda">. Para este nuevo juego, el valor Minimax debe ser positivo:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A0%20%3C%20%5Cmin_%7By%5Cin%5CDelta_n%7D%20%5Cmax_%7Bx%5Cin%5CDelta_m%7D%20x%5ET%20%7B%5Chat%7BA%7D%7D%20y%0A"></p>
</section>
<section id="c.-construcción-del-conjunto-convexo-k" class="level5" data-number="4.2.3.3">
<h5 data-number="4.2.3.3" class="anchored" data-anchor-id="c.-construcción-del-conjunto-convexo-k"><span class="header-section-number">4.2.3.3</span> C. Construcción del Conjunto Convexo <img src="https://latex.codecogs.com/png.latex?K"></h5>
<p>Definimos el conjunto <img src="https://latex.codecogs.com/png.latex?K%20%5Csubseteq%20%5Cmathbb%7BR%7D%5Em">, que representa todos los vectores que <em>dominan</em> los posibles vectores de ganancia del Jugador I en el juego <img src="https://latex.codecogs.com/png.latex?%7B%5Chat%7BA%7D%7D">:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AK%20=%20%5C%7B%7B%5Chat%7BA%7D%7D%20y%20+%20v%20:%20y%20%5Cin%20%5CDelta_n,%20v%20%5Cin%20%5Cmathbb%7BR%7D%5Em,%20v%20%5Cgeq%20%5Cmathbf%7B0%7D%5C%7D%0A"></p>
<ul>
<li>El conjunto <img src="https://latex.codecogs.com/png.latex?K"> es cerrado y convexo.</li>
<li>Se comprueba que <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7B0%7D%20%5Cnotin%20K">. Si <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7B0%7D%20%5Cin%20K">, existiría un <img src="https://latex.codecogs.com/png.latex?y%20%5Cin%20%5CDelta_n"> tal que <img src="https://latex.codecogs.com/png.latex?%7B%5Chat%7BA%7D%7D%20y%20%5Cleq%20%5Cmathbf%7B0%7D">, implicando que <img src="https://latex.codecogs.com/png.latex?%5Cmax_%7Bx%7D%20x%5ET%20%7B%5Chat%7BA%7D%7D%20y%20%5Cleq%200">, lo cual contradice la positividad establecida en el paso B.</li>
</ul>
</section>
<section id="d.-aplicación-del-teorema-del-hiperplano-separador" class="level5" data-number="4.2.3.4">
<h5 data-number="4.2.3.4" class="anchored" data-anchor-id="d.-aplicación-del-teorema-del-hiperplano-separador"><span class="header-section-number">4.2.3.4</span> D. Aplicación del Teorema del Hiperplano Separador</h5>
<p>Dado que <img src="https://latex.codecogs.com/png.latex?K"> es cerrado y convexo y no contiene el origen, el Teorema del Hiperplano Separador (Theorem 2.6.2) garantiza la existencia de un vector <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bz%7D%20%5Cin%20%5Cmathbb%7BR%7D%5Em"> y una constante <img src="https://latex.codecogs.com/png.latex?c%20%3E%200"> tal que:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmathbf%7Bz%7D%5ET%20w%20%3E%20c%20%3E%200%20%5Cquad%20%5Ctext%7Bpara%20todo%20%7D%20w%20%5Cin%20K%0A"></p>
<p>Sustituyendo la definición de <img src="https://latex.codecogs.com/png.latex?w%20=%20%7B%5Chat%7BA%7D%7D%20y%20+%20v">: <img src="https://latex.codecogs.com/png.latex?%0A%5Cmathbf%7Bz%7D%5ET%20(%7B%5Chat%7BA%7D%7D%20y%20+%20v)%20%3E%20c%20%3E%200%20%5Cquad%20%5Ctext%7Bpara%20todo%20%7D%20y%20%5Cin%20%5CDelta_n%20%5Ctext%7B%20y%20%7D%20v%20%5Cgeq%20%5Cmathbf%7B0%7D%0A"></p>
</section>
<section id="e.-identificación-de-la-estrategia-tildex" class="level5" data-number="4.2.3.5">
<h5 data-number="4.2.3.5" class="anchored" data-anchor-id="e.-identificación-de-la-estrategia-tildex"><span class="header-section-number">4.2.3.5</span> E. Identificación de la Estrategia <img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bx%7D"></h5>
<ol type="1">
<li><strong>Restricción en <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bz%7D">:</strong> Se demuestra que <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bz%7D%20%5Cgeq%20%5Cmathbf%7B0%7D">. Si algún componente <img src="https://latex.codecogs.com/png.latex?z_j%20%3C%200">, la elección de un <img src="https://latex.codecogs.com/png.latex?v"> adecuado anularía la desigualdad.</li>
<li><strong>Normalización:</strong> Ya que <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bz%7D%20%5Cgeq%20%5Cmathbf%7B0%7D"> y <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bz%7D%20%5Cneq%20%5Cmathbf%7B0%7D">, se define una estrategia mixta <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7B%5Ctilde%7Bx%7D%7D%20%5Cin%20%5CDelta_m">: <img src="https://latex.codecogs.com/png.latex?%0A%7B%5Ctilde%7Bx%7D%7D%20=%20%5Cfrac%7B%5Cmathbf%7Bz%7D%7D%7B%5Csum_%7Bi%7D%20z_i%7D%20%5Cin%20%5CDelta_m%0A"></li>
</ol>
</section>
<section id="f.-conclusión-por-contradicción" class="level5" data-number="4.2.3.6">
<h5 data-number="4.2.3.6" class="anchored" data-anchor-id="f.-conclusión-por-contradicción"><span class="header-section-number">4.2.3.6</span> F. Conclusión por Contradicción</h5>
<p>Al elegir <img src="https://latex.codecogs.com/png.latex?v%20=%20%5Cmathbf%7B0%7D"> y sustituir <img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7Bz%7D%20=%20(%5Csum%20z_i)%20%7B%5Ctilde%7Bx%7D%7D"> en la desigualdad del hiperplano, se obtiene:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A(%5Csum%20z_i)%20%7B%5Ctilde%7Bx%7D%7D%5ET%20%7B%5Chat%7BA%7D%7D%20y%20%3E%20c%0A"></p>
<p>Dividiendo por la suma positiva <img src="https://latex.codecogs.com/png.latex?(%5Csum%20z_i)"> y volviendo al juego original <img src="https://latex.codecogs.com/png.latex?A"> (<img src="https://latex.codecogs.com/png.latex?%5Cmathbf%7B%5Chat%7BA%7D%7D%20=%20A%20-%20%5Clambda%20I">):</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%7B%5Ctilde%7Bx%7D%7D%5ET%20A%20y%20-%20%5Clambda%20%3E%20%5Cfrac%7Bc%7D%7B%5Csum%20z_i%7D%20%3E%200%0A"></p>
<p>Esto implica que <img src="https://latex.codecogs.com/png.latex?%7B%5Ctilde%7Bx%7D%7D%5ET%20A%20y"> es estrictamente mayor que <img src="https://latex.codecogs.com/png.latex?%5Clambda"> para <em>todo</em> <img src="https://latex.codecogs.com/png.latex?y%20%5Cin%20%5CDelta_n">. Por lo tanto, el Maximin del juego <img src="https://latex.codecogs.com/png.latex?A"> es mayor que <img src="https://latex.codecogs.com/png.latex?%5Clambda">:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmax_%7Bx%5Cin%5CDelta_m%7D%20%5Cmin_%7By%5Cin%5CDelta_n%7D%20x%5ET%20Ay%20%3E%20%5Clambda%0A"></p>
<p>Dado que esto es cierto para <strong>cualquier</strong> <img src="https://latex.codecogs.com/png.latex?%5Clambda"> menor que el Minimax (según nuestra hipótesis inicial), la única conclusión posible es que el Maximin debe ser mayor o igual que el Minimax, forzando la igualdad:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cmax_%7Bx%5Cin%5CDelta_m%7D%20%5Cmin_%7By%5Cin%5CDelta_n%7D%20x%5ET%20Ay%20=%20%5Cmin_%7By%5Cin%5CDelta_n%7D%20%5Cmax_%7Bx%5Cin%5CDelta_m%7D%20x%5ET%20Ay%0A"></p>
<p><em>La prueba es una adaptación de una presenteación sobre GANs de Matias Carrasco, gracias mati :)</em></p>



</section>
</section>
</section>
</section>

<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Volver arriba</a><div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">Referencias</h2><div id="refs" class="references csl-bib-body hanging-indent">
<div id="ref-alqahtani2021applications" class="csl-entry">
Alqahtani, Hamed, Manolya Kavakli-Thorne, y G Kumar. 2021. <span>«Applications of Generative Adversarial Networks (GANs): An Updated Review»</span>. <em>Archives of Computational Methods in Engineering</em> 28: 525-52.
</div>
<div id="ref-arjovsky2017wasserstein" class="csl-entry">
Arjovsky, Martin, Soumith Chintala, y Léon Bottou. 2017. <span>«Wasserstein gan»</span>. <em>arXiv preprint arXiv:1701.07875</em>.
</div>
<div id="ref-daskalakis2018training" class="csl-entry">
Daskalakis, Constantinos, Andrew Ilyas, Vasilis Syrgkanis, y Haipeng Zeng. 2018. <span>«Training GANs with Optimism»</span>. <em>International Conference on Learning Representations</em>.
</div>
<div id="ref-ermon2023deepgenerative" class="csl-entry">
<span class="nocase">Ermon, Stefano et&nbsp;al.</span> 2023. <em>CS236: Deep Generative Models</em>. Stanford University; Course Website. <a href="https://deepgenerativemodels.github.io/">https://deepgenerativemodels.github.io/</a>.
</div>
<div id="ref-farnia2020gans" class="csl-entry">
Farnia, Farzan, y Asuman Ozdaglar. 2020. <span>«GANs May Have No Nash Equilibria»</span>. <em>International Conference on Machine Learning (ICML)</em>, 3010-20.
</div>
<div id="ref-goodfellow2014generative" class="csl-entry">
Goodfellow, Ian, Jean Pouget-Abadie, Mehdi Mirza, et&nbsp;al. 2014. <span>«Generative adversarial nets»</span>. <em>Advances in neural information processing systems</em> 27.
</div>
<div id="ref-hong2019generative" class="csl-entry">
Hong, Yongjun, Uiwon Hwang, Jaeyoon Yoo, y Sungroh Yoon. 2019. <span>«Generative adversarial networks: An overview»</span>. <em>IEEE Signal Processing Magazine</em> 36 (5): 46-54.
</div>
<div id="ref-KarlinPeres2017GameTheoryAlive" class="csl-entry">
Karlin, Anna R., y Yuval Peres. 2017. <em>Game Theory, Alive</em>. Vol. 101. Graduate Studies en Mathematics. American Mathematical Society.
</div>
<div id="ref-mao2017least" class="csl-entry">
Mao, Xudong, Qing Li, Haoran Xie, Raymond YK Lau, Zhen Wang, y Stephen Paul Smolley. 2017. <span>«Least squares generative adversarial networks»</span>. <em>Proceedings of the IEEE International Conference on Computer Vision (ICCV)</em>, 2794-802.
</div>
<div id="ref-mescheder2017numerics" class="csl-entry">
Mescheder, Lars, Sebastian Nowozin, y Andreas Geiger. 2017. <span>«The numerics of gans»</span>. <em>Advances in neural information processing systems</em> 30.
</div>
<div id="ref-moghadam2023games" class="csl-entry">
Mohebbi Moghaddam, Monireh, Bahar Boroumand, Mohammad Jalali, et&nbsp;al. 2023. <span>«Games of GANs: game-theoretical models for generative adversarial networks»</span>. <em>Artificial Intelligence Review</em> 56 (10): 9771-807.
</div>
<div id="ref-nisan2007algorithmic" class="csl-entry">
Nisan, Noam, Tim Roughgarden, Eva Tardos, y Vijay V Vazirani. 2007. <em>Algorithmic game theory</em>. Cambridge University Press.
</div>
<div id="ref-wang2017generative" class="csl-entry">
Wang, Kunfeng, Chao Gou, Yanjie Duan, Yilun Lin, Xinhu Zheng, y Fei-Yue Wang. 2017. <span>«Generative adversarial networks: introduction and outlook»</span>. <em>IEEE/CAA Journal of Automatica Sinica</em> 4 (4): 588-98.
</div>
<div id="ref-wang2019generative" class="csl-entry">
Wang, Zhengwei, Qi She, y Tomas E Ward. 2019. <span>«Generative adversarial networks: A survey and taxonomy»</span>. <em>arXiv preprint arXiv:1906.01529</em>.
</div>
</div></section></div> ]]></description>
  <category>AGT</category>
  <category>ML</category>
  <guid>https://luccafrachelle.github.io/porfolio_academia/posts/gans-teoria-juegos.html</guid>
  <pubDate>Sat, 01 Nov 2025 00:00:00 GMT</pubDate>
</item>
</channel>
</rss>
