/ Proyectos / Salud / Clasificación de severidad de retinopatía diabética
Clasificación de severidad de retinopatía diabética: por qué el accuracy no basta sin atención ni transfer learning
ResNet50 con bloques de atención frente a VGG16 y a un baseline sin transfer learning, y el hallazgo que solo aparece cuando se mira el recall por clase, no el accuracy general.
El objetivo era clasificar el grado de severidad de retinopatía diabética (5 niveles, de sano a proliferativa) a partir de imágenes de fondo de ojo. El problema real no era solo entrenar un modelo que clasificara bien: era entender qué significa "bien" cuando el dataset está fuertemente desbalanceado hacia la clase sana, y casi todo lo interesante para un caso de uso médico está en las clases minoritarias.
Se compararon tres configuraciones sobre la misma tarea: un backbone ResNet50 preentrenado en ImageNet con dos bloques de atención añadidos, el mismo esquema de atención pero con VGG16 como backbone, y un modelo VGG16 sin atención y sin pesos preentrenados, como punto de comparación. El hallazgo no estuvo en cuál modelo "ganó" en accuracy, estuvo en lo que el accuracy por sí solo no mostraba.
Nota importante
Este es un ejercicio técnico de clasificación de imágenes con fines educativos y de investigación aplicada. No es un dispositivo médico, no está validado clínicamente, y no debe usarse como herramienta de diagnóstico. El dataset es público y de acceso abierto.
Mapa del casoContenido
- Diagrama de la solución. El backbone preentrenado, los dos bloques de atención y qué pasa si se quitan
- Construcción de la solución. Cómo el desbalance de clases dirigió cada decisión
- Condiciones del proyecto. Restricciones reales y opciones descartadas antes de construir
- Pasos. Qué se dejó al agente y qué criterio se aplicó en cada uno
- Proceso de iteración. El hallazgo del recall en cero, y un bug honesto que se encontró en el camino
- Descargables. Notebook, diagrama y repositorio del proyecto
- Lecciones importantes. La lección concreta que deja este caso
Estructura de alto nivelDiagrama de la solución
Pasa el cursor sobre cada bloque. El bloque punteado naranja muestra qué pasa si se elimina el transfer learning y la atención.
Los dos bloques de atención y el transfer learning no son mejoras cosméticas sobre una arquitectura que ya funcionaba. Son, literalmente, la diferencia entre un modelo que detecta enfermedad y uno que no detecta ninguna, como se ve en la siguiente sección.
Cómo se construyóConstrucción de la solución
Condiciones del proyecto
| Restricción | Dato |
|---|---|
| Objetivo | Ejercicio técnico de clasificación de imágenes médicas, no un producto clínico |
| Dataset | Imágenes públicas de fondo de ojo, 5 grados de severidad, fuertemente desbalanceado hacia la clase sana |
| Detalle a detectar | Muy fino: las diferencias visuales entre grados de severidad son sutiles, difíciles para una CNN convencional |
| Split de datos | 70% entrenamiento, 15% validación, 15% prueba, sin mezclar imágenes del mismo paciente entre splits |
| Opción | Por qué se descartó |
|---|---|
| Entrenar una CNN convencional sin atención | No captura los detalles finos que distinguen un grado de severidad de otro |
| Entrenar desde cero, sin pesos preentrenados | Con un dataset médico de tamaño moderado, no alcanza a aprender patrones visuales generales desde el inicio |
| Reportar solo el accuracy general de cada modelo | Con un dataset desbalanceado, un accuracy alto puede lograrse sin detectar ninguna clase minoritaria |
| Backbone preentrenado (ImageNet) + atención global + atención por categoría, evaluado con recall por clase | Da contexto visual general (transfer learning) y detalle específico por severidad (atención), y el recall por clase expone si realmente detecta enfermedad |
Pasos
Cómo manejar el desbalance de clases
Agente: propuso solo hacer oversampling de las clases minoritarias antes de entrenar.
Criterio aplicado: combinar el balanceo del set de entrenamiento con un bloque de atención por categoría dentro de la arquitectura, porque el desbalance no es solo un problema de cantidad de ejemplos, también lo es de qué tan distinguibles son los detalles visuales de cada clase.
Qué backbone usar
Agente: sugirió usar el backbone más simple posible para acelerar el entrenamiento.
Criterio aplicado: comparar explícitamente ResNet50 contra VGG16 con la misma configuración de atención, porque la elección del backbone resultó tener más impacto en el resultado que cualquier otro ajuste probado.
Qué métrica reportar como resultado principal
Agente: propuso reportar el accuracy general de cada modelo como métrica principal.
Criterio aplicado: reportar el recall por clase junto con el accuracy, porque en un problema médico desbalanceado, el costo de no detectar un caso real de enfermedad es mucho mayor que el de una falsa alarma.
Estrategia de fine-tuning del backbone
Agente: propuso descongelar todas las capas del backbone desde la primera época.
Criterio aplicado: congelar el backbone durante una fase inicial (para no destruir los pesos preentrenados con gradientes grandes al principio) y descongelarlo después, con un learning rate mucho menor, para hacer fine-tuning fino sin perder lo aprendido en ImageNet.
Proceso de iteración
El hallazgo central, sin suavizarlo
El modelo baseline (VGG16, sin atención, sin transfer learning) llegó a 68% de accuracy general, un número que a primera vista no se ve mal. Pero al revisar el recall por clase, ese modelo tuvo 0% de recall en las 4 clases de severidad de retinopatía (grados 1 a 4): nunca predijo ninguna de ellas, solo aprendió a decir "sano" siempre, la clase mayoritaria del dataset. El 68% de accuracy es, en los hechos, el porcentaje de pacientes sanos en el set de prueba, no una medida de que el modelo funcione. Solo al agregar transfer learning y los bloques de atención (ResNet50) el recall en las clases severas subió a valores entre 0.71 y 0.88, con VGG16+atención en un punto intermedio (0.13 a 0.31 de recall en esas mismas clases). Este es el mismo modelo, la misma tarea, y una diferencia de arquitectura que decide si el sistema detecta enfermedad o no detecta nada.
Ajuste documentado, no oculto
Al revisar el código original para adaptarlo, se encontró un error real en la fase de entrenamiento con capas congeladas del modelo ResNet: la llamada de entrenamiento apuntaba a una variable de modelo distinta a la que se había construido, un error de copiar y pegar entre secciones del notebook. Se corrigió en la versión reproducible de este repositorio. Se documenta aquí porque es exactamente el tipo de error silencioso que puede pasar desapercibido en un notebook largo, y que vale la pena revisar activamente antes de confiar en cualquier resultado.
CierreLecciones importantes
En un problema médico con clases desbalanceadas, el accuracy general puede ser, literalmente, el porcentaje de la clase mayoritaria. Reportarlo solo, sin desglosar por clase, no es un error técnico menor, es ocultar la pregunta que más importa: ¿el modelo detecta los casos que realmente importan detectar?
La combinación de transfer learning y atención no fue una mejora incremental sobre un modelo que ya funcionaba: fue la diferencia entre un sistema que nunca identifica enfermedad y uno que sí lo hace, aunque de forma imperfecta. Esa diferencia solo se ve si, desde el diseño del experimento, se decide medir lo que de verdad importa, no solo lo que es fácil de reportar.
El esquema de atención global + atención por categoría usado aquí sigue la misma idea de CABNet, un enfoque publicado específicamente para el desbalance de clases en gradación de retinopatía diabética: CABNet: Category Attention Block for Imbalanced Diabetic Retinopathy Grading.
Termina ya tu proyecto
¿Tienes un proyecto similar y no sabes por dónde empezar? Yo te ayudo a diseñarlo.
El 92% de los profesionales de datos desbloquea sus proyectos al ver ejemplos completos resueltos.
Sin registro. Acceso inmediato