Skip to content

Repository files navigation

Job Intelligence API

ML-powered API that classifies job postings into ML Engineer, Data Engineer, and Data Scientist roles and extracts structured skill tags from job descriptions.

Built as an end-to-end MLOps project: real data preprocessing, multi-model experiment tracking, containerized API serving, interactive demo UI, and CI/CD.

Demo

Screenshot 2026-03-10 at 2 35 47 PM

Architecture

                     ┌──────────────────┐
                     │  Kaggle Dataset  │
                     │  7.3K LinkedIn   │
                     │  Job Postings    │
                     └────────┬─────────┘
                              │
                              ▼
┌─────────────┐     ┌──────────────────┐     ┌─────────────────┐
│  Data       │────▶│  Training        │────▶│  MLflow         │
│  Preprocess │     │  Pipeline        │     │  Tracking +     │
│  (Label +   │     │  3 Models:       │     │  Model Registry │
│   Merge)    │     │  LR / RF / GB    │     │  :5001          │
└─────────────┘     └──────────────────┘     └─────────────────┘
                           │
                           ▼ (best model)
┌─────────────┐     ┌──────────────────┐     ┌─────────────────┐
│  Streamlit  │────▶│  FastAPI         │◀───▶│  Skill          │
│  Demo UI    │     │  Serving Layer   │     │  Extractor      │
│  :8501      │     │  /classify       │     │  (Taxonomy from │
└─────────────┘     │  /extract-skills │     │   real data)    │
                    │  /batch-classify │     └─────────────────┘
                    └──────────────────┘
                           │
                    ┌──────────────────┐
                    │  Docker +        │
                    │  docker-compose  │
                    │  CI/CD (GH Act.) │
                    └──────────────────┘

Model Performance

Trained on 7,343 real LinkedIn job postings from the Data Science Job Postings & Skills (2024) dataset. Three classifiers compared via MLflow experiment tracking:

Model F1 Macro Accuracy CV F1 (5-fold)
Logistic Regression 0.9495 0.9503 0.9587
Random Forest 0.9633 0.9632 0.9690
Gradient Boosting 0.9711 0.9721 0.9759

Best model (Gradient Boosting) per-class performance:

Role Precision Recall F1 Support
Data Engineer 0.96 0.99 0.97 700
Data Scientist 0.99 0.96 0.97 559
ML Engineer 0.99 0.95 0.97 210

Class imbalance handled via stratified splitting and class_weight="balanced" where supported.

MLflow Experiment Tracking

Screenshot 2026-03-09 at 11 30 33 PM

The training pipeline logs per model:

  • Parameters: classifier type, TF-IDF config, class balance, train/test sizes
  • Metrics: accuracy, F1 (macro/weighted), precision, recall, CV scores
  • Artifacts: trained model, classification report, confusion matrix
  • Model comparison: side-by-side view of all 3 classifiers

Access the MLflow UI at http://localhost:5001 when running via docker-compose.

Tech Stack

Component Technology
API Framework FastAPI + Pydantic v2
Demo UI Streamlit
ML Pipeline Scikit-learn (TF-IDF + Gradient Boosting)
Experiment Tracking MLflow (tracking + model registry)
Containerization Docker + docker-compose (4 services)
CI/CD GitHub Actions (lint, test, build)
Testing Pytest + pytest-asyncio
Linting Ruff
Data Source 7.3K LinkedIn job postings (Kaggle)

Quick Start

Local Development

# Install dependencies
pip install -r requirements.txt -r requirements-dev.txt

# Preprocess real data (requires data/raw/ CSVs from Kaggle)
make preprocess

# Train all models (Logistic Regression, Random Forest, Gradient Boosting)
make train

# Start API server
make serve
# -> http://localhost:8000/docs

Docker

# Preprocess + train locally first (generates artifacts)
make preprocess
make train

# Start full stack: API + MLflow + Streamlit UI
docker compose up -d

# -> Streamlit UI: http://localhost:8501
# -> FastAPI Docs: http://localhost:8000/docs
# -> MLflow UI:    http://localhost:5001

# Run training inside Docker (logs to MLflow server)
docker compose --profile train run trainer

API Usage

# Classify a job posting
curl -X POST http://localhost:8000/classify \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Senior Machine Learning Engineer",
    "description": "Build production ML pipelines using PyTorch and Kubernetes. Experience with MLflow and distributed training required. 5+ years."
  }'

# Response:
# {
#   "predicted_role": "ml_engineer",
#   "confidence": 0.97,
#   "probabilities": {"ml_engineer": 0.97, "data_engineer": 0.02, "data_scientist": 0.01},
#   "skills": [
#     {"skill": "PyTorch", "confidence": 0.8, "primary_roles": ["ml_engineer"]},
#     {"skill": "Machine Learning", "confidence": 0.8, "primary_roles": ["ml_engineer"]}
#   ]
# }

# Extract skills only
curl -X POST http://localhost:8000/extract-skills \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Data Engineer",
    "description": "Design ETL pipelines with Apache Spark, Airflow, and dbt. Experience with Snowflake and AWS required."
  }'

# Batch classification (up to 100 postings)
curl -X POST http://localhost:8000/batch-classify \
  -H "Content-Type: application/json" \
  -d '{"postings": [{"title": "ML Engineer", "description": "..."}, {"title": "DE", "description": "..."}]}'

# Health check
curl http://localhost:8000/health

Data Pipeline

Raw data flows through three stages:

  1. Raw ingestion (data/raw/): Kaggle CSVs containing 12K+ LinkedIn job postings with titles, metadata, and extracted skills.

  2. Preprocessing (src/data/preprocess.py): Assigns role labels via keyword matching on job titles (60% mapping rate), merges postings with skills, builds text features (title | skills), and generates a real-data skill taxonomy.

  3. Training (src/models/train.py): Trains 3 classifier types with TF-IDF vectorization, logs all experiments to MLflow, selects the best model by F1 macro, and saves it for API serving.

Project Structure

job-intelligence-api/
├── src/
│   ├── api/
│   │   ├── app.py              # FastAPI application (4 endpoints)
│   │   └── schemas.py          # Pydantic request/response models
│   ├── data/
│   │   ├── preprocess.py       # Real data preprocessing pipeline
│   │   └── generate.py         # Synthetic data generator (fallback)
│   ├── models/
│   │   ├── train.py            # Multi-model training + MLflow tracking
│   │   └── skill_extractor.py  # Taxonomy-based skill extraction
│   └── utils/
├── tests/
│   ├── test_api.py             # API endpoint tests
│   ├── test_data.py            # Data generation tests
│   └── test_skill_extractor.py # Skill extraction tests
├── configs/
│   └── train_config.yaml       # Training hyperparameters
├── data/
│   └── raw/                    # Kaggle CSVs (not tracked in git)
├── .github/workflows/
│   └── ci.yml                  # CI/CD pipeline
├── Dockerfile                  # API container
├── Dockerfile.streamlit        # UI container
├── streamlit_app.py            # Demo UI
├── docker-compose.yml          # 4 services: API, MLflow, UI, Trainer
├── Makefile
├── pyproject.toml
└── requirements.txt

Development

make preprocess    # Preprocess Kaggle data
make train         # Train all models
make serve         # Start API locally
make test          # Run test suite
make lint          # Check code style
make format        # Auto-format code
make docker-up     # Start Docker services
make docker-down   # Stop Docker services
make help          # Show all commands

Dataset

This project uses the Data Science Job Postings & Skills (2024) dataset by asaniczka. The raw CSVs are not included in this repository. To reproduce:

  1. Download the dataset from Kaggle
  2. Place job_postings.csv and job_skills.csv in data/raw/
  3. Run make preprocess to generate the training data

About

ML-powered API that classifies job postings into ML Engineer, Data Engineer, and Data Scientist roles and extracts structured skill tags. Built with FastAPI, MLflow, Docker, and CI/CD.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages