Spaces:
Running
title: Basic Docker SDK Space
emoji: 🐳
colorFrom: purple
colorTo: gray
sdk: docker
app_port: 7860
Derm Foundation FastAPI Two-Stage Classifier
This project deploys a two-stage inference pipeline:
image -> Google Derm Foundation SavedModel -> embedding -> PyTorch MLP head -> class probabilities
The preprocessing follows the notebook pipeline:
RGB -> resize 448x448 -> PNG bytes -> tf.train.Example with key image/encoded
Project structure
derm_fastapi_project/
app/
main.py # FastAPI app and endpoints
config.py # Environment settings
schemas.py # API response models
services/
preprocessing.py # image -> serialized tf.train.Example
derm_backbone.py # Derm Foundation wrapper
predictor.py # two-stage sequential forward pass
models/
mlp_head.py # load model_state_dict from .pt checkpoint
scripts/
test_request.py # local API test client
requirements.txt
class_names.json # replace with your real class order
.env.example
Setup
Create a virtual environment, then install dependencies:
pip install -r requirements.txt
Put your PyTorch checkpoint in the project root:
derm_foundation_mlp_head.pt
The checkpoint should contain:
{
"model_state_dict": mlp_head.state_dict(),
# optional but recommended:
"class_names": [...]
}
If the checkpoint does not contain class_names, edit class_names.json so the order exactly matches your training label order.
Hugging Face token
Do not put your token in the source code.
Use an environment variable:
export HF_TOKEN="hf_your_token_here"
You must already have access to google/derm-foundation on Hugging Face.
Run the API
uvicorn app.main:app --host 0.0.0.0 --port 8000
Test in browser:
http://127.0.0.1:8000/docs
Test with Python:
python scripts/test_request.py path/to/image.jpg
API endpoint
POST /predict
Input: multipart image upload named file.
Output:
{
"predicted_index": 0,
"predicted_class": "class_0",
"confidence": 0.91,
"probabilities": [
{"index": 0, "class_name": "class_0", "probability": 0.91},
{"index": 1, "class_name": "class_1", "probability": 0.02}
]
}
Important note about the MLP head
app/models/mlp_head.py reconstructs the MLP from Linear layer tensors in model_state_dict.
It assumes Linear layers with ReLU between hidden layers. This is usually fine for a simple MLP head.
If your original head used a different activation, BatchNorm, or a more complex custom architecture, replace InferredMLPHead with the exact same class used during training.