5. Arquitectura LLM

Reading time: 20 minutes

tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Arquitectura LLM

tip

El objetivo de esta quinta fase es muy simple: Desarrollar la arquitectura del LLM completo. Juntar todo, aplicar todas las capas y crear todas las funciones para generar texto o transformar texto a IDs y viceversa.

Esta arquitectura se utilizar谩 tanto para entrenar como para predecir texto despu茅s de haber sido entrenada.

Ejemplo de arquitectura LLM de https://github.com/rasbt/LLMs-from-scratch/blob/main/ch04/01_main-chapter-code/ch04.ipynb:

Se puede observar una representaci贸n de alto nivel en:

https://camo.githubusercontent.com/6c8c392f72d5b9e86c94aeb9470beab435b888d24135926f1746eb88e0cc18fb/68747470733a2f2f73656261737469616e72617363686b612e636f6d2f696d616765732f4c4c4d732d66726f6d2d736372617463682d696d616765732f636830345f636f6d707265737365642f31332e776562703f31

  1. Entrada (Texto Tokenizado): El proceso comienza con texto tokenizado, que se convierte en representaciones num茅ricas.
  2. Capa de Embedding de Tokens y Capa de Embedding Posicional: El texto tokenizado se pasa a trav茅s de una capa de embedding de tokens y una capa de embedding posicional, que captura la posici贸n de los tokens en una secuencia, cr铆tica para entender el orden de las palabras.
  3. Bloques de Transformer: El modelo contiene 12 bloques de transformer, cada uno con m煤ltiples capas. Estos bloques repiten la siguiente secuencia:
  • Atenci贸n Multi-Cabeza enmascarada: Permite que el modelo se enfoque en diferentes partes del texto de entrada a la vez.
  • Normalizaci贸n de Capa: Un paso de normalizaci贸n para estabilizar y mejorar el entrenamiento.
  • Capa Feed Forward: Responsable de procesar la informaci贸n de la capa de atenci贸n y hacer predicciones sobre el siguiente token.
  • Capas de Dropout: Estas capas previenen el sobreajuste al eliminar aleatoriamente unidades durante el entrenamiento.
  1. Capa de Salida Final: El modelo produce un tensor de 4x50,257 dimensiones, donde 50,257 representa el tama帽o del vocabulario. Cada fila en este tensor corresponde a un vector que el modelo utiliza para predecir la siguiente palabra en la secuencia.
  2. Objetivo: El objetivo es tomar estos embeddings y convertirlos de nuevo en texto. Espec铆ficamente, la 煤ltima fila de la salida se utiliza para generar la siguiente palabra, representada como "adelante" en este diagrama.

Representaci贸n de C贸digo

python
import torch
import torch.nn as nn
import tiktoken

class GELU(nn.Module):
def __init__(self):
super().__init__()

def forward(self, x):
return 0.5 * x * (1 + torch.tanh(
torch.sqrt(torch.tensor(2.0 / torch.pi)) *
(x + 0.044715 * torch.pow(x, 3))
))

class FeedForward(nn.Module):
def __init__(self, cfg):
super().__init__()
self.layers = nn.Sequential(
nn.Linear(cfg["emb_dim"], 4 * cfg["emb_dim"]),
GELU(),
nn.Linear(4 * cfg["emb_dim"], cfg["emb_dim"]),
)

def forward(self, x):
return self.layers(x)

class MultiHeadAttention(nn.Module):
def __init__(self, d_in, d_out, context_length, dropout, num_heads, qkv_bias=False):
super().__init__()
assert d_out % num_heads == 0, "d_out must be divisible by num_heads"

self.d_out = d_out
self.num_heads = num_heads
self.head_dim = d_out // num_heads # Reduce the projection dim to match desired output dim

self.W_query = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_key = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_value = nn.Linear(d_in, d_out, bias=qkv_bias)
self.out_proj = nn.Linear(d_out, d_out)  # Linear layer to combine head outputs
self.dropout = nn.Dropout(dropout)
self.register_buffer('mask', torch.triu(torch.ones(context_length, context_length), diagonal=1))

def forward(self, x):
b, num_tokens, d_in = x.shape

keys = self.W_key(x) # Shape: (b, num_tokens, d_out)
queries = self.W_query(x)
values = self.W_value(x)

# We implicitly split the matrix by adding a `num_heads` dimension
# Unroll last dim: (b, num_tokens, d_out) -> (b, num_tokens, num_heads, head_dim)
keys = keys.view(b, num_tokens, self.num_heads, self.head_dim)
values = values.view(b, num_tokens, self.num_heads, self.head_dim)
queries = queries.view(b, num_tokens, self.num_heads, self.head_dim)

# Transpose: (b, num_tokens, num_heads, head_dim) -> (b, num_heads, num_tokens, head_dim)
keys = keys.transpose(1, 2)
queries = queries.transpose(1, 2)
values = values.transpose(1, 2)

# Compute scaled dot-product attention (aka self-attention) with a causal mask
attn_scores = queries @ keys.transpose(2, 3)  # Dot product for each head

# Original mask truncated to the number of tokens and converted to boolean
mask_bool = self.mask.bool()[:num_tokens, :num_tokens]

# Use the mask to fill attention scores
attn_scores.masked_fill_(mask_bool, -torch.inf)

attn_weights = torch.softmax(attn_scores / keys.shape[-1]**0.5, dim=-1)
attn_weights = self.dropout(attn_weights)

# Shape: (b, num_tokens, num_heads, head_dim)
context_vec = (attn_weights @ values).transpose(1, 2)

# Combine heads, where self.d_out = self.num_heads * self.head_dim
context_vec = context_vec.contiguous().view(b, num_tokens, self.d_out)
context_vec = self.out_proj(context_vec) # optional projection

return context_vec

class LayerNorm(nn.Module):
def __init__(self, emb_dim):
super().__init__()
self.eps = 1e-5
self.scale = nn.Parameter(torch.ones(emb_dim))
self.shift = nn.Parameter(torch.zeros(emb_dim))

def forward(self, x):
mean = x.mean(dim=-1, keepdim=True)
var = x.var(dim=-1, keepdim=True, unbiased=False)
norm_x = (x - mean) / torch.sqrt(var + self.eps)
return self.scale * norm_x + self.shift

class TransformerBlock(nn.Module):
def __init__(self, cfg):
super().__init__()
self.att = MultiHeadAttention(
d_in=cfg["emb_dim"],
d_out=cfg["emb_dim"],
context_length=cfg["context_length"],
num_heads=cfg["n_heads"],
dropout=cfg["drop_rate"],
qkv_bias=cfg["qkv_bias"])
self.ff = FeedForward(cfg)
self.norm1 = LayerNorm(cfg["emb_dim"])
self.norm2 = LayerNorm(cfg["emb_dim"])
self.drop_shortcut = nn.Dropout(cfg["drop_rate"])

def forward(self, x):
# Shortcut connection for attention block
shortcut = x
x = self.norm1(x)
x = self.att(x)  # Shape [batch_size, num_tokens, emb_size]
x = self.drop_shortcut(x)
x = x + shortcut  # Add the original input back

# Shortcut connection for feed forward block
shortcut = x
x = self.norm2(x)
x = self.ff(x)
x = self.drop_shortcut(x)
x = x + shortcut  # Add the original input back

return x


class GPTModel(nn.Module):
def __init__(self, cfg):
super().__init__()
self.tok_emb = nn.Embedding(cfg["vocab_size"], cfg["emb_dim"])
self.pos_emb = nn.Embedding(cfg["context_length"], cfg["emb_dim"])
self.drop_emb = nn.Dropout(cfg["drop_rate"])

self.trf_blocks = nn.Sequential(
*[TransformerBlock(cfg) for _ in range(cfg["n_layers"])])

self.final_norm = LayerNorm(cfg["emb_dim"])
self.out_head = nn.Linear(
cfg["emb_dim"], cfg["vocab_size"], bias=False
)

def forward(self, in_idx):
batch_size, seq_len = in_idx.shape
tok_embeds = self.tok_emb(in_idx)
pos_embeds = self.pos_emb(torch.arange(seq_len, device=in_idx.device))
x = tok_embeds + pos_embeds  # Shape [batch_size, num_tokens, emb_size]
x = self.drop_emb(x)
x = self.trf_blocks(x)
x = self.final_norm(x)
logits = self.out_head(x)
return logits

GPT_CONFIG_124M = {
"vocab_size": 50257,    # Vocabulary size
"context_length": 1024, # Context length
"emb_dim": 768,         # Embedding dimension
"n_heads": 12,          # Number of attention heads
"n_layers": 12,         # Number of layers
"drop_rate": 0.1,       # Dropout rate
"qkv_bias": False       # Query-Key-Value bias
}

torch.manual_seed(123)
model = GPTModel(GPT_CONFIG_124M)
out = model(batch)
print("Input batch:\n", batch)
print("\nOutput shape:", out.shape)
print(out)

Funci贸n de Activaci贸n GELU

python
# From https://github.com/rasbt/LLMs-from-scratch/tree/main/ch04
class GELU(nn.Module):
def __init__(self):
super().__init__()

def forward(self, x):
return 0.5 * x * (1 + torch.tanh(
torch.sqrt(torch.tensor(2.0 / torch.pi)) *
(x + 0.044715 * torch.pow(x, 3))
))

Prop贸sito y Funcionalidad

  • GELU (Unidad Lineal de Error Gaussiano): Una funci贸n de activaci贸n que introduce no linealidad en el modelo.
  • Activaci贸n Suave: A diferencia de ReLU, que anula las entradas negativas, GELU mapea suavemente las entradas a salidas, permitiendo valores peque帽os y no nulos para entradas negativas.
  • Definici贸n Matem谩tica:

tip

El objetivo del uso de esta funci贸n despu茅s de las capas lineales dentro de la capa FeedForward es cambiar los datos lineales a no lineales para permitir que el modelo aprenda relaciones complejas y no lineales.

Red Neuronal FeedForward

Las formas se han a帽adido como comentarios para entender mejor las formas de las matrices:

python
# From https://github.com/rasbt/LLMs-from-scratch/tree/main/ch04
class FeedForward(nn.Module):
def __init__(self, cfg):
super().__init__()
self.layers = nn.Sequential(
nn.Linear(cfg["emb_dim"], 4 * cfg["emb_dim"]),
GELU(),
nn.Linear(4 * cfg["emb_dim"], cfg["emb_dim"]),
)

def forward(self, x):
# x shape: (batch_size, seq_len, emb_dim)

x = self.layers[0](x)# x shape: (batch_size, seq_len, 4 * emb_dim)
x = self.layers[1](x) # x shape remains: (batch_size, seq_len, 4 * emb_dim)
x = self.layers[2](x) # x shape: (batch_size, seq_len, emb_dim)
return x  # Output shape: (batch_size, seq_len, emb_dim)

Prop贸sito y Funcionalidad

  • Red FeedForward por Posici贸n: Aplica una red completamente conectada de dos capas a cada posici贸n de manera separada e id茅ntica.
  • Detalles de la Capa:
  • Primera Capa Lineal: Expande la dimensionalidad de emb_dim a 4 * emb_dim.
  • Activaci贸n GELU: Aplica no linealidad.
  • Segunda Capa Lineal: Reduce la dimensionalidad de nuevo a emb_dim.

tip

Como puedes ver, la red Feed Forward utiliza 3 capas. La primera es una capa lineal que multiplicar谩 las dimensiones por 4 usando pesos lineales (par谩metros a entrenar dentro del modelo). Luego, se utiliza la funci贸n GELU en todas esas dimensiones para aplicar variaciones no lineales y capturar representaciones m谩s ricas y, finalmente, se utiliza otra capa lineal para volver al tama帽o original de las dimensiones.

Mecanismo de Atenci贸n Multi-Cabeza

Esto ya fue explicado en una secci贸n anterior.

Prop贸sito y Funcionalidad

  • Autoatenci贸n Multi-Cabeza: Permite que el modelo se enfoque en diferentes posiciones dentro de la secuencia de entrada al codificar un token.
  • Componentes Clave:
  • Consultas, Claves, Valores: Proyecciones lineales de la entrada, utilizadas para calcular puntajes de atenci贸n.
  • Cabezas: M煤ltiples mecanismos de atenci贸n que funcionan en paralelo (num_heads), cada uno con una dimensi贸n reducida (head_dim).
  • Puntajes de Atenci贸n: Calculados como el producto punto de consultas y claves, escalados y enmascarados.
  • Enmascaramiento: Se aplica una m谩scara causal para evitar que el modelo preste atenci贸n a tokens futuros (importante para modelos autorregresivos como GPT).
  • Pesos de Atenci贸n: Softmax de los puntajes de atenci贸n enmascarados y escalados.
  • Vector de Contexto: Suma ponderada de los valores, de acuerdo con los pesos de atenci贸n.
  • Proyecci贸n de Salida: Capa lineal para combinar las salidas de todas las cabezas.

tip

El objetivo de esta red es encontrar las relaciones entre tokens en el mismo contexto. Adem谩s, los tokens se dividen en diferentes cabezas para prevenir el sobreajuste, aunque las relaciones finales encontradas por cabeza se combinan al final de esta red.

Adem谩s, durante el entrenamiento se aplica una m谩scara causal para que los tokens posteriores no se tengan en cuenta al buscar las relaciones espec铆ficas con un token y tambi茅n se aplica un dropout para prevenir el sobreajuste.

Normalizaci贸n de Capa

python
# From https://github.com/rasbt/LLMs-from-scratch/tree/main/ch04
class LayerNorm(nn.Module):
def __init__(self, emb_dim):
super().__init__()
self.eps = 1e-5 # Prevent division by zero during normalization.
self.scale = nn.Parameter(torch.ones(emb_dim))
self.shift = nn.Parameter(torch.zeros(emb_dim))

def forward(self, x):
mean = x.mean(dim=-1, keepdim=True)
var = x.var(dim=-1, keepdim=True, unbiased=False)
norm_x = (x - mean) / torch.sqrt(var + self.eps)
return self.scale * norm_x + self.shift

Prop贸sito y Funcionalidad

  • Layer Normalization: Una t茅cnica utilizada para normalizar las entradas a trav茅s de las caracter铆sticas (dimensiones de embedding) para cada ejemplo individual en un lote.
  • Componentes:
  • eps: Una constante peque帽a (1e-5) a帽adida a la varianza para prevenir la divisi贸n por cero durante la normalizaci贸n.
  • scale y shift: Par谩metros aprendibles (nn.Parameter) que permiten al modelo escalar y desplazar la salida normalizada. Se inicializan en unos y ceros, respectivamente.
  • Proceso de Normalizaci贸n:
  • Calcular Media (mean): Calcula la media de la entrada x a trav茅s de la dimensi贸n de embedding (dim=-1), manteniendo la dimensi贸n para la difusi贸n (keepdim=True).
  • Calcular Varianza (var): Calcula la varianza de x a trav茅s de la dimensi贸n de embedding, tambi茅n manteniendo la dimensi贸n. El par谩metro unbiased=False asegura que la varianza se calcule utilizando el estimador sesgado (dividiendo por N en lugar de N-1), lo cual es apropiado al normalizar sobre caracter铆sticas en lugar de muestras.
  • Normalizar (norm_x): Resta la media de x y divide por la ra铆z cuadrada de la varianza m谩s eps.
  • Escalar y Desplazar: Aplica los par谩metros aprendibles scale y shift a la salida normalizada.

tip

El objetivo es asegurar una media de 0 con una varianza de 1 a trav茅s de todas las dimensiones del mismo token. El objetivo de esto es estabilizar el entrenamiento de redes neuronales profundas al reducir el cambio interno de covariables, que se refiere al cambio en la distribuci贸n de las activaciones de la red debido a la actualizaci贸n de par谩metros durante el entrenamiento.

Bloque Transformer

Las formas se han a帽adido como comentarios para entender mejor las formas de las matrices:

python
# From https://github.com/rasbt/LLMs-from-scratch/tree/main/ch04

class TransformerBlock(nn.Module):
def __init__(self, cfg):
super().__init__()
self.att = MultiHeadAttention(
d_in=cfg["emb_dim"],
d_out=cfg["emb_dim"],
context_length=cfg["context_length"],
num_heads=cfg["n_heads"],
dropout=cfg["drop_rate"],
qkv_bias=cfg["qkv_bias"]
)
self.ff = FeedForward(cfg)
self.norm1 = LayerNorm(cfg["emb_dim"])
self.norm2 = LayerNorm(cfg["emb_dim"])
self.drop_shortcut = nn.Dropout(cfg["drop_rate"])

def forward(self, x):
# x shape: (batch_size, seq_len, emb_dim)

# Shortcut connection for attention block
shortcut = x  # shape: (batch_size, seq_len, emb_dim)
x = self.norm1(x)  # shape remains (batch_size, seq_len, emb_dim)
x = self.att(x)    # shape: (batch_size, seq_len, emb_dim)
x = self.drop_shortcut(x)  # shape remains (batch_size, seq_len, emb_dim)
x = x + shortcut   # shape: (batch_size, seq_len, emb_dim)

# Shortcut connection for feedforward block
shortcut = x       # shape: (batch_size, seq_len, emb_dim)
x = self.norm2(x)  # shape remains (batch_size, seq_len, emb_dim)
x = self.ff(x)     # shape: (batch_size, seq_len, emb_dim)
x = self.drop_shortcut(x)  # shape remains (batch_size, seq_len, emb_dim)
x = x + shortcut   # shape: (batch_size, seq_len, emb_dim)

return x  # Output shape: (batch_size, seq_len, emb_dim)

Prop贸sito y Funcionalidad

  • Composici贸n de Capas: Combina atenci贸n multi-cabeza, red de avance, normalizaci贸n de capas y conexiones residuales.
  • Normalizaci贸n de Capas: Aplicada antes de las capas de atenci贸n y avance para un entrenamiento estable.
  • Conexiones Residuales (Atajos): A帽aden la entrada de una capa a su salida para mejorar el flujo de gradientes y permitir el entrenamiento de redes profundas.
  • Dropout: Aplicado despu茅s de las capas de atenci贸n y avance para regularizaci贸n.

Funcionalidad Paso a Paso

  1. Primer Camino Residual (Autoatenci贸n):
  • Entrada (shortcut): Guarda la entrada original para la conexi贸n residual.
  • Norma de Capa (norm1): Normaliza la entrada.
  • Atenci贸n Multi-Cabeza (att): Aplica autoatenci贸n.
  • Dropout (drop_shortcut): Aplica dropout para regularizaci贸n.
  • A帽adir Residual (x + shortcut): Combina con la entrada original.
  1. Segundo Camino Residual (FeedForward):
  • Entrada (shortcut): Guarda la entrada actualizada para la siguiente conexi贸n residual.
  • Norma de Capa (norm2): Normaliza la entrada.
  • Red de Avance (ff): Aplica la transformaci贸n de avance.
  • Dropout (drop_shortcut): Aplica dropout.
  • A帽adir Residual (x + shortcut): Combina con la entrada del primer camino residual.

tip

El bloque transformer agrupa todas las redes y aplica alguna normalizaci贸n y dropouts para mejorar la estabilidad y los resultados del entrenamiento.
Nota c贸mo se realizan los dropouts despu茅s del uso de cada red mientras que la normalizaci贸n se aplica antes.

Adem谩s, tambi茅n utiliza atajos que consisten en a帽adir la salida de una red con su entrada. Esto ayuda a prevenir el problema del gradiente que se desvanece al asegurar que las capas iniciales contribuyan "tanto" como las 煤ltimas.

GPTModel

Las formas se han a帽adido como comentarios para entender mejor las formas de las matrices:

python
# From https://github.com/rasbt/LLMs-from-scratch/tree/main/ch04
class GPTModel(nn.Module):
def __init__(self, cfg):
super().__init__()
self.tok_emb = nn.Embedding(cfg["vocab_size"], cfg["emb_dim"])
# shape: (vocab_size, emb_dim)

self.pos_emb = nn.Embedding(cfg["context_length"], cfg["emb_dim"])
# shape: (context_length, emb_dim)

self.drop_emb = nn.Dropout(cfg["drop_rate"])

self.trf_blocks = nn.Sequential(
*[TransformerBlock(cfg) for _ in range(cfg["n_layers"])]
)
# Stack of TransformerBlocks

self.final_norm = LayerNorm(cfg["emb_dim"])
self.out_head = nn.Linear(cfg["emb_dim"], cfg["vocab_size"], bias=False)
# shape: (emb_dim, vocab_size)

def forward(self, in_idx):
# in_idx shape: (batch_size, seq_len)
batch_size, seq_len = in_idx.shape

# Token embeddings
tok_embeds = self.tok_emb(in_idx)
# shape: (batch_size, seq_len, emb_dim)

# Positional embeddings
pos_indices = torch.arange(seq_len, device=in_idx.device)
# shape: (seq_len,)
pos_embeds = self.pos_emb(pos_indices)
# shape: (seq_len, emb_dim)

# Add token and positional embeddings
x = tok_embeds + pos_embeds  # Broadcasting over batch dimension
# x shape: (batch_size, seq_len, emb_dim)

x = self.drop_emb(x)  # Dropout applied
# x shape remains: (batch_size, seq_len, emb_dim)

x = self.trf_blocks(x)  # Pass through Transformer blocks
# x shape remains: (batch_size, seq_len, emb_dim)

x = self.final_norm(x)  # Final LayerNorm
# x shape remains: (batch_size, seq_len, emb_dim)

logits = self.out_head(x)  # Project to vocabulary size
# logits shape: (batch_size, seq_len, vocab_size)

return logits  # Output shape: (batch_size, seq_len, vocab_size)

Prop贸sito y Funcionalidad

  • Capas de Embedding:
  • Embeddings de Tokens (tok_emb): Convierte 铆ndices de tokens en embeddings. Como recordatorio, estos son los pesos dados a cada dimensi贸n de cada token en el vocabulario.
  • Embeddings Posicionales (pos_emb): Agrega informaci贸n posicional a los embeddings para capturar el orden de los tokens. Como recordatorio, estos son los pesos dados a los tokens de acuerdo a su posici贸n en el texto.
  • Dropout (drop_emb): Aplicado a los embeddings para regularizaci贸n.
  • Bloques de Transformer (trf_blocks): Pila de n_layers bloques de transformer para procesar embeddings.
  • Normalizaci贸n Final (final_norm): Normalizaci贸n de capa antes de la capa de salida.
  • Capa de Salida (out_head): Proyecta los estados ocultos finales al tama帽o del vocabulario para producir logits para la predicci贸n.

tip

El objetivo de esta clase es usar todas las otras redes mencionadas para predecir el siguiente token en una secuencia, lo cual es fundamental para tareas como la generaci贸n de texto.

Nota c贸mo usar谩 tantos bloques de transformer como se indique y que cada bloque de transformer est谩 utilizando una red de atenci贸n multi-cabeza, una red de avance y varias normalizaciones. As铆 que si se utilizan 12 bloques de transformer, multiplica esto por 12.

Adem谩s, se agrega una capa de normalizaci贸n antes de la salida y se aplica una capa lineal final al final para obtener los resultados con las dimensiones adecuadas. Nota c贸mo cada vector final tiene el tama帽o del vocabulario utilizado. Esto se debe a que est谩 tratando de obtener una probabilidad por cada token posible dentro del vocabulario.

N煤mero de Par谩metros a Entrenar

Teniendo la estructura de GPT definida, es posible averiguar el n煤mero de par谩metros a entrenar:

python
GPT_CONFIG_124M = {
"vocab_size": 50257,    # Vocabulary size
"context_length": 1024, # Context length
"emb_dim": 768,         # Embedding dimension
"n_heads": 12,          # Number of attention heads
"n_layers": 12,         # Number of layers
"drop_rate": 0.1,       # Dropout rate
"qkv_bias": False       # Query-Key-Value bias
}

model = GPTModel(GPT_CONFIG_124M)
total_params = sum(p.numel() for p in model.parameters())
print(f"Total number of parameters: {total_params:,}")
# Total number of parameters: 163,009,536

C谩lculo Paso a Paso

1. Capas de Embedding: Embedding de Tokens y Embedding de Posici贸n

  • Capa: nn.Embedding(vocab_size, emb_dim)
  • Par谩metros: vocab_size * emb_dim
python
token_embedding_params = 50257 * 768 = 38,597,376
  • Capa: nn.Embedding(context_length, emb_dim)
  • Par谩metros: context_length * emb_dim
python
position_embedding_params = 1024 * 768 = 786,432

Total de Par谩metros de Embedding

python
embedding_params = token_embedding_params + position_embedding_params
embedding_params = 38,597,376 + 786,432 = 39,383,808

2. Bloques de Transformador

Hay 12 bloques de transformador, as铆 que calcularemos los par谩metros para un bloque y luego multiplicaremos por 12.

Par谩metros por Bloque de Transformador

a. Atenci贸n Multi-Cabeza

  • Componentes:

  • Capa Lineal de Consulta (W_query): nn.Linear(emb_dim, emb_dim, bias=False)

  • Capa Lineal de Clave (W_key): nn.Linear(emb_dim, emb_dim, bias=False)

  • Capa Lineal de Valor (W_value): nn.Linear(emb_dim, emb_dim, bias=False)

  • Proyecci贸n de Salida (out_proj): nn.Linear(emb_dim, emb_dim)

  • C谩lculos:

  • Cada uno de W_query, W_key, W_value:

python
qkv_params = emb_dim * emb_dim = 768 * 768 = 589,824

Dado que hay tres capas de este tipo:

python
total_qkv_params = 3 * qkv_params = 3 * 589,824 = 1,769,472
  • Proyecci贸n de Salida (out_proj):
python
out_proj_params = (emb_dim * emb_dim) + emb_dim = (768 * 768) + 768 = 589,824 + 768 = 590,592
  • Total de Par谩metros de Atenci贸n Multi-Cabeza:
python
mha_params = total_qkv_params + out_proj_params
mha_params = 1,769,472 + 590,592 = 2,360,064

b. Red FeedForward

  • Componentes:

  • Primera Capa Lineal: nn.Linear(emb_dim, 4 * emb_dim)

  • Segunda Capa Lineal: nn.Linear(4 * emb_dim, emb_dim)

  • C谩lculos:

  • Primera Capa Lineal:

python
ff_first_layer_params = (emb_dim * 4 * emb_dim) + (4 * emb_dim)
ff_first_layer_params = (768 * 3072) + 3072 = 2,359,296 + 3,072 = 2,362,368
  • Segunda Capa Lineal:
python
ff_second_layer_params = (4 * emb_dim * emb_dim) + emb_dim
ff_second_layer_params = (3072 * 768) + 768 = 2,359,296 + 768 = 2,360,064
  • Total de Par谩metros FeedForward:
python
ff_params = ff_first_layer_params + ff_second_layer_params
ff_params = 2,362,368 + 2,360,064 = 4,722,432

c. Normalizaciones de Capa

  • Componentes:
  • Dos instancias de LayerNorm por bloque.
  • Cada LayerNorm tiene 2 * emb_dim par谩metros (escala y desplazamiento).
  • C谩lculos:
python
layer_norm_params_per_block = 2 * (2 * emb_dim) = 2 * 768 * 2 = 3,072

d. Total de Par谩metros por Bloque de Transformador

python
pythonCopy codeparams_per_block = mha_params + ff_params + layer_norm_params_per_block
params_per_block = 2,360,064 + 4,722,432 + 3,072 = 7,085,568

Total de Par谩metros para Todos los Bloques de Transformadores

python
pythonCopy codetotal_transformer_blocks_params = params_per_block * n_layers
total_transformer_blocks_params = 7,085,568 * 12 = 85,026,816

3. Capas Finales

a. Normalizaci贸n de la Capa Final

  • Par谩metros: 2 * emb_dim (escalar y desplazar)
python
pythonCopy codefinal_layer_norm_params = 2 * 768 = 1,536

b. Capa de Proyecci贸n de Salida (out_head)

  • Capa: nn.Linear(emb_dim, vocab_size, bias=False)
  • Par谩metros: emb_dim * vocab_size
python
pythonCopy codeoutput_projection_params = 768 * 50257 = 38,597,376

4. Resumiendo Todos los Par谩metros

python
pythonCopy codetotal_params = (
embedding_params +
total_transformer_blocks_params +
final_layer_norm_params +
output_projection_params
)
total_params = (
39,383,808 +
85,026,816 +
1,536 +
38,597,376
)
total_params = 163,009,536

Generar Texto

Teniendo un modelo que predice el siguiente token como el anterior, solo es necesario tomar los valores del 煤ltimo token de la salida (ya que ser谩n los del token predicho), que ser谩 un valor por entrada en el vocabulario y luego usar la funci贸n softmax para normalizar las dimensiones en probabilidades que sumen 1 y luego obtener el 铆ndice de la entrada m谩s grande, que ser谩 el 铆ndice de la palabra dentro del vocabulario.

C贸digo de https://github.com/rasbt/LLMs-from-scratch/blob/main/ch04/01_main-chapter-code/ch04.ipynb:

python
def generate_text_simple(model, idx, max_new_tokens, context_size):
# idx is (batch, n_tokens) array of indices in the current context
for _ in range(max_new_tokens):

# Crop current context if it exceeds the supported context size
# E.g., if LLM supports only 5 tokens, and the context size is 10
# then only the last 5 tokens are used as context
idx_cond = idx[:, -context_size:]

# Get the predictions
with torch.no_grad():
logits = model(idx_cond)

# Focus only on the last time step
# (batch, n_tokens, vocab_size) becomes (batch, vocab_size)
logits = logits[:, -1, :]

# Apply softmax to get probabilities
probas = torch.softmax(logits, dim=-1)  # (batch, vocab_size)

# Get the idx of the vocab entry with the highest probability value
idx_next = torch.argmax(probas, dim=-1, keepdim=True)  # (batch, 1)

# Append sampled index to the running sequence
idx = torch.cat((idx, idx_next), dim=1)  # (batch, n_tokens+1)

return idx


start_context = "Hello, I am"

encoded = tokenizer.encode(start_context)
print("encoded:", encoded)

encoded_tensor = torch.tensor(encoded).unsqueeze(0)
print("encoded_tensor.shape:", encoded_tensor.shape)

model.eval() # disable dropout

out = generate_text_simple(
model=model,
idx=encoded_tensor,
max_new_tokens=6,
context_size=GPT_CONFIG_124M["context_length"]
)

print("Output:", out)
print("Output length:", len(out[0]))

Referencias

tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks