| --- |
| license: mit |
| language: |
| - en |
| base_model: |
| - JobSelect/JobAnalyze_6k |
| new_version: JobSelect/JobAnalyze_6k |
| pipeline_tag: text-classification |
| tags: |
| - NLP |
| - TF-IDF |
| - MLP |
| - MCP |
| - API |
| - CLI |
| - JobSelect |
| - JobAnalyze |
| --- |
| |
| # JobAnalyze 6k v1.0 Skill Classifier |
|
|
| A lightweight **multi-label** PyTorch model that predicts a fixed set of **skills/keywords** from a job description plus a provided **role** and **job type**. |
|
|
| This README documents the exact artifacts and behavior implemented in: |
|
|
| - `model/model.py` (training definition) |
| - `model/pred.py` (inference wrapper) |
| - `model/prep/data_prep.py` (feature creation) |
|
|
| Sample runs and evaluation numbers referenced from: |
|
|
| - `data/sample_data/test.txt` |
| - `data/sample_data/eval.txt` |
| - `data/sample_data/cli.txt` |
|
|
| --- |
|
|
| ## Model summary |
|
|
| ### Task type |
|
|
| - **Multi-label classification** (each skill is predicted independently) |
|
|
| ### Inputs |
|
|
| - `job_desc` (job description text) |
| - `role` (free text, appended) |
| - `job_type` / `type` (free text, appended) |
|
|
| These are concatenated during prediction as: |
|
|
| ```text |
| {job_desc} {role} {job_type} |
| ``` |
|
|
| ### Features |
|
|
| - TF-IDF features created by `model/prep/data_prep.py` using: |
| - `TfidfVectorizer(max_features=150, stop_words='english', ngram_range=(1, 2), min_df=2)` |
| - Artifacts: |
| - `model/prep/vectorizer.pkl` |
| - `model/prep/label_vocab.json` |
| - `model/prep/prepared_data.npz` |
|
|
| ### Labels |
|
|
| - A fixed vocabulary of skills/keywords stored in `model/prep/label_vocab.json` |
| - In training artifacts: |
| - `NUM_LABELS = len(VOCAB)` |
| - In sample data: **48 Keywords/labels** |
|
|
| ### Network architecture (the “6k / 6000 parameter” model) |
|
|
| `model/model.py` defines a small feed-forward network: |
|
|
| - Linear(input_dim → hidden_dim=32) |
| - ReLU |
| - Dropout(p=0.3) |
| - Linear(hidden_dim=32 → num_labels) |
|
|
| The file name and training printout refer to the total parameter count computed at runtime. |
|
|
| --- |
|
|
| ## Training (model/model.py) |
|
|
| **Do not run `model/model.py` directly for day-to-day use.** It is designed to be executed via `pipeline.py` (and/or the notebooks). |
|
|
| Training uses: |
|
|
| - Loss: `torch.nn.BCEWithLogitsLoss(pos_weight=pos_weight)` |
| - `pos_weight` is computed per-label from the training set (class imbalance handling) |
| - Clamped with `max=10.0` |
| - Optimizer: `Adam(lr=1e-3, weight_decay=1e-4)` |
| - Epochs: `300` |
|
|
| ### Outputs (saved artifacts) |
|
|
| At the end of training, the following are written to `model_out/`: |
|
|
| - `model_out/skill_classifier.pt` |
| - `model_out/training_history.json` |
|
|
| The test suite asserts these exist (see `test/test_model.py`). |
|
|
| --- |
|
|
| ## Inference (model/pred.py) |
|
|
| `model/pred.py` exposes a prediction helper: |
|
|
| ```python |
| JobAnalyze_6k(job_desc, role="", job_type="", top_k=50) -> List[(str, float)] |
| ``` |
|
|
| Key behavior: |
|
|
| - Loads artifacts from repo-relative paths: |
| - `model/prep/label_vocab.json` |
| - `model/prep/vectorizer.pkl` |
| - `model_out/skill_classifier.pt` |
| - Vectorizes the concatenated text with TF-IDF and produces logits through the trained network. |
| - Converts logits to probabilities with `sigmoid`. |
| - Ranks all labels by probability descending and returns the top results. |
|
|
| > Note: `top_k` is capped by the number of labels effectively returned from the ranked list (in the code, slicing is applied directly). |
| |
| --- |
| |
| ## Evaluation snapshot (from data/sample_data) |
|
|
| ### Micro/Macro F1 |
|
|
| From `data/sample_data/eval.txt`: |
|
|
| - **Micro-F1: 0.624** |
| - **Macro-F1: 0.420** |
|
|
| ### Baseline comparison |
|
|
| Also from `data/sample_data/eval.txt`, baseline always predicts a fixed set of frequent labels: |
|
|
| - Baseline Micro-F1: **0.538** |
| - Baseline Macro-F1: **0.152** |
|
|
| The evaluation script prints: |
|
|
| > “Model meaningfully beats the naive baseline.” |
|
|
| ### Per-label and “trap” analysis |
|
|
| The evaluation output includes per-label precision/recall/F1 and an additional heuristic: |
|
|
| - “trap?” flags labels where the model does not perform better than a trivial always-zero expectation (within a small tolerance). |
|
|
| From `data/sample_data/eval.txt`: |
|
|
| - Right: **15** |
| - Wrong: **33** |
| - Total labels: **48** |
| - Keyword Accuracy: **31.25%** |
|
|
| --- |
|
|
| ## Example: single inference test (data/sample_data/test.txt) |
| |
| `data/sample_data/test.txt` contains a job description plus: |
|
|
| - Role: **AI Engineer** |
| - Type: **Junior** |
|
|
| It lists **15 keys to be predicted**, including: |
|
|
| - Python, LLMs, LangGraph, MCP, GenAI, VectorDB, SQL, APIs, Docker, Agents, Github, CI/CD, Git, AWS/Azure, Prompt Engineering |
|
|
| Reported performance: |
|
|
| - Accuracy (recall): **93.34%** (14/15) |
|
|
| --- |
|
|
| ## Example: CLI output format (data/sample_data/cli.txt) |
| |
| `cli/jobauto.py` uses `JobAnalyze_6k` from `model.pred` and prints: |
|
|
| - The job description provided |
| - Role and Type |
| - A ranked “TOP Skills” list as `{label} {probability}` plus a text bar |
|
|
| From `data/sample_data/cli.txt`, the top-ranked skills include (with example probabilities): |
|
|
| - apis ~0.78 |
| - langgraph ~0.78 |
| - vectordb ~0.76 |
| - mcp ~0.75 |
| - langchain ~0.74 |
| - rag ~0.72 |
|
|
| --- |
|
|
| ## Data prep artifacts (model/prep/data_prep.py) |
| |
| `model/prep/data_prep.py` creates all required inputs for training and inference. |
|
|
| It: |
|
|
| 1. Loads cleaned job description dataset |
| 2. Normalizes and fixes known skill spelling issues (`SKILLS_FIX`) |
| 3. Applies synonym replacement using `model/prep/sym_map.py` |
| 4. Builds a multi-hot label vector for the vocabulary |
| 5. Vectorizes job text with TF-IDF |
| 6. Splits into train/test and saves: |
| - `prepared_data.npz` |
| - `label_vocab.json` |
| - `vectorizer.pkl` |
|
|
| --- |
|
|
| ## Reproducibility / how to use |
|
|
| ### Requirements (high level) |
|
|
| See `requirements.txt` and `pyproject.toml`. |
|
|
| ### Minimum artifacts required for prediction |
|
|
| For `model/pred.py` to work, these must exist: |
|
|
| - `model/prep/label_vocab.json` |
| - `model/prep/vectorizer.pkl` |
| - `model_out/skill_classifier.pt` |
|
|
| If any are missing, `model/pred.py` raises a `FileNotFoundError` with guidance. |
|
|
| ### Recommended workflow |
|
|
| - Run the full pipeline via `pipeline.py` (which coordinates data prep + training). |
| - Use `cli/jobauto.py` for interactive predictions. |
|
|
| --- |
|
|
| ## Notes on “JobAnalyze 6k” |
|
|
| Despite the “6k / 6000 parameter” naming, the true parameter count is computed dynamically in `model/model.py`. |
|
|
| The implementation is intentionally small: |
|
|
| - TF-IDF input features |
| - 1 hidden layer with 32 units |
| - Multi-label BCE loss with class imbalance reweighting |
|
|
| This design keeps inference fast and model size small while still providing meaningful gains over the naive baseline (see evaluation snapshot above). |
|
|
|
|