Skip to content

pankaj2k9/MLOpsE2EBirdDiseaseClassificationDeepLearningProject

Repository files navigation

MLOps E2E Poultry Disease Classification

Production-grade end-to-end MLOps pipeline for chicken disease detection from fecal images. Transfer learning with VGG16 / ResNet50 / EfficientNetB0 / MobileNetV2, FastAPI, MLflow, DVC, Grad-CAM, Evidently AI, Docker, and AWS ECR + EC2.

CI/CD Python TensorFlow FastAPI MLflow Docker


Problem Statement

Coccidiosis is a parasitic intestinal disease in poultry caused by Eimeria protozoa, responsible for significant economic losses worldwide. Manual diagnosis from fecal images requires expert knowledge and cannot scale to large flocks. This system automates binary classification (Healthy vs Coccidiosis) using transfer learning with model-specific preprocessing and a full MLOps production pipeline.


Dataset

Chicken Fecal Images — RGB photographs of chicken fecal matter for binary disease classification.

Coccidiosis Healthy Total
Total 195 195 390
Train (70%) 136 136 272
Val (15%) 29 29 58
Test (15%) 30 30 60
  • Perfectly balanced classes (1:1 ratio) — no class weighting needed
  • Fixed 70/15/15 split, random.seed(42) — bitwise reproducible across machines
  • Source: private AWS S3 bucket (chicken-data-for-classification) — downloaded in Stage 1
  • Input size: 224×224×3 (RGB), resized at preprocessing

Model Performance (Test Set, n=60, seed=42)

Model Accuracy F1 Macro AUC-ROC Params Train Time
MobileNetV2 98.3% 0.9833 0.9933 3.4M 47.5s
ResNet50 98.3% 0.9833 0.9900 25.6M 153.0s
EfficientNetB0 96.7% 0.9667 0.9978 5.3M 58.8s
VGG16 91.7% 0.9161 0.9678 138.4M 347.7s

All models: frozen backbone, Adam lr=1e-4, EarlyStopping patience=7, 70/15/15 split, seed=42. Full results: artifacts/model_comparison/results.json


System Architecture

AWS S3 (data.zip)
      │
      ▼
[Stage 1] Data Ingestion      → artifacts/data_ingestion/Chicken-fecal-images/
      │
      ▼
[Stage 2] Data Pipeline       → train/ val/ test/ (70/15/15 seeded split, seed=42)
      │
      ▼
[Stage 3] Model Training      → artifacts/training/{MODEL}_best.h5
      │                          MLflow run logged per model
      ▼
[Stage 4] Evaluation          → artifacts/evaluation/{MODEL}/metrics.json
      │                          confusion_matrix.png · roc_curve.png
      ▼
[Stage 5] Explainability      → artifacts/explainability/gradcam/{MODEL}/{class}/
      │                          Grad-CAM heatmap overlays
      ▼
[Stage 6] Model Comparison    → artifacts/model_comparison/comparison_results.json
      │                          MLflow parent run with nested child runs
      ▼
FastAPI Inference Service     → GET /  (web UI)
      │                          POST /predict  (base64 → label + confidence)
      │                          GET  /health   (liveness probe)
      │                          GET  /metrics  (latest eval metrics)
      │                          GET  /model/version
      │                          GET  /docs  (Swagger UI)
      ▼
Docker Image → AWS ECR → AWS EC2 (port 8080)

Repository Structure

├── .github/workflows/cicd.yaml           # Lint → Security → Test → Build → Deploy
├── config/config.yaml                    # All artifact paths
├── params.yaml                           # All hyperparameters
├── dvc.yaml                              # 5-stage DVC pipeline
├── main.py                               # Full training pipeline entry point
├── static/index.html                     # Web UI (served by FastAPI at GET /)
├── src/cnnClassifier/
│   ├── components/
│   │   ├── data_ingestion.py             # S3 streaming download
│   │   ├── data_pipeline.py             # 70/15/15 seeded split
│   │   ├── prepare_base_model.py        # 4 backbones + 3 fine-tuning strategies
│   │   ├── model_trainer.py             # Train + MLflow logging
│   │   └── evaluation.py               # Full metrics + CM + ROC curve
│   ├── explainability/
│   │   └── grad_cam.py                  # Grad-CAM heatmaps
│   ├── experiments/
│   │   └── mlflow_tracker.py            # MLflow context manager
│   ├── monitoring/
│   │   └── drift_detector.py            # Evidently drift reports
│   ├── api/
│   │   ├── main.py                      # FastAPI app (startup model load)
│   │   ├── schemas.py                   # Pydantic v2 request/response models
│   │   ├── dependencies.py              # Singleton model state
│   │   └── routers/
│   │       ├── predict.py               # POST /predict
│   │       ├── health.py                # GET  /health
│   │       └── metrics.py              # GET  /metrics, /model/version
│   ├── pipeline/
│   │   ├── stage_01_data_ingestion.py
│   │   ├── stage_02_data_pipeline.py
│   │   ├── stage_03_model_trainer.py
│   │   ├── stage_04_evaluation.py
│   │   ├── stage_05_explainability.py
│   │   ├── stage_06_model_comparison.py
│   │   └── predict.py
│   ├── config/configuration.py
│   ├── entity/config_entity.py
│   └── utils/common.py
├── tests/
│   ├── test_data_pipeline.py            # 7 unit tests
│   ├── test_api.py                      # 10 API tests (mocked model)
│   └── test_model_factory.py           # 7 model build tests
├── research/                            # Exploratory notebooks
├── documents/                           # HLD, LLD, wireframe, paper
├── Dockerfile                           # Multi-stage build (builder + runtime)
├── docker-compose.yml                   # api + mlflow services
├── pyproject.toml                       # Ruff + Black + pytest config
├── requirements.txt
└── .env.example

Quick Start — Inference Only

Pre-trained model at model/model.h5. No retraining needed.

git clone https://github.com/pankaj2k9/MLOpsE2EBirdDiseaseClassificationDeepLearningProject.git
cd MLOpsE2EBirdDiseaseClassificationDeepLearningProject

# Create and activate conda env (Python 3.11 + TF 2.15)
conda create -n cnnclassify311 python=3.11
conda activate cnnclassify311

pip install --upgrade pip setuptools wheel
pip install -r requirements.txt     # installs cnnClassifier package in editable mode

python -m uvicorn cnnClassifier.api.main:app --host 0.0.0.0 --port 8080

Open http://localhost:8080 — upload a fecal image, click Classify Image.

API docs at http://localhost:8080/docs (Swagger UI).


Full Local Setup — Training Pipeline

1. Clone & install

git clone https://github.com/pankaj2k9/MLOpsE2EBirdDiseaseClassificationDeepLearningProject.git
cd MLOpsE2EBirdDiseaseClassificationDeepLearningProject

conda create -n cnnclassify311 python=3.11
conda activate cnnclassify311

pip install --upgrade pip setuptools wheel
pip install -r requirements.txt     # includes -e . to install cnnClassifier package

2. Configure environment

cp .env.example .env

Edit .env:

AWS_ACCESS_KEY_ID=your_access_key_here
AWS_SECRET_ACCESS_KEY=your_secret_key_here
AWS_REGION=us-east-1
MLFLOW_TRACKING_URI=mlruns

AWS credentials only needed for Stage 1 (S3 download). Inference does not require them.

3. Run full pipeline (all 6 stages)

python main.py VGG16        # trains VGG16 through all stages

Or run each stage individually — all commands from project root:

# Stage 1: Download data from S3
python -m cnnClassifier.pipeline.stage_01_data_ingestion

# Stage 2: Create 70/15/15 train/val/test split (seed=42)
python -m cnnClassifier.pipeline.stage_02_data_pipeline

# Stage 3: Train a model (default VGG16)
python -m cnnClassifier.pipeline.stage_03_model_trainer        # VGG16
MODEL_NAME=ResNet50 python -m cnnClassifier.pipeline.stage_03_model_trainer

# Stage 4: Evaluate on held-out test set
python -m cnnClassifier.pipeline.stage_04_evaluation

# Stage 5: Generate Grad-CAM explanations
python -m cnnClassifier.pipeline.stage_05_explainability

# Stage 6: Train + evaluate all 4 models, log to MLflow
python -m cnnClassifier.pipeline.stage_06_model_comparison

4. View MLflow experiments

MLFLOW_ALLOW_FILE_STORE=true mlflow ui --backend-store-uri mlruns
# open http://localhost:5000

5. Run with DVC

dvc repro                   # re-runs only invalidated stages
dvc metrics show            # compare metrics across git commits

Local DVC remote (no AWS required)

# 1. Place data manually (skip S3)
mkdir -p artifacts/data_ingestion
# copy data.zip → artifacts/data_ingestion/data.zip
# extract   → artifacts/data_ingestion/Chicken-fecal-images/

# 2. Commit outputs to DVC cache
dvc commit

# 3. One-time setup — create local remote and push
mkdir ../dvc-storage
dvc remote add -d localremote ../dvc-storage
dvc push

# On a fresh clone (no AWS credentials needed)
dvc pull
dvc repro --downstream data_pipeline   # skips S3 data_ingestion stage

The raw dataset (Chicken-fecal-images, 390 fecal images) is downloaded from private S3 during stage_01_data_ingestion. For fully offline testing, place data.zip and extracted Chicken-fecal-images/ under artifacts/data_ingestion/, run dvc commit, then push to local remote.

Switch back to S3 remote: dvc remote default myS3remote

6. View Evidently Drift Report

Run drift detection first (requires reference + current batch):

python -c "
from cnnClassifier.monitoring.drift_detector import DriftDetector
from cnnClassifier.config.configuration import ConfigurationManager
config = ConfigurationManager().get_monitoring_config()
detector = DriftDetector(config)
# detector.build_reference(ref_generator)   # run once after deployment
# detector.detect(current_generator)        # run periodically
"

Reports saved to artifacts/monitoring/reports/drift_report_{timestamp}.html

Open locally:

# macOS
open artifacts/monitoring/reports/$(ls -t artifacts/monitoring/reports/*.html | head -1)

# or any browser
python -m http.server 8888 --directory artifacts/monitoring/reports
# → http://localhost:8888

7. Start the API server

python -m uvicorn cnnClassifier.api.main:app --host 0.0.0.0 --port 8080
# → http://localhost:8080       (web UI)
# → http://localhost:8080/docs  (Swagger UI)

API Endpoints

Method Path Description
GET / Web UI (upload image, view prediction)
POST /predict {"image": "<base64>"}{label, confidence, class_probabilities}
GET /health {status, model_loaded, model_name, model_version}
GET /metrics Latest evaluation metrics JSON
GET /model/version Model name and version
GET /docs Swagger UI
GET /redoc ReDoc UI

Example prediction request

# Encode image to base64
B64=$(base64 -i path/to/image.jpg)

curl -X POST http://localhost:8080/predict \
  -H "Content-Type: application/json" \
  -d "{\"image\": \"$B64\"}"

Response:

{
  "prediction": {
    "label": "Coccidiosis",
    "confidence": 0.923,
    "class_probabilities": {"Coccidiosis": 0.923, "Healthy": 0.077}
  },
  "model_name": "VGG16",
  "model_version": "2.0.0"
}

Hyperparameters

Key settings in params.yaml:

SEED: 42
TRAIN_RATIO: 0.70
VAL_RATIO: 0.15
TEST_RATIO: 0.15

IMAGE_SIZE: [224, 224, 3]
BATCH_SIZE: 16
EPOCHS: 30
LEARNING_RATE: 0.0001
OPTIMIZER: adam

FINETUNE_STRATEGY: frozen     # frozen | partial | full
UNFREEZE_LAYERS: 20           # used when strategy=partial

EARLY_STOPPING_PATIENCE: 7
REDUCE_LR_PATIENCE: 3
REDUCE_LR_FACTOR: 0.3

MODELS:
  - VGG16
  - ResNet50
  - EfficientNetB0
  - MobileNetV2

Run Tests

pytest tests/ -v --cov=src/cnnClassifier --cov-report=term-missing

Lint and format:

ruff check src/ tests/
black --check src/ tests/

Security scan:

bandit -r src/ -ll

Docker — Local

Build

docker build --target runtime -t bird-disease-classifier .

Run

docker run -p 8080:8080 bird-disease-classifier

Open http://localhost:8080

Run with credentials (for S3 ingestion inside container)

docker run -p 8080:8080 \
  -e AWS_ACCESS_KEY_ID=your_key \
  -e AWS_SECRET_ACCESS_KEY=your_secret \
  -e AWS_REGION=us-east-1 \
  bird-disease-classifier

Docker Compose (API + MLflow)

docker-compose up
# API   → http://localhost:8080
# MLflow UI → http://localhost:5000

Production — AWS Deployment

Required AWS services

  • ECR — Docker image registry
  • EC2 — compute (Ubuntu, t2.medium or higher)

IAM permissions

AmazonEC2ContainerRegistryFullAccess
AmazonEC2FullAccess

GitHub Actions secrets

Secret Value
AWS_ACCESS_KEY_ID IAM access key
AWS_SECRET_ACCESS_KEY IAM secret key
AWS_REGION e.g. us-east-1
AWS_ECR_LOGIN_URI e.g. 123456789.dkr.ecr.us-east-1.amazonaws.com
ECR_REPOSITORY_NAME e.g. bird-disease-classifier

EC2 one-time setup

sudo apt-get update -y && sudo apt-get upgrade -y

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker ubuntu
newgrp docker

Register EC2 as GitHub Actions self-hosted runner: Settings → Actions → Runners → New self-hosted runner.

Manual ECR push (without CI/CD)

aws ecr get-login-password --region us-east-1 \
  | docker login --username AWS --password-stdin <ECR_LOGIN_URI>

docker build --target runtime -t bird-disease-classifier .
docker tag bird-disease-classifier:latest <ECR_LOGIN_URI>/<REPO_NAME>:latest
docker push <ECR_LOGIN_URI>/<REPO_NAME>:latest

CI/CD pipeline (auto-triggered on push to main)

push to main
    │
    ├─ [lint]     ruff check + black --check
    ├─ [security] bandit -r src/ -ll
    ├─ [test]     pytest + coverage upload to Codecov
    │
    ├─ [build]    docker build --target runtime → push to ECR
    │
    └─ [deploy]   EC2 self-hosted runner
                   └─ docker pull
                   └─ docker stop old container (|| true)
                   └─ docker run --restart unless-stopped -p 80:8080
                   └─ health-check wait (12 × 10s polls on /health)

Tech Stack

Layer Technology
Language Python 3.8
ML Framework TensorFlow 2.13 / Keras
Models VGG16, ResNet50, EfficientNetB0, MobileNetV2
Explainability Grad-CAM (GradientTape)
Experiment Tracking MLflow 2.17
Data Versioning DVC 3.x + S3 remote
API FastAPI 0.104+ + Pydantic v2
Frontend Vanilla HTML/JS (served at GET /)
Monitoring Evidently AI (drift detection)
Containerization Docker multi-stage (non-root runtime)
Registry AWS ECR
Compute AWS EC2
CI/CD GitHub Actions (lint + security + test + build + deploy)
Config YAML + python-box
Data Source AWS S3

Troubleshooting

ModuleNotFoundError: No module named 'cnnClassifier' → Run pip install -e . from project root. The -e . in requirements.txt does this automatically.

FileNotFoundError: config/config.yaml → All scripts must run from project root, not from inside src/.

model/model.h5 not found on API startup → Either use pre-trained model in model/ or after training: cp artifacts/training/VGG16_final.h5 model/model.h5

AWS S3 download fails with 301/403 → Data ingestion auto-detects bucket region on redirect. Check IAM has s3:GetObject on the bucket.

Slow training on M1/M2 Mac → TF 2.13 uses legacy Keras optimizer automatically on Apple Silicon. Normal — training still works correctly.


Original Contributions

The following are contributions built as part of this project:

  1. Backbone-specific preprocessing — model-specific preprocess_input per backbone (VGG16, ResNet50, EfficientNetB0, MobileNetV2) matching each model's ImageNet training distribution
  2. Deterministic 70/15/15 split — physical train/val/test directories, seeded, idempotent
  3. Multi-model comparison pipeline — Stage 6 trains all 4 backbones under identical conditions
  4. Grad-CAM explainability — full GradientTape implementation for all 4 backbones
  5. MLflow experiment tracking — context-manager wrapper, nested runs, best-run retrieval
  6. Evidently drift monitoring — embedding-based DataDrift + TargetDrift reports
  7. FastAPI migration — 4 endpoints, Pydantic validation, singleton model state, startup lifecycle
  8. Web UIstatic/index.html with drag-drop upload, probability bars, served at GET /
  9. Multi-stage Docker — builder + non-root runtime user, HEALTHCHECK
  10. Real CI/CD — Ruff, Bandit, pytest-cov gates; EC2 health-check wait on deploy
  11. System design docs — HLD, LLD, wireframe, IEEE research paper

Future Work

  • Optuna hyperparameter search
  • ViT-B/16 baseline comparison
  • Model calibration (temperature scaling)
  • Multi-class extension (Salmonella, Newcastle disease)
  • Kubernetes (EKS) canary deployment
  • Prometheus + Grafana operational dashboard
  • Albumentations advanced augmentation

Author

Pankaj Kumar Pramanik B.Sc. (Honours) Data Science and AI — IIT Guwahati

GitHub: https://github.com/pankaj2k9

About

End-to-end MLOps project for Bird Disease Classification using Deep Learning, TensorFlow, Flask API, YAML-based configuration, and cloud storage integration.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors