Inspiration

Pneumonia is one of the leading causes of death in young children worldwide, and it is highly treatable when caught early. The bottleneck is often not treatment but diagnosis: in many clinics there are too few radiologists, and a chest X-ray can wait a long time to be read.

AI can help, but most medical AI tools are black boxes. They output "pneumonia: 94%" with no explanation, and a doctor has little reason to trust a number they can't question. I wanted to build something that doesn't just predict, but shows its reasoning and admits when it isn't sure. That became LungLens: see what the AI sees.

What it does

Upload a chest X-ray and LungLens returns:

  • A prediction (Normal vs. Pneumonia) with a confidence score
  • A Grad-CAM heatmap highlighting the lung regions that drove the decision
  • An "Uncertain, please consult a doctor" result when confidence is too low, instead of forcing a guess

It is designed as a triage aid to help prioritize scans, not to replace clinicians.

How I built it

  • Model: transfer learning with a pretrained [ResNet18 / MobileNetV2] in PyTorch, fine-tuned on the public Chest X-Ray Images (Pneumonia) dataset
  • Data handling: the provided validation set was too small to be reliable, so I created my own stratified train/validation/test split, with augmentation (rotation, brightness, flips) to improve generalization
  • Class imbalance: pneumonia images outnumber normal ones, so I used a weighted loss:

$$\mathcal{L} = -\frac{1}{N}\sum_{i=1}^{N} w_{y_i}\,\log p_{\theta}(y_i \mid x_i)$$

  • Explainability: Grad-CAM weights each feature map $A^k$ by the average gradient of the class score $y^c$:

$$\alpha_k^c = \frac{1}{Z}\sum_{i}\sum_{j}\frac{\partial y^c}{\partial A^k_{ij}}, \qquad L^c_{\text{Grad-CAM}} = \text{ReLU}!\left(\sum_k \alpha_k^c A^k\right)$$

  • Uncertainty: if the top softmax probability is below a threshold $\tau$, the app returns "uncertain" instead of a label
  • App: a Streamlit web interface so anyone can upload an image and get results instantly

Tech stack: Python, PyTorch, torchvision, Streamlit, NumPy, OpenCV, Matplotlib, GitHub

Results

Evaluated on a held-out test set, I focused on sensitivity (not missing sick patients) rather than accuracy alone:

$$\text{Sensitivity} = \frac{TP}{TP + FN}, \qquad \text{Specificity} = \frac{TN}{TN + FP}$$

  • Sensitivity: [X%]
  • Specificity: [X%]
  • Accuracy: [X%]

Challenges I ran into

  • Misleading accuracy: because the data is imbalanced, a model could look accurate while missing sick patients. I switched to sensitivity, specificity and a confusion matrix.
  • Unreliable validation set: the provided validation split had only a handful of images, so I rebuilt the splits myself to get trustworthy evaluation.
  • Overconfident predictions: neural networks can be confidently wrong, which is dangerous in healthcare. This motivated the uncertainty threshold.
  • Checking the heatmaps: a good score isn't enough if the model is looking at the wrong thing (like text markers or borders). Grad-CAM let me catch this. [Describe anything you actually found.]
  • [Add any real bug or setback you hit while building.]

What I learned

  • Accuracy alone can be dangerous in medical AI. Choosing the right metric matters as much as choosing the right model.
  • Explainability builds trust: seeing why a model decided helps spot errors and shortcuts.
  • A responsible health tool should know its limits and say "I'm not sure."
  • How to take a model from a notebook to a usable app that non-technical people can try.

Limitations and ethics

LungLens is a research prototype, not a medical device. It was trained on a single public dataset of pediatric X-rays and has not been clinically validated. It should never be used for real diagnosis without review by a qualified clinician.

What's next

  • Test on more diverse datasets (different hospitals, ages, scanners)
  • Add more conditions (e.g., tuberculosis, COVID-19)
  • Calibrate confidence scores more rigorously
  • Work with clinicians to evaluate real-world usefulness

Built With

Share this project:

Updates

Submission history