EN
NeumonIA — banner
← Volver a proyectos

NeumonIA

· 4 min de lectura

Aplicación web progresiva que clasifica radiografías de tórax con IA directamente en el navegador usando ONNX Runtime Web, sin enviar datos médicos a un servidor remoto.

React TypeScript TailwindCSS ONNX Python Ultralytics Vite

Motivación

La neumonía causa aproximadamente 2.5 millones de muertes al año a nivel global. La radiografía de tórax es la herramienta diagnóstica más utilizada, pero su interpretación requiere radiólogos especializados, un recurso escaso en muchas regiones.

El objetivo fue explorar cómo la inteligencia artificial puede asistir en el tamizaje de neumonía directamente desde el navegador, sin depender de un servidor remoto ni comprometer la privacidad de los datos médicos. La imagen nunca abandona el dispositivo.

Este proyecto también parte de Predicción de Neumonía en imágenes de tórax (un modelo DenseNet-201), llevando ese objetivo al navegador.


Solución

NeumonIA es una PWA que carga un modelo de clasificación en el navegador mediante ONNX Runtime Web y realiza inferencia completamente del lado del cliente. El usuario arrastra una radiografía de tórax; el modelo la procesa en segundos y muestra el resultado con el nivel de confianza y el tiempo de inferencia.

Incluye galería de imágenes de muestra, historial de predicciones, modo oscuro, interfaz responsive y soporte para español.


Arquitectura técnica

Entrenamiento del modelo

El modelo se entrenó en Google Colab con una GPU NVIDIA RTX PRO 6000 Blackwell (97 GB VRAM). Se utilizó YOLO26n-cls (47 capas, 1.5M parámetros) sobre el Chest X-Ray Pneumonia Balanced Dataset con 6 800 imágenes de entrenamiento y 1 700 de validación (balanceado 50/50).

Se eligió YOLO en lugar de construir una CNN personalizada desde cero por tres razones:

  1. Exportación nativa a ONNX mediante Ultralytics: esencial para la inferencia en el navegador con onnxruntime-web.
  2. Huella compacta: YOLO26n-cls tiene solo 1.5M de parámetros y produce un archivo ONNX de 5.9 MB, manteniendo tiempos de carga razonables en la web.
  3. Cabezal de clasificación probado: la variante -cls de YOLO está diseñada específicamente para clasificación de imágenes, evitando la complejidad de adaptar una arquitectura de detección.

Esto contrasta con el proyecto anterior de Predicción de Neumonía, que utilizó DenseNet-201 (18M+ parámetros): una arquitectura potente pero mucho más pesada. Según los benchmarks de Ultralytics, YOLO26n alcanza solo 1.7 ms de latencia en T4 TensorRT y 38.9 ms en CPU ONNX (hasta un 43% más rápido que su predecesor), por lo que encaja con la inferencia en tiempo real del lado del cliente.

HiperparámetroValor
Épocas150 (early stopping, patience=20)
Batch size512
Tamaño de imagen224×224
Learning rateCosine scheduler + AMP
Precisión validación98.5% top-1
Precisión test100% (30/30 imágenes)

Exportación a ONNX

El checkpoint de PyTorch (3.0 MB) se exportó a ONNX con opset 17, precisión FP32 y grafo simplificado. El tamaño final es de 5.9 MB, compatible con onnxruntime-web.

Inferencia en el navegador

La clase inference.ts (126 líneas) implementa el pipeline en tres fases:

  1. Carga del modelo: detección en cascada de WebGPU → WebGL → WASM para garantizar compatibilidad con cualquier navegador moderno.
  2. Preprocesado: la imagen se redimensiona a 224×224 en un canvas, se extraen los canales R, G, B por separado y se organizan en formato NCHW (planar) como espera YOLO.
  3. Predicción: el tensor se alimenta a la sesión ONNX y se obtienen dos probabilidades [normal, neumonía].

Plugin Vite para archivos WASM

El mayor desafío de integración fue que onnxruntime-web requiere 4 archivos WASM en runtime que Vite no sirve automáticamente desde node_modules. Escribí un plugin personalizado que:

  • En desarrollo: sirve los .wasm con el header Content-Type: application/wasm.
  • En producción: copia los archivos WASM a dist/ durante el build.

Además, onnxruntime-web se excluyó de optimizeDeps para evitar que Vite rompa las importaciones del WASM.


Frontend

  • React 19.2 con hooks personalizados: useModel (ciclo de vida del modelo ONNX), useTheme (claro/oscuro/sistema) y useHistory (hasta 50 predicciones en memoria).
  • Navegación sin router: useState<View> con renderizado condicional para mantener el bundle ligero.
  • Sistema de temas: custom properties CSS en :root, .dark y @media (prefers-color-scheme: dark), con persistencia en localStorage.
  • 14 componentes: DropZone, SampleGallery, ImagePreview, ResultCard, ProgressBar, Sidebar, HistoryList, entre otros.

Resultados

MétricaValor
Precisión top-1 (validación)98.5%
Precisión top-1 (test)100.0%
Latencia WebGPU~5–20 ms
Latencia WebGL~20–80 ms
Latencia WASM~100–500 ms
Tamaño del modelo ONNX5.88 MB
Tiempo de carga~2–8 s

El modelo mantiene precisión perfecta sobre el conjunto de prueba y ejecuta inferencia en tiempo real incluso en hardware de consumo.

Desafíos y aprendizajes

Integración ONNX + Vite

Los archivos WASM no se resuelven automáticamente desde node_modules. Lo resolví escribiendo un plugin personalizado y excluyendo onnxruntime-web del pre-bundling de Vite.

Fallback de execution providers

WebGPU aún no es universal. La detección en cascada (WebGPU → WebGL → WASM) fue esencial para garantizar funcionamiento en cualquier navegador.

Preprocesado NCHW

YOLO espera canales en formato planar (R plano, G plano, B plano), no intercalado. La implementación manual desde getImageData() requirió cuidado para mantener rendimiento en CPU.

Diseño para contexto médico

La UI incluye disclaimer visible, barra de confianza (no solo veredicto binario) e historial efímero en memoria para no almacenar datos médicos sin consentimiento.


Referencias