Skip to content

10. Riesgos técnicos

Cuellos de botella, deuda técnica, suposiciones ocultas y componentes frágiles. Ser honesto sobre las limitaciones es parte de una buena defensa: un jurado valora que conozcas los puntos débiles y tengas plan para ellos.

10.1. Cuellos de botella (rendimiento)

E/S de imágenes

El pipeline lee decenas de miles de imágenes de disco. En almacenamiento lento (o volúmenes remotos como Modal), la E/S puede dominar el tiempo, dejando la GPU ociosa esperando datos. Mitigaciones ya presentes: carga perezosa, num_workers paralelos, pin_memory en CUDA, indexado paralelo en create_splits.py (ThreadPoolExecutor), SPLITS_INDEX_WORKERS para ajustar en contenedores. Riesgo residual: con num_workers mal configurado (o 0), la GPU se subutiliza.

LIME es caro

Cada explicación LIME hace num_samples (=1000) forwards. Explicar muchas imágenes es lento. Mitigación: muestreo balanceado pequeño para reportes visuales; persistencia .json/.npy para reanálisis sin re-ejecutar. Riesgo: correr explain-report sobre todo el test es costoso.

Reproducibilidad cuesta velocidad

cudnn.benchmark=False (para determinismo) desactiva el autotuning de convoluciones → algo más lento. Es un trade-off consciente, no un bug.

10.2. Suposiciones ocultas

SuposiciónRiesgo si se rompe
DATASET_ROOT apunta a un clean/ completo y actualizadocreate_splits.py falla (bien: falla ruidoso) o entrena con datos viejos
El orden de dataset.classes nunca cambia tras entrenarRótulos permutados; checkpoints inutilizables
Los índices de clase viven en summary.json, no en el state_dictCargar un checkpoint sin su summary.json → mapeo reconstruido, posible desalineación
Las imágenes de test representan el dominio de despliegue (campo real)Si el test es mayormente lab, las métricas son optimistas
La dedup exacta bastaCuasi-duplicados (recorte/recompresión) escapan → leakage parcial

El punto más frágil: el mapeo de clases

El acoplamiento entre el state_dict (que no guarda los nombres de clase) y el summary.json (que sí) es una suposición estructural. Si alguien mueve un best.pth sin su summary.json, o reconstruye el mapeo desde una lista con orden distinto, obtiene etiquetas equivocadas sin ningún error visible. El código lo mitiga con load_run_metadata (prioriza summary.json) y la advertencia documentada, pero sigue siendo el punto donde un descuido causa el bug más silencioso del proyecto.

Deduplicación solo exacta

create_splits.py deduplica por SHA-256 (bit-a-bit). Dos versiones de la misma foto (recortada, recomprimida a JPEG, redimensionada) tienen hashes distintos y pasan como imágenes diferentes → posible fuga entre train y test. El grupo analysis incluye imagededup (perceptual) para la fase de construcción, pero el pipeline de splits no lo aplica. Riesgo conocido; mejora futura: dedup perceptual en el split.

10.3. Deuda técnica

train.py es un esqueleto

El pipeline "principal" no tiene loop de entrenamiento (solo un TODO). Hoy el único entrenamiento funcional es train_baselines.py. Riesgo: confusión para nuevos integrantes que esperen que make train entrene algo. Documentado claramente, pero es deuda pendiente. Al implementarlo, debe reusar _run_epoch y llamar update_latest_pointer.

Sin tests automatizados formales

Hay smoke checks (smoke_loader.py) y auditorías manuales (lime_stability.py), pero no una suite de tests unitarios (pytest) que corra en CI. Riesgo: una regresión en CornDataset o el splitter podría pasar desapercibida. Mejora recomendada: tests unitarios de compute_minority_classes, build_weighted_sampler, el splitter y el mapeo de clases.

Scripts de cleanup no reproducibles como conjunto

Los ~25 scripts de scripts/cleanup/ construyeron clean/ pero no hay un orquestador único que los ejecute en orden. Reconstruir clean/ desde raw/ requeriría correrlos manualmente en la secuencia correcta. Mitigación real: clean/ está versionado en HF Hub, así que la reconstrucción rara vez se necesita.

Exportación a TFLite pendiente

Todo el objetivo de despliegue (cuantización Int8, app Android) está por hacer. Riesgo: el modelo elegido podría degradarse al cuantizar, o no cumplir ≤20 MB/≤300 ms, y eso no se sabrá hasta implementarlo. Recomendación: validar la cuantización temprano sobre el líder actual.

10.4. Componentes frágiles

GRADCAM_TARGET_LAYERS debe mantenerse sincronizado con el registry

Añadir un modelo sin su entrada aquí → Grad-CAM se omite silenciosamente (warning, 3 paneles). No es un crash, pero degrada el reporte sin que se note. Frágil por omisión.

Hooks de Grad-CAM y modificaciones inplace

El uso de out.register_hook (en vez de register_full_backward_hook) resuelve el conflicto con ReLU inplace, pero es un detalle sutil: un refactor ingenuo del hook podría reintroducir el error "view is being modified inplace" en modelos como GhostNetV2. Documentado en el código, pero frágil ante cambios descuidados.

Estratificación exige ≥7 imágenes por estrato

Si una fuente nueva aporta una clase con muy pocas imágenes en un entorno (p.ej. 5 lab de una clase rara), create_splits.py falla (bien: ruidoso) exigiendo agregar imágenes o excluir la clase. Es una restricción dura del método de split.

LIME es estocástico

Las explicaciones varían entre seeds. Si se presentan como "la" explicación sin auditar estabilidad, pueden ser engañosas. lime_stability.py existe justo para cuantificar esto, pero requiere ejecutarlo conscientemente.

10.5. Posibles fallos futuros

EscenarioSíntomaPrevención
Actualizar torch sin torchvisionErrores de carga de modelosActualizar en pareja; validar con smoke test
pandas mayor con cambio de groupbySplits corruptos (columna perdida)Rango acotado; el bug ya se corrigió una vez
Nueva clase con estrato < 7create_splits.py abortaAgregar imágenes o excluir
Mover checkpoint sin summary.jsonRótulos permutados silenciososMantener el run_dir íntegro
Cuantización degrada macro-F1Modelo móvil peor que el reportadoValidar Int8 temprano

10.6. Resumen de salud del proyecto

Fortalezas: pipeline de datos robusto y determinista; separación limpia src/scripts; balanceo bien pensado; explicabilidad doble; versionado de runs; guardas que fallan ruidoso.

Debilidades a comunicar en una defensa: loop principal pendiente; dedup solo exacta; sin tests formales; despliegue TFLite no validado; dependencia estructural en el mapeo de clases.

Siguiente

Con los riesgos claros, prepárate para las preguntas: Banco de preguntas y respuestas.