A production-ready boilerplate for containerizing and serving a Scikit-learn sentiment classifier through a FastAPI web service, packaged into a portable Docker container.
The model is a genuine text-classification pipeline — TF-IDF vectorization feeding a Logistic Regression classifier — trained to classify review-style text as positive or negative. This setup ensures the model can be consistently deployed and run on any environment that supports Docker, eliminating "it works on my machine" problems.
graph TD
A["Client<br/>curl / browser / HTTP client"] -->|"POST /predict<br/>{'text': '...'}"| B["FastAPI Service<br/>Uvicorn, containerized"]
B --> C["Scikit-learn Pipeline<br/>TF-IDF Vectorizer → Logistic Regression"]
C --> D["JSON Response<br/>{text, sentiment, confidence}"]
- Model: Scikit-learn —
TfidfVectorizer+LogisticRegression, packaged as a singlePipelinewithjoblib - Serving API: FastAPI (Python web framework, auto-generates OpenAPI docs)
- Data Validation: Pydantic — request/response schemas and type-safe input validation
- Web Server: Uvicorn (ASGI server)
- Containerization: Docker
- Testing: pytest + FastAPI's
TestClient - CI/CD: GitHub Actions — runs the test suite and validates the Docker build on every push/PR
A transformer-based classifier (e.g. a fine-tuned BERT) would likely score higher on a large, real-world dataset, but it comes with a much heavier container image, slower cold starts, and GPU-friendly infrastructure that a lightweight boilerplate shouldn't assume. TF-IDF + Logistic Regression is fast, fully interpretable, trains in seconds on a laptop, and keeps the Docker image small — the right tradeoff for a service meant to demonstrate the deployment pattern, not chase state-of-the-art accuracy.
Trained on 800 synthetic labeled reviews (400 positive / 400 negative), generated from 40 sentiment-bearing sentence templates applied across 20 product/service categories (data/reviews.csv, produced by train.py).
-
Held-out test accuracy: 100% — expected given the templated nature of the training data; this confirms the pipeline trains and evaluates correctly, not a claim of production-grade generalization.
-
More meaningful check — genuinely novel sentences never seen during training:
Input Prediction Confidence "The customer service team went above and beyond, truly a pleasure to deal with." positive0.6492 "This was a complete letdown, nothing worked as promised and support never replied." negative0.6411 Both were classified correctly despite using none of the exact template wording.
├── .github/
│ └── workflows/
│ └── ci.yml # Runs pytest + validates the Docker build on every push/PR
├── app/
│ ├── main.py # FastAPI service (loads model, defines endpoints)
│ ├── requirements.txt # Runtime dependencies for the container
│ └── __init__.py
├── model/
│ └── sentiment_pipeline.pkl # Trained TF-IDF + LogisticRegression pipeline
├── data/
│ └── reviews.csv # Synthetic training dataset (generated by train.py)
├── tests/
│ └── test_api.py # pytest tests for the API and model behavior
├── train.py # Builds the dataset, trains, evaluates, saves the pipeline
├── requirements-dev.txt # Training/testing deps (not shipped in the Docker image)
├── .dockerignore
├── .gitignore
├── Dockerfile
└── README.md
- Docker Desktop (or Docker Engine) installed and running.
- Terminal open in the project's root directory.
The Docker build expects model/sentiment_pipeline.pkl to already exist — train it first:
pip install -r app/requirements.txt -r requirements-dev.txt
python train.pydocker build -t sentiment-api:v1 .Maps the container's internal port 80 to your host's port 8888:
docker run -d -p 8888:80 --name sentiment-api-container sentiment-api:v1curl -X POST http://localhost:8888/predict \
-H "Content-Type: application/json" \
-d '{"text": "The customer service team went above and beyond, truly a pleasure to deal with."}'Expected output:
{
"text": "The customer service team went above and beyond, truly a pleasure to deal with.",
"sentiment": "positive",
"confidence": 0.6492
}FastAPI auto-generates interactive Swagger documentation. Once the container is running, open:
http://localhost:8888/docs
pip install -r app/requirements.txt -r requirements-dev.txt
pytestEvery push and pull request to main runs this same test suite automatically via GitHub Actions (.github/workflows/ci.yml), plus a second job that validates the Docker image still builds cleanly.
docker stop sentiment-api-container
docker rm sentiment-api-container
docker rmi sentiment-api:v1 # optional — if no longer neededMIT — see LICENSE.
