RLcapstone.ai

MoleCheck: Privacy-Preserving Skin-Lesion Analysis

A mobile application that classifies a photographed skin lesion as lower- or higher-risk using a convolutional neural network. All inference runs on-device — no server, no upload, and full offline operation — establishing a strong privacy guarantee for sensitive medical imagery.

Not a medical device. MoleCheck is an educational demonstration of image classification. It cannot diagnose skin cancer or any other condition and must not inform a health decision. If you are concerned about a mole or a skin change, consult a dermatologist. Read the full disclaimer.

Try it in your browser →
Inference runs entirely on your device — your photo is never uploaded.

Android application
MoleCheck · Android (arm64) · ~44 MB · v1.0.0

The full application: capture or select a photo of a lesion and receive an on-device estimate, with the same disclaimer acknowledgement flow. No data is uploaded.

Download APK →
Installation (sideloading)
  1. On an Android phone, open this page and tap Download APK.
  2. When prompted, permit your browser to install from unknown sources (Android requests this once, for safety).
  3. Open the downloaded file and tap Install.

Educational application — not a medical device and not a diagnosis. It is distributed outside the Play Store and signed with a debug key, so Android displays a warning before installation; this is expected for a sideloaded educational build. Built for arm64 devices (the large majority of Android phones from recent years). Android only at present — an iOS build can be produced from the same codebase.

📄 Read the full capstone reportPDF, opens in your browser

Summary

TaskBinary classification of skin lesions (benign vs. malignant) from photographs
DatasetISIC Archive — 11,720 dermatoscopic images (a superset of HAM10000)
ModelYOLO11s-cls, transfer-learned, 224×224 input (5.4M parameters)
Resultv1 baseline: ROC-AUC 0.914 on dermoscopy (held-out test set)
Shipped nowv2 — retrained on smartphone photos: ROC-AUC 0.920 on phone photos, threshold 0.368 (see the v2 write-up below)
DeploymentTensorFlow Lite in the app, ONNX in this browser demo — both on-device
ApplicationFlutter — a single codebase targeting Android and iOS

Rationale for on-device inference

Photographs of skin are sensitive personal data. The most robust way to protect them is to ensure they never leave the device: the model is bundled inside the application, inference executes on the phone's own hardware, and the app functions in airplane mode. This design also eliminates an entire class of engineering concerns — servers, APIs, uptime, and data retention policy — from the project.

Dataset and labeling

Training used a public ISIC Archive collection of 11,720 dermatoscopic images (a superset of the HAM10000 dataset). The diagnosis field for each image was mapped to a binary label — benign or malignant — with indeterminate cases excluded. The dataset is heavily imbalanced toward benign lesions, which informed both the training procedure and the selection of the decision threshold.

Model and training

The classifier is YOLO11s-cls, the classification variant of Ultralytics' YOLO11, fine-tuned for 40 epochs on an NVIDIA RTX 5060 Ti. A pretrained backbone with a clean mobile-export path was chosen deliberately: a strong, reproducible baseline that deploys reliably is more valuable for this project than a bespoke architecture that is difficult to ship.

Threshold selection: prioritizing sensitivity

The default 0.5 decision threshold is inappropriate for a screening task because the two error types are asymmetric: a false negative (a malignant lesion reported as benign) is far more costly than a false positive (a benign lesion flagged for a dermatologist visit). The final model achieves ROC-AUC 0.914, and the operating threshold was set to 0.137 — the point at which the model detects 90% of malignant lesions while still correctly clearing 75% of benign ones. The application is deliberately tuned to err toward recommending a professional evaluation.

Training progression

The chart tracks validation accuracy across training. A key nuance: at epoch 1 the model already scores roughly 80% — which appears strong until one observes that a trivial "always benign" baseline also scores 80%, because most lesions in the dataset are benign. The meaningful learning is everything above that dashed baseline.

Model export and verification

On-device inference required converting the PyTorch model to TensorFlow Lite. Because the direct export path is unsupported on Windows, the model was routed through ONNX and converted with onnx2tf. Conversion correctness was verified rather than assumed: the exported model was evaluated against the original PyTorch model and reproduced its predictions to within 0.001 probability, with identical decisions. Several preprocessing parameters (input layout and normalization) had to match exactly between model and application, or predictions would silently degrade.

Application

The Flutter application allows the user to capture or select an image, runs the TensorFlow Lite model locally, and presents the output as risk awareness rather than a diagnosis. A first-run onboarding flow requires the user to acknowledge the educational disclaimer, and every result screen reiterates the recommendation to consult a dermatologist.

Limitations

v2 — closing the smartphone gap

The first limitation above is the important one, and it is a real flaw: v1 was trained on dermatoscopic images — taken through a clinical lens pressed against the skin, under controlled light — but the app receives an ordinary smartphone photo. The 0.914 headline was therefore measured on a kind of image the app never actually sees. v2 sets out to measure how much that mismatch costs, and then to fix it.

The test data is PAD-UFES-20: 2,298 real smartphone photos of skin lesions collected in a Brazilian screening program, with a patient ID attached to every image. The data is split by patient — no patient on both sides — using the same benign/malignant labels as v1. Actinic keratosis is excluded, because v1's source data marked it "indeterminate" and dropped it, so scoring it here would not be a fair test of the same model. That leaves 969 patients and 1,564 photos.

ModelTested onROC-AUCSpecificity at 90% sensitivity
v1 — trained on dermoscopyits own dermoscopy test0.91475%
v1 — trained on dermoscopyreal smartphone photos 0.74332%
v2 — fine-tuned on smartphonethe same smartphone photos 0.92075%

This is the same idea that runs through the rest of the portfolio — the ECG model tested on patients it never saw, the leukemia model tested on held-out patients: measure the model on what it will really face, not on the friendliest version of the problem. The prep step also records each photo's Fitzpatrick skin type, which makes the fairness audit below possible.

Fairness across skin tones

A model that catches cancer well on average can still fail unevenly — and in dermatology AI that usually means worse performance on darker skin, because public datasets under-represent it. Since the prep step recorded each photo's Fitzpatrick skin type, v2 can be audited for exactly that. The test is deliberately strict: one global decision threshold — the app's 90%-sensitivity operating point — is applied to every group, and the question is simply whether the errors land unevenly.

Skin type (Fitzpatrick)ImagesMalignant lesions Sensitivity (cancers caught)
I–II (lighter)13012394.3%
III–IV (medium)484178.0%
V–VI (darker)33too few to assess

The honest takeaway is uncomfortable and worth stating plainly: v2 is measurably better at catching cancer on lighter skin than on darker skin, and the data available cannot even test the darkest skin at all. Closing that gap is not a modelling trick — it needs training and test data that actually include darker skin, which is the real next step for this project.