English · Español
Lab 02 — Compresión SVD de la matriz de conteos de conjugaciones §A13¶
Objetivo: comprimir una matriz de conteos de conjugaciones de verbos con SVD de rango-k; ver Eckart-Young en acción sobre la propia estructura de datos que motiva los word embeddings.
Tiempo estimado: 60–90 minutos.
Prerrequisito:
theory/03-svd-and-rank.mdytheory/04-norms-and-conditioning.mdleídas.
Lo que produces¶
Un directorio experiments/03-svd-compression/ que contiene:
build_matrix.py— sintetiza la matriz de conteos de conjugacionesCa partir de los verbos §A13 (Borja escribe).compress.py— ejecuta SVD y reconstrucciones de rango-k.spectrum.png— valores singulares vs índice, log-y.reconstructions.png— heatmaps deC,C_1,C_3,C_5,C_10,C_15lado a lado.error_curve.png—||C - C_k||_F / ||C||_Fvsk.results.json— error, energía capturada, mediciones de almacenamiento pork.manifest.json.README.md— interpretación, 2-3 párrafos.
La matriz §A13¶
Construye C de shape (20, 15):
- Filas: los 20 verbos del §A13 — 12 regulares (
work, play, walk, talk, listen, watch, study, finish, start, look, want, like) + 8 irregulares (be, have, do, go, come, see, eat, write). - Columnas: las 15 celdas de conjugación = 5 tiempos × 3 personas. Orden:
(infinitive, I) (infinitive, you) (infinitive, he) (present, I) (present, you) (present, he) (past, I) (past, you) (past, he) (pastPart, I) (pastPart, you) (pastPart, he) (future, I) (future, you) (future, he). - Entrada
C[i, j]: el conteo de cuántas veces el verboiaparece en la conjugaciónjen un corpus sintético de 10,000 frases.
Sintetizar conteos¶
Aún no tienes un corpus real (eso es la Fase 13). Para la Fase 3, falséalo con un generador estructurado para que el espectro sea interesante:
- Muestrea una "frecuencia" por verbo
f_v ~ Zipf(s=1.2)sobre 20 verbos, normalizada para que el verbo más común aparezca ≈10× más que el más raro. - Muestrea una "frecuencia" por tiempo
t_τde una distribución discreta aproximadamente[0.05, 0.45, 0.20, 0.05, 0.25]sobre[infinitive, present, past, pastPart, future]. - Muestrea una "frecuencia" por persona
p_πaproximadamente[0.35, 0.25, 0.40]sobre[I, you, he/she/it]. - Por celda, el conteo esperado es
10_000 · f_v · t_τ · p_π. Añade ruido Poisson. - Para verbos irregulares, perturba ligeramente la fila para romper la estructura estricta de producto externo rango-1 — si no,
Ctendría rango exactamente 1 y el lab es trivial.
Documenta el generador en build_matrix.py y siémbralo (np.random.default_rng(42)).
TODOs¶
Bloque A — construir la matriz¶
-
build_matrix.pyescribeC.npy(shape(20, 15), dtype fp32) yverb_order.json+cell_order.json(arrays de etiquetas). - Imprime
Cpor stdout al ejecutar — observa con vista que parece sensato (los verbos comunes tienen filas mayores; las columnas de futuro y participio pasado son más dispersas). - Reproducibilidad: RNG sembrado, una sola entrada
manifest.jsonpor ejecución.
Bloque B — SVD + reconstrucciones¶
-
compress.pycargaC.npy, calculaU, S, Vt = np.linalg.svd(C, full_matrices=False). -
S.shape == (15,),U.shape == (20, 15),Vt.shape == (15, 15). - Para cada
k ∈ {1, 2, 3, 5, 8, 10, 12, 15}: - Reconstruye
C_k = U[:, :k] @ diag(S[:k]) @ Vt[:k, :](o usa la forma broadcastU[:, :k] * S[:k] @ Vt[:k, :]). - Mide
frobenius_error = np.linalg.norm(C - C_k, 'fro'). - Mide
relative_error = frobenius_error / np.linalg.norm(C, 'fro'). - Mide
captured_energy = sum(S[:k]**2) / sum(S**2). - Mide
op_norm_error = S[k] if k < 15 else 0.0(Eckart-Young en norma de operador). - Verifica
frobenius_error == sqrt(sum(S[k:]**2))con tolerancia fp32 (la identidad de Eckart-Young).
Bloque C — visualizar¶
-
spectrum.png: valores singularesσ_kvsken escala log-y. Espera una caída brusca tras los primeros 3-5 valores. -
reconstructions.png: grid de heatmaps deC, C_1, C_3, C_5, C_10, C_15. Usa la misma escala de color entre paneles. Etiqueta cada uno conky el error relativo. -
error_curve.png:relative_errorvsk(log-y). Anota el "codo" — el menorkdonde el error relativo cae por debajo del 5%.
Bloque D — análisis de almacenamiento¶
En README.md, calcula:
- Almacenamiento original:
20 × 15 = 300números fp32 =1200bytes. - Almacenamiento SVD rango-k:
k × (20 + 1 + 15) = 36knúmeros fp32 =144kbytes. - Cruce: en
k = 8, el almacenamiento son288números — menos que el original. Enk ≥ 9, la forma SVD ya no es una ganancia (para esta matriz minúscula). Indica el cruce.
Para matrices más grandes la ganancia es enorme — vista previa de LoRA en la Fase 28: un adaptador (D, D) con D = 4096, r = 8 da 260× de compresión. Menciónalo en el README.
Bloque E — preguntas de interpretación¶
En README.md, responde:
- ¿Dónde está el codo? ¿Cuál es el menor
ktal que||C - C_k||_F / ||C||_F < 5%? - ¿Qué captura el patrón dominante? Grafica
U[:, 0](el primer vector singular izquierdo, indexado por verbo) yVt[0, :](el primer vector singular derecho, indexado por celda de conjugación). ¿A qué ejes §A13 corresponden? (Pista: el producto externo de σ_1σ_1 · u_1 ⊗ v_1es la mejor aproximación rango-1; para lenguaje natural suele capturar el eje de "frecuencia general" — verbos comunes × formas comunes.) - ¿Qué capturan los patrones secundarios? Repítelo para
U[:, 1], Vt[1, :]yU[:, 2], Vt[2, :]. Busca: split regular-vs-irregular, splits persona-vs-tiempo, etc. - Rango efectivo. Con un umbral de ruido de
0.01 × σ_1, ¿cuántos valores singulares están por encima del umbral? Este es el rango efectivo deC. Compáralo con el rango completo de la matriz (≤ 15). - Adelanto de la Fase 13 (embeddings). Si tratas la factorización rango-
kC ≈ (U[:, :k] · sqrt(S[:k])) · (sqrt(S[:k]) · Vt[:k, :])como una representación aprendida, el factor izquierdo es un embedding de verbo enR^ky el factor derecho es un embedding de conjugación enR^k. ¿Quékescogerías? ¿Por qué importa esto cuando construyamos la matriz de embedding real en la Fase 13?
Bloque F — manifest¶
Manifest estándar. Incluye el seed del generador de matrices + el conteo de (verbos, celdas) en config.
Restricciones¶
np.linalg.svdúnicamente. No implementes SVD a mano — eso es una optativa de álgebra lineal numérica. Siemprefull_matrices=False.- fp32 en todo. Castea
Ca fp32 antes de la SVD. (La matriz tiene conteos enteros pequeños; el dtype importa más para los productos internos de vectores singulares que para los propios valores.) - Sin
sklearn.decomposition.TruncatedSVD. Usa la SVD densa completa; la matriz es minúscula. - Reproducibilidad. Un solo seed de RNG;
manifest.jsonlo registra.
Escollos¶
- Olvidar
full_matrices=False. Sin eso,U.shape == (20, 20)yVt.shape == (15, 15); el lab sigue funcionando pero desperdicia 5 columnas deU. - Off-by-one en la comprobación de Eckart-Young.
||C - C_k||_F² = Σ_{i ≥ k} σ_i²— la suma empieza en el índicek(con índice base cero,S[k]es el primer σ no conservado). - Ambigüedad de signo de vectores singulares. Al graficar
U[:, 0], los signos son arbitrarios; para interpretación, invierte para que la entrada de mayor valor absoluto sea positiva. Documenta la convención. - Tratar el almacenamiento como solo
k · (M + N)sin el+1paraS[:k]. Pequeño en sentido absoluto; importa en el cálculo del cruce. - Generar
Ccon estructura demasiado limpia (por ejemplo, producto externo rango-1 puro). El lab necesita algo de estructura más allá del rango-1 para que puedas ver valores singulares secundarios — para eso están el ruido Poisson + la perturbación de verbos irregulares.
Recapitulación del anclaje §A13¶
La matriz de conteos de conjugaciones C es el ejemplo más pequeño y limpio de una matriz de co-ocurrencia. La Fase 13 (word embeddings) explicará cómo Word2Vec / GloVe extraen representaciones vectoriales densas de la estadística de co-ocurrencia — lo que hacen es aproximadamente este lab, sobre un vocabulario mucho mayor con un kernel de ventana de contexto. La SVD sobre C es el algoritmo original de embedding (Análisis Semántico Latente, años 90). LoRA de la Fase 28 es la misma idea aplicada a actualizaciones de pesos en lugar de co-ocurrencias.
Condiciones de parada¶
- Tres PNGs commiteados (
spectrum.png,reconstructions.png,error_curve.png). results.jsontiene filas para al menosk ∈ {1, 2, 3, 5, 8, 10, 12, 15}.- Eckart-Young verificado numéricamente:
||C - C_k||_F == sqrt(sum(S[k:]**2))con tolerancia fp32 para al menos unkde rango medio. - README responde a las cinco preguntas de interpretación.
- Los tres primeros vectores singulares están interpretados en términos §A13.
Cuándo consultar solutions/¶
Tras commitear los tres PNGs + interpretación. solutions/02-svd-compression-ref.md (en la apertura de fase) discute cómo el espectro §A13 se compara con un espectro real de co-ocurrencia en corpus español y adelanta la conexión con la Fase 13.
Siguiente lab: lab/03-norms-by-experiment.md.