Yousefxp8/ml-inference-api
ML Inference API
[Live Demo / Swagger Docs: https://Yousefxp8-ml-inference-api.hf.space/docs](https://Yousefxp8-ml-inference-api.hf.space/docs)
A Dockerized REST API for celebrity face recognition, built with FastAPI, Celery, Redis, and a transfer-learned ResNet50 model. Image predictions are queued as background tasks, results can be polled by task ID, and Redis-backed metrics report request and inference activity.
Features
- Celebrity recognition using a fine-tuned ResNet50 model
- Asynchronous prediction jobs backed by Celery and Redis
- Single-image and batch prediction endpoints
- Pollable job status and result endpoints
- Shared Redis-backed inference metrics
- Configurable thread-based inference concurrency through Docker Compose
Architecture
Client
|
| POST /predict or /predict-batch
v
FastAPI API ---- enqueue task ----> Redis broker/result backend
^ |
| GET /job/{task_id} | task delivery/result storage
| GET /metrics v
+------------------------------- Celery worker
|
v
ResNet50 inferenceThe API returns a Celery task_id after submission. The client polls the job endpoint until Celery reports SUCCESS or FAILURE. The worker uses a thread pool because PyTorch inference runs correctly with the loaded model in that configuration, while a prefork worker caused tasks to remain pending in this environment.
Endpoints
Example completed result:
{
"task_id": "task-uuid",
"status": "SUCCESS",
"result": {
"class_id": 0,
"label": "pins_Adriana Lima",
"latency": 0.099
}
}Model
Training and validation data are expected under trianing/train/ and trianing/val/. Running trianing/train.py writes the model and label artifacts used by the API into weights/.
Run With Docker
Start the API, Redis broker/backend, and Celery worker:
docker compose up -d --buildOpen the API documentation at:
http://127.0.0.1:8000/docsCheck the running services:
docker compose ps
docker compose logs --tail=30 workerThe worker configuration is defined in docker-compose.yml:
command: >
celery -A app.workers.inference_worker worker
--pool=threads
--concurrency=8
--loglevel=infoTo test another concurrency level, change --concurrency, recreate the worker, and run the concurrent benchmark:
docker compose up -d --force-recreate worker
python -u tests/test_concurrent.pyKeep --pool=threads for the current model-loading approach. A prefork pool was tested and caused queued tasks to hang in this environment.
Hugging Face Space
This repository is also configured as a Docker Space. Hugging Face builds the Dockerfile and exposes the FastAPI application on port 7860; start-space.sh launches an internal Redis instance and Celery thread worker before starting Uvicorn.
The Space endpoint is:
https://Yousefxp8-ml-inference-api.hf.space/docsLocal Docker Compose remains separate from the Space entrypoint: Compose runs API, Redis, and worker as individual services, while the Space runs them inside its single container.
Training
Install Python dependencies and prepare data inside trianing/train/, with one directory per celebrity. Then run:
python trianing/split_dataset.py
python trianing/train.pyThe scripts create/use trianing/val/ and write updated serving artifacts to weights/.
Benchmarks
The concurrent benchmark submitted 20 images through /predict and polled /job/{task_id} until completion. It was measured with one Celery worker container using the thread pool and different --concurrency values.
The largest gains occur from 1 to 4 worker threads. Increasing from 4 to 8 reduced total wall time by 0.12s, showing diminishing returns for this 20-request workload.
Project Structure
.
|-- main.py
|-- docker-compose.yml
|-- Dockerfile
|-- requirements.txt
|-- app/
| |-- core/
| | |-- celery_app.py
| | `-- state.py
| |-- models/
| | `-- celeb_model.py
| |-- routes/
| | |-- jobs.py
| | |-- metrics.py
| | `-- predict.py
| |-- services/
| | |-- inference_service.py
| | `-- metrics_service.py
| `-- workers/
| `-- inference_worker.py
|-- tests/
| |-- test_batch.py
| `-- test_concurrent.py
|-- trianing/
| |-- split_dataset.py
| `-- train.py
`-- weights/
|-- celeb.pth
`-- labels.json