Skip to content

Repository files navigation

CI Python FastAPI Pydantic Docker scikit--learn Uvicorn pytest GitHub Actions

Sentiment Analysis API — Containerized MLOps Service 📦

🎯 Project Overview

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.

🏗️ Architecture

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}"]
Loading

🛠️ Tech Stack

  • Model: Scikit-learn — TfidfVectorizer + LogisticRegression, packaged as a single Pipeline with joblib
  • 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

Why TF-IDF + Logistic Regression, not a transformer model?

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.

📊 Model

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." positive 0.6492
    "This was a complete letdown, nothing worked as promised and support never replied." negative 0.6411

    Both were classified correctly despite using none of the exact template wording.

📂 Project Structure

├── .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

🚀 Building and Running the Service

Prerequisites

  1. Docker Desktop (or Docker Engine) installed and running.
  2. Terminal open in the project's root directory.

Step 1: Train the Model

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

Step 2: Build the Docker Image

docker build -t sentiment-api:v1 .

Step 3: Run the Container

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

Step 4: Test the API

curl -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
}

Interactive API Docs

FastAPI auto-generates interactive Swagger documentation. Once the container is running, open:

http://localhost:8888/docs

Swagger UI

🧪 Running Tests

pip install -r app/requirements.txt -r requirements-dev.txt
pytest

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

🧹 Cleanup

docker stop sentiment-api-container
docker rm sentiment-api-container
docker rmi sentiment-api:v1   # optional — if no longer needed

License

MIT — see LICENSE.

About

A production-ready boilerplate to easily containerize and serve a Scikit-learn sentiment model with a modern, high-performance API using FastAPI and Docker.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages