Preparación para entrevistas de LLM e IA Generativa

Preguntas de Entrevista para Ingenieros de LLM e IA Generativa

15 preguntas seleccionadas de entrevista sobre LLM e IA generativa, agrupadas por nivel de experiencia. Úsalas para repasar los fundamentos, las compensaciones prácticas y el razonamiento de producción a nivel senior.

Iniciar una Entrevista de IA de LLM e IA GenerativaNo se requiere tarjeta de crédito. 1 sesión gratuita disponible.
Práctica de entrevistas técnicas en inglésDiseñado para hablantes no nativos que desean practicar entrevistas técnicas en inglés.

Preguntas para Junior

1Explica la tokenización de subpalabras y por qué se prefiere a la tokenización a nivel de palabra o a nivel de carácter en los modelos de lenguaje modernos.

La tokenización de subpalabras es un enfoque híbrido de segmentación de texto que divide el texto en fragmentos morfológicos de longitud variable o subcadenas basadas en frecuencia (como 'in', 'des', 'componer') en lugar de palabras completas o caracteres individuales. Algoritmos como Byte-Pair Encoding (BPE), WordPiece y Unigram LM aprenden un vocabulario de tamaño fijo a partir de un corpus de entrenamiento, donde las palabras frecuentes permanecen intactas como tokens únicos, mientras que las palabras raras o no vistas se descomponen en unidades de subpalabras conocidas. La tokenización de subpalabras se prefiere en los modelos de lenguaje modernos porque equilibra el tamaño del vocabulario, la longitud de la secuencia y la robustez ante palabras fuera de vocabulario (OOV). La tokenización pura a nivel de palabra requiere un vocabulario excesivamente grande (lo que lleva a enormes matrices de embeddings) y aún sufre de tokens OOV que se mapean a tokens genéricos '[UNK]'. Por el contrario, la tokenización pura a nivel de carácter elimina los problemas OOV, pero produce secuencias muy largas que aumentan drásticamente la complejidad computacional en los mecanismos de atención (que escalan cuadráticamente con la longitud de la secuencia) y diluyen la densidad semántica por token. La tokenización de subpalabras logra un equilibrio óptimo al mantener las longitudes de secuencia manejables, los tamaños de vocabulario prácticos (típicamente de 32k a 128k tokens) y las tasas OOV en cero (especialmente cuando se combina con soluciones de respaldo a nivel de byte).

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained('gpt2')
text = 'unbelievable'
tokens = tokenizer.tokenize(text)
token_ids = tokenizer.encode(text)

print('Tokens:', tokens)
print('Token IDs:', token_ids)
Probar responder esta pregunta con un coach de IA

2Explicar la diferencia entre incrustaciones de palabras estáticas, incrustaciones contextuales y estados ocultos del transformador.

Las incrustaciones de palabras estáticas, las incrustaciones contextuales y los estados ocultos de los transformadores representan evoluciones progresivas en cómo las representaciones de texto capturan el significado y el contexto sintáctico. 1. **Incrustaciones de palabras estáticas** (por ejemplo, `Word2Vec`, `GloVe`, `FastText`) asignan un único vector fijo a cada *token* del vocabulario, independientemente de su contexto oracional. En este paradigma, palabras polisémicas como 'bank' (orilla de río vs. banco financiero) o 'apple' (fruta vs. empresa de tecnología) tienen representaciones vectoriales idénticas en todos los contextos, basándose en una tabla de consulta estática. 2. **Incrustaciones contextuales** (por ejemplo, `ELMo` temprano, representaciones de *tokens* de `BERT` o incrustaciones de oraciones de `Bi-Encoders`) producen representaciones donde el vector para un *token* es una función dinámica de su contexto circundante. En `BERT` o `ELMo`, 'bank' en 'river bank' (orilla de río) recibe un vector de incrustación completamente diferente al de 'bank' en 'deposit money at the bank' (depositar dinero en el banco). 3. Los **estados ocultos del transformador** se refieren a las representaciones vectoriales intermedias producidas en cada capa individual de una red transformadora durante un paso hacia adelante. Dadas las incrustaciones de *tokens* de entrada en la capa 0, cada capa sucesiva del transformador aplica transformaciones de autoatención y de avance, produciendo una secuencia de vectores de estado oculto `h_l` en la capa `l`. Si bien los estados ocultos de la capa final actúan como incrustaciones contextuales de alto nivel, los estados ocultos de las capas inferiores e intermedias capturan características sintácticas, léxicas y estructurales de bajo nivel. Por lo tanto, los estados ocultos del transformador abarcan el continuo vertical completo de representaciones capa por capa a través de la red.

import torch
from transformers import AutoTokenizer, AutoModel

tokenizer = AutoTokenizer.from_pretrained('bert-base-uncased')
model = AutoModel.from_pretrained('bert-base-uncased', output_hidden_states=True)

text1 = 'River bank'
text2 = 'Bank deposit'

inputs1 = tokenizer(text1, return_tensors='pt')
inputs2 = tokenizer(text2, return_tensors='pt')

with torch.no_grad():
    out1 = model(**inputs1)
    out2 = model(**inputs2)

# Layer 0 (Input token embeddings - static lookup before self-attention)
static_bank_1 = out1.hidden_states[0][0, 2] # 'bank'
# Layer 12 (Final contextual hidden state after all attention layers)
contextual_bank_1 = out1.hidden_states[12][0, 2]
contextual_bank_2 = out2.hidden_states[12][0, 1]

print('Cosine similarity of bank in different contexts (Layer 12):',
      torch.cosine_similarity(contextual_bank_1, contextual_bank_2, dim=0).item())
Probar responder esta pregunta con un coach de IA

3Explica qué representa un espacio de incrustación y cómo se interpreta la similitud del coseno para las incrustaciones de texto.

Un espacio de incrustación es un espacio vectorial continuo y de alta dimensionalidad R^d donde las entidades textuales discretas (palabras, oraciones o documentos) se mapean de tal manera que las similitudes semánticas, sintácticas o relacionales corresponden a la proximidad geométrica y a las relaciones direccionales. En este espacio, las distancias y los ángulos reflejan la relación semántica. La similitud del coseno mide el coseno del ángulo theta entre dos vectores `u` y `v`, calculado como: `Cosine Similarity(u, v) = (u . v) / (||u|| ||v||)` La similitud del coseno se interpreta en las incrustaciones de texto de la siguiente manera: - **Rango y orientación**: Produce un valor escalar típicamente acotado en [-1, 1] (o [0, 1] para incrustaciones no negativas). Un valor cercano a 1.0 indica que los dos vectores apuntan virtualmente en la misma dirección, reflejando una alta similitud semántica o alineación temática. Un valor cercano a 0.0 implica ortogonalidad (independencia semántica o falta de relación), y los valores negativos indican orientaciones opuestas. - **Invariancia de magnitud**: A diferencia de la distancia euclidiana (distancia L2) o el producto escalar, la similitud del coseno normaliza la longitud del vector. En las incrustaciones de texto, la magnitud del vector a veces puede correlacionarse con la longitud de la secuencia, la frecuencia del token o la especificidad del término. Al centrarse puramente en la alineación direccional, la similitud del coseno aísla la orientación semántica de las diferencias de magnitud vectorial.

import numpy as np

def cosine_sim(a, b):
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

# Hypothetical 3D embeddings
v_king = np.array([0.9, 0.1, 0.4])
v_queen = np.array([0.85, 0.15, 0.42])
v_apple = np.array([0.1, 0.9, -0.2])

print('Sim(king, queen):', round(cosine_sim(v_king, v_queen), 4))
print('Sim(king, apple):', round(cosine_sim(v_king, v_apple), 4))
Probar responder esta pregunta con un coach de IA

4¿Cuál es la diferencia entre las incrustaciones de tokens, las incrustaciones posicionales y las incrustaciones de segmento o de tipo en las entradas del transformador?

En las entradas del transformador (especialmente en arquitecturas tipo BERT), la representación de entrada para cada token se forma típicamente sumando elemento a elemento tres vectores de incrustación distintos: 1. Incrustaciones de tokens: Mapean los IDs de tokens de vocabulario discretos en vectores densos que representan la identidad semántica y léxica central de los tokens. 2. Incrustaciones posicionales: Inyectan información sobre el orden de los tokens y el índice secuencial en la representación, compensando el hecho de que la autoatención es inherentemente invariante a permutaciones. 3. Incrustaciones de segmento (o tipo de token): Distinguen entre diferentes tramos de texto u oraciones empaquetadas en una única secuencia de entrada (como la oración A frente a la oración B en tareas de clasificación emparejada o de respuesta a preguntas). La combinación de estas incrustaciones proporciona un único tensor de entrada denso que codifica el significado del token, la posición y la agrupación de secuencias antes de pasar a la primera capa del transformador.

import torch
import torch.nn as nn

vocab_size, max_seq_len, num_segments, d_model = 30522, 512, 2, 768
tok_embed = nn.Embedding(vocab_size, d_model)
pos_embed = nn.Embedding(max_seq_len, d_model)
seg_embed = nn.Embedding(num_segments, d_model)

input_ids = torch.tensor([[101, 7592, 102, 2023, 102]]) # Token IDs
type_ids = torch.tensor([[0,   0,    0,   1,    1  ]]) # Segment IDs (Sentence A vs B)
positions = torch.arange(input_ids.size(1)).unsqueeze(0)   # Indices: [0, 1, 2, 3, 4]

# Final representation is the element-wise sum
input_rep = tok_embed(input_ids) + pos_embed(positions) + seg_embed(type_ids)
print(input_rep.shape)
Probar responder esta pregunta con un coach de IA

5Explica la atención como un mecanismo para relacionar tokens en una secuencia, incluyendo consultas, claves, valores y atención multi-cabeza.

La atención es un mecanismo que permite a los tokens en una secuencia enrutar información dinámicamente y ponderar la relevancia de todos los demás tokens basándose en la coincidencia contextual. Las proyecciones lineales convierten la entrada de cada token en tres vectores: - Consulta (Q): Representa la información que el token actual está buscando. - Clave (K): Representa los atributos o el contenido que un token ofrece para coincidir con las consultas. - Valor (V): Contiene la carga útil de información real que debe ser agregada. En la atención de producto escalar escalado, las puntuaciones de atención se calculan multiplicando Consultas y Claves ($Q K^T$), escaladas por $\frac{1}{\sqrt{d_k}}$ para prevenir el desvanecimiento del gradiente en grandes dimensiones, y normalizadas con una función softmax. La salida final es la suma ponderada de Valores: $$\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V$$ La Atención Multi-Cabeza (MHA) proyecta $Q$, $K$ y $V$ en múltiples subespacios de representación independientes (cabezas) en paralelo. Esto permite al modelo atender simultáneamente a diferentes tipos de relaciones (por ejemplo, estructura sintáctica, correferencia, dependencias de largo alcance) a través de las posiciones. Las salidas de las cabezas se concatenan y se proyectan linealmente de nuevo a la dimensión del modelo.

import torch
import torch.nn.functional as F

# Q, K, V: [batch_size, seq_len, head_dim]
Q = torch.randn(1, 4, 64)
K = torch.randn(1, 4, 64)
V = torch.randn(1, 4, 64)
d_k = Q.size(-1)

scores = torch.matmul(Q, K.transpose(-2, -1)) / (d_k ** 0.5)
attn_weights = F.softmax(scores, dim=-1)
output = torch.matmul(attn_weights, V)

print("Output shape:", output.shape)
Probar responder esta pregunta con un coach de IA

6Explica cómo difiere el enmascaramiento de atención para los decodificadores causales frente a los codificadores bidireccionales y qué comportamientos permite o previene.

El enmascaramiento de atención controla qué tokens pueden atender a qué otros tokens estableciendo los logits de atención (puntuaciones antes de la función softmax) en $-\infty$ para los pares no permitidos, asegurando que su peso de atención post-softmax sea estrictamente 0. 1. Decodificadores Causales (por ejemplo, GPT, LLaMA): Utilizan una máscara causal (autorregresiva) triangular inferior. Un token en la posición $i$ solo puede atender a las posiciones $j \le i$. Esto evita la atención a tokens futuros, permitiendo la generación autorregresiva token por token durante la inferencia y previniendo la fuga de etiquetas de tokens futuros durante el entrenamiento paralelizado. 2. Codificadores Bidireccionales (por ejemplo, BERT): No utilizan una máscara causal; cada token puede atender a todos los tokens pasados y futuros a lo largo de la secuencia. Utilizan máscaras de relleno para evitar que los tokens válidos atiendan a tokens `[PAD]` vacíos en secuencias por lotes. La atención bidireccional produce ricas representaciones contextuales completas ideales para tareas de comprensión, pero impide la generación directa de texto autorregresivo en una sola pasada.

import torch
import torch.nn.functional as F

scores = torch.randn(3, 3)
# Create upper-triangular mask for future positions
causal_mask = torch.triu(torch.ones(3, 3, dtype=torch.bool), diagonal=1)

# Mask future positions with -inf before softmax
masked_scores = scores.masked_fill(causal_mask, float('-inf'))
atten_weights = F.softmax(masked_scores, dim=-1)
print(atten_weights)
Probar responder esta pregunta con un coach de IA

7Explique la diferencia entre las arquitecturas de transformadores solo-codificador, solo-decodificador y codificador-decodificador para tareas de lenguaje.

Las tres arquitecturas de transformadores principales difieren fundamentalmente en sus patrones de enmascaramiento de atención y objetivos operativos: 1. **Solo-Codificador** (por ejemplo, BERT, RoBERTa): Utiliza autoatención bidireccional donde cada token puede atender a todos los demás tokens en la secuencia simultáneamente. Produce representaciones contextuales ricas para una secuencia de entrada completa, lo que lo hace ideal para clasificación, QA extractivo (Question Answering) y representación de características. No puede generar texto autorregresivo de forma natural. 2. **Solo-Decodificador** (por ejemplo, GPT-3, Llama, Mistral): Utiliza autoatención causal (unidireccional), donde el token $i$ solo puede atender a los tokens en las posiciones $j \le i$. Se entrena autorregresivamente utilizando la predicción del siguiente token y sirve como la arquitectura estándar para modelos de lenguaje generativos, generación de código y conversación abierta. 3. **Codificador-Decodificador** (por ejemplo, T5, BART): Combina un codificador bidireccional con un decodificador causal autorregresivo. Además de la autoatención causal sobre los tokens generados, el decodificador utiliza capas de atención cruzada que consultan las representaciones de salida del codificador. Esta arquitectura está diseñada específicamente para tareas de transformación secuencia-a-secuencia, como la traducción y el resumen.

# Causal mask (Decoder-Only) vs Full mask (Encoder-Only)
import torch

seq_len = 4
encoder_mask = torch.ones(seq_len, seq_len)  # Full bidirectional attention
decoder_causal_mask = torch.tril(torch.ones(seq_len, seq_len))  # Lower-triangular

print("Encoder Mask:
", encoder_mask)
print("Decoder Causal Mask:
", decoder_causal_mask)
Probar responder esta pregunta con un coach de IA

Preguntas para Middle

8Explique cómo las elecciones de tokenizador, como los que operan a nivel de byte, los conscientes de Unicode y los multilingües, afectan la calidad del modelo, el costo y la equidad entre diferentes idiomas.

Las decisiones de diseño del tokenizador —como las segmentaciones a nivel de byte versus conscientes de Unicode y las asignaciones de vocabulario multilingües— impactan directamente la calidad del modelo subsiguiente, el costo de inferencia/entrenamiento y la equidad lingüística. En términos de costo y equidad, los tokenizadores entrenados predominantemente en corpus en inglés o en escritura latina asignan la mayoría de las entradas de vocabulario a palabras y morfemas en inglés. Consecuentemente, el inglés logra una alta compresión (por ejemplo, ~1.3 tokens por palabra), mientras que las escrituras no latinas (por ejemplo, árabe, devanagari, tailandés, chino) o los idiomas de bajos recursos a menudo se fragmentan en múltiples subpalabras o bytes `UTF-8` sin procesar (frecuentemente de 3 a 6 tokens por palabra). Esta disparidad a menudo se denomina el 'impuesto de tokens' o 'desequilibrio en la tasa de fertilidad': los usuarios no angloparlantes pagan significativamente más por unidad de contenido semántico en la facturación de la `API` (Application Programming Interface), consumen los límites de la ventana de contexto mucho más rápido y sufren una mayor latencia. En términos de calidad, los tokenizadores a nivel de byte (como `Byte-level BPE` en `GPT-2/GPT-4` o `SentencePiece` con `byte fallback` en `LLaMA`) evitan por completo los fallos por caracteres no vistos y los errores de palabras fuera de vocabulario (`UNK`) porque cualquier cadena `UTF-8` válida se descompone en tokens de byte. Sin embargo, la fragmentación excesiva de bytes degrada la calidad de la representación porque el transformador debe dedicar capas a recombinar fragmentos de bytes en conceptos semánticos antes de realizar un razonamiento de alto nivel. Aumentar el tamaño del vocabulario multilingüe (por ejemplo, expandir de 32k a 128k+ tokens) equilibra las tasas de fertilidad y mejora el rendimiento en tareas subsiguientes en diversos idiomas, a costa de una capa de incrustación de entrada/salida moderadamente más grande.

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained('gpt2')

english_text = 'Hello world'
hindi_text = 'नमस्ते दुनिया'

print('English tokens:', tokenizer.tokenize(english_text))
print('Hindi tokens:', tokenizer.tokenize(hindi_text))
print('English token count:', len(tokenizer.encode(english_text)))
print('Hindi token count:', len(tokenizer.encode(hindi_text)))
Probar responder esta pregunta con un coach de IA

9¿Qué artefactos de tokenización aparecen en el razonamiento numérico, la generación de código o el texto Unicode raro, y cómo pueden los tokenizadores especializados reducirlos?

Los artefactos de tokenización ocurren cuando los tokenizadores de subpalabras dividen de forma inconsistente texto estructurado, numérico o poco común, impidiendo que el modelo reconozca la regularidad semántica o sintáctica subyacente. Los artefactos clave incluyen: 1. **Artefactos de razonamiento numérico:** Los tokenizadores BPE (Byte Pair Encoding) estándar entrenados en texto general dividen los números en longitudes de fragmentos arbitrarias basadas en la frecuencia (por ejemplo, '12345' podría tokenizarse como ['12', '345'] mientras que '12346' se tokeniza como ['123', '46']). Este agrupamiento inconsistente rompe la alineación del valor posicional (dígitos, decenas, centenas) y dificulta el razonamiento aritmético. 2. **Artefactos de generación de código:** La indentación (espacios/tabulaciones iniciales) y los operadores de múltiples caracteres (por ejemplo, '==', '!=', '->') a menudo se dividen de forma irregular a través de los límites de espacio-carácter, lo que lleva a errores de indentación, recuentos de tokens inflados en código profundamente indentado y corrupciones de sintaxis. 3. **Artefactos de Unicode y Emoji poco comunes:** Las secuencias UTF-8 de múltiples bytes (como emojis complejos con uniones de ancho cero o scripts poco comunes) se dividen en tokens de bytes brutos que no tienen un significado semántico individual, causando caracteres alucinatorios o un renderizado de glifos corrupto tras la generación. Los tokenizadores especializados reducen estos artefactos utilizando reglas de pre-tokenización adaptadas y restricciones de vocabulario: - **División de dígitos:** Imponer tokenización de un solo dígito (por ejemplo, dividir cada dígito `0-9` mediante expresiones regulares en su propio token) asegura una representación uniforme del valor posicional para el razonamiento matemático. - **Tokens dedicados para espacios en blanco/indentación:** Añadir tokens explícitos para indentaciones de múltiples espacios (por ejemplo, 2, 4, 8 espacios) y preservar palabras clave/operadores del lenguaje de programación. - **Pre-tokenización con expresiones regulares / Retrocesos a nivel de byte:** Usar divisores de expresiones regulares (como las expresiones regulares de GPT-4/tiktoken) que separan la puntuación, letras y números en categorías estrictas antes de que se calculen las fusiones BPE, evitando fusiones entre categorías (por ejemplo, evitando que 'a=10' se fusione en un solo token).

import tiktoken

# tiktoken cl100k_base (GPT-4 / ChatGPT) enforces digit and whitespace handling
enc = tiktoken.get_encoding('cl100k_base')

num1 = '12345'
num2 = '12346'
code_indent = '    def foo():'

print('Tokens num1:', [enc.decode([t]) for t in enc.encode(num1)])
print('Tokens num2:', [enc.decode([t]) for t in enc.encode(num2)])
print('Tokens code:', [enc.decode([t]) for t in enc.encode(code_indent)])
Probar responder esta pregunta con un coach de IA

10Razone sobre las compensaciones de la atención de contexto largo, incluyendo la atención completa cuadrática, la atención de ventana deslizante, la atención dispersa o global, el costo de la caché KV y la dilución de la atención.

Escalar la atención a ventanas de contexto largas presenta compensaciones entre el cálculo, el uso de memoria y la fidelidad del modelo: 1. **Atención Completa Cuadrática vs. Atención de Ventana Deslizante / Dispersa:** La atención completa estándar escala cuadráticamente ($O(N^2)$) en cálculo y memoria de activación con la longitud de secuencia $N$. La atención de ventana deslizante (local) restringe la atención a un vecindario fijo $W$, reduciendo la complejidad a $O(N \cdot W)$, pero requiere múltiples capas para propagar información a través de tokens distantes. Los patrones de atención dispersa o global combinan ventanas locales con tokens ancla globales seleccionados para mantener una escala de $O(N)$ al tiempo que permiten la comunicación de largo alcance. 2. **Costo de Memoria de la Caché KV:** Durante la generación autorregresiva, las claves y valores para todos los tokens anteriores se almacenan en caché para evitar cálculos redundantes. La memoria de la caché KV escala linealmente con la longitud de secuencia ($O(B \cdot L \cdot H_{KV} \cdot D \cdot N)$). Para contextos muy largos (más de 32k–128k tokens), la caché KV consume decenas de gigabytes de VRAM de GPU por lote, limitando el tamaño máximo del lote y el ancho de banda de memoria. 3. **Dilución de la Atención (Perdido en el Medio):** A medida que el contexto crece, el denominador softmax suma decenas de miles de tokens, distribuyendo la masa de probabilidad de forma tenue a lo largo del contexto irrelevante. Este aumento de la entropía diluye la nitidez de la atención, degradando la capacidad del modelo para recordar de forma fiable información específica incrustada en el medio de prompts largos.

def kv_cache_gb(batch_size, seq_len, layers=32, kv_heads=8, head_dim=128, bytes_per_elem=2):
    # 2 for Key and Value
    total_bytes = batch_size * seq_len * layers * kv_heads * head_dim * 2 * bytes_per_elem
    return total_bytes / (1024 ** 3)

print(f"32k context: {kv_cache_gb(4, 32768):.2f} GB")
print(f"128k context: {kv_cache_gb(4, 131072):.2f} GB")
Probar responder esta pregunta con un coach de IA

11Compare la atención multi-cabezal (MHA), la atención multi-consulta (MQA) y la atención de consulta agrupada (GQA), y explique cómo afectan la memoria de la caché KV y el rendimiento de decodificación.

La atención multi-cabezal (MHA), la atención multi-consulta (MQA) y la atención de consulta agrupada (GQA) difieren en cómo los cabezales de clave ($K$) y valor ($V$) se comparten entre los cabezales de consulta ($Q$): 1. **Atención multi-cabezal (MHA)**: Tiene un número igual de cabezales $Q$, $K$ y $V$ ($H_Q = H_{KV}$, relación 1:1). Cada cabezal de consulta atiende a sus propias representaciones independientes de clave/valor. Aunque es expresiva, requiere el almacenamiento en caché de matrices KV distintas para cada cabezal. 2. **Atención multi-consulta (MQA)**: Utiliza múltiples cabezales $Q$ ($H$) pero solo 1 cabezal $K$ compartido y 1 cabezal $V$ compartido (relación $H:1$). Esto reduce el tamaño de la caché KV por un factor de $H$, pero puede llevar a una ligera pérdida de calidad o inestabilidad en el entrenamiento. 3. **Atención de consulta agrupada (GQA)**: Agrupa los cabezales $Q$ en $G$ particiones, donde cada grupo comparte un único cabezal $K$ y $V$ (por ejemplo, 8 cabezales $Q$ por cabezal KV). GQA ofrece un equilibrio óptimo, recuperando prácticamente toda la calidad de modelado de MHA mientras mantiene los beneficios de memoria de MQA. **Impacto en la caché KV y el rendimiento de decodificación:** La generación de tokens auto-regresiva (decodificación) está limitada por el ancho de banda de la memoria porque la GPU (Graphics Processing Unit) debe transferir toda la caché KV desde la HBM (High-Bandwidth Memory) a la SRAM (Static Random-Access Memory) en chip para cada token generado. Al reducir el número de cabezales KV en $H/G$ (por ejemplo, $4\times$ a $8\times$ en GQA, o $32\times+$ en MQA): - La huella de memoria de la caché KV se reduce proporcionalmente, permitiendo tamaños de lote de servicio mucho mayores en la VRAM (Video Random-Access Memory) de la GPU. - El tráfico de lectura de memoria HBM por token disminuye sustancialmente, aumentando drásticamente el rendimiento de decodificación de tokens.

# Model with 32 Query Heads
num_q_heads = 32

mha_kv_heads = 32 # 1:1 ratio
gqa_kv_heads = 8  # 4:1 ratio (4 query heads per KV head)
mqa_kv_heads = 1  # 32:1 ratio (1 shared KV head)

print(f"KV Cache Size Relative to MHA:")
print(f"MHA: {mha_kv_heads / mha_kv_heads * 100:.1f}%")
print(f"GQA: {gqa_kv_heads / mha_kv_heads * 100:.1f}%")
print(f"MQA: {mqa_kv_heads / mha_kv_heads * 100:.1f}%")
Probar responder esta pregunta con un coach de IA

12Explica cómo FlashAttention acelera el cálculo exacto de la atención sin cambiar las salidas de atención.

FlashAttention acelera el cálculo de la atención al hacer que el algoritmo sea consciente de E/S, minimizando el tráfico de memoria de lectura y escritura entre la Memoria de Alto Ancho de Banda (HBM) lenta de la GPU y la SRAM (Memoria Estática de Acceso Aleatorio) rápida en el chip, en lugar de intentar reducir el recuento total de operaciones de coma flotante (FLOP) aritméticas. La atención estándar materializa matrices intermedias N x N de puntuación de atención y probabilidad en la HBM, lo que causa un importante cuello de botella en el ancho de banda de la memoria. FlashAttention supera esto mediante tres mecanismos clave: 1. Tiling: Divide las matrices Query, Key y Value en bloques que encajan completamente en la SRAM en el chip de la GPU. 2. Softmax en línea: Calcula softmax incrementalmente sobre bloques rastreando los máximos en ejecución y las sumas normalizadoras, actualizando salidas parciales sin necesidad de tener la matriz N x N materializada completa en la memoria. 3. Recálculo Exacto: Durante la pasada hacia atrás, no lee las matrices de atención intermedias almacenadas de la HBM; en su lugar, las recalcula sobre la marcha en SRAM a partir de las estadísticas en ejecución almacenadas. Debido a que no se utilizan aproximaciones, factorizaciones de bajo rango o heurísticas de eliminación de tokens, la salida es matemáticamente exacta hasta la precisión numérica de punto flotante, mientras que se reduce la huella de memoria HBM de O(N^2) a O(N).

import torch

def online_softmax_step(m_prev, l_prev, out_prev, scores_block, v_block):
    # scores_block: (B, H, Br, Bc), v_block: (B, H, Bc, D)
    m_block = scores_block.max(dim=-1, keepdim=True).values
    m_new = torch.maximum(m_prev, m_block)
    
    # Rescale previous and current accumulators
    p_prev_scale = torch.exp(m_prev - m_new)
    p_block = torch.exp(scores_block - m_new)
    
    l_new = p_prev_scale * l_prev + p_block.sum(dim=-1, keepdim=True)
    out_new = (p_prev_scale * l_prev * out_prev + p_block @ v_block) / l_new
    return m_new, l_new, out_new
Probar responder esta pregunta con un coach de IA

Preguntas para Senior

13Diseñe flujos de trabajo agénticos deterministas utilizando planificadores, máquinas de estados, DAGs, estado intermedio tipado, reintentos acotados y verificación de resultados de herramientas en lugar de bucles de agente de propósito general.

Los bucles de agente de propósito general (p. ej., bucles ReAct autónomos no restringidos) en producción a menudo sufren de ramificación no determinista, bucles infinitos, gasto descontrolado de tokens y deriva de estado. Un flujo de trabajo agéntico determinista reemplaza los bucles de forma libre con un flujo de control estructurado y observable: 1. **Máquinas de Estados y DAGs (Grafos Dirigidos Acíclicos)**: El flujo de control se define como un Grafo Dirigido Acíclico explícito o una máquina de estados finitos (p. ej., LangGraph, Temporal, AWS Step Functions). Las transiciones de nodo dependen de condiciones explícitas y resultados tipados en lugar de decisiones de modelo de propósito general. 2. **Estado Intermedio Tipado**: El estado compartido entre nodos se modela con esquemas estrictos (p. ej., modelos Pydantic o dataclasses). Los nodos realizan operaciones de lectura y escritura validadas, previniendo la deriva de esquema o el estado mal formado. 3. **Planificadores**: Los planificadores estructurados emiten un plan restringido de antemano (p. ej., una lista ordenada de pasos respaldados por enumeraciones) o eligen de un conjunto restringido de transiciones de estado válidas en lugar de decidir libremente las próximas acciones sin restricciones. 4. **Verificación de Resultados de Herramientas**: Las salidas devueltas por las herramientas se validan de forma determinista contra esquemas y reglas de negocio antes de actualizar el estado o pasar a los pasos posteriores del LLM (Modelo de Lenguaje Grande). 5. **Reintentos Acotados y Contingencias**: Cada paso impone presupuestos de reintento explícitos, retrocesos exponenciales, tiempos de espera y transiciones de contingencia (p. ej., escalar a revisión humana o activar la abstención segura) para garantizar la terminación.

from pydantic import BaseModel
from typing import Optional, Literal

class WorkflowState(BaseModel):
    user_query: str
    extracted_id: Optional[str] = None
    verification_status: Literal["PENDING", "VERIFIED", "FAILED"] = "PENDING"
    retry_count: int = 0
    max_retries: int = 3

def execute_validation_node(state: WorkflowState) -> WorkflowState:
    if state.retry_count >= state.max_retries:
        state.verification_status = "FAILED"
        return state
    try:
        result = call_verification_service(state.extracted_id)
        state.verification_status = "VERIFIED" if result.is_valid else "FAILED"
    except Exception:
        state.retry_count += 1
    return state
Probar responder esta pregunta con un coach de IA

14Diseñe una política de confianza y abstención para un asistente de LLM (Large Language Model) que responde preguntas de dominios regulados.

En dominios regulados (como salud, banca, legal y cumplimiento), las respuestas incorrectas conllevan sanciones regulatorias, responsabilidad legal y riesgos de seguridad. Una política robusta de confianza y abstención combina una puntuación de confianza calibrada con múltiples señales, umbrales de respuesta por niveles y flujos de trabajo de escalada determinísticos: 1. **Calibración de Confianza Multi-Señal:** Las probabilidades logarítmicas (logprobs) crudas del LLM a menudo están mal calibradas en consultas fuera del dominio. La puntuación de confianza debe sintetizar múltiples señales independientes: * **Puntuación de Fundamentación de Recuperación:** Similitud semántica y confianza en el reordenamiento de los fragmentos de evidencia recuperados. * **Inferencia de Lenguaje Natural (NLI) a Nivel de Afirmación:** Modelos de NLI que verifican que cada afirmación extraída esté implicada por el contexto fuente recuperado. * **Entropía Semántica / Auto-Consistencia:** Medición de la consistencia semántica en múltiples generaciones muestreadas. * **Probabilidades Logarítmicas de Tokens del Modelo:** Probabilidades logarítmicas mínimas y promedio en entidades nombradas clave y tokens fácticos. 2. **Política de Abstención por Niveles:** * **Confianza Alta (Puntuación >= Umbral Alto):** Servir directamente la respuesta generada con citas en línea. * **Confianza Media / Ambigua (Umbral Bajo <= Puntuación < Umbral Alto):** Servir una respuesta conservadora con advertencias explícitas, descargos de responsabilidad o pedir al usuario detalles aclaratorios. * **Confianza Baja / Fuera de Alcance (Puntuación < Umbral Bajo):** Abstención dura con un mensaje de rechazo estandarizado. 3. **Escalada y Auditabilidad de Cumplimiento:** * **Escalada Determinística:** Las abstenciones o discrepancias críticas se enrutan automáticamente a colas de intervención humana (HITL) o sistemas de tickets de agentes con el contexto completo. * **Pista de Auditoría y Linaje:** Se debe registrar la telemetría completa —incluidos los hashes de los prompts, los IDs de los documentos recuperados, las puntuaciones individuales de los componentes de confianza y las decisiones finales de enrutamiento— para la auditabilidad regulatoria.

from dataclasses import dataclass
from typing import Literal

@dataclass
class DecisionResult:
    action: Literal["SERVE", "SERVE_WITH_CAVEAT", "ABSTAIN_AND_ESCALATE"]
    confidence_score: float
    reason: str

def evaluate_confidence_policy(retrieval_score: float, nli_entailment_score: float, semantic_entropy: float) -> DecisionResult:
    # Composite calibrated confidence index [0, 1]
    composite_score = (0.4 * retrieval_score) + (0.4 * nli_entailment_score) + (0.2 * (1.0 - semantic_entropy))
    
    if composite_score >= 0.85:
        return DecisionResult("SERVE", composite_score, "High evidence grounding")
    elif composite_score >= 0.60:
        return DecisionResult("SERVE_WITH_CAVEAT", composite_score, "Partial evidence support")
    else:
        return DecisionResult("ABSTAIN_AND_ESCALATE", composite_score, "Insufficient ground truth")
Probar responder esta pregunta con un coach de IA

15Diseñe una estrategia de enrutamiento de modelos que elija entre modelos pequeños, medianos y grandes basándose en la complejidad de la solicitud, el costo, el riesgo y los requisitos de calidad.

Una arquitectura de enrutamiento de modelos en producción dirige las solicitudes entrantes entre niveles de modelos pequeños (por ejemplo, SLM (Small Language Model) de 1B–8B), medianos (por ejemplo, modelos de 14B–70B) y grandes (por ejemplo, modelos de vanguardia o grandes MoE (Mixture of Experts)), equilibrando la complejidad, la latencia, el riesgo y el costo computacional. El flujo de trabajo de enrutamiento generalmente combina reglas estáticas, enrutamiento predictivo y cascadas de respaldo dinámicas: 1. **Compuertas de Política Determinística/Estática:** Filtran las solicitudes por nivel de cliente, SLAs de latencia estrictos, riesgo regulatorio/de dominio (por ejemplo, diagnóstico médico o redacción legal enrutados directamente a modelos de nivel superior), o tareas simples que coinciden con reglas (por ejemplo, expresiones regulares/formato básico a modelos pequeños). 2. **Enrutamiento Predictivo de Complejidad:** Un clasificador rápido y ligero (como una búsqueda de similitud de embeddings, un codificador cruzado o un enrutador SLM pequeño) puntúa la complejidad de la solicitud, la profundidad del razonamiento y la ambigüedad del dominio para seleccionar el nivel más rentable de antemano. 3. **Cascadas Dinámicas de Ejecución y Escalada:** Envían el prompt primero a un modelo más pequeño y evalúan la confianza de la salida (mediante probabilidades logarítmicas/entropía de tokens, validez del esquema estructurado o comprobaciones de barandilla (guardrail)). Si la confianza está por debajo del umbral o la validación falla, el enrutador escala a un modelo mediano o grande. Los compromisos clave del sistema incluyen la sobrecarga de latencia del enrutador versus el ahorro de computación, los presupuestos de tiempo de espera de respaldo bajo picos de tráfico y la evaluación continua (por ejemplo, evaluación en sombra para rastrear la deriva de la calidad de la salida entre los niveles).

class DynamicModelRouter:
    def __init__(self, small_client, medium_client, large_client, classifier, guardrail):
        self.small = small_client
        self.medium = medium_client
        self.large = large_client
        self.classifier = classifier
        self.guardrail = guardrail

    async def route_and_execute(self, request):
        # 1. Deterministic Risk Gate
        if request.risk_level == "high" or request.domain in ["legal", "medical_compliance"]:
            return await self.large.generate(request.prompt)
        
        # 2. Predictive Complexity Classifier
        complexity = self.classifier.predict_complexity(request.prompt) # 0.0 to 1.0
        
        if complexity < 0.35:
            response = await self.small.generate(request.prompt)
            if self.guardrail.is_acceptable(response):
                return response
            return await self.medium.generate(request.prompt) # Fallback
            
        if complexity < 0.75:
            response = await self.medium.generate(request.prompt)
            if self.guardrail.is_acceptable(response):
                return response
            return await self.large.generate(request.prompt) # Fallback
            
        # 3. High complexity frontier execution
        return await self.large.generate(request.prompt)
Probar responder esta pregunta con un coach de IA