An MLOps-driven platform that extracts required skills directly from real job-posting text, predicts placement readiness on genuine placement outcomes, recommends the best-fit learning resource per candidate using per-resource causal uplift models, and closes the loop with three independent drift signals and cross-coupled automated retraining.
Data honesty note: every student/candidate feature, every skill-demand
count, and every placement label in this pipeline is real, publicly
sourced data (see Section 2). The one documented exception is the
per-resource outcome data used to train the uplift models
(data/simulate_resource_outcomes.py) — no public dataset records which
specific learning resource a specific candidate completed and whether it
helped, so that layer is a disclosed simulation on top of real candidate
features. See the docstring in that file for the full explanation, and
Section 3 (Novelty #5) for how real usage data is meant to replace it over
time via /log_outcome.
Students often complete courses and earn certifications without knowing whether their skills actually match what employers are looking for right now. Existing tools either give generic aptitude tests, or compare skills against a fixed, manually maintained list that goes stale the moment the market shifts, and none of them personalize which resource to recommend for a given gap or check whether a recommendation actually worked. Recruiters, meanwhile, have no easy way to see how placement-ready a candidate really is before an interview. This mismatch between what students learn and what the market demands leads to wasted learning effort and high rejection rates.
| File | Source | What it contains |
|---|---|---|
skill2vec_1K.csv |
duyet/skill2vec-dataset (MIT License), from Van-Duyet et al., Skill2vec, arXiv:1707.09751 | 1,000 real candidate/resume skill profiles extracted from real job descriptions |
ds_skills_cleaned.csv |
mikeasilva/data-scientist-skills | Real skill demand counts scraped from live Data Scientist job postings on Dice.com, with skill categories — used as the seed vocabulary for NLP extraction |
indeed-job-listings.csv |
stevendoll/kaggle-jobs | Real raw, unstructured Indeed.com job-posting text — what src/skill_extraction.py actually extracts skills from |
placement_dataset.csv |
iamgalenc/placement_dataset_MachineLearning | 100 real, genuinely labeled student placement outcomes (CGPA, IQ score, placed/not placed) |
Honesty note on the readiness classifier: the real placement dataset
(100 rows) has no skills column, and the real skill2vec dataset has no
placement outcome — they are independent real populations collected by
different people. We do not fabricate a row-by-row join between them. The
readiness classifier is trained on the placement dataset's own genuine
placed/not placed labels; a real, independently-computed
skill_match_ratio feature is blended in at both training and serving
time. This is documented in src/train_readiness_model.py.
Honesty note on the resource catalog: data/build_resource_catalog.py
maps real, named learning platforms (Coursera, NPTEL, Kaggle, etc.) to
skill categories — the platforms are real, but per-candidate outcome data
for them doesn't exist publicly (see the data-honesty note above).
- Self-updating skill taxonomy via real NLP extraction, not a static config file.
src/skill_extraction.pyextracts skill mentions directly from raw, unstructured Indeed job-posting text using a two-tier approach: exact/substring match first, then embedding similarity (sentence-transformers if available, automatic TF-IDF cosine-similarity fallback otherwise) for skills worded differently. Feeds the Data Collection stage from live text rather than a one-time pre-extracted list. - Skill-relevance drift, not just statistical drift.
monitoring/drift_detection.pychecks whether a skill's real demand share has shifted between two real job-posting snapshots, giving the Drift Detection stage a market-meaningful trigger. - Retraining triggered by independent evidence signals, not a calendar.
retrain/retrain_trigger.pyonly retrains when a signal actually fires — never on a fixed schedule. - Per-resource causal uplift models replace the static recommendation lookup.
src/train_uplift_models.pytrains one GradientBoostingRegressor per learning resource (8 resources), each predicting expected gap-score reduction for a specific candidate profile — tracked as a coordinated parent/nested-run family in MLflow, giving the Model Registry something substantive to version. - A genuine feedback loop closes the cycle.
/log_outcomelogs real usage data (did a candidate complete a recommended resource, did readiness improve).monitoring/drift_detection.py'scheck_recommendation_effectiveness_drift()compares real logged improvement rates against the uplift models' training-time baseline, and retraining responds to that real signal. - Two coupled model families, not one. The skill taxonomy and the uplift-model family are versioned independently, but a taxonomy rebuild (Novelty #1/#2) cross-triggers uplift-model retraining too, since a changed taxonomy changes which gaps the resources need to address — implemented in
retrain_trigger.pyand confirmed inretrain_history.csv.
From POST /analyze:
| Parameter | Description |
|---|---|
readiness_probability |
Model's predicted probability of placement |
readiness_band |
Not Ready / Moderately Ready / Placement Ready |
matched_skills |
Candidate's skills that overlap real market demand |
missing_skills_ranked |
Missing skills ranked by real posting-count demand |
demand_weighted_gap_score |
0–1 score: how much of real market demand is unmet |
matched_skill_count / missing_skill_count |
Raw counts |
resource_recommendation |
Object: recommended_resource_id, recommended_resource_name, expected_gap_reduction, all_resource_scores — the per-resource uplift model's pick for this candidate |
From GET /skills/demand: the full real skill-demand taxonomy — role, skill, skill_group, posting_count, demand_weight.
From POST /log_outcome: input student_id, resource_id, completed, readiness_improved → output {status, student_id}.
From GET /metrics: total_analyses, avg_readiness_probability, readiness_band_distribution, avg_gap_score.
skillsync-mlops/ ├── data/ │ ├── fetch_real_data.py # downloads skill2vec, Dice.com skills, placement outcomes │ ├── fetch_job_postings_text.py # downloads real raw Indeed job-posting text │ ├── prepare_data.py # builds candidate features (uses skill_extraction's taxonomy if present) │ ├── build_resource_catalog.py # real named learning-resource catalog │ ├── simulate_resource_outcomes.py # documented simulated outcome layer for uplift training │ └── raw/ # downloaded/raw real CSVs land here ├── src/ │ ├── data_validation.py │ ├── skill_extraction.py # NOVELTY #1 -- real NLP skill extraction │ ├── skill_gap.py # real-demand-weighted matching logic │ ├── train_readiness_model.py # trains on real placement outcomes │ └── train_uplift_models.py # NOVELTY #4 -- per-resource uplift model family ├── api/ │ └── main.py # FastAPI backend, serves frontend + uplift recommendations ├── frontend/ │ └── index.html # dark-themed dashboard (vanilla JS, no build step) ├── monitoring/ │ └── drift_detection.py # data drift + skill-demand drift + effectiveness drift (Novelty #2, #5) ├── retrain/ │ └── retrain_trigger.py # 3-signal, cross-coupled retraining (Novelty #3, #6) ├── models/ │ └── uplift/ # per-resource uplift model artifacts (generated) ├── .github/workflows/ci.yml ├── Dockerfile ├── docker-compose.yml └── requirements.txt
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
pip install requests scikit-learnpython data/fetch_real_data.py
python data/fetch_job_postings_text.pypython src/skill_extraction.pypython data/prepare_data.py
python data/build_resource_catalog.py
python data/simulate_resource_outcomes.pypython src/data_validation.pypython src/train_readiness_model.py
python src/train_uplift_models.pymlflow ui --port 5000
# http://127.0.0.1:5000uvicorn api.main:app --reload --port 8000Open http://127.0.0.1:8000/ for the dashboard, http://127.0.0.1:8000/docs for the API.
docker compose up --buildpython monitoring/drift_detection.pyTo test skill-demand drift specifically, save a newer postings snapshot as
data/raw/ds_skills_cleaned_new.csv first.
python retrain/retrain_trigger.pyRetrains whichever model family(ies) the drift report flags — including the Novelty #6 cross-trigger (taxonomy rebuild → uplift retrain).
.github/workflows/ci.yml runs validation + training + Docker build on
every push, and a scheduled daily drift-check/retrain job.
- Show
data/fetch_job_postings_text.py+src/skill_extraction.pyoutput — real skills extracted live from raw text, not a pre-built list. - Open the dashboard, enter a skill set missing "Machine Learning" and "SQL" — show the platform ranks these as top-priority gaps by real demand, and recommends a specific resource (not a generic list).
- Log a batch of
/analyze+/log_outcomecalls, rundrift_detection.py, show all three signals in the JSON report. - Run
retrain_trigger.py, showretrain_history.csv— including a case where skill-demand drift alone triggers uplift-model retraining (Novelty #6).
This demo directly shows all 6 novelty claims end-to-end, grounded in data you can point to and defend by name and source.