Sync card and scripts from the monorepo
Browse files- README.md +31 -21
- data_generator.py +2 -17
- requirements.txt +7 -8
- train.py +2 -2
README.md
CHANGED
|
@@ -11,16 +11,10 @@ metrics:
|
|
| 11 |
- accuracy
|
| 12 |
- f1
|
| 13 |
pipeline_tag: text-classification
|
|
|
|
|
|
|
| 14 |
language:
|
| 15 |
- en
|
| 16 |
-
library_name: transformers
|
| 17 |
-
widget:
|
| 18 |
-
- text: "B.S. in Computer Science, Stanford University, 2021. GPA: 3.9. Relevant coursework: Machine Learning, Distributed Systems."
|
| 19 |
-
example_title: Education
|
| 20 |
-
- text: "Senior Software Engineer at Stripe, 2019-Present. Led migration of payment infrastructure to Kubernetes, reducing deploy times by 60%."
|
| 21 |
-
example_title: Experience
|
| 22 |
-
- text: "Python, TypeScript, React, PostgreSQL, Docker, AWS, Terraform"
|
| 23 |
-
example_title: Skills
|
| 24 |
---
|
| 25 |
|
| 26 |
# Resume Section Classifier
|
|
@@ -76,23 +70,39 @@ Default configuration produces **1,920 examples** (80 base examples x 3 variants
|
|
| 76 |
|
| 77 |
## Metrics
|
| 78 |
|
| 79 |
-
|
| 80 |
|
| 81 |
-
|
| 82 |
-
|
|
|
|
|
|
|
|
|
|
| 83 |
| Accuracy | 1.000 |
|
| 84 |
| F1 (macro) | 1.000 |
|
| 85 |
| F1 (weighted) | 1.000 |
|
| 86 |
-
|
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
|
| 97 |
## Usage
|
| 98 |
|
|
|
|
| 11 |
- accuracy
|
| 12 |
- f1
|
| 13 |
pipeline_tag: text-classification
|
| 14 |
+
datasets:
|
| 15 |
+
- custom-synthetic
|
| 16 |
language:
|
| 17 |
- en
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
---
|
| 19 |
|
| 20 |
# Resume Section Classifier
|
|
|
|
| 70 |
|
| 71 |
## Metrics
|
| 72 |
|
| 73 |
+
> **These figures are INDICATIVE only.** They are measured on a held-out split of the model's own **synthetic, self-generated** data (192 examples, stratified) — not a real-world benchmark. Because the train and test sets are produced by the same template-based generator, they share vocabulary and structure, so these scores **overstate** how the model will perform on real resumes.
|
| 74 |
|
| 75 |
+
Measured on the published checkpoint (4 epochs, seed 42, 192-example
|
| 76 |
+
stratified synthetic test split):
|
| 77 |
+
|
| 78 |
+
| Metric | Score (synthetic test set) |
|
| 79 |
+
|--------|----------------------------|
|
| 80 |
| Accuracy | 1.000 |
|
| 81 |
| F1 (macro) | 1.000 |
|
| 82 |
| F1 (weighted) | 1.000 |
|
| 83 |
+
| Precision (weighted) | 1.000 |
|
| 84 |
+
| Recall (weighted) | 1.000 |
|
| 85 |
+
| Eval loss | 0.195 |
|
| 86 |
+
|
| 87 |
+
A perfect score here is evidence of the leak described above, not of quality:
|
| 88 |
+
the test split is generated by the same templates as the training split.
|
| 89 |
+
|
| 90 |
+
### Out-of-distribution spot-check
|
| 91 |
+
|
| 92 |
+
To give the synthetic score some context, the checkpoint was run against eight
|
| 93 |
+
hand-written snippets in the style of real resumes, one per section type, none
|
| 94 |
+
drawn from the generator:
|
| 95 |
+
|
| 96 |
+
| Result | Count |
|
| 97 |
+
|--------|-------|
|
| 98 |
+
| Correct | 6 / 8 |
|
| 99 |
+
|
| 100 |
+
Both misses were low-confidence and collapsed toward the broader categories —
|
| 101 |
+
an `experience` bullet read as `summary` (0.39), and a `projects` bullet read
|
| 102 |
+
as `skills` (0.48). Treat **~0.75 on real text** as the more realistic
|
| 103 |
+
expectation, and treat predictions under ~0.5 confidence as unreliable.
|
| 104 |
+
|
| 105 |
+
> Note: exact metrics depend on the random seed and training run.
|
| 106 |
|
| 107 |
## Usage
|
| 108 |
|
data_generator.py
CHANGED
|
@@ -10,9 +10,7 @@ Author: Lorenzo Scaturchio (gr8monk3ys)
|
|
| 10 |
|
| 11 |
import csv
|
| 12 |
import random
|
| 13 |
-
import itertools
|
| 14 |
from pathlib import Path
|
| 15 |
-
from typing import Optional
|
| 16 |
|
| 17 |
# ---------------------------------------------------------------------------
|
| 18 |
# Entity pools – used to fill templates with realistic variation
|
|
@@ -854,33 +852,20 @@ def save_to_csv(dataset: list[dict], path: str) -> None:
|
|
| 854 |
|
| 855 |
def load_as_hf_dataset(dataset: list[dict]):
|
| 856 |
"""Convert to HuggingFace Dataset with train/val/test splits."""
|
| 857 |
-
from datasets import
|
| 858 |
|
| 859 |
ds = Dataset.from_list(dataset)
|
| 860 |
|
| 861 |
-
# Stratified splitting requires a ClassLabel column
|
| 862 |
-
class_label = ClassLabel(names=sorted(set(d["label"] for d in dataset)))
|
| 863 |
-
ds = ds.cast_column("label", class_label)
|
| 864 |
-
|
| 865 |
# 80/10/10 split
|
| 866 |
train_test = ds.train_test_split(test_size=0.2, seed=42, stratify_by_column="label")
|
| 867 |
val_test = train_test["test"].train_test_split(test_size=0.5, seed=42, stratify_by_column="label")
|
| 868 |
|
| 869 |
-
|
| 870 |
"train": train_test["train"],
|
| 871 |
"validation": val_test["train"],
|
| 872 |
"test": val_test["test"],
|
| 873 |
})
|
| 874 |
|
| 875 |
-
# Downstream tokenization maps label names via label2id, so restore strings.
|
| 876 |
-
# Explicit output features are required: mapping into a ClassLabel column
|
| 877 |
-
# would silently re-encode the names back to ints.
|
| 878 |
-
return splits.map(
|
| 879 |
-
lambda batch: {"label": class_label.int2str(batch["label"])},
|
| 880 |
-
batched=True,
|
| 881 |
-
features=Features({"text": Value("string"), "label": Value("string")}),
|
| 882 |
-
)
|
| 883 |
-
|
| 884 |
|
| 885 |
def get_label_mapping(dataset: list[dict]) -> tuple[dict, dict]:
|
| 886 |
"""Create label <-> id mappings."""
|
|
|
|
| 10 |
|
| 11 |
import csv
|
| 12 |
import random
|
|
|
|
| 13 |
from pathlib import Path
|
|
|
|
| 14 |
|
| 15 |
# ---------------------------------------------------------------------------
|
| 16 |
# Entity pools – used to fill templates with realistic variation
|
|
|
|
| 852 |
|
| 853 |
def load_as_hf_dataset(dataset: list[dict]):
|
| 854 |
"""Convert to HuggingFace Dataset with train/val/test splits."""
|
| 855 |
+
from datasets import Dataset, DatasetDict
|
| 856 |
|
| 857 |
ds = Dataset.from_list(dataset)
|
| 858 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 859 |
# 80/10/10 split
|
| 860 |
train_test = ds.train_test_split(test_size=0.2, seed=42, stratify_by_column="label")
|
| 861 |
val_test = train_test["test"].train_test_split(test_size=0.5, seed=42, stratify_by_column="label")
|
| 862 |
|
| 863 |
+
return DatasetDict({
|
| 864 |
"train": train_test["train"],
|
| 865 |
"validation": val_test["train"],
|
| 866 |
"test": val_test["test"],
|
| 867 |
})
|
| 868 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 869 |
|
| 870 |
def get_label_mapping(dataset: list[dict]) -> tuple[dict, dict]:
|
| 871 |
"""Create label <-> id mappings."""
|
requirements.txt
CHANGED
|
@@ -1,8 +1,7 @@
|
|
| 1 |
-
transformers>=4.36.0
|
| 2 |
-
datasets>=2.16.0
|
| 3 |
-
torch>=2.1.0
|
| 4 |
-
scikit-learn>=1.3.0
|
| 5 |
-
accelerate>=0.25.0
|
| 6 |
-
evaluate>=0.4.0
|
| 7 |
-
|
| 8 |
-
huggingface_hub>=0.20.0
|
|
|
|
| 1 |
+
transformers>=4.36.0,<5.0.0
|
| 2 |
+
datasets>=2.16.0,<4.0.0
|
| 3 |
+
torch>=2.1.0,<3.0.0
|
| 4 |
+
scikit-learn>=1.3.0,<2.0.0
|
| 5 |
+
accelerate>=0.25.0,<2.0.0
|
| 6 |
+
evaluate>=0.4.0,<1.0.0
|
| 7 |
+
huggingface_hub>=0.20.0,<1.0.0
|
|
|
train.py
CHANGED
|
@@ -16,7 +16,6 @@ Usage:
|
|
| 16 |
|
| 17 |
import json
|
| 18 |
import logging
|
| 19 |
-
import os
|
| 20 |
import sys
|
| 21 |
from pathlib import Path
|
| 22 |
|
|
@@ -219,6 +218,7 @@ def train(
|
|
| 219 |
|
| 220 |
training_args = TrainingArguments(
|
| 221 |
output_dir=output_dir,
|
|
|
|
| 222 |
# Training hyperparameters
|
| 223 |
num_train_epochs=epochs,
|
| 224 |
per_device_train_batch_size=batch_size,
|
|
@@ -261,7 +261,7 @@ def train(
|
|
| 261 |
args=training_args,
|
| 262 |
train_dataset=tokenized_dataset["train"],
|
| 263 |
eval_dataset=tokenized_dataset["validation"],
|
| 264 |
-
|
| 265 |
data_collator=data_collator,
|
| 266 |
compute_metrics=build_compute_metrics(id2label),
|
| 267 |
callbacks=callbacks,
|
|
|
|
| 16 |
|
| 17 |
import json
|
| 18 |
import logging
|
|
|
|
| 19 |
import sys
|
| 20 |
from pathlib import Path
|
| 21 |
|
|
|
|
| 218 |
|
| 219 |
training_args = TrainingArguments(
|
| 220 |
output_dir=output_dir,
|
| 221 |
+
overwrite_output_dir=True,
|
| 222 |
# Training hyperparameters
|
| 223 |
num_train_epochs=epochs,
|
| 224 |
per_device_train_batch_size=batch_size,
|
|
|
|
| 261 |
args=training_args,
|
| 262 |
train_dataset=tokenized_dataset["train"],
|
| 263 |
eval_dataset=tokenized_dataset["validation"],
|
| 264 |
+
tokenizer=tokenizer,
|
| 265 |
data_collator=data_collator,
|
| 266 |
compute_metrics=build_compute_metrics(id2label),
|
| 267 |
callbacks=callbacks,
|