Spaces:
Running
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: | |
| ```text | |
| image -> Google Derm Foundation SavedModel -> embedding -> PyTorch MLP head -> class probabilities | |
| ``` | |
| The preprocessing follows the notebook pipeline: | |
| ```text | |
| RGB -> resize 448x448 -> PNG bytes -> tf.train.Example with key image/encoded | |
| ``` | |
| ## Project structure | |
| ```text | |
| 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: | |
| ```bash | |
| pip install -r requirements.txt | |
| ``` | |
| Put your PyTorch checkpoint in the project root: | |
| ```text | |
| derm_foundation_mlp_head.pt | |
| ``` | |
| The checkpoint should contain: | |
| ```python | |
| { | |
| "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: | |
| ```bash | |
| export HF_TOKEN="hf_your_token_here" | |
| ``` | |
| You must already have access to `google/derm-foundation` on Hugging Face. | |
| ## Run the API | |
| ```bash | |
| uvicorn app.main:app --host 0.0.0.0 --port 8000 | |
| ``` | |
| Test in browser: | |
| ```text | |
| http://127.0.0.1:8000/docs | |
| ``` | |
| Test with Python: | |
| ```bash | |
| python scripts/test_request.py path/to/image.jpg | |
| ``` | |
| ## API endpoint | |
| ### POST `/predict` | |
| Input: multipart image upload named `file`. | |
| Output: | |
| ```json | |
| { | |
| "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. | |