ai-talent-finder-backend / scripts /demo_soutenance.py
ilyass yani
Deploiement backend dans HF Spaces
9df97a2
Raw
History Blame
3.63 kB
"""Demo script for soutenance.
This offline demo loads the saved baseline model and prints a small ranked
comparison between two sample candidates against one sample job profile.
Run from repo root:
/Users/elhadjibassirousy/Desktop/AI-Talent-Finder/.venv/bin/python backend/scripts/demo_soutenance.py
"""
import sys
from pathlib import Path
import joblib
import numpy as np
script_dir = Path(__file__).resolve().parent
repo_root = script_dir.parent.parent
if str(repo_root / 'backend') not in sys.path:
sys.path.insert(0, str(repo_root / 'backend'))
from app.services.feature_engineering import PairFeatureMeta, build_pair_features
from app.services.normalization import normalize_text
def build_features(candidate_text: str, job_text: str, meta: dict):
if isinstance(meta, PairFeatureMeta):
feature_meta = meta
else:
tf = meta.get("tfidf") or meta.get("tf")
svd = meta.get("svd")
feature_meta = PairFeatureMeta(tfidf=tf, svd=svd)
return build_pair_features(candidate_text, job_text, feature_meta)
def score_candidate(model, meta, candidate_text: str, job_text: str):
features = build_features(candidate_text, job_text, meta)
try:
probability = model.predict_proba(features)[:, 1][0]
except Exception:
decision = model.decision_function(features)[0]
probability = 1 / (1 + np.exp(-decision))
candidate_tokens = set(normalize_text(candidate_text).split())
job_tokens = set(normalize_text(job_text).split())
coverage = len(candidate_tokens & job_tokens) / max(1, len(job_tokens))
blended_score = (0.25 * float(probability) + 0.75 * float(coverage)) * 100
return float(blended_score), float(probability * 100), float(coverage * 100)
def main():
model_candidates = [
repo_root / "models" / "final_match_model.joblib",
repo_root / "models" / "baseline_model.joblib",
]
model_path = next((path for path in model_candidates if path.exists()), None)
if model_path is None:
raise SystemExit(f"Model not found: {model_candidates[0]}")
bundle = joblib.load(model_path)
model = bundle["model"]
meta = bundle["meta"]
job_text = (
"Senior Python Backend Developer\n"
"Required: Python, FastAPI, SQL, Docker, AWS, microservices."
)
candidates = [
{
"name": "Jean Dupont",
"text": "Jean Dupont; Senior Python Backend Developer; Python; FastAPI; SQL; Docker; AWS; microservices; backend architecture; 6 years backend experience; team leadership",
},
{
"name": "Sarah Martin",
"text": "Sarah Martin; React; UI/UX; Figma; 4 years frontend experience; communication; design systems; product design",
},
]
scored = []
for candidate in candidates:
blended_score, model_score, coverage_score = score_candidate(model, meta, candidate["text"], job_text)
scored.append((candidate["name"], blended_score, model_score, coverage_score, candidate["text"]))
scored.sort(key=lambda item: item[1], reverse=True)
print("=== Démo soutenance - Baseline matching ===")
print("Poste:")
print(job_text)
print()
for index, (name, score, model_score, coverage_score, text) in enumerate(scored, start=1):
print(f"#{index} {name}: {score:.1f}% (modèle {model_score:.1f}%, couverture {coverage_score:.1f}%)")
print(f" CV: {text}")
print()
print("Conclusion: la démo combine le score du modèle et la couverture des compétences pour présenter un ranking lisible.")
if __name__ == "__main__":
main()