Skip to content

English · Español

Lab 03 — Generar REPORT.md y comparar dos checkpoints lado a lado

Objetivo: producir un informe Markdown legible por humanos para un checkpoint, después construir un comparador que difere dos informes y destaque cambios significativos. Termina con una interpretación escrita de un párrafo por checkpoint.

Tiempo estimado: 90-120 minutos.

Prerrequisito: Labs 00-02 hechos; results.json, per_slice.csv, reliability.png, adversarial_by_category.png existen bajo experiments/20-eval-report/<checkpoint_name>/ tanto para el checkpoint final de la Fase 18 como para el best-val de la Fase 19.


Lo que produces

Un módulo nuevo:

  • src/eval/report.py — renderiza results.jsonREPORT.md.
  • src/eval/compare.py — difere dos archivos results.json; renderiza COMPARE.md.
  • tests/eval/test_report.py — tests de golden-file sobre un results.json fixture.

Artefactos de salida:

  • experiments/20-eval-report/<checkpoint_name>/REPORT.md (uno por checkpoint).
  • experiments/20-eval-report/COMPARE.md (uno comparando los dos).
  • learners/borja/phase-20/reflections.md (la interpretación escrita de Borja).

La plantilla REPORT.md

Secciones, en orden:

# Eval Report — <checkpoint_name>

**Checkpoint hash:** <sha256>
**Training step:** <step>
**Parent manifest:** <hash>
**Probe set version:** <hash>
**Eval date:** <iso8601>
**Seed:** <int>

## 1. Headline

| Metric | Value | 95% CI |
|---|---|---|
| PPL (test, overall) | 7.42 | — |
| PPL (test, EN) | 6.81 | — |
| PPL (test, ES) | 8.93 | — |
| Aggregate accuracy | 0.78 | [0.66, 0.87] |
| ECE | 0.092 | — |
| Brier | 0.171 | — |
| Adversarial accuracy | 0.55 | [0.32, 0.76] |

## 2. Per-slice accuracy

### By language
<table>

### By regularity
<table>

### By tense
<table>

### By person
<table>

### By verb (counts only — small N per cell)
<table>

## 3. Reliability diagram

![reliability](reliability.png)

## 4. Adversarial slice

| Category | n | Accuracy | 95% CI |
|---|---|---|---|
| Over-regularization | 5 | 0.40 | [0.12, 0.77] |
| Wrong-person agreement | 4 | 0.75 | [0.30, 0.95] |
| ...

![adversarial](adversarial_by_category.png)

## 5. Confusion matrix

|              | pred CORRECT | pred INCORRECT | pred AMBIGUOUS |
|---|---|---|---|
| true CORRECT | 28 | 4 | 2 |
| true INCORRECT | 6 | 18 | 0 |
| true AMBIGUOUS | 1 | 0 | 1 |

## 6. Generation sub-eval (pass@k)

| Prompt cell | pass@1 | pass@10 |
|---|---|---|

## 7. Interpretation

<one paragraph; Borja writes>

## 8. Caveats

- Probe set N=60 → per-cell counts are small → wide CIs.
- ES tokenizer was Phase-11 EN-biased; expect inflated ES PPL.
- Adversarial category cells with n<3 are flagged but reported.

TODOs

Bloque A — src/eval/report.py

def render_report(results_path: Path, out_path: Path) -> None:
    """Read results.json + per_slice.csv; write REPORT.md."""
    ...
  • Carga results.json, per_slice.csv, incrusta referencias a PNG.
  • Renderiza cada tabla como Markdown.
  • Inserta un bloque marcador para §7 Interpretación con la marca TODO <!-- INTERPRETATION: Borja writes 1-2 paragraphs here. -->.
  • Escribe en out_path (debe estar dentro del directorio por-checkpoint).

Bloque B — src/eval/compare.py

def render_compare(results_a: Path, results_b: Path, out_path: Path) -> None:
    """Diff two results.json files; flag significant differences."""
    ...
  • Tablas lado a lado para cada métrica del headline.
  • Marca de significancia por fila: calcula CIs de Wilson superpuestos. Si los CIs se solapan, marca (within noise); si no, marca o .
  • Diff por slice: solo se resaltan las filas donde la dirección del cambio es opuesta al cambio agregado, o donde la magnitud excede los 10 puntos porcentuales.
  • Diff por categoría adversarial: mismo formato.
  • Diagramas de fiabilidad: ambos PNGs incrustados lado a lado vía HTML en Markdown (<table><tr><td>...</td></tr></table>).

Bloque C — tests/eval/test_report.py

  1. test_renders_minimal_report — dado un results.json fixture pequeño, render_report emite un REPORT.md que contiene los encabezados de sección requeridos.
  2. test_compare_within_noise — dados dos results.json que difieren solo en 1pp de accuracy con N=60, la comparación marca el diff como (within noise).
  3. test_compare_significant_drop — dados dos results.json donde la accuracy cae en 20pp, la comparación emite .
  4. test_interpretation_placeholder_presentREPORT.md contiene el marcador TODO.

Bloque D — escribir learners/borja/phase-20/reflections.md

Con tus propias palabras (300-500 palabras), responde:

  1. ¿Qué hace bien este modelo? Cita slices concretos.
  2. ¿Qué hace mal? Cita slices concretos y categorías adversariales.
  3. ¿Por qué? Hipotetiza la causa a partir de señales de corpus o dinámica de entrenamiento (estadísticas del corpus de la Fase 12, curvas de pérdida de la Fase 19). Ejemplos:
  4. "El modelo va al 60% en past-simple irregular vs 92% en past-simple regular. Sospecho que el corpus de la Fase 12 está dominado por formas regulares; las irregulares aparecen ~3× menos según data/processed/stats.json."
  5. "ECE es 0.18 en el checkpoint best-val de la Fase 19 vs 0.09 en el final de la Fase 18. El best-val se seleccionó solo contra val loss, que no penaliza la sobreconfianza — el entrenamiento más largo de la Fase 18 llevó a salidas mejor calibradas incluso con val loss ligeramente más alta."
  6. ¿Qué cambiaría? No lo arregles aquí — la Fase 20 mide, no entrena. Lista los puntos como TODOs para fases futuras (Fase 28 LoRA, regeneración de corpus de la Fase 12, etc.).

Bloque E — también rellena la interpretación §7 en cada REPORT.md

El marcador debe reemplazarse. La interpretación es el entregable; los números son andamiaje para la frase.

Restricciones

  • Solo Markdown. Sin trucos HTML salvo la tabla de dos-PNGs-lado-a-lado (aceptable porque mkdocs la renderiza).
  • REPORT.md cabe en una pantalla de encabezados. Usa details colapsables si hace falta, pero los headlines deben verse sin hacer scroll.
  • Borja escribe la prosa. Claude puede borrar un borrador, pero la reflexión en learners/borja/phase-20/reflections.md es inconfundiblemente de Borja — primera persona.

Condiciones de parada

Hecho cuando:

  1. pytest tests/eval/test_report.py -v pasa.
  2. Ambos checkpoints tienen un REPORT.md poblado (sin marcadores TODO restantes).
  3. experiments/20-eval-report/COMPARE.md existe y resalta al menos una diferencia significativa (o declara explícitamente "no significant differences detected" con el razonamiento).
  4. learners/borja/phase-20/reflections.md tiene al menos 300 palabras e identifica los tres slices más débiles, con causas hipotetizadas.

Trampas

  • Reportar medias donde los slices discrepan. Si EN está en 0.85 y ES en 0.55, el agregado (~0.70) es el número menos informativo. Lidera con la tabla por slice, no con el agregado.
  • Marcar significancia con demasiada alegría. N=60 y diferencias de 5pp están dentro del ruido. Solo marca diferencias ≥10pp con CIs no solapados. De lo contrario los lectores se acostumbran a ignorar las flechas.
  • Rellenar §7 reformulando métricas. "PPL es 7.42 y accuracy es 0.78" — eso es la tabla. La interpretación dice qué implican esos números. Fuérzate a escribir al menos una frase que ate el comportamiento del modelo a una preocupación downstream (agente tutor de la Fase 32, riesgo de deployment, hueco en el corpus).
  • Olvidar los caveats. El sesgo del tokenizer ES, el N pequeño por celda, el sesgo de curación manual del probe set — todo esto acota la validez del informe. Lístalos.

Cuándo consultar solutions/

Después de que pasen los cuatro tests y ambos REPORTs + COMPARE estén poblados. La solución en solutions/03-report-ref.md (escrita al abrir la fase) muestra un párrafo de interpretación de ejemplo que es específico, hipotetiza causas, y apunta a un arreglo futuro sin intentar arreglarlo en la Fase 20.


Fin de los labs de la Fase 20. Toca escribir PHASE_20_REPORT.md (el informe a nivel de fase, no el REPORT.md de eval) y prepararse para la Fase 21.

Siguiente: Fase 21 — Inference Internals & Sampling.