Skip to content

8. Escenarios "¿Qué pasaría si...?"

Análisis hipotéticos de las consecuencias técnicas de cambiar cada componente. Sirven doble propósito: entender la sensibilidad del sistema y prepararse para preguntas de "¿y si hubieran hecho...?". Cada escenario indica el efecto esperado y los archivos afectados.

8.1. Cambios de arquitectura

¿Y si cambio EfficientNet-B0 por MobileNetV3?

Cómo: make train-baselines MODELS=mobilenet_v3_large. Ambos ya están registrados. Efecto esperado: MobileNetV3-Large es de capacidad comparable pero optimizado para latencia móvil. Probablemente macro-F1 similar (dentro de ±0.02); ventaja en velocidad de inferencia. Es un cambio de una línea gracias al registry — ninguna otra parte del código se toca.

¿Y si uso un ViT grande (no ligero)?

Efecto esperado: con solo 25k imágenes, un ViT-Base sobreajustaría o rendiría por debajo de las CNNs (los ViT necesitan datasets grandes o pre-entrenamiento masivo). Además violaría la restricción de tamaño/latencia móvil. FastViT-T8 (ya incluido) es el compromiso: arquitectura híbrida ligera. Un ViT grande sería un paso atrás en este proyecto.

¿Y si entreno desde cero (sin pesos preentrenados)?

Cómo: --no-pretrained. Efecto esperado: caída grande de macro-F1 (posiblemente de ~0.91 a ~0.6-0.7 o peor), convergencia mucho más lenta, y sobreajuste severo en las clases minoritarias (cientos de imágenes no bastan para aprender features desde cero). Confirmaría empíricamente por qué se usa transfer learning. Es un experimento valioso para la defensa (demuestra el valor del transfer learning con datos propios).

8.2. Cambios en los datos

¿Y si elimino el data augmentation?

Cómo: pasar transforms de validación a train. Efecto esperado: sobreajuste notable, especialmente en minoritarias; peor generalización al conjunto real de campo. La brecha train-val se ampliaría. El augmentation es el principal defensor contra el sobreajuste dado el tamaño del dataset.

¿Y si aplico ColorJitter agresivo (saturación/hue altos) a todas las clases?

Efecto esperado: caída específica en las deficiencias nutricionales (nitrogen/phosphorus/potassium_deficiency), cuyo diagnóstico depende del color. El modelo aprendería a ignorar el tono, perdiendo la señal. Es el error que el diseño evita a propósito manteniendo saturación/hue casi en cero. Un jurado que pregunte "¿por qué no más augmentation de color?" recibe esta respuesta.

¿Y si duplico el dataset (más imágenes por clase)?

Cómo: NO_CAP=1 o subir max_images_per_class. Efecto esperado: mejor generalización en las clases que crecen (las mayoritarias, que estaban capadas a 1500); las minoritarias no cambian (ya estaban bajo el cap). Entrenamiento más lento. Probable mejora modesta de macro-F1 si las clases grandes tenían margen. Riesgo: el desbalance se acentúa, exigiendo más del sampler.

¿Y si reduzco el dataset a la mitad?

Efecto esperado: más sobreajuste, macro-F1 más baja, especialmente en minoritarias que ya eran escasas. El transfer learning amortigua parte del golpe, pero las clases raras sufrirían primero.

¿Y si quito la deduplicación SHA-256?

Efecto esperado: fuga de datos (data leakage). Imágenes idénticas caerían en train y test → métricas infladas (macro-F1 aparentemente mejor pero engañosa). El modelo parecería mejor de lo que es en producción. Es un error metodológico grave que el diseño previene.

¿Y si no estratifico por entorno (solo por clase)?

Efecto esperado: el test podría quedar dominado por imágenes lab (fondo limpio), evaluando en un dominio distinto al de despliegue (campo real). Las métricas serían optimistas y no representativas del uso real. La estratificación por clase+entorno lo evita.

8.3. Cambios en el entrenamiento

¿Y si cambio CrossEntropy por Focal Loss?

Efecto esperado: Focal Loss enfoca en ejemplos difíciles, útil en desbalance extremo. Pero el proyecto ya balancea con el sampler; añadir Focal Loss sería una segunda forma de compensar el desbalance → riesgo de sobre-corrección. Podría ayudar en las clases más difíciles o desestabilizar. Sería un experimento a probar quitando el sampler, no encima de él. Requiere cambiar criterion en _train_model.

¿Y si añado también pesos a la CrossEntropy (encima del sampler)?

Efecto esperado: doble compensación. El modelo sobre-representaría las minoritarias tanto en muestreo como en penalización → probable caída del F1 de las mayoritarias sin ganancia neta. Es exactamente lo que el diseño evita a propósito. Ver decisiones: balanceo.

¿Y si cambio AdamW por SGD?

Efecto esperado: SGD probablemente necesitaría un LR mayor y un schedule (cosine/step) para converger bien en 30 épocas; con LR constante 1e-4 quedaría subentrenado. Bien ajustado, SGD podría generalizar ligeramente mejor, pero a costa de más tuning — contrario al objetivo de "comparar rápido" de los baselines.

¿Y si añado un learning rate scheduler?

Efecto esperado: probable mejora de la precisión final (bajar el LR al final afina la convergencia). Es la primera mejora recomendada para el pipeline principal train.py. Bajo riesgo, beneficio probable. Requiere añadir torch.optim.lr_scheduler al loop.

¿Y si aumento la resolución a 384?

Efecto esperado: potencial mejora en lesiones finas, pero ~2.9× más cómputo/memoria, batch auto-reducido, y alejamiento de los pesos preentrenados (salvo modelos diseñados para 384). Además empeora la viabilidad móvil. Beneficio incierto, costo seguro.

¿Y si elimino toda la regularización (weight_decay=0, sin augmentation)?

Efecto esperado: sobreajuste severo; la brecha train-val se dispararía; macro-F1 de test caería. Demostraría por qué la regularización es necesaria con un dataset de este tamaño.

8.4. Cambios de metodología

¿Y si uso validación cruzada (k-fold) en vez de split fijo?

Efecto esperado: estimación de rendimiento más robusta (menos dependiente de un split particular), a costa de k× el tiempo de entrenamiento. Para comparar baselines rápido, el split fijo es suficiente; k-fold sería apropiado para la evaluación final del modelo elegido. Requeriría reestructurar create_splits.py y el loop.

¿Y si cambio la semilla (seed)?

Efecto esperado: splits, inicialización de la cabeza y muestreo distintos → resultados ligeramente diferentes pero estadísticamente equivalentes. Si los resultados cambiaran mucho con la semilla, indicaría inestabilidad (dataset muy pequeño o modelo frágil). Correr varias semillas y reportar media±desviación sería una mejora de rigor para la defensa.

8.5. Cambios de despliegue

¿Y si cuantizo a Int8 (TFLite)?

Efecto esperado: tamaño ~4× menor, inferencia más rápida en móvil, pequeña caída de precisión (típicamente <1-2 % de accuracy con cuantización bien calibrada). Es el objetivo de despliegue. Riesgo: algunas operaciones o arquitecturas cuantizan peor; habría que validar macro-F1 post-cuantización. Ver fundamentos: cuantización.

¿Y si el modelo no cabe en ≤20 MB tras cuantizar?

Efecto esperado: habría que (a) elegir una arquitectura más pequeña (MobileNetV3-Small, ShuffleNet), (b) aplicar pruning, o (c) knowledge distillation a un estudiante pequeño. ShuffleNetV2 y EfficientNet-Lite0 ya son candidatos por su ligereza.

8.6. Tabla resumen de sensibilidad

CambioImpacto en macro-F1Impacto en velocidadRiesgo
Modelo ligero ↔ ligeroBajo (±0.02)VariableBajo
Desde cero (--no-pretrained)🔴 Alto (caída grande)🔴 Más lentoAlto
Quitar augmentation🟠 Caída (sobreajuste)🟢 Más rápidoMedio
Color aug agresivo🟠 Caída en deficienciasNeutroMedio
Quitar dedup🔴 "Sube" (falso)Neutro🔴 Leakage
Doble compensación (sampler+loss)🟠 Caída netaNeutroMedio
Añadir scheduler🟢 Mejora probableNeutroBajo
Cuantizar Int8🟡 Caída pequeña🟢 Mucho más rápidoMedio

Siguiente

Las herramientas sobre las que se apoya todo esto: Dependencias y librerías.