English · Español
Break 00 — Atar embeddings de entrada/salida a tamaños de vocabulario diferentes¶
🇪🇸 Rompemos la atadura entre embedding de entrada (V_in = 512) y la cabeza LM de salida (V_out = 256). La Fase 17 va a "atar" estas dos matrices (
W_out = E.T) — pero si tienen tamaños distintos, falla en runtime con shape mismatch. Es un bug clásico que aparece al fusionar tokenizadores.Anclas:
LYNX_CORTEX.md§4 / FASE 13; teoría §01 embedding como lookup; Fase 17 §03 tied embeddings;.claude/commands/break.md.
El break¶
En src/minimodel/nn/embedding.py (donde vive la embedding de §A13) y src/minimodel/nn/lm_head.py:
# embedding.py
class Embedding(Module):
def __init__(self, vocab_size: int, d_model: int) -> None:
super().__init__()
self.vocab_size = vocab_size
self.weight = Parameter(np.random.uniform(...,(vocab_size, d_model)))
...
# lm_head.py — the "tied" version:
class LMHead(Module):
def __init__(self, embedding: Embedding, vocab_size_out: int) -> None:
# BUG: vocab_size_out is wrong — should be the same as embedding.vocab_size.
super().__init__()
self.embedding = embedding
self.vocab_size_out = vocab_size_out # e.g., 256 instead of 512
def forward(self, h: Tensor) -> Tensor:
# Untied case would be: logits = h @ W_lm.T where W_lm is (V_out, d).
# Tied case: logits = h @ self.embedding.weight.T → shape (B, T, V_in).
# But the rest of the pipeline expects (B, T, V_out).
logits = h @ self.embedding.weight.transpose((1, 0))
return logits # shape (B, T, V_in) ≠ (B, T, V_out) — but who is checking?
El bug es el desajuste entre embedding.vocab_size = 512 y lm_head.vocab_size_out = 256. Si los atas ingenuamente, los logits tienen forma (B, T, 512) mientras que la entropía cruzada espera (B, T, 256).
Predice, luego ejecuta¶
Predicciones¶
- Crash en runtime en la llamada de entropía cruzada:
- Si silencias la comprobación (p. ej., lookup por índice ingenuo contra
vocab_size_out), el modelo entrena pero sus primeros 256 huecos de vocabulario se convierten en una quimera — en parte las primeras 256 filas de la embedding, en parte la matriz(256, d_model)de la cabeza LM. - Curva de pérdida: empieza en
log(256) = 5,55(baseline aleatoria para 256 clases) y se queda ahí o crece.
Escribe las predicciones en learners/borja/phase-13/notes/breaks.md antes de ejecutar.
Observa¶
Diagnósticos:
- Mira el traceback en el primer forward pass. El shape mismatch debería ser ruidoso.
- Si el mismatch está silenciado (un bug relacionado), comprueba
logits.shape == (B, T, V_out)explícitamente. - La pérdida debería ser
log(V_out)— verifica connp.log(V_out)≈ 5,55 paraV_out = 256.
Síntoma que verá Borja¶
ValueErroroRuntimeErroren la llamada de entropía cruzada.- El traceback apunta o bien a
.weight.Tde la embedding o a la comprobación de forma de la función de pérdida. - Si está silenciado: la pérdida empieza en
log(V_out)y se queda ahí.
Causa oculta (una frase)¶
La cabeza LM reutiliza la matriz de embeddings (tied) pero el tamaño de vocabulario de la embedding es 512 mientras que el tamaño de salida esperado por la cabeza LM es 256 — desajuste de forma en la ruta atada.
Cascada de pistas¶
- ¿Cuál es la forma de
embedding.weight, y qué forma produceh @ embedding.weight.T? - ¿Qué forma espera la pérdida de entropía cruzada para los
logits? - Compara
embedding.vocab_sizecon el valor pasado aLMHead(vocab_size_out=...).
Diff de arreglo¶
class LMHead(Module):
def __init__(self, embedding: Embedding) -> None:
super().__init__()
self.embedding = embedding
self.vocab_size_out = embedding.vocab_size # take from the source
(En el mini-GPT de la Fase 17 lo veremos codificado como model.lm_head = TiedHead(model.token_emb).)
Por qué esto enseña el concepto¶
El weight tying (Press & Wolf 2017, "Using the Output Embedding to Improve Language Models", e Inan et al. 2017) es un truco estándar de transformer que ata W_lm = E.T para reducir el número de parámetros a la mitad y mejorar la perplejidad en ~5%. Requiere V_in == V_out. El bug — pasar tamaños de vocabulario inconsistentes — es la forma más común en que se rompe el tying en la práctica (un refactor cambia el vocabulario del tokenizer pero no el de la cabeza LM). El lab de mini-GPT de la Fase 17 usará tied embeddings; este break asegura que Borja tenga el reflejo de disciplina de forma antes de la Fase 17.
Siguiente: El /break de la Fase 14 sobre el sigmoide de la puerta LSTM.