Todos los proyectos

4 min de lectura

Predicción de Neumonía en imágenes de tórax

Aplicación de arquitectura DenseNet-201 para clasificar radiografías de tórax y predecir la presencia de neumonía usando el dataset público Chest X-Ray Images.

  • Python
  • TensorFlow
  • Scikit-learn
  • Matplotlib
  • Docker

Galería 3

Contexto Médico

La neumonía es una infección pulmonar que inflama los sacos de aire de uno o ambos pulmones, llenándolos de líquido o pus. Esto provoca síntomas como tos con flema, fiebre, escalofríos y dificultad para respirar. Diversos microorganismos (bacterias, virus y hongos) pueden causarla.

En una radiografía de tórax, las diferencias clave son:

Pulmón Sano Pulmón con Neumonía
Transparencia pulmonar (se ven oscuros por el aire) Opacidades o consolidaciones (áreas blancas/grises por líquido o pus)
Sin zonas blancas anormales Broncograma aéreo (líneas oscuras dentro de zonas blancas)
Estructuras definidas (corazón, diafragma, vasos) Infiltrados lobares o intersticiales

DenseNet-201

DenseNet-201 es una red neuronal convolucional profunda de 201 capas, perteneciente a la familia de las Dense Convolutional Networks (DenseNets), introducidas en 2016 por Gao Huang y colaboradores.

En una red convolucional tradicional con L capas existen L conexiones (cada capa se conecta solo con su sucesora). En una DenseNet con L capas existen L(L+1)/2 conexiones directas: cada capa recibe como entrada los mapas de características de todas las capas anteriores y entrega los suyos a todas las capas siguientes. Esto permite un flujo de gradientes más eficiente y reduce el problema del desvanecimiento del gradiente.


Dataset

Se utilizó el dataset Pneumonia X-Ray Images de Paulo Breviglieri (Kaggle), una versión más balanceada del dataset original de Paul Mooney.

Distribución de imágenes:

Split Neumonía Normal Total
Train 3.110 1.082 4.192
Validation 773 267 1.040
Test 390 234 624

Se observa un desbalance significativo: la clase “Neumonía” tiene aproximadamente el triple de imágenes que la clase “Normal” en cada split.


Preprocesamiento

  • Redimensionamiento a 224×224 píxeles con 3 canales RGB
  • Normalización de píxeles (rescaling 1/255)
  • Data augmentation aplicada al generador de entrenamiento:
    • Rotación aleatoria (±30°)
    • Zoom aleatorio (0.2)
    • Desplazamiento horizontal y vertical (0.2)
    • Variación de brillo (rango 0.7–1.3)
    • Volteo horizontal aleatorio
    • Relleno con el vecino más cercano (nearest neighbor)

Arquitectura del Modelo

Se aplicó transfer learning usando DenseNet-201 pre-entrenada con pesos de ImageNet como extractor de características (capas congeladas), añadiendo capas superiores personalizadas:

Input (224×224×3)
  └── DenseNet-201 (congelada, pesos ImageNet)
        └── GlobalAveragePooling2D
              └── Dense(1536, relu) → Dropout(0.1)
                    └── Dense(512, relu) → Dropout(0.1)
                          └── Dense(1024, relu) → Dropout(0.1)
                                └── Dense(512, relu) → Dropout(0.1)
                                      └── Dense(2, softmax)

La configuración de capas y dropout (0.1) se basó en los resultados del paper “Optimizing DenseNet for Potato Leaf Disease Classification” (arXiv 2402.03347), que demostró que un dropout de 0.1 logra el mejor balance entre rendimiento en entrenamiento y validación.


Entrenamiento

Parámetro Valor
Optimizador Adam
Función de pérdida Categorical Crossentropy
Métrica Accuracy
Épocas máximas 20
Early Stopping Paciencia de 6 épocas, monitorizando val_loss
Batch size 32 (entrenamiento), 8 (test)

Se calcularon los pesos de clase inversamente proporcionales a la frecuencia, pero no se aplicaron durante el entrenamiento.


Resultados

Curva de entrenamiento

El modelo alcanzó ~95–96% de accuracy en entrenamiento y validación durante las 20 épocas, con pérdidas decrecientes y sobreajuste casi nulo entre ambas curvas.

Evaluación en test

Métrica Clase Normal (0) Clase Neumonía (1)
Precisión 0.95 0.80
Recall 0.59 0.98
F1-score 0.73 0.88

Accuracy global en test: 83.8% (loss: 0.42)

Matriz de Confusión

Predicho Normal Predicho Neumonía
Real Normal 135 99
Real Neumonía 6 384

Interpretación

  • El modelo detecta correctamente la mayoría de los casos de neumonía (98% de recall).
  • Sin embargo, tiene un rendimiento deficiente en la clase Normal (solo 59% de recall), clasificando erróneamente 99 imágenes normales como neumonía.
  • Esto evidencia un sesgo hacia la clase dominante debido al desbalance del dataset y la no utilización de los pesos de clase calculados.

Conclusiones

  1. Alta precisión en neumonía: el modelo identifica correctamente la mayoría de los casos positivos.
  2. Bajo desempeño en clase normal: alta tasa de falsos positivos (99 imágenes) debido al desbalance.
  3. Sobreajuste: aunque la validación mostraba buen rendimiento, el test reveló una caída significativa, posiblemente porque train y validation provenían del mismo ImageDataGenerator, lo que pudo llevar a memorización de patrones.

Mejoras Propuestas

  1. Aplicar class_weights durante el entrenamiento para equilibrar la importancia de ambas clases.
  2. Fine-tuning descongelando capas superiores de DenseNet-201 con un learning rate reducido.
  3. Data augmentation específica para la clase minoritaria (Normal) para mejorar la generalización.

Versiones

El proyecto usa las siguientes librerías y versiones:

Paquete Versión
Python 3.12.3
TensorFlow 2.18.0
Keras 3.7.0
scikit-learn 1.5.1
matplotlib 3.9.2
numpy 1.26.4
pandas 2.2.2
seaborn 0.13.2

Referencias

  • Huang, G., Liu, Z., Van Der Maaten, L., & Weinberger, K. Q. (2017). Densely Connected Convolutional Networks. arXiv:1608.06993.
  • Breviglieri, P. Pneumonia X-Ray Images. Kaggle.
  • Mooney, P. Chest X-Ray Images (Pneumonia). Kaggle.
  • Paper de referencia para arquitectura adaptada: arXiv 2402.03347.