CoolFace
Apppublic

Yousefxp8/ml-inference-api

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
App README

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

text
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 inference

The 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

MethodEndpointDescription
POST/predictQueue one uploaded image and return a task_id
POST/predict-batchQueue multiple images and return their task IDs
GET/job/{task_id}Return Celery status and a completed prediction result
POST/jobs-statusReturn status/result data for multiple task IDs
GET/metricsReturn total requests, average inference latency, and queue size

Example completed result:

json
{
  "task_id": "task-uuid",
  "status": "SUCCESS",
  "result": {
    "class_id": 0,
    "label": "pins_Adriana Lima",
    "latency": 0.099
  }
}

Model

PropertyValue
ArchitectureResNet50
Training methodTransfer learning with a replaced classification head
Output classes105 celebrities
Runtime artifactsweights/celeb.pth, weights/labels.json
Inference modemodel.eval() with torch.no_grad()

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:

bash
docker compose up -d --build

Open the API documentation at:

text
http://127.0.0.1:8000/docs

Check the running services:

bash
docker compose ps
docker compose logs --tail=30 worker

The worker configuration is defined in docker-compose.yml:

yaml
command: >
  celery -A app.workers.inference_worker worker
  --pool=threads
  --concurrency=8
  --loglevel=info

To test another concurrency level, change --concurrency, recreate the worker, and run the concurrent benchmark:

bash
docker compose up -d --force-recreate worker
python -u tests/test_concurrent.py

Keep --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:

text
https://Yousefxp8-ml-inference-api.hf.space/docs

Local 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:

bash
python trianing/split_dataset.py
python trianing/train.py

The 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.

Worker ThreadsTotal Wall TimeAverage LatencyFastest UserSlowest User
16.38s3.24s0.20s6.33s
21.94s1.07s0.31s1.94s
41.07s0.68s0.32s1.03s
80.95s0.64s0.43s0.89s

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

text
.
|-- 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