6 cosas que aprendí creando LLM desde cero y que ningún tutorial te enseña

Los LLM han conquistado el mundo. La mayoría de las personas utilizan API pulidas de LLM, escriben un mensaje y obtienen una respuesta. Lo que pasan por alto es la importancia arquitectónica que conlleva y dónde puede sobresalir y dónde necesita mejorarse. Debajo del capó se encuentran opciones de diseño no obvias que determinan la velocidad, el costo y la capacidad; opciones que son muy importantes si desea construir, ajustar u optimizar estos modelos.

Implementé GPT-2 desde cero solo con PyTorch para comprender la arquitectura de un extremo a otro. Además, agregué LoRA (adaptadores de bajo rango), RoPE (incrustaciones posicionales rotativas), caché KV y más. Durante la implementación, hubo varios momentos que me hicieron rascarme la cabeza y seguí documentándolos todos. Hoy les comparto 6 de los más importantes. Para una mirada más profunda a la arquitectura, puede encontrarla en mi análisis profundo anterior.

1. LoRA vs RsLoRA (rango estabilizado):

LoRA ajusta un modelo entrenando solo dos matrices de rango bajo, B y A, con formas (dimensión, rango) y (rango, dimensión), mientras mantiene los pesos originales congelados (W).[1]. Esto reduce drásticamente la cantidad de parámetros entrenables (en mi caso, solo el 0,18% de todos los pesos).
Hay alfa (α) y rango (r) asociados con LoRA, y alfa/rango actúa como factor de escala. La fórmula es así:

W+ΔW=W+αr(B×A)W + Delta W = W + frac{alpha}{r}(B times A)

Este factor de escala decide la importancia que se le dará a los parámetros ajustados. Si alfa es 32 y el rango es 16, entonces el factor de escala es 2, por lo que los pesos reciben el doble de importancia. Por lo tanto, este factor de escala puede variar. A continuación se ofrece una idea visual:

LoRA agrega una ruta de actualización de bajo rango junto con los pesos congelados previamente entrenados. Imagen del autor.

Pero hay un problema con LoRA, como informó Kalajdzievski, donde argumentó que si el rango sigue aumentando, dividir los parámetros ajustados con el rango eventualmente reduce la importancia de los pesos.[2]. En términos simples: a medida que aumenta el rango, las actualizaciones de peso individuales se reducen y LoRA silenciosamente se vuelve menos efectivo sin que usted se dé cuenta.
Nota: Para aquellos interesados ​​en las matemáticas subyacentes, he incluido la prueba estadística a continuación. De lo contrario, no dudes en pasar directamente a la siguiente sección.

Prueba: las entradas de B y A se inicializan aleatoriamente cuando comenzamos a realizar el ajuste fino (práctica estándar: distribución normal), es decir, Bⱼₖ, Aₖᵢ ~ N(0,σ²).
Entonces, a medida que aumentamos la “r”, la varianza de B*A aumenta proporcionalmente:

[
begin{alineado}
Var(B cdot A) &= Var(sum B_{jk} cdot A_{ki}) \
&= Var(X_1 + X_2 + puntos + X_r) \
&text{(que denota } sum B_{jk} cdot A_{ki} sim text{variables independientes de X)} \
&= Var(X_1) + Var(X_2) + puntos + Var(X_r) \
&text{(recordar la regla básica de probabilidad)} \
&= rcdotc\
&text{(asumiendo que c es un valor de varianza constante)} \
text{Resultado: } &Var(B cdot A) propto r
end{alineado}
]

Pero no podemos detenernos aquí, ya que necesitamos la varianza del factor de escalabilidad contable de pesos ajustados completos (ΔW):

[
begin{alineado}
&phantom{text{(que denota } sum B_{jk} cdot A_{ki} sim text{variables independientes de X)}} \[-2.5ex]
Var(Delta W) &= Varleft(frac{alpha}{r} cdot (B cdot A)right) \
&= frac{alpha^2}{r^2} cdot Var(B cdot A) \
&text{(Regla de variación usada aquí)} \
&= frac{alpha^2}{r^2} cdot (r cdot c) \
&text{(Usando los resultados anteriores)} \
&= frac{1}{r^2} cdot r \
&text{(ya que } alpha^2 text{ yc son constantes)} \
&= 1/r\
text{Resultado: } &Var(Delta W) propto 1/r
end{alineado}
]

Esto muestra que a medida que aumenta la clasificación, la variación de los pesos ajustados disminuye, lo que significa que las actualizaciones de peso se vuelven cada vez más pequeñas. Para resolver este problema cada vez menor, Kalajdzievski introdujo una solución simple y efectiva: reemplazar “r” por “√r”. No volveré a realizar los cálculos, pero lo que resultó fue Var(ΔW) = r/r = 1. Lo que eventualmente hizo que la varianza fuera constante y la magnitud de los pesos se mantuviera estable con cada actualización (como se muestra en el gráfico a continuación). Por lo tanto, es mejor seguir con RsLoRA que con LoRA.

la imagen muestra la estabilidad en términos de variación que ofrece LoRA de rango estabilizado en comparación con LoRA
A medida que aumenta el rango, las actualizaciones de peso de LoRA se reducen (α/r). RsLoRA soluciona este problema manteniendo estable la varianza (α/√r). Imagen por autor

2. RoPE en lugar de parámetros aprendidos o incrustaciones posicionales sinusoidales (PE)

Las incrustaciones posicionales a menudo se tratan como un detalle secundario, pero podríamos subestimar la importancia que tienen y cómo un enfoque incorrecto puede arruinar por completo un modelo LLM enorme. El trabajo de investigación "La atención es todo lo que necesitas"[3]centrado en incrustaciones posicionales sinusoidales (PE). Este enfoque no implicaba parámetros y utilizaba una fórmula fija para generar valores. Sin embargo, conllevaba muchas salvedades: la fórmula fija no era lo suficientemente flexible para captar posiciones relativas y sólo proporcionaba posiciones absolutas. Otro problema importante fue que estas incrustaciones posicionales se agregaron directamente a las incrustaciones de tokens, alterando así la magnitud de la información real que llevaban las incrustaciones de tokens.

Para superarlos, modelos como GPT-2 y GPT-3 comenzaron a utilizar un enfoque basado en parámetros aprendidos. En lugar de depender de una única fórmula fija, se dejó a la red neuronal encontrar la información posicional mediante retropropagación. Si bien esto funcionó en la dirección correcta, nuevamente tuvo algunas advertencias: agregó más carga de parámetros al modelo (context_size * dimensión) y el problema principal, la adición directa a las incrustaciones de tokens, aún persistía.

RoPE (Rotary Positional Embeddings) vino al rescate[4]. Superó la mayoría de los inconvenientes que presentaban los otros dos enfoques. La mayoría de los LLM modernos ahora vienen con RoPE de forma predeterminada, y por una buena razón. A diferencia de los enfoques aprendidos o sinusoidales, RoPE codifica la posición rotando las matrices de consulta y clave en función de su posición y frecuencia, dejando intactas las incrustaciones de tokens. Por lo tanto, logró dos objetivos con un solo esfuerzo: carga cero de parámetros en el modelo y sin adición directa, lo que garantiza que la información real transportada por las incrustaciones de tokens no se modifique.

He cubierto los tres en profundidad con imágenes y un desglose de los pros y los contras: lea el artículo completo aquí.

3. Atado de peso

La vinculación de pesos se refiere a compartir pesos entre la capa de incrustación del token y el cabezal de proyección de salida. Históricamente, GPT, GPT-2 y BERT lo utilizaron. En un modelo de 124 millones de parámetros, guarda 38 millones de parámetros, que es aproximadamente el 30 % de todo el modelo, lo cual fue significativo. La intuición también tenía sentido ya que se incrustaron mapas token → vector y mapas de salida vector → token, convirtiéndolos en transposiciones naturales entre sí. Sin embargo, a medida que los modelos se ampliaron a miles de millones de parámetros, este ahorro de 38 millones pasó a ser menos del 0,5% del total, prácticamente sin sentido. Por eso, la mayoría de los LLM modernos como LLaMA, Mistral y Falcon los mantienen separados, también porque los pesos separados le dan al cabezal de salida libertad para especializarse de forma independiente. La limitación del peso tiene sentido para los modelos pequeños, pero desapareció silenciosamente a medida que los modelos crecieron.

Entonces, si estás construyendo un modelo pequeño desde cero, vale la pena conservarlo. Si está afinando un modelo de mil millones de parámetros, no se moleste en buscarlo, probablemente ya haya desaparecido.

4. Norma previa a la capa frente a norma posterior a la capa

Pre-LN y Post-LN se encuentran en extremos opuestos de una compensación entre estabilidad y desempeño. La arquitectura original "La atención es todo lo que necesita" utilizaba Post-LN (donde la normalización ocurre después de la adición residual). Si bien Post-LN puede conducir a un mejor rendimiento final, es notoriamente difícil de entrenar porque puede provocar que los gradientes exploten o desaparezcan en redes profundas.

A partir de GPT-2, la industria cambió a Pre-LN (donde la normalización ocurre dentro del bloque residual). Esta elección prioriza la estabilidad del entrenamiento, aunque a menudo tiene un ligero costo para el poder de representación final del modelo. Desde entonces, los investigadores han intentado romper con este equilibrio, lo que ha dado lugar a variaciones modernas como DeepNorm, RMSNorm y Double Norm.

La imagen muestra la compensación de estabilidad y rendimiento mostrada por la normalización previa a la capa y la normalización posterior a la capa y comparándolas con DeepNorm, RMSNorm, DoubleNorm.
Compensación estabilidad-rendimiento post-LN y Pre-LN. Imagen del autor: Publicada originalmente en Pre-LN vs Post-LN: el tira y afloja arquitectónico que todo ingeniero de LLM debe conocer

5. Caché KV

El mecanismo de atención es el motor central de Transformer, lo que permite al modelo ponderar dinámicamente la importancia de diferentes tokens a lo largo de una secuencia. Podría decirse que es la innovación más crítica en la IA moderna, ya que permite que el modelo mantenga un contexto de largo alcance y se "centre" en información relevante.
Existen tres componentes diferentes dentro del mecanismo de atención: Consulta, Clave y Valor.

Consulta (Q): representa el token actual en el que se centra el modelo Clave (K): se utiliza con la consulta para encontrar la relación del token actual con otros tokens Valor (V): el contenido real que comparte un token si se selecciona

Durante la inferencia, los tokens se predicen uno a la vez de forma autorregresiva. Cada nuevo token atiende a todos los tokens anteriores, lo que significa que las matrices K y V se recalculaban desde cero para cada token visto anteriormente, cada vez. Antieconómico.

La solución es simple: simplemente almacene en caché las matrices K y V a medida que avanza. Cada token nuevo solo necesita calcular su propia K y V y luego recuperar el resto del caché. Esto reduce la complejidad del tiempo de O(T²) a O(T) para una secuencia de longitud T.

La imagen muestra la diferencia entre los enfoques KV Cache y Without KV Cache, lo que genera una mayor complejidad temporal.
Sin KV Cache, K y V se vuelven a calcular desde cero en cada paso. Con KV Cache, solo se calculan K y V del nuevo token, el resto se recupera instantáneamente. Imagen del autor.

La aceleración real: para una secuencia de 15 tokens, sin caché KV, estás haciendo 15 cálculos completos de K y V por paso. Con el caché, haces 1. Eso es aproximadamente una reducción de 15 veces en el cálculo de atención. En la práctica, se ve una aceleración general del doble teniendo en cuenta otras operaciones.

Pero hay una desventaja que nadie menciona: la caché KV no es gratuita. Consume memoria proporcional a la dimensión número_de_capas * longitud_secuencia *. Para contextos largos, esto se vuelve significativo, y es exactamente por eso que la memoria es el cuello de botella en el servicio LLM, no la computación.

Esta sobrecarga de memoria ha sido un importante desafío de investigación y, recientemente, Google Research introdujo un gran avance para abordarla. En su artículo de 2026, "TurboQuant: cuantificación de vectores en línea con una tasa de distorsión casi óptima"[5], los investigadores demostraron una forma de comprimir la caché KV a solo 3 bits por valor.

Esta técnica logra una reducción de 5 a 6 veces en el consumo de memoria sin pérdida de precisión. Funciona girando las coordenadas dimensionales para que sigan una distribución Beta y luego aplicando la cuantificación Lloyd-Max combinada con una transformación cuantificada de Johnson-Lindenstrauss (QJL) de 1 bit para corregir los errores residuales. B Este enfoque desbloquea la pared de la memoria y permite que los modelos manejen contextos masivos que antes requerían múltiples GPU en un solo chip.

6. Compensación de cuantificación: por qué se omite LayerNorm durante la cuantificación INT8

Los LLM modernos son enormes. Almacenarlos y ejecutarlos con precisión de punto flotante de 32 o 16 bits es costoso, tanto en memoria como en computación. La cuantización es el proceso de reducir la precisión numérica de los pesos del modelo, generalmente desde flotantes de 32 bits hasta enteros de 8 bits (INT8) o incluso de 4 bits. Esto hace que los modelos sean significativamente más baratos de almacenar y más rápidos de ejecutar, razón por la cual casi todas las implementaciones de LLM de producción utilizan alguna forma de cuantificación.[6].

Pero la cuantización no se aplica ciegamente a todas las capas por igual, y aquí es donde se pone interesante.

LayerNorm casi siempre se omite durante la cuantización INT8. La razón es un simple cálculo de costo-beneficio que la mayoría de los artículos nunca explican.

El beneficio es insignificante: LayerNorm casi no tiene parámetros, solo γ y β, un puñado de valores en comparación con los millones que se encuentran en una sola capa lineal. En un modelo de parámetros de 124M, esta es una fracción insignificante de la memoria total. Los ahorros que se obtienen al cuantificarlos son esencialmente nulos. El costo es alto: LayerNorm es matemáticamente sensible. Calcula la media y la varianza en la incorporación de cada token y luego aplica γ y β para reescalar. Los pequeños errores de precisión en estos parámetros, que introduce INT8, distorsionan directamente la salida normalizada y caen en cascada en cada capa posterior.

La compensación es clara: cuantice LayerNorm y no ganará casi nada al mismo tiempo que introduce una degradación significativa de la calidad. Así se mantiene con total precisión.

Esta es una lección más amplia sobre cuantización, no todos los parámetros son iguales. La pregunta nunca es simplemente “¿cuántos bytes ahorra esto?” pero “¿qué tan sensible es esta capa a la pérdida de precisión en relación con lo que ahorramos?”.

Conclusión

Estas 6 cosas no son secretos, se esconden a plena vista en cada LLM importante. Pero los tutoriales rara vez se detienen a explicar el por qué detrás de ellos. Por qué rsLoRA soluciona un problema de variación que la mayoría de la gente nunca nota. Por qué RoPE deja intactas las incrustaciones de tokens. Por qué la sujeción del peso desapareció silenciosamente a medida que los modelos crecieron. Por qué Pre-LN cambia el desempeño por la estabilidad. Por qué KV Cache convierte O(T²) en O(T). Por qué LayerNorm sobrevive a la cuantificación con total precisión.

Construir desde cero te obliga a afrontar cada una de estas decisiones. No puedes abstraerlos. Y es exactamente por eso que lo recomendaría a cualquiera que quiera entender realmente cómo funcionan estos sistemas, no sólo usarlos.

Estas seis observaciones son sólo la superficie de lo que encontré mientras construía este modelo. En mis próximas publicaciones, profundizaré en las matemáticas específicas de los errores de cuantificación y los desafíos prácticos de implementar LLM a escala. Si está interesado en la intersección de la teoría estadística y la ingeniería de ML, siga la próxima entrega.

Referencias

[1]E. Hu, Y. Shen, P. Wallis et al., LoRA: Adaptación de bajo rango de modelos de lenguaje grandes (2021), arXiv:2106.09685

[2]D. Kalajdzievski, Un factor de escala de estabilización de rango para el ajuste fino con LoRA (2023), arXiv:2312.03732

[3]A. Vaswani, N. Shazeer, N. Parmar et al., La atención es todo lo que necesita (2017), arXiv:1706.03762

[4]J. Su, Y. Lu, S. Pan et al., RoFormer: Transformador mejorado con incrustación de posición giratoria (2021), arXiv:2104.09864

[5]Zandieh et al., TurboQuant: Cuantización de vectores en línea con una tasa de distorsión casi óptima (2025), arXiv:2504.19874.

[6]T. Dettmers, M. Lewis, Y. Belkada, L. Zettlemoyer, LLM.int8(): Multiplicación de matrices de 8 bits para transformadores a escala (2022), arXiv:2208.07339