gr8monk3ys commited on
Commit
43bd56b
·
verified ·
1 Parent(s): 34535ff

Sync card and scripts from the monorepo

Browse files
Files changed (4) hide show
  1. README.md +3 -1
  2. data_generator.py +792 -231
  3. inference.py +58 -21
  4. train.py +105 -43
README.md CHANGED
@@ -116,7 +116,9 @@ classifier = pipeline(
116
  model="gr8monk3ys/resume-section-classifier",
117
  )
118
 
119
- result = classifier("Bachelor of Science in Computer Science, Stanford University, 2023. GPA: 3.9/4.0")
 
 
120
  print(result)
121
  # [{'label': 'education', 'score': 0.98}]
122
  ```
 
116
  model="gr8monk3ys/resume-section-classifier",
117
  )
118
 
119
+ result = classifier(
120
+ "Bachelor of Science in Computer Science, Stanford University, 2023. GPA: 3.9/4.0"
121
+ )
122
  print(result)
123
  # [{'label': 'education', 'score': 0.98}]
124
  ```
data_generator.py CHANGED
@@ -17,51 +17,172 @@ from pathlib import Path
17
  # ---------------------------------------------------------------------------
18
 
19
  FIRST_NAMES = [
20
- "James", "Mary", "Robert", "Patricia", "John", "Jennifer", "Michael",
21
- "Linda", "David", "Elizabeth", "William", "Barbara", "Richard", "Susan",
22
- "Joseph", "Jessica", "Thomas", "Sarah", "Charles", "Karen", "Daniel",
23
- "Lisa", "Matthew", "Nancy", "Anthony", "Betty", "Mark", "Sandra",
24
- "Aisha", "Wei", "Carlos", "Priya", "Olga", "Hiroshi", "Fatima", "Liam",
25
- "Sofia", "Andrei", "Mei", "Alejandro", "Yuki", "Omar", "Elena", "Raj",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  ]
27
 
28
  LAST_NAMES = [
29
- "Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller",
30
- "Davis", "Rodriguez", "Martinez", "Hernandez", "Lopez", "Gonzalez",
31
- "Wilson", "Anderson", "Thomas", "Taylor", "Moore", "Jackson", "Martin",
32
- "Lee", "Perez", "Thompson", "White", "Harris", "Sanchez", "Clark",
33
- "Patel", "Chen", "Kim", "Nakamura", "Ivanov", "Silva", "Okafor",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  ]
35
 
36
  COMPANIES = [
37
- "Google", "Microsoft", "Amazon", "Apple", "Meta", "Netflix", "Stripe",
38
- "Airbnb", "Uber", "Salesforce", "Adobe", "IBM", "Oracle", "Intel",
39
- "Tesla", "SpaceX", "Palantir", "Snowflake", "Databricks", "Confluent",
40
- "JPMorgan Chase", "Goldman Sachs", "Morgan Stanley", "Deloitte",
41
- "McKinsey & Company", "Boston Consulting Group", "Accenture",
42
- "Lockheed Martin", "Boeing", "Raytheon", "General Electric",
43
- "Procter & Gamble", "Johnson & Johnson", "Pfizer", "Moderna",
44
- "Shopify", "Square", "Twilio", "Cloudflare", "HashiCorp",
45
- "DataRobot", "Hugging Face", "OpenAI", "Anthropic", "Cohere",
46
- "Startup XYZ", "TechCorp Inc.", "InnovateTech", "DataDriven LLC",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  ]
48
 
49
  UNIVERSITIES = [
50
- "Massachusetts Institute of Technology", "Stanford University",
51
- "Harvard University", "University of California, Berkeley",
52
- "Carnegie Mellon University", "Georgia Institute of Technology",
53
- "University of Michigan", "University of Illinois Urbana-Champaign",
54
- "California Institute of Technology", "Princeton University",
55
- "Columbia University", "University of Washington",
56
- "University of Texas at Austin", "Cornell University",
57
- "University of Pennsylvania", "University of Southern California",
58
- "New York University", "University of Wisconsin-Madison",
59
- "Duke University", "Northwestern University",
60
- "University of California, Los Angeles", "Rice University",
61
- "University of Maryland", "Purdue University",
62
- "Ohio State University", "Arizona State University",
63
- "University of Virginia", "University of Florida",
64
- "Boston University", "Northeastern University",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  ]
66
 
67
  DEGREES = [
@@ -77,78 +198,216 @@ DEGREES = [
77
  ]
78
 
79
  MAJORS = [
80
- "Computer Science", "Software Engineering", "Data Science",
81
- "Electrical Engineering", "Mechanical Engineering",
82
- "Information Technology", "Mathematics", "Statistics",
83
- "Business Administration", "Economics", "Finance",
84
- "Biomedical Engineering", "Chemical Engineering",
85
- "Civil Engineering", "Physics", "Biology",
86
- "Artificial Intelligence", "Machine Learning",
87
- "Human-Computer Interaction", "Cybersecurity",
88
- "Information Systems", "Operations Research",
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  ]
90
 
91
  MINORS = [
92
- "Mathematics", "Statistics", "Psychology", "Business",
93
- "Economics", "Philosophy", "Linguistics", "Physics",
94
- "Data Science", "Communication", "Sociology", "History",
 
 
 
 
 
 
 
 
 
95
  ]
96
 
97
  GPA_VALUES = [
98
- "3.5", "3.6", "3.7", "3.8", "3.9", "4.0",
99
- "3.52", "3.65", "3.78", "3.85", "3.92", "3.45",
 
 
 
 
 
 
 
 
 
 
100
  ]
101
 
102
  GRAD_YEARS = list(range(2015, 2027))
103
 
104
  JOB_TITLES = [
105
- "Software Engineer", "Senior Software Engineer", "Staff Engineer",
106
- "Principal Engineer", "Engineering Manager", "Tech Lead",
107
- "Data Scientist", "Senior Data Scientist", "Machine Learning Engineer",
108
- "ML Research Scientist", "Data Engineer", "Data Analyst",
109
- "Product Manager", "Senior Product Manager", "Program Manager",
110
- "DevOps Engineer", "Site Reliability Engineer", "Cloud Architect",
111
- "Full Stack Developer", "Frontend Engineer", "Backend Engineer",
112
- "Mobile Developer", "iOS Engineer", "Android Developer",
113
- "QA Engineer", "Security Engineer", "Solutions Architect",
114
- "Research Scientist", "AI Engineer", "NLP Engineer",
115
- "Quantitative Analyst", "Financial Analyst", "Business Analyst",
116
- "UX Designer", "UI Engineer", "Technical Writer",
117
- "Intern", "Software Engineering Intern", "Data Science Intern",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  ]
119
 
120
  PROGRAMMING_LANGUAGES = [
121
- "Python", "Java", "JavaScript", "TypeScript", "C++", "C", "C#",
122
- "Go", "Rust", "Kotlin", "Swift", "Ruby", "PHP", "Scala",
123
- "R", "MATLAB", "Julia", "Haskell", "Elixir", "Dart",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  ]
125
 
126
  FRAMEWORKS = [
127
- "React", "Angular", "Vue.js", "Next.js", "Django", "Flask",
128
- "FastAPI", "Spring Boot", "Express.js", "Node.js", "Rails",
129
- "TensorFlow", "PyTorch", "Keras", "scikit-learn", "Pandas",
130
- "NumPy", "Spark", "Hadoop", "Kubernetes", "Docker",
131
- "AWS", "GCP", "Azure", "Terraform", "Ansible",
132
- ".NET", "Laravel", "Svelte", "Remix", "Astro",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  ]
134
 
135
  TOOLS = [
136
- "Git", "GitHub", "GitLab", "Jira", "Confluence", "Slack",
137
- "VS Code", "IntelliJ", "PyCharm", "Vim", "Emacs",
138
- "PostgreSQL", "MySQL", "MongoDB", "Redis", "Elasticsearch",
139
- "Kafka", "RabbitMQ", "Airflow", "dbt", "Snowflake",
140
- "Tableau", "Power BI", "Grafana", "Prometheus", "Datadog",
141
- "Jenkins", "CircleCI", "GitHub Actions", "ArgoCD",
142
- "Figma", "Sketch", "Adobe XD", "Postman", "Swagger",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  ]
144
 
145
  SOFT_SKILLS = [
146
- "Leadership", "Communication", "Team Collaboration",
147
- "Problem Solving", "Critical Thinking", "Time Management",
148
- "Project Management", "Agile Methodologies", "Scrum",
149
- "Cross-functional Collaboration", "Mentoring",
150
- "Strategic Planning", "Stakeholder Management",
151
- "Technical Writing", "Public Speaking", "Negotiation",
 
 
 
 
 
 
 
 
 
 
152
  ]
153
 
154
  CERTIFICATIONS_LIST = [
@@ -178,47 +437,120 @@ CERTIFICATIONS_LIST = [
178
  ]
179
 
180
  AWARDS_LIST = [
181
- "Dean's List", "Summa Cum Laude", "Magna Cum Laude", "Cum Laude",
182
- "Phi Beta Kappa", "Tau Beta Pi", "National Merit Scholar",
183
- "Employee of the Quarter", "Spot Bonus Award", "President's Club",
184
- "Best Paper Award", "Innovation Award", "Hackathon Winner",
185
- "Outstanding Graduate Student Award", "Research Fellowship",
186
- "Teaching Assistant Excellence Award", "Community Service Award",
187
- "IEEE Best Student Paper", "ACM ICPC Regional Finalist",
188
- "Google Code Jam Qualifier", "Facebook Hacker Cup Participant",
189
- "Patent Holder", "Top Performer Award", "Rising Star Award",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  ]
191
 
192
  CITIES = [
193
- "San Francisco, CA", "New York, NY", "Seattle, WA", "Austin, TX",
194
- "Boston, MA", "Chicago, IL", "Los Angeles, CA", "Denver, CO",
195
- "Portland, OR", "Atlanta, GA", "Washington, DC", "San Jose, CA",
196
- "Raleigh, NC", "Pittsburgh, PA", "Minneapolis, MN", "Dallas, TX",
197
- "Miami, FL", "Phoenix, AZ", "San Diego, CA", "Philadelphia, PA",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  ]
199
 
200
  MONTHS = [
201
- "January", "February", "March", "April", "May", "June",
202
- "July", "August", "September", "October", "November", "December",
 
 
 
 
 
 
 
 
 
 
203
  ]
204
 
205
  MONTHS_SHORT = [
206
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
207
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
 
 
 
 
 
 
 
 
 
 
208
  ]
209
 
210
  PROJECT_ADJECTIVES = [
211
- "Real-time", "Scalable", "Distributed", "Cloud-native",
212
- "AI-powered", "Automated", "Interactive", "Cross-platform",
213
- "Open-source", "End-to-end", "High-performance", "Serverless",
214
- "Event-driven", "Microservice-based", "Full-stack",
 
 
 
 
 
 
 
 
 
 
 
215
  ]
216
 
217
  PROJECT_NOUNS = [
218
- "Dashboard", "Platform", "Pipeline", "Application", "System",
219
- "API", "Framework", "Tool", "Service", "Engine",
220
- "Chatbot", "Recommendation System", "Search Engine",
221
- "Analytics Platform", "Monitoring System", "Marketplace",
 
 
 
 
 
 
 
 
 
 
 
 
222
  ]
223
 
224
  IMPACT_METRICS = [
@@ -239,21 +571,42 @@ IMPACT_METRICS = [
239
  ]
240
 
241
  PHONE_AREA_CODES = [
242
- "415", "650", "408", "510", "212", "646", "718", "206",
243
- "512", "617", "312", "213", "303", "503", "404", "202",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  ]
245
 
246
  LINKEDIN_PREFIXES = [
247
- "linkedin.com/in/", "www.linkedin.com/in/",
 
248
  ]
249
 
250
  GITHUB_PREFIXES = [
251
- "github.com/", "www.github.com/",
 
252
  ]
253
 
254
  DOMAINS = [
255
- "gmail.com", "outlook.com", "yahoo.com", "protonmail.com",
256
- "icloud.com", "hotmail.com", "mail.com",
 
 
 
 
 
257
  ]
258
 
259
  # ---------------------------------------------------------------------------
@@ -261,12 +614,53 @@ DOMAINS = [
261
  # ---------------------------------------------------------------------------
262
 
263
  SYNONYMS = {
264
- "developed": ["built", "created", "engineered", "designed", "implemented", "constructed", "authored"],
265
- "managed": ["led", "oversaw", "directed", "supervised", "coordinated", "administered"],
266
- "improved": ["enhanced", "optimized", "upgraded", "refined", "boosted", "strengthened"],
267
- "implemented": ["deployed", "executed", "delivered", "rolled out", "launched", "shipped"],
268
- "analyzed": ["examined", "evaluated", "assessed", "investigated", "studied", "reviewed"],
269
- "collaborated": ["partnered", "worked closely with", "teamed up with", "cooperated with"],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
  "responsible for": ["in charge of", "accountable for", "tasked with", "owned"],
271
  "utilized": ["leveraged", "employed", "used", "applied", "harnessed"],
272
  "achieved": ["accomplished", "attained", "reached", "secured", "delivered"],
@@ -278,6 +672,7 @@ SYNONYMS = {
278
  # Helper utilities
279
  # ---------------------------------------------------------------------------
280
 
 
281
  def _pick(pool, k=1):
282
  """Return k unique random items from a pool."""
283
  k = min(k, len(pool))
@@ -342,7 +737,7 @@ def _synonym_replace(text: str) -> str:
342
  if w[0].isupper():
343
  replacement = replacement.capitalize()
344
  # Preserve trailing punctuation
345
- trailing = w[len(lower):]
346
  result.append(replacement + trailing)
347
  else:
348
  result.append(w)
@@ -365,6 +760,7 @@ def _reorder_bullets(bullets: list) -> list:
365
  # Section generators – each returns a string of realistic text
366
  # ---------------------------------------------------------------------------
367
 
 
368
  def generate_education() -> str:
369
  """Generate a realistic education section."""
370
  templates = []
@@ -399,34 +795,56 @@ def generate_education() -> str:
399
 
400
  # Optional coursework
401
  if random.random() < 0.5:
402
- courses = _pick(MAJORS + ["Algorithms", "Data Structures",
403
- "Operating Systems", "Database Systems",
404
- "Computer Networks", "Linear Algebra",
405
- "Probability and Statistics",
406
- "Deep Learning", "Natural Language Processing",
407
- "Computer Vision", "Distributed Systems"], k=random.randint(3, 6))
408
- prefix = random.choice(["Relevant Coursework:", "Key Courses:", "Coursework:"])
 
 
 
 
 
 
 
 
 
 
 
 
 
409
  lines.append(f"{prefix} {', '.join(courses)}")
410
 
411
  # Optional honors
412
  if random.random() < 0.3:
413
- honor = random.choice(["Summa Cum Laude", "Magna Cum Laude",
414
- "Cum Laude", "Dean's List (all semesters)",
415
- "Honors Program", "University Scholar"])
 
 
 
 
 
 
 
416
  lines.append(honor)
417
 
418
  # Optional thesis
419
  if "Ph.D." in deg_short or ("M.S." in deg_short and random.random() < 0.4):
420
- topic = random.choice([
421
- "Transformer-based approaches to document classification",
422
- "Scalable distributed systems for real-time data processing",
423
- "Graph neural networks for molecular property prediction",
424
- "Federated learning in healthcare applications",
425
- "Efficient attention mechanisms for long-sequence modeling",
426
- "Reinforcement learning for autonomous navigation",
427
- ])
 
 
428
  label = "Dissertation" if "Ph.D." in deg_short else "Thesis"
429
- lines.append(f"{label}: \"{topic}\"")
430
 
431
  return "\n".join(lines)
432
 
@@ -438,7 +856,9 @@ def generate_education() -> str:
438
  deg_full, deg_short = _pick_one(DEGREES)
439
  major = _pick_one(MAJORS)
440
  year = _pick_one(GRAD_YEARS)
441
- gpa_line = f" | GPA: {_pick_one(GPA_VALUES)}" if random.random() < 0.5 else ""
 
 
442
  entries.append(f"{deg_short} in {major}, {uni}, {year}{gpa_line}")
443
  return "\n".join(entries)
444
 
@@ -450,18 +870,21 @@ def generate_education() -> str:
450
  year = _pick_one(GRAD_YEARS)
451
  lines = [f"{uni}", f"{deg_full} in {major} | {_pick_one(MONTHS)} {year}"]
452
 
453
- activities = random.sample([
454
- "Teaching Assistant for Introduction to Computer Science",
455
- "President, Computer Science Student Association",
456
- "Member, ACM Student Chapter",
457
- "Undergraduate Research Assistant, ML Lab",
458
- "Peer Tutor, Mathematics Department",
459
- "Captain, University Programming Competition Team",
460
- "Volunteer, Engineering Outreach Program",
461
- "Member, Honors College",
462
- "Study Abroad Program, Technical University of Munich",
463
- "Resident Advisor, Engineering Living-Learning Community",
464
- ], k=random.randint(1, 3))
 
 
 
465
 
466
  b = _bullet()
467
  for a in activities:
@@ -492,11 +915,11 @@ def generate_experience() -> str:
492
 
493
  # Generate bullet points
494
  bullet_templates = [
495
- f"Developed and maintained {random.choice(['microservices', 'APIs', 'web applications', 'data pipelines', 'ML models', 'backend systems', 'frontend components'])} using {', '.join(_pick(PROGRAMMING_LANGUAGES, k=random.randint(1,3)))} and {', '.join(_pick(FRAMEWORKS, k=random.randint(1,2)))}",
496
- f"Collaborated with cross-functional teams of {random.randint(3,15)} engineers to deliver {random.choice(['product features', 'platform improvements', 'system migrations', 'infrastructure upgrades'])} on schedule",
497
  f"Designed and implemented {random.choice(['CI/CD pipelines', 'testing frameworks', 'monitoring solutions', 'data models', 'caching strategies', 'authentication systems'])} that {_impact()}",
498
  f"Led migration of {random.choice(['legacy monolith', 'on-premise infrastructure', 'batch processing system', 'manual workflows'])} to {random.choice(['cloud-native architecture', 'microservices', 'real-time streaming', 'automated pipelines'])}",
499
- f"Mentored {random.randint(2,8)} junior engineers through code reviews, pair programming, and technical design sessions",
500
  f"Optimized {random.choice(['database queries', 'API response times', 'model inference', 'data processing pipelines', 'search indexing'])} resulting in {_impact()}",
501
  f"Wrote comprehensive technical documentation and {random.choice(['RFCs', 'design docs', 'runbooks', 'architecture decision records'])} for {random.choice(['system design', 'API contracts', 'deployment procedures', 'incident response'])}",
502
  f"Built {random.choice(['real-time', 'batch', 'streaming', 'event-driven'])} {random.choice(['data pipeline', 'ETL process', 'analytics system', 'feature store'])} processing {random.choice(['1M+', '10M+', '100M+', '1B+'])} records {random.choice(['daily', 'per hour', 'in real-time'])}",
@@ -506,7 +929,9 @@ def generate_experience() -> str:
506
  ]
507
 
508
  n_bullets = random.randint(2, 5)
509
- selected = random.sample(bullet_templates, min(n_bullets, len(bullet_templates)))
 
 
510
  selected = _reorder_bullets(selected)
511
  b = _bullet()
512
  for bullet in selected:
@@ -535,7 +960,9 @@ def generate_skills() -> str:
535
 
536
  if random.random() < 0.9:
537
  fws = _pick(FRAMEWORKS, k=random.randint(3, 7))
538
- label = random.choice(["Frameworks", "Frameworks & Libraries", "Technologies"])
 
 
539
  categories.append((label, fws))
540
 
541
  if random.random() < 0.8:
@@ -556,9 +983,11 @@ def generate_skills() -> str:
556
  return "\n".join(lines)
557
 
558
  def _t_flat():
559
- all_skills = (_pick(PROGRAMMING_LANGUAGES, k=random.randint(3, 6)) +
560
- _pick(FRAMEWORKS, k=random.randint(3, 6)) +
561
- _pick(TOOLS, k=random.randint(2, 4)))
 
 
562
  random.shuffle(all_skills)
563
  joiner = random.choice([", ", " | ", " · ", " • "])
564
  return joiner.join(all_skills)
@@ -568,7 +997,9 @@ def generate_skills() -> str:
568
  levels = ["Expert", "Advanced", "Proficient", "Intermediate", "Familiar"]
569
  used = set()
570
  for level in random.sample(levels, k=random.randint(2, 4)):
571
- pool = [s for s in PROGRAMMING_LANGUAGES + FRAMEWORKS + TOOLS if s not in used]
 
 
572
  items = _pick(pool, k=random.randint(2, 5))
573
  used.update(items)
574
  lines.append(f"{level}: {', '.join(items)}")
@@ -621,28 +1052,45 @@ def generate_projects() -> str:
621
  def generate_summary() -> str:
622
  """Generate a realistic professional summary / objective section."""
623
  years = random.randint(2, 15)
624
- specialties = _pick(MAJORS + [
625
- "full-stack development", "distributed systems", "machine learning",
626
- "data engineering", "cloud architecture", "mobile development",
627
- "DevOps", "backend development", "frontend development",
628
- "natural language processing", "computer vision",
629
- ], k=random.randint(1, 3))
 
 
 
 
 
 
 
 
 
 
 
630
 
631
  templates = [
632
  # Template 1: Traditional summary
633
- lambda: f"Results-driven {_pick_one(JOB_TITLES).lower()} with {years}+ years of experience in {' and '.join(specialties)}. Proven track record of {random.choice(['delivering high-impact solutions', 'building scalable systems', 'driving technical excellence', 'leading cross-functional teams'])} at companies like {_pick_one(COMPANIES)} and {_pick_one(COMPANIES)}. Passionate about {random.choice(['clean code', 'system design', 'open source', 'mentorship', 'continuous learning', 'innovation'])} and {random.choice(['building products that scale', 'solving complex problems', 'leveraging data-driven insights', 'improving developer experience'])}.",
634
-
 
635
  # Template 2: Technical focus
636
- lambda: f"Experienced {_pick_one(JOB_TITLES).lower()} specializing in {', '.join(specialties)}. Skilled in {', '.join(_pick(PROGRAMMING_LANGUAGES, k=3))} with deep expertise in {', '.join(_pick(FRAMEWORKS, k=2))}. {random.choice(['Strong background in', 'Demonstrated ability in', 'Track record of'])} {random.choice(['building distributed systems at scale', 'developing ML models for production', 'architecting cloud-native applications', 'leading agile engineering teams'])}. Seeking to {random.choice(['contribute to cutting-edge products', 'drive technical innovation', 'solve challenging problems', 'build impactful technology'])} at a {random.choice(['fast-growing startup', 'leading technology company', 'mission-driven organization'])}.",
637
-
 
638
  # Template 3: Achievement-oriented
639
- lambda: f"{_pick_one(JOB_TITLES)} with {years} years of experience building {random.choice(['enterprise-scale', 'consumer-facing', 'B2B', 'data-intensive'])} applications. Key achievements include {_impact()}, {_impact()}, and {_impact()}. Proficient in {', '.join(_pick(PROGRAMMING_LANGUAGES, k=3))} and {', '.join(_pick(FRAMEWORKS, k=2))}.",
640
-
 
641
  # Template 4: Brief objective
642
- lambda: f"Motivated {random.choice(['professional', 'engineer', 'developer', 'technologist'])} seeking a {_pick_one(JOB_TITLES).lower()} role where I can apply my expertise in {' and '.join(specialties)} to {random.choice(['build innovative products', 'solve real-world problems', 'drive business impact', 'push the boundaries of technology'])}.",
643
-
 
644
  # Template 5: Narrative style
645
- lambda: f"I am a {_pick_one(JOB_TITLES).lower()} who thrives at the intersection of {_pick_one(specialties)} and {_pick_one(specialties)}. Over the past {years} years, I have {random.choice(['shipped products used by millions', 'built ML systems processing petabytes of data', 'led engineering teams through rapid growth', 'contributed to open-source projects with thousands of stars'])}. I bring a {random.choice(['data-driven', 'user-centric', 'systems-thinking', 'first-principles'])} approach to every problem I tackle.",
 
 
646
  ]
647
 
648
  return random.choice(templates)()
@@ -660,9 +1108,11 @@ def generate_certifications() -> str:
660
  f"{cert} ({year})",
661
  f"{cert} — Issued {_pick_one(MONTHS)} {year}",
662
  f"{cert}, {year}",
663
- f"{cert}\n Issued: {_pick_one(MONTHS_SHORT)} {year}" + (
 
664
  f" | Expires: {_pick_one(MONTHS_SHORT)} {year + random.randint(2, 3)}"
665
- if random.random() < 0.3 else ""
 
666
  ),
667
  ]
668
  lines.append(random.choice(styles))
@@ -679,15 +1129,17 @@ def generate_contact() -> str:
679
  last = _pick_one(LAST_NAMES)
680
  city = _pick_one(CITIES)
681
  area_code = _pick_one(PHONE_AREA_CODES)
682
- email_user = random.choice([
683
- f"{first.lower()}.{last.lower()}",
684
- f"{first.lower()}{last.lower()}",
685
- f"{first[0].lower()}{last.lower()}",
686
- f"{first.lower()}_{last.lower()}",
687
- f"{first.lower()}{random.randint(1, 99)}",
688
- ])
 
 
689
  email = f"{email_user}@{_pick_one(DOMAINS)}"
690
- phone = f"({area_code}) {random.randint(100,999)}-{random.randint(1000,9999)}"
691
  linkedin_user = f"{first.lower()}-{last.lower()}-{random.randint(100, 999)}"
692
  github_user = f"{first.lower()}{last.lower()}"
693
 
@@ -704,7 +1156,11 @@ def generate_contact() -> str:
704
  if random.random() < 0.4:
705
  parts.append(f"{_pick_one(GITHUB_PREFIXES)}{github_user}")
706
  if random.random() < 0.2:
707
- parts.append(f"{github_user}.dev" if random.random() < 0.5 else f"{first.lower()}{last.lower()}.com")
 
 
 
 
708
 
709
  sep = random.choice(["\n", " | ", " · ", "\n"])
710
  return sep.join(parts)
@@ -718,12 +1174,23 @@ def generate_awards() -> str:
718
 
719
  for award in awards:
720
  year = random.randint(2015, 2025)
721
- org = random.choice([
722
- _pick_one(UNIVERSITIES),
723
- _pick_one(COMPANIES),
724
- random.choice(["ACM", "IEEE", "Google", "Facebook", "Microsoft",
725
- "National Science Foundation", "Department of Education"]),
726
- ])
 
 
 
 
 
 
 
 
 
 
 
727
  styles = [
728
  f"{award}, {org} ({year})",
729
  f"{award} — {org}, {year}",
@@ -743,14 +1210,77 @@ def generate_awards() -> str:
743
  # ---------------------------------------------------------------------------
744
 
745
  SECTION_HEADERS = {
746
- "education": ["EDUCATION", "Education", "Academic Background", "ACADEMIC BACKGROUND", "Education & Training"],
747
- "experience": ["EXPERIENCE", "Experience", "WORK EXPERIENCE", "Work Experience", "PROFESSIONAL EXPERIENCE", "Professional Experience", "Employment History"],
748
- "skills": ["SKILLS", "Skills", "TECHNICAL SKILLS", "Technical Skills", "Core Competencies", "CORE COMPETENCIES", "Technologies"],
749
- "projects": ["PROJECTS", "Projects", "PERSONAL PROJECTS", "Personal Projects", "SIDE PROJECTS", "Selected Projects", "Portfolio"],
750
- "summary": ["SUMMARY", "Summary", "PROFESSIONAL SUMMARY", "Professional Summary", "OBJECTIVE", "Objective", "PROFILE", "Profile", "About Me", "ABOUT"],
751
- "certifications": ["CERTIFICATIONS", "Certifications", "CERTIFICATES", "Certificates", "Licenses & Certifications", "PROFESSIONAL CERTIFICATIONS"],
752
- "contact": ["CONTACT", "Contact", "CONTACT INFORMATION", "Contact Information", "Personal Information"],
753
- "awards": ["AWARDS", "Awards", "HONORS & AWARDS", "Honors & Awards", "ACHIEVEMENTS", "Achievements", "Awards & Honors", "RECOGNITION"],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
754
  }
755
 
756
  GENERATORS = {
@@ -769,7 +1299,10 @@ GENERATORS = {
769
  # Dataset generation
770
  # ---------------------------------------------------------------------------
771
 
772
- def generate_example(label: str, include_header: bool = False, augment: bool = False) -> str:
 
 
 
773
  """
774
  Generate a single synthetic example for the given label.
775
 
@@ -832,7 +1365,9 @@ def generate_dataset(
832
 
833
  # Generate augmented versions
834
  for _ in range(augmented_copies):
835
- aug_text = generate_example(label, include_header=include_header, augment=True)
 
 
836
  dataset.append({"text": aug_text, "label": label})
837
 
838
  random.shuffle(dataset)
@@ -865,7 +1400,9 @@ def load_as_hf_dataset(dataset: list[dict]):
865
 
866
  # 80/10/10 split, stratified at both steps
867
  train_test = ds.train_test_split(test_size=0.2, seed=42, stratify_by_column="label")
868
- val_test = train_test["test"].train_test_split(test_size=0.5, seed=42, stratify_by_column="label")
 
 
869
 
870
  # Decode ids back to names. Casting ClassLabel -> string does NOT do this
871
  # (it stringifies the ids, giving "0"/"1"), and mapping into a column that
@@ -881,10 +1418,12 @@ def load_as_hf_dataset(dataset: list[dict]):
881
  "validation": val_test["train"],
882
  "test": val_test["test"],
883
  }
884
- return DatasetDict({
885
- name: split.map(to_label_name, batched=True, features=string_features)
886
- for name, split in splits.items()
887
- })
 
 
888
 
889
 
890
  def get_label_mapping(dataset: list[dict]) -> tuple[dict, dict]:
@@ -902,25 +1441,46 @@ def get_label_mapping(dataset: list[dict]) -> tuple[dict, dict]:
902
  if __name__ == "__main__":
903
  import argparse
904
 
905
- parser = argparse.ArgumentParser(description="Generate synthetic resume section data")
906
- parser.add_argument("--examples-per-category", type=int, default=80,
907
- help="Number of base examples per category (default: 80)")
908
- parser.add_argument("--augmented-copies", type=int, default=2,
909
- help="Number of augmented copies per example (default: 2)")
910
- parser.add_argument("--output", type=str, default="data/resume_sections.csv",
911
- help="Output CSV path (default: data/resume_sections.csv)")
912
- parser.add_argument("--seed", type=int, default=42,
913
- help="Random seed (default: 42)")
914
- parser.add_argument("--print-stats", action="store_true",
915
- help="Print dataset statistics")
916
- parser.add_argument("--print-samples", type=int, default=0,
917
- help="Print N sample examples")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
918
 
919
  args = parser.parse_args()
920
 
921
- print(f"Generating dataset with {args.examples_per_category} base examples per category...")
 
 
922
  print(f"Augmented copies per example: {args.augmented_copies}")
923
- print(f"Total expected examples: {args.examples_per_category * (1 + args.augmented_copies) * 8}")
 
 
924
 
925
  dataset = generate_dataset(
926
  examples_per_category=args.examples_per_category,
@@ -932,6 +1492,7 @@ if __name__ == "__main__":
932
 
933
  if args.print_stats:
934
  from collections import Counter
 
935
  counts = Counter(d["label"] for d in dataset)
936
  print("\nDataset Statistics:")
937
  print(f" Total examples: {len(dataset)}")
@@ -942,11 +1503,11 @@ if __name__ == "__main__":
942
  print(f" Average text length: {avg_len:.0f} chars")
943
 
944
  if args.print_samples > 0:
945
- print(f"\n{'='*60}")
946
  print(f"Sample Examples (first {args.print_samples}):")
947
- print(f"{'='*60}")
948
- for i, example in enumerate(dataset[:args.print_samples]):
949
- print(f"\n--- Example {i+1} [{example['label']}] ---")
950
  print(example["text"][:300])
951
  if len(example["text"]) > 300:
952
  print("...")
 
17
  # ---------------------------------------------------------------------------
18
 
19
  FIRST_NAMES = [
20
+ "James",
21
+ "Mary",
22
+ "Robert",
23
+ "Patricia",
24
+ "John",
25
+ "Jennifer",
26
+ "Michael",
27
+ "Linda",
28
+ "David",
29
+ "Elizabeth",
30
+ "William",
31
+ "Barbara",
32
+ "Richard",
33
+ "Susan",
34
+ "Joseph",
35
+ "Jessica",
36
+ "Thomas",
37
+ "Sarah",
38
+ "Charles",
39
+ "Karen",
40
+ "Daniel",
41
+ "Lisa",
42
+ "Matthew",
43
+ "Nancy",
44
+ "Anthony",
45
+ "Betty",
46
+ "Mark",
47
+ "Sandra",
48
+ "Aisha",
49
+ "Wei",
50
+ "Carlos",
51
+ "Priya",
52
+ "Olga",
53
+ "Hiroshi",
54
+ "Fatima",
55
+ "Liam",
56
+ "Sofia",
57
+ "Andrei",
58
+ "Mei",
59
+ "Alejandro",
60
+ "Yuki",
61
+ "Omar",
62
+ "Elena",
63
+ "Raj",
64
  ]
65
 
66
  LAST_NAMES = [
67
+ "Smith",
68
+ "Johnson",
69
+ "Williams",
70
+ "Brown",
71
+ "Jones",
72
+ "Garcia",
73
+ "Miller",
74
+ "Davis",
75
+ "Rodriguez",
76
+ "Martinez",
77
+ "Hernandez",
78
+ "Lopez",
79
+ "Gonzalez",
80
+ "Wilson",
81
+ "Anderson",
82
+ "Thomas",
83
+ "Taylor",
84
+ "Moore",
85
+ "Jackson",
86
+ "Martin",
87
+ "Lee",
88
+ "Perez",
89
+ "Thompson",
90
+ "White",
91
+ "Harris",
92
+ "Sanchez",
93
+ "Clark",
94
+ "Patel",
95
+ "Chen",
96
+ "Kim",
97
+ "Nakamura",
98
+ "Ivanov",
99
+ "Silva",
100
+ "Okafor",
101
  ]
102
 
103
  COMPANIES = [
104
+ "Google",
105
+ "Microsoft",
106
+ "Amazon",
107
+ "Apple",
108
+ "Meta",
109
+ "Netflix",
110
+ "Stripe",
111
+ "Airbnb",
112
+ "Uber",
113
+ "Salesforce",
114
+ "Adobe",
115
+ "IBM",
116
+ "Oracle",
117
+ "Intel",
118
+ "Tesla",
119
+ "SpaceX",
120
+ "Palantir",
121
+ "Snowflake",
122
+ "Databricks",
123
+ "Confluent",
124
+ "JPMorgan Chase",
125
+ "Goldman Sachs",
126
+ "Morgan Stanley",
127
+ "Deloitte",
128
+ "McKinsey & Company",
129
+ "Boston Consulting Group",
130
+ "Accenture",
131
+ "Lockheed Martin",
132
+ "Boeing",
133
+ "Raytheon",
134
+ "General Electric",
135
+ "Procter & Gamble",
136
+ "Johnson & Johnson",
137
+ "Pfizer",
138
+ "Moderna",
139
+ "Shopify",
140
+ "Square",
141
+ "Twilio",
142
+ "Cloudflare",
143
+ "HashiCorp",
144
+ "DataRobot",
145
+ "Hugging Face",
146
+ "OpenAI",
147
+ "Anthropic",
148
+ "Cohere",
149
+ "Startup XYZ",
150
+ "TechCorp Inc.",
151
+ "InnovateTech",
152
+ "DataDriven LLC",
153
  ]
154
 
155
  UNIVERSITIES = [
156
+ "Massachusetts Institute of Technology",
157
+ "Stanford University",
158
+ "Harvard University",
159
+ "University of California, Berkeley",
160
+ "Carnegie Mellon University",
161
+ "Georgia Institute of Technology",
162
+ "University of Michigan",
163
+ "University of Illinois Urbana-Champaign",
164
+ "California Institute of Technology",
165
+ "Princeton University",
166
+ "Columbia University",
167
+ "University of Washington",
168
+ "University of Texas at Austin",
169
+ "Cornell University",
170
+ "University of Pennsylvania",
171
+ "University of Southern California",
172
+ "New York University",
173
+ "University of Wisconsin-Madison",
174
+ "Duke University",
175
+ "Northwestern University",
176
+ "University of California, Los Angeles",
177
+ "Rice University",
178
+ "University of Maryland",
179
+ "Purdue University",
180
+ "Ohio State University",
181
+ "Arizona State University",
182
+ "University of Virginia",
183
+ "University of Florida",
184
+ "Boston University",
185
+ "Northeastern University",
186
  ]
187
 
188
  DEGREES = [
 
198
  ]
199
 
200
  MAJORS = [
201
+ "Computer Science",
202
+ "Software Engineering",
203
+ "Data Science",
204
+ "Electrical Engineering",
205
+ "Mechanical Engineering",
206
+ "Information Technology",
207
+ "Mathematics",
208
+ "Statistics",
209
+ "Business Administration",
210
+ "Economics",
211
+ "Finance",
212
+ "Biomedical Engineering",
213
+ "Chemical Engineering",
214
+ "Civil Engineering",
215
+ "Physics",
216
+ "Biology",
217
+ "Artificial Intelligence",
218
+ "Machine Learning",
219
+ "Human-Computer Interaction",
220
+ "Cybersecurity",
221
+ "Information Systems",
222
+ "Operations Research",
223
  ]
224
 
225
  MINORS = [
226
+ "Mathematics",
227
+ "Statistics",
228
+ "Psychology",
229
+ "Business",
230
+ "Economics",
231
+ "Philosophy",
232
+ "Linguistics",
233
+ "Physics",
234
+ "Data Science",
235
+ "Communication",
236
+ "Sociology",
237
+ "History",
238
  ]
239
 
240
  GPA_VALUES = [
241
+ "3.5",
242
+ "3.6",
243
+ "3.7",
244
+ "3.8",
245
+ "3.9",
246
+ "4.0",
247
+ "3.52",
248
+ "3.65",
249
+ "3.78",
250
+ "3.85",
251
+ "3.92",
252
+ "3.45",
253
  ]
254
 
255
  GRAD_YEARS = list(range(2015, 2027))
256
 
257
  JOB_TITLES = [
258
+ "Software Engineer",
259
+ "Senior Software Engineer",
260
+ "Staff Engineer",
261
+ "Principal Engineer",
262
+ "Engineering Manager",
263
+ "Tech Lead",
264
+ "Data Scientist",
265
+ "Senior Data Scientist",
266
+ "Machine Learning Engineer",
267
+ "ML Research Scientist",
268
+ "Data Engineer",
269
+ "Data Analyst",
270
+ "Product Manager",
271
+ "Senior Product Manager",
272
+ "Program Manager",
273
+ "DevOps Engineer",
274
+ "Site Reliability Engineer",
275
+ "Cloud Architect",
276
+ "Full Stack Developer",
277
+ "Frontend Engineer",
278
+ "Backend Engineer",
279
+ "Mobile Developer",
280
+ "iOS Engineer",
281
+ "Android Developer",
282
+ "QA Engineer",
283
+ "Security Engineer",
284
+ "Solutions Architect",
285
+ "Research Scientist",
286
+ "AI Engineer",
287
+ "NLP Engineer",
288
+ "Quantitative Analyst",
289
+ "Financial Analyst",
290
+ "Business Analyst",
291
+ "UX Designer",
292
+ "UI Engineer",
293
+ "Technical Writer",
294
+ "Intern",
295
+ "Software Engineering Intern",
296
+ "Data Science Intern",
297
  ]
298
 
299
  PROGRAMMING_LANGUAGES = [
300
+ "Python",
301
+ "Java",
302
+ "JavaScript",
303
+ "TypeScript",
304
+ "C++",
305
+ "C",
306
+ "C#",
307
+ "Go",
308
+ "Rust",
309
+ "Kotlin",
310
+ "Swift",
311
+ "Ruby",
312
+ "PHP",
313
+ "Scala",
314
+ "R",
315
+ "MATLAB",
316
+ "Julia",
317
+ "Haskell",
318
+ "Elixir",
319
+ "Dart",
320
  ]
321
 
322
  FRAMEWORKS = [
323
+ "React",
324
+ "Angular",
325
+ "Vue.js",
326
+ "Next.js",
327
+ "Django",
328
+ "Flask",
329
+ "FastAPI",
330
+ "Spring Boot",
331
+ "Express.js",
332
+ "Node.js",
333
+ "Rails",
334
+ "TensorFlow",
335
+ "PyTorch",
336
+ "Keras",
337
+ "scikit-learn",
338
+ "Pandas",
339
+ "NumPy",
340
+ "Spark",
341
+ "Hadoop",
342
+ "Kubernetes",
343
+ "Docker",
344
+ "AWS",
345
+ "GCP",
346
+ "Azure",
347
+ "Terraform",
348
+ "Ansible",
349
+ ".NET",
350
+ "Laravel",
351
+ "Svelte",
352
+ "Remix",
353
+ "Astro",
354
  ]
355
 
356
  TOOLS = [
357
+ "Git",
358
+ "GitHub",
359
+ "GitLab",
360
+ "Jira",
361
+ "Confluence",
362
+ "Slack",
363
+ "VS Code",
364
+ "IntelliJ",
365
+ "PyCharm",
366
+ "Vim",
367
+ "Emacs",
368
+ "PostgreSQL",
369
+ "MySQL",
370
+ "MongoDB",
371
+ "Redis",
372
+ "Elasticsearch",
373
+ "Kafka",
374
+ "RabbitMQ",
375
+ "Airflow",
376
+ "dbt",
377
+ "Snowflake",
378
+ "Tableau",
379
+ "Power BI",
380
+ "Grafana",
381
+ "Prometheus",
382
+ "Datadog",
383
+ "Jenkins",
384
+ "CircleCI",
385
+ "GitHub Actions",
386
+ "ArgoCD",
387
+ "Figma",
388
+ "Sketch",
389
+ "Adobe XD",
390
+ "Postman",
391
+ "Swagger",
392
  ]
393
 
394
  SOFT_SKILLS = [
395
+ "Leadership",
396
+ "Communication",
397
+ "Team Collaboration",
398
+ "Problem Solving",
399
+ "Critical Thinking",
400
+ "Time Management",
401
+ "Project Management",
402
+ "Agile Methodologies",
403
+ "Scrum",
404
+ "Cross-functional Collaboration",
405
+ "Mentoring",
406
+ "Strategic Planning",
407
+ "Stakeholder Management",
408
+ "Technical Writing",
409
+ "Public Speaking",
410
+ "Negotiation",
411
  ]
412
 
413
  CERTIFICATIONS_LIST = [
 
437
  ]
438
 
439
  AWARDS_LIST = [
440
+ "Dean's List",
441
+ "Summa Cum Laude",
442
+ "Magna Cum Laude",
443
+ "Cum Laude",
444
+ "Phi Beta Kappa",
445
+ "Tau Beta Pi",
446
+ "National Merit Scholar",
447
+ "Employee of the Quarter",
448
+ "Spot Bonus Award",
449
+ "President's Club",
450
+ "Best Paper Award",
451
+ "Innovation Award",
452
+ "Hackathon Winner",
453
+ "Outstanding Graduate Student Award",
454
+ "Research Fellowship",
455
+ "Teaching Assistant Excellence Award",
456
+ "Community Service Award",
457
+ "IEEE Best Student Paper",
458
+ "ACM ICPC Regional Finalist",
459
+ "Google Code Jam Qualifier",
460
+ "Facebook Hacker Cup Participant",
461
+ "Patent Holder",
462
+ "Top Performer Award",
463
+ "Rising Star Award",
464
  ]
465
 
466
  CITIES = [
467
+ "San Francisco, CA",
468
+ "New York, NY",
469
+ "Seattle, WA",
470
+ "Austin, TX",
471
+ "Boston, MA",
472
+ "Chicago, IL",
473
+ "Los Angeles, CA",
474
+ "Denver, CO",
475
+ "Portland, OR",
476
+ "Atlanta, GA",
477
+ "Washington, DC",
478
+ "San Jose, CA",
479
+ "Raleigh, NC",
480
+ "Pittsburgh, PA",
481
+ "Minneapolis, MN",
482
+ "Dallas, TX",
483
+ "Miami, FL",
484
+ "Phoenix, AZ",
485
+ "San Diego, CA",
486
+ "Philadelphia, PA",
487
  ]
488
 
489
  MONTHS = [
490
+ "January",
491
+ "February",
492
+ "March",
493
+ "April",
494
+ "May",
495
+ "June",
496
+ "July",
497
+ "August",
498
+ "September",
499
+ "October",
500
+ "November",
501
+ "December",
502
  ]
503
 
504
  MONTHS_SHORT = [
505
+ "Jan",
506
+ "Feb",
507
+ "Mar",
508
+ "Apr",
509
+ "May",
510
+ "Jun",
511
+ "Jul",
512
+ "Aug",
513
+ "Sep",
514
+ "Oct",
515
+ "Nov",
516
+ "Dec",
517
  ]
518
 
519
  PROJECT_ADJECTIVES = [
520
+ "Real-time",
521
+ "Scalable",
522
+ "Distributed",
523
+ "Cloud-native",
524
+ "AI-powered",
525
+ "Automated",
526
+ "Interactive",
527
+ "Cross-platform",
528
+ "Open-source",
529
+ "End-to-end",
530
+ "High-performance",
531
+ "Serverless",
532
+ "Event-driven",
533
+ "Microservice-based",
534
+ "Full-stack",
535
  ]
536
 
537
  PROJECT_NOUNS = [
538
+ "Dashboard",
539
+ "Platform",
540
+ "Pipeline",
541
+ "Application",
542
+ "System",
543
+ "API",
544
+ "Framework",
545
+ "Tool",
546
+ "Service",
547
+ "Engine",
548
+ "Chatbot",
549
+ "Recommendation System",
550
+ "Search Engine",
551
+ "Analytics Platform",
552
+ "Monitoring System",
553
+ "Marketplace",
554
  ]
555
 
556
  IMPACT_METRICS = [
 
571
  ]
572
 
573
  PHONE_AREA_CODES = [
574
+ "415",
575
+ "650",
576
+ "408",
577
+ "510",
578
+ "212",
579
+ "646",
580
+ "718",
581
+ "206",
582
+ "512",
583
+ "617",
584
+ "312",
585
+ "213",
586
+ "303",
587
+ "503",
588
+ "404",
589
+ "202",
590
  ]
591
 
592
  LINKEDIN_PREFIXES = [
593
+ "linkedin.com/in/",
594
+ "www.linkedin.com/in/",
595
  ]
596
 
597
  GITHUB_PREFIXES = [
598
+ "github.com/",
599
+ "www.github.com/",
600
  ]
601
 
602
  DOMAINS = [
603
+ "gmail.com",
604
+ "outlook.com",
605
+ "yahoo.com",
606
+ "protonmail.com",
607
+ "icloud.com",
608
+ "hotmail.com",
609
+ "mail.com",
610
  ]
611
 
612
  # ---------------------------------------------------------------------------
 
614
  # ---------------------------------------------------------------------------
615
 
616
  SYNONYMS = {
617
+ "developed": [
618
+ "built",
619
+ "created",
620
+ "engineered",
621
+ "designed",
622
+ "implemented",
623
+ "constructed",
624
+ "authored",
625
+ ],
626
+ "managed": [
627
+ "led",
628
+ "oversaw",
629
+ "directed",
630
+ "supervised",
631
+ "coordinated",
632
+ "administered",
633
+ ],
634
+ "improved": [
635
+ "enhanced",
636
+ "optimized",
637
+ "upgraded",
638
+ "refined",
639
+ "boosted",
640
+ "strengthened",
641
+ ],
642
+ "implemented": [
643
+ "deployed",
644
+ "executed",
645
+ "delivered",
646
+ "rolled out",
647
+ "launched",
648
+ "shipped",
649
+ ],
650
+ "analyzed": [
651
+ "examined",
652
+ "evaluated",
653
+ "assessed",
654
+ "investigated",
655
+ "studied",
656
+ "reviewed",
657
+ ],
658
+ "collaborated": [
659
+ "partnered",
660
+ "worked closely with",
661
+ "teamed up with",
662
+ "cooperated with",
663
+ ],
664
  "responsible for": ["in charge of", "accountable for", "tasked with", "owned"],
665
  "utilized": ["leveraged", "employed", "used", "applied", "harnessed"],
666
  "achieved": ["accomplished", "attained", "reached", "secured", "delivered"],
 
672
  # Helper utilities
673
  # ---------------------------------------------------------------------------
674
 
675
+
676
  def _pick(pool, k=1):
677
  """Return k unique random items from a pool."""
678
  k = min(k, len(pool))
 
737
  if w[0].isupper():
738
  replacement = replacement.capitalize()
739
  # Preserve trailing punctuation
740
+ trailing = w[len(lower) :]
741
  result.append(replacement + trailing)
742
  else:
743
  result.append(w)
 
760
  # Section generators – each returns a string of realistic text
761
  # ---------------------------------------------------------------------------
762
 
763
+
764
  def generate_education() -> str:
765
  """Generate a realistic education section."""
766
  templates = []
 
795
 
796
  # Optional coursework
797
  if random.random() < 0.5:
798
+ courses = _pick(
799
+ MAJORS
800
+ + [
801
+ "Algorithms",
802
+ "Data Structures",
803
+ "Operating Systems",
804
+ "Database Systems",
805
+ "Computer Networks",
806
+ "Linear Algebra",
807
+ "Probability and Statistics",
808
+ "Deep Learning",
809
+ "Natural Language Processing",
810
+ "Computer Vision",
811
+ "Distributed Systems",
812
+ ],
813
+ k=random.randint(3, 6),
814
+ )
815
+ prefix = random.choice(
816
+ ["Relevant Coursework:", "Key Courses:", "Coursework:"]
817
+ )
818
  lines.append(f"{prefix} {', '.join(courses)}")
819
 
820
  # Optional honors
821
  if random.random() < 0.3:
822
+ honor = random.choice(
823
+ [
824
+ "Summa Cum Laude",
825
+ "Magna Cum Laude",
826
+ "Cum Laude",
827
+ "Dean's List (all semesters)",
828
+ "Honors Program",
829
+ "University Scholar",
830
+ ]
831
+ )
832
  lines.append(honor)
833
 
834
  # Optional thesis
835
  if "Ph.D." in deg_short or ("M.S." in deg_short and random.random() < 0.4):
836
+ topic = random.choice(
837
+ [
838
+ "Transformer-based approaches to document classification",
839
+ "Scalable distributed systems for real-time data processing",
840
+ "Graph neural networks for molecular property prediction",
841
+ "Federated learning in healthcare applications",
842
+ "Efficient attention mechanisms for long-sequence modeling",
843
+ "Reinforcement learning for autonomous navigation",
844
+ ]
845
+ )
846
  label = "Dissertation" if "Ph.D." in deg_short else "Thesis"
847
+ lines.append(f'{label}: "{topic}"')
848
 
849
  return "\n".join(lines)
850
 
 
856
  deg_full, deg_short = _pick_one(DEGREES)
857
  major = _pick_one(MAJORS)
858
  year = _pick_one(GRAD_YEARS)
859
+ gpa_line = (
860
+ f" | GPA: {_pick_one(GPA_VALUES)}" if random.random() < 0.5 else ""
861
+ )
862
  entries.append(f"{deg_short} in {major}, {uni}, {year}{gpa_line}")
863
  return "\n".join(entries)
864
 
 
870
  year = _pick_one(GRAD_YEARS)
871
  lines = [f"{uni}", f"{deg_full} in {major} | {_pick_one(MONTHS)} {year}"]
872
 
873
+ activities = random.sample(
874
+ [
875
+ "Teaching Assistant for Introduction to Computer Science",
876
+ "President, Computer Science Student Association",
877
+ "Member, ACM Student Chapter",
878
+ "Undergraduate Research Assistant, ML Lab",
879
+ "Peer Tutor, Mathematics Department",
880
+ "Captain, University Programming Competition Team",
881
+ "Volunteer, Engineering Outreach Program",
882
+ "Member, Honors College",
883
+ "Study Abroad Program, Technical University of Munich",
884
+ "Resident Advisor, Engineering Living-Learning Community",
885
+ ],
886
+ k=random.randint(1, 3),
887
+ )
888
 
889
  b = _bullet()
890
  for a in activities:
 
915
 
916
  # Generate bullet points
917
  bullet_templates = [
918
+ f"Developed and maintained {random.choice(['microservices', 'APIs', 'web applications', 'data pipelines', 'ML models', 'backend systems', 'frontend components'])} using {', '.join(_pick(PROGRAMMING_LANGUAGES, k=random.randint(1, 3)))} and {', '.join(_pick(FRAMEWORKS, k=random.randint(1, 2)))}",
919
+ f"Collaborated with cross-functional teams of {random.randint(3, 15)} engineers to deliver {random.choice(['product features', 'platform improvements', 'system migrations', 'infrastructure upgrades'])} on schedule",
920
  f"Designed and implemented {random.choice(['CI/CD pipelines', 'testing frameworks', 'monitoring solutions', 'data models', 'caching strategies', 'authentication systems'])} that {_impact()}",
921
  f"Led migration of {random.choice(['legacy monolith', 'on-premise infrastructure', 'batch processing system', 'manual workflows'])} to {random.choice(['cloud-native architecture', 'microservices', 'real-time streaming', 'automated pipelines'])}",
922
+ f"Mentored {random.randint(2, 8)} junior engineers through code reviews, pair programming, and technical design sessions",
923
  f"Optimized {random.choice(['database queries', 'API response times', 'model inference', 'data processing pipelines', 'search indexing'])} resulting in {_impact()}",
924
  f"Wrote comprehensive technical documentation and {random.choice(['RFCs', 'design docs', 'runbooks', 'architecture decision records'])} for {random.choice(['system design', 'API contracts', 'deployment procedures', 'incident response'])}",
925
  f"Built {random.choice(['real-time', 'batch', 'streaming', 'event-driven'])} {random.choice(['data pipeline', 'ETL process', 'analytics system', 'feature store'])} processing {random.choice(['1M+', '10M+', '100M+', '1B+'])} records {random.choice(['daily', 'per hour', 'in real-time'])}",
 
929
  ]
930
 
931
  n_bullets = random.randint(2, 5)
932
+ selected = random.sample(
933
+ bullet_templates, min(n_bullets, len(bullet_templates))
934
+ )
935
  selected = _reorder_bullets(selected)
936
  b = _bullet()
937
  for bullet in selected:
 
960
 
961
  if random.random() < 0.9:
962
  fws = _pick(FRAMEWORKS, k=random.randint(3, 7))
963
+ label = random.choice(
964
+ ["Frameworks", "Frameworks & Libraries", "Technologies"]
965
+ )
966
  categories.append((label, fws))
967
 
968
  if random.random() < 0.8:
 
983
  return "\n".join(lines)
984
 
985
  def _t_flat():
986
+ all_skills = (
987
+ _pick(PROGRAMMING_LANGUAGES, k=random.randint(3, 6))
988
+ + _pick(FRAMEWORKS, k=random.randint(3, 6))
989
+ + _pick(TOOLS, k=random.randint(2, 4))
990
+ )
991
  random.shuffle(all_skills)
992
  joiner = random.choice([", ", " | ", " · ", " • "])
993
  return joiner.join(all_skills)
 
997
  levels = ["Expert", "Advanced", "Proficient", "Intermediate", "Familiar"]
998
  used = set()
999
  for level in random.sample(levels, k=random.randint(2, 4)):
1000
+ pool = [
1001
+ s for s in PROGRAMMING_LANGUAGES + FRAMEWORKS + TOOLS if s not in used
1002
+ ]
1003
  items = _pick(pool, k=random.randint(2, 5))
1004
  used.update(items)
1005
  lines.append(f"{level}: {', '.join(items)}")
 
1052
  def generate_summary() -> str:
1053
  """Generate a realistic professional summary / objective section."""
1054
  years = random.randint(2, 15)
1055
+ specialties = _pick(
1056
+ MAJORS
1057
+ + [
1058
+ "full-stack development",
1059
+ "distributed systems",
1060
+ "machine learning",
1061
+ "data engineering",
1062
+ "cloud architecture",
1063
+ "mobile development",
1064
+ "DevOps",
1065
+ "backend development",
1066
+ "frontend development",
1067
+ "natural language processing",
1068
+ "computer vision",
1069
+ ],
1070
+ k=random.randint(1, 3),
1071
+ )
1072
 
1073
  templates = [
1074
  # Template 1: Traditional summary
1075
+ lambda: (
1076
+ f"Results-driven {_pick_one(JOB_TITLES).lower()} with {years}+ years of experience in {' and '.join(specialties)}. Proven track record of {random.choice(['delivering high-impact solutions', 'building scalable systems', 'driving technical excellence', 'leading cross-functional teams'])} at companies like {_pick_one(COMPANIES)} and {_pick_one(COMPANIES)}. Passionate about {random.choice(['clean code', 'system design', 'open source', 'mentorship', 'continuous learning', 'innovation'])} and {random.choice(['building products that scale', 'solving complex problems', 'leveraging data-driven insights', 'improving developer experience'])}."
1077
+ ),
1078
  # Template 2: Technical focus
1079
+ lambda: (
1080
+ f"Experienced {_pick_one(JOB_TITLES).lower()} specializing in {', '.join(specialties)}. Skilled in {', '.join(_pick(PROGRAMMING_LANGUAGES, k=3))} with deep expertise in {', '.join(_pick(FRAMEWORKS, k=2))}. {random.choice(['Strong background in', 'Demonstrated ability in', 'Track record of'])} {random.choice(['building distributed systems at scale', 'developing ML models for production', 'architecting cloud-native applications', 'leading agile engineering teams'])}. Seeking to {random.choice(['contribute to cutting-edge products', 'drive technical innovation', 'solve challenging problems', 'build impactful technology'])} at a {random.choice(['fast-growing startup', 'leading technology company', 'mission-driven organization'])}."
1081
+ ),
1082
  # Template 3: Achievement-oriented
1083
+ lambda: (
1084
+ f"{_pick_one(JOB_TITLES)} with {years} years of experience building {random.choice(['enterprise-scale', 'consumer-facing', 'B2B', 'data-intensive'])} applications. Key achievements include {_impact()}, {_impact()}, and {_impact()}. Proficient in {', '.join(_pick(PROGRAMMING_LANGUAGES, k=3))} and {', '.join(_pick(FRAMEWORKS, k=2))}."
1085
+ ),
1086
  # Template 4: Brief objective
1087
+ lambda: (
1088
+ f"Motivated {random.choice(['professional', 'engineer', 'developer', 'technologist'])} seeking a {_pick_one(JOB_TITLES).lower()} role where I can apply my expertise in {' and '.join(specialties)} to {random.choice(['build innovative products', 'solve real-world problems', 'drive business impact', 'push the boundaries of technology'])}."
1089
+ ),
1090
  # Template 5: Narrative style
1091
+ lambda: (
1092
+ f"I am a {_pick_one(JOB_TITLES).lower()} who thrives at the intersection of {_pick_one(specialties)} and {_pick_one(specialties)}. Over the past {years} years, I have {random.choice(['shipped products used by millions', 'built ML systems processing petabytes of data', 'led engineering teams through rapid growth', 'contributed to open-source projects with thousands of stars'])}. I bring a {random.choice(['data-driven', 'user-centric', 'systems-thinking', 'first-principles'])} approach to every problem I tackle."
1093
+ ),
1094
  ]
1095
 
1096
  return random.choice(templates)()
 
1108
  f"{cert} ({year})",
1109
  f"{cert} — Issued {_pick_one(MONTHS)} {year}",
1110
  f"{cert}, {year}",
1111
+ f"{cert}\n Issued: {_pick_one(MONTHS_SHORT)} {year}"
1112
+ + (
1113
  f" | Expires: {_pick_one(MONTHS_SHORT)} {year + random.randint(2, 3)}"
1114
+ if random.random() < 0.3
1115
+ else ""
1116
  ),
1117
  ]
1118
  lines.append(random.choice(styles))
 
1129
  last = _pick_one(LAST_NAMES)
1130
  city = _pick_one(CITIES)
1131
  area_code = _pick_one(PHONE_AREA_CODES)
1132
+ email_user = random.choice(
1133
+ [
1134
+ f"{first.lower()}.{last.lower()}",
1135
+ f"{first.lower()}{last.lower()}",
1136
+ f"{first[0].lower()}{last.lower()}",
1137
+ f"{first.lower()}_{last.lower()}",
1138
+ f"{first.lower()}{random.randint(1, 99)}",
1139
+ ]
1140
+ )
1141
  email = f"{email_user}@{_pick_one(DOMAINS)}"
1142
+ phone = f"({area_code}) {random.randint(100, 999)}-{random.randint(1000, 9999)}"
1143
  linkedin_user = f"{first.lower()}-{last.lower()}-{random.randint(100, 999)}"
1144
  github_user = f"{first.lower()}{last.lower()}"
1145
 
 
1156
  if random.random() < 0.4:
1157
  parts.append(f"{_pick_one(GITHUB_PREFIXES)}{github_user}")
1158
  if random.random() < 0.2:
1159
+ parts.append(
1160
+ f"{github_user}.dev"
1161
+ if random.random() < 0.5
1162
+ else f"{first.lower()}{last.lower()}.com"
1163
+ )
1164
 
1165
  sep = random.choice(["\n", " | ", " · ", "\n"])
1166
  return sep.join(parts)
 
1174
 
1175
  for award in awards:
1176
  year = random.randint(2015, 2025)
1177
+ org = random.choice(
1178
+ [
1179
+ _pick_one(UNIVERSITIES),
1180
+ _pick_one(COMPANIES),
1181
+ random.choice(
1182
+ [
1183
+ "ACM",
1184
+ "IEEE",
1185
+ "Google",
1186
+ "Facebook",
1187
+ "Microsoft",
1188
+ "National Science Foundation",
1189
+ "Department of Education",
1190
+ ]
1191
+ ),
1192
+ ]
1193
+ )
1194
  styles = [
1195
  f"{award}, {org} ({year})",
1196
  f"{award} — {org}, {year}",
 
1210
  # ---------------------------------------------------------------------------
1211
 
1212
  SECTION_HEADERS = {
1213
+ "education": [
1214
+ "EDUCATION",
1215
+ "Education",
1216
+ "Academic Background",
1217
+ "ACADEMIC BACKGROUND",
1218
+ "Education & Training",
1219
+ ],
1220
+ "experience": [
1221
+ "EXPERIENCE",
1222
+ "Experience",
1223
+ "WORK EXPERIENCE",
1224
+ "Work Experience",
1225
+ "PROFESSIONAL EXPERIENCE",
1226
+ "Professional Experience",
1227
+ "Employment History",
1228
+ ],
1229
+ "skills": [
1230
+ "SKILLS",
1231
+ "Skills",
1232
+ "TECHNICAL SKILLS",
1233
+ "Technical Skills",
1234
+ "Core Competencies",
1235
+ "CORE COMPETENCIES",
1236
+ "Technologies",
1237
+ ],
1238
+ "projects": [
1239
+ "PROJECTS",
1240
+ "Projects",
1241
+ "PERSONAL PROJECTS",
1242
+ "Personal Projects",
1243
+ "SIDE PROJECTS",
1244
+ "Selected Projects",
1245
+ "Portfolio",
1246
+ ],
1247
+ "summary": [
1248
+ "SUMMARY",
1249
+ "Summary",
1250
+ "PROFESSIONAL SUMMARY",
1251
+ "Professional Summary",
1252
+ "OBJECTIVE",
1253
+ "Objective",
1254
+ "PROFILE",
1255
+ "Profile",
1256
+ "About Me",
1257
+ "ABOUT",
1258
+ ],
1259
+ "certifications": [
1260
+ "CERTIFICATIONS",
1261
+ "Certifications",
1262
+ "CERTIFICATES",
1263
+ "Certificates",
1264
+ "Licenses & Certifications",
1265
+ "PROFESSIONAL CERTIFICATIONS",
1266
+ ],
1267
+ "contact": [
1268
+ "CONTACT",
1269
+ "Contact",
1270
+ "CONTACT INFORMATION",
1271
+ "Contact Information",
1272
+ "Personal Information",
1273
+ ],
1274
+ "awards": [
1275
+ "AWARDS",
1276
+ "Awards",
1277
+ "HONORS & AWARDS",
1278
+ "Honors & Awards",
1279
+ "ACHIEVEMENTS",
1280
+ "Achievements",
1281
+ "Awards & Honors",
1282
+ "RECOGNITION",
1283
+ ],
1284
  }
1285
 
1286
  GENERATORS = {
 
1299
  # Dataset generation
1300
  # ---------------------------------------------------------------------------
1301
 
1302
+
1303
+ def generate_example(
1304
+ label: str, include_header: bool = False, augment: bool = False
1305
+ ) -> str:
1306
  """
1307
  Generate a single synthetic example for the given label.
1308
 
 
1365
 
1366
  # Generate augmented versions
1367
  for _ in range(augmented_copies):
1368
+ aug_text = generate_example(
1369
+ label, include_header=include_header, augment=True
1370
+ )
1371
  dataset.append({"text": aug_text, "label": label})
1372
 
1373
  random.shuffle(dataset)
 
1400
 
1401
  # 80/10/10 split, stratified at both steps
1402
  train_test = ds.train_test_split(test_size=0.2, seed=42, stratify_by_column="label")
1403
+ val_test = train_test["test"].train_test_split(
1404
+ test_size=0.5, seed=42, stratify_by_column="label"
1405
+ )
1406
 
1407
  # Decode ids back to names. Casting ClassLabel -> string does NOT do this
1408
  # (it stringifies the ids, giving "0"/"1"), and mapping into a column that
 
1418
  "validation": val_test["train"],
1419
  "test": val_test["test"],
1420
  }
1421
+ return DatasetDict(
1422
+ {
1423
+ name: split.map(to_label_name, batched=True, features=string_features)
1424
+ for name, split in splits.items()
1425
+ }
1426
+ )
1427
 
1428
 
1429
  def get_label_mapping(dataset: list[dict]) -> tuple[dict, dict]:
 
1441
  if __name__ == "__main__":
1442
  import argparse
1443
 
1444
+ parser = argparse.ArgumentParser(
1445
+ description="Generate synthetic resume section data"
1446
+ )
1447
+ parser.add_argument(
1448
+ "--examples-per-category",
1449
+ type=int,
1450
+ default=80,
1451
+ help="Number of base examples per category (default: 80)",
1452
+ )
1453
+ parser.add_argument(
1454
+ "--augmented-copies",
1455
+ type=int,
1456
+ default=2,
1457
+ help="Number of augmented copies per example (default: 2)",
1458
+ )
1459
+ parser.add_argument(
1460
+ "--output",
1461
+ type=str,
1462
+ default="data/resume_sections.csv",
1463
+ help="Output CSV path (default: data/resume_sections.csv)",
1464
+ )
1465
+ parser.add_argument(
1466
+ "--seed", type=int, default=42, help="Random seed (default: 42)"
1467
+ )
1468
+ parser.add_argument(
1469
+ "--print-stats", action="store_true", help="Print dataset statistics"
1470
+ )
1471
+ parser.add_argument(
1472
+ "--print-samples", type=int, default=0, help="Print N sample examples"
1473
+ )
1474
 
1475
  args = parser.parse_args()
1476
 
1477
+ print(
1478
+ f"Generating dataset with {args.examples_per_category} base examples per category..."
1479
+ )
1480
  print(f"Augmented copies per example: {args.augmented_copies}")
1481
+ print(
1482
+ f"Total expected examples: {args.examples_per_category * (1 + args.augmented_copies) * 8}"
1483
+ )
1484
 
1485
  dataset = generate_dataset(
1486
  examples_per_category=args.examples_per_category,
 
1492
 
1493
  if args.print_stats:
1494
  from collections import Counter
1495
+
1496
  counts = Counter(d["label"] for d in dataset)
1497
  print("\nDataset Statistics:")
1498
  print(f" Total examples: {len(dataset)}")
 
1503
  print(f" Average text length: {avg_len:.0f} chars")
1504
 
1505
  if args.print_samples > 0:
1506
+ print(f"\n{'=' * 60}")
1507
  print(f"Sample Examples (first {args.print_samples}):")
1508
+ print(f"{'=' * 60}")
1509
+ for i, example in enumerate(dataset[: args.print_samples]):
1510
+ print(f"\n--- Example {i + 1} [{example['label']}] ---")
1511
  print(example["text"][:300])
1512
  if len(example["text"]) > 300:
1513
  print("...")
inference.py CHANGED
@@ -39,9 +39,11 @@ from transformers import AutoModelForSequenceClassification, AutoTokenizer
39
  # Data classes
40
  # ---------------------------------------------------------------------------
41
 
 
42
  @dataclass
43
  class SectionPrediction:
44
  """A single section classification result."""
 
45
  text: str
46
  label: str
47
  confidence: float
@@ -59,6 +61,7 @@ class SectionPrediction:
59
  @dataclass
60
  class ResumeAnalysis:
61
  """Complete resume analysis output."""
 
62
  sections: list
63
  section_count: int = 0
64
  label_distribution: dict = field(default_factory=dict)
@@ -114,7 +117,9 @@ SECTION_HEADER_PATTERNS = [
114
  r"ACTIVITIES|LEADERSHIP|RESEARCH)\s*:?\s*$",
115
  ]
116
 
117
- COMPILED_HEADERS = [re.compile(p, re.MULTILINE | re.IGNORECASE) for p in SECTION_HEADER_PATTERNS]
 
 
118
 
119
 
120
  def is_section_header(line: str) -> bool:
@@ -196,6 +201,7 @@ def split_resume_into_sections(text: str, min_section_length: int = 20) -> list:
196
  # Classifier
197
  # ---------------------------------------------------------------------------
198
 
 
199
  class ResumeSectionClassifier:
200
  """
201
  Classifies resume text sections into categories.
@@ -313,17 +319,21 @@ class ResumeSectionClassifier:
313
 
314
  results = []
315
  for i, text in enumerate(texts):
316
- scores = {self.id2label[j]: probs[i][j].item() for j in range(probs.shape[1])}
 
 
317
  predicted_id = probs[i].argmax().item()
318
  predicted_label = self.id2label[predicted_id]
319
  confidence = probs[i][predicted_id].item()
320
 
321
- results.append(SectionPrediction(
322
- text=text,
323
- label=predicted_label,
324
- confidence=confidence,
325
- all_scores=scores,
326
- ))
 
 
327
 
328
  return results
329
 
@@ -361,6 +371,7 @@ class ResumeSectionClassifier:
361
  # CLI
362
  # ---------------------------------------------------------------------------
363
 
 
364
  def main():
365
  import argparse
366
 
@@ -380,18 +391,42 @@ Examples:
380
  input_group.add_argument("--file", type=str, help="Path to resume text file")
381
  input_group.add_argument("--text", type=str, help="Direct text to classify")
382
 
383
- parser.add_argument("--model", type=str, default="./model_output/final_model",
384
- help="Path to fine-tuned model (default: ./model_output/final_model)")
385
- parser.add_argument("--device", type=str, default=None,
386
- help="Device: cpu, cuda, mps (auto-detected if omitted)")
387
- parser.add_argument("--max-length", type=int, default=256,
388
- help="Maximum token sequence length (default: 256)")
389
- parser.add_argument("--min-section-length", type=int, default=20,
390
- help="Minimum section length in characters (default: 20)")
391
- parser.add_argument("--format", type=str, choices=["text", "json"], default="text",
392
- help="Output format (default: text)")
393
- parser.add_argument("--single", action="store_true",
394
- help="Classify as single section (no splitting)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
395
 
396
  args = parser.parse_args()
397
 
@@ -430,7 +465,9 @@ Examples:
430
  bar = "#" * int(score * 40)
431
  print(f" {label:20s} {score:.4f} {bar}")
432
  else:
433
- analysis = classifier.classify_resume(text, min_section_length=args.min_section_length)
 
 
434
  if args.format == "json":
435
  print(analysis.to_json())
436
  else:
 
39
  # Data classes
40
  # ---------------------------------------------------------------------------
41
 
42
+
43
  @dataclass
44
  class SectionPrediction:
45
  """A single section classification result."""
46
+
47
  text: str
48
  label: str
49
  confidence: float
 
61
  @dataclass
62
  class ResumeAnalysis:
63
  """Complete resume analysis output."""
64
+
65
  sections: list
66
  section_count: int = 0
67
  label_distribution: dict = field(default_factory=dict)
 
117
  r"ACTIVITIES|LEADERSHIP|RESEARCH)\s*:?\s*$",
118
  ]
119
 
120
+ COMPILED_HEADERS = [
121
+ re.compile(p, re.MULTILINE | re.IGNORECASE) for p in SECTION_HEADER_PATTERNS
122
+ ]
123
 
124
 
125
  def is_section_header(line: str) -> bool:
 
201
  # Classifier
202
  # ---------------------------------------------------------------------------
203
 
204
+
205
  class ResumeSectionClassifier:
206
  """
207
  Classifies resume text sections into categories.
 
319
 
320
  results = []
321
  for i, text in enumerate(texts):
322
+ scores = {
323
+ self.id2label[j]: probs[i][j].item() for j in range(probs.shape[1])
324
+ }
325
  predicted_id = probs[i].argmax().item()
326
  predicted_label = self.id2label[predicted_id]
327
  confidence = probs[i][predicted_id].item()
328
 
329
+ results.append(
330
+ SectionPrediction(
331
+ text=text,
332
+ label=predicted_label,
333
+ confidence=confidence,
334
+ all_scores=scores,
335
+ )
336
+ )
337
 
338
  return results
339
 
 
371
  # CLI
372
  # ---------------------------------------------------------------------------
373
 
374
+
375
  def main():
376
  import argparse
377
 
 
391
  input_group.add_argument("--file", type=str, help="Path to resume text file")
392
  input_group.add_argument("--text", type=str, help="Direct text to classify")
393
 
394
+ parser.add_argument(
395
+ "--model",
396
+ type=str,
397
+ default="./model_output/final_model",
398
+ help="Path to fine-tuned model (default: ./model_output/final_model)",
399
+ )
400
+ parser.add_argument(
401
+ "--device",
402
+ type=str,
403
+ default=None,
404
+ help="Device: cpu, cuda, mps (auto-detected if omitted)",
405
+ )
406
+ parser.add_argument(
407
+ "--max-length",
408
+ type=int,
409
+ default=256,
410
+ help="Maximum token sequence length (default: 256)",
411
+ )
412
+ parser.add_argument(
413
+ "--min-section-length",
414
+ type=int,
415
+ default=20,
416
+ help="Minimum section length in characters (default: 20)",
417
+ )
418
+ parser.add_argument(
419
+ "--format",
420
+ type=str,
421
+ choices=["text", "json"],
422
+ default="text",
423
+ help="Output format (default: text)",
424
+ )
425
+ parser.add_argument(
426
+ "--single",
427
+ action="store_true",
428
+ help="Classify as single section (no splitting)",
429
+ )
430
 
431
  args = parser.parse_args()
432
 
 
465
  bar = "#" * int(score * 40)
466
  print(f" {label:20s} {score:.4f} {bar}")
467
  else:
468
+ analysis = classifier.classify_resume(
469
+ text, min_section_length=args.min_section_length
470
+ )
471
  if args.format == "json":
472
  print(analysis.to_json())
473
  else:
train.py CHANGED
@@ -69,10 +69,18 @@ def build_compute_metrics(id2label: dict):
69
  predictions = np.argmax(logits, axis=-1)
70
 
71
  acc = accuracy_metric.compute(predictions=predictions, references=labels)
72
- f1_macro = f1_metric.compute(predictions=predictions, references=labels, average="macro")
73
- f1_weighted = f1_metric.compute(predictions=predictions, references=labels, average="weighted")
74
- precision = precision_metric.compute(predictions=predictions, references=labels, average="weighted")
75
- recall = recall_metric.compute(predictions=predictions, references=labels, average="weighted")
 
 
 
 
 
 
 
 
76
 
77
  return {
78
  "accuracy": acc["accuracy"],
@@ -88,7 +96,9 @@ def build_compute_metrics(id2label: dict):
88
  # ---------------------------------------------------------------------------
89
  # Tokenization
90
  # ---------------------------------------------------------------------------
91
- def tokenize_dataset(dataset_dict: DatasetDict, tokenizer, label2id: dict, max_length: int = MAX_LENGTH):
 
 
92
  """Tokenize all splits and encode labels as integers."""
93
 
94
  def preprocess(examples):
@@ -166,7 +176,9 @@ def train(
166
  logger.info(f"Model: {model_name}")
167
  logger.info(f"Output: {output_dir}")
168
  logger.info(f"Epochs: {epochs}, Batch size: {batch_size}, LR: {learning_rate}")
169
- logger.info(f"Device: {'CUDA' if torch.cuda.is_available() else 'MPS' if torch.backends.mps.is_available() else 'CPU'}")
 
 
170
  logger.info(f"FP16: {fp16}")
171
 
172
  # ------------------------------------------------------------------
@@ -209,7 +221,9 @@ def train(
209
  label2id=label2id,
210
  )
211
  logger.info(f" Parameters: {sum(p.numel() for p in model.parameters()):,}")
212
- logger.info(f" Trainable: {sum(p.numel() for p in model.parameters() if p.requires_grad):,}")
 
 
213
 
214
  # ------------------------------------------------------------------
215
  # 4. Training
@@ -253,7 +267,9 @@ def train(
253
 
254
  callbacks = []
255
  if early_stopping_patience > 0:
256
- callbacks.append(EarlyStoppingCallback(early_stopping_patience=early_stopping_patience))
 
 
257
 
258
  trainer = Trainer(
259
  model=model,
@@ -281,7 +297,9 @@ def train(
281
 
282
  logger.info("\nTest Results:")
283
  for key, value in test_results.items():
284
- logger.info(f" {key}: {value:.4f}" if isinstance(value, float) else f" {key}: {value}")
 
 
285
 
286
  # ------------------------------------------------------------------
287
  # Save artifacts
@@ -365,48 +383,92 @@ if __name__ == "__main__":
365
  )
366
 
367
  # Model & output
368
- parser.add_argument("--model-name", type=str, default=MODEL_NAME,
369
- help="Pretrained model name or path")
370
- parser.add_argument("--output-dir", type=str, default=DEFAULT_OUTPUT_DIR,
371
- help="Output directory for model and artifacts")
 
 
 
 
 
 
 
 
372
 
373
  # Training hyperparameters
374
- parser.add_argument("--epochs", type=int, default=4,
375
- help="Number of training epochs")
376
- parser.add_argument("--batch-size", type=int, default=16,
377
- help="Training batch size per device")
378
- parser.add_argument("--learning-rate", type=float, default=2e-5,
379
- help="Peak learning rate")
380
- parser.add_argument("--weight-decay", type=float, default=0.01,
381
- help="Weight decay for AdamW")
382
- parser.add_argument("--warmup-ratio", type=float, default=0.1,
383
- help="Fraction of total steps for linear warmup")
384
- parser.add_argument("--max-length", type=int, default=MAX_LENGTH,
385
- help="Maximum token sequence length")
386
- parser.add_argument("--gradient-accumulation-steps", type=int, default=1,
387
- help="Number of gradient accumulation steps")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
388
 
389
  # Data
390
- parser.add_argument("--examples-per-category", type=int, default=80,
391
- help="Base synthetic examples per category")
392
- parser.add_argument("--augmented-copies", type=int, default=2,
393
- help="Augmented copies per base example")
394
- parser.add_argument("--seed", type=int, default=42,
395
- help="Random seed for reproducibility")
 
 
 
 
 
 
 
 
 
396
 
397
  # Training config
398
- parser.add_argument("--fp16", action="store_true", default=None,
399
- help="Force FP16 training")
400
- parser.add_argument("--no-fp16", action="store_true",
401
- help="Disable FP16 training")
402
- parser.add_argument("--early-stopping-patience", type=int, default=3,
403
- help="Early stopping patience (0 to disable)")
 
 
 
 
404
 
405
  # Hub
406
- parser.add_argument("--push-to-hub", action="store_true",
407
- help="Push trained model to HuggingFace Hub")
408
- parser.add_argument("--hub-model-id", type=str, default=HUB_MODEL_ID,
409
- help="HuggingFace Hub model ID")
 
 
 
 
 
 
 
410
 
411
  args = parser.parse_args()
412
 
 
69
  predictions = np.argmax(logits, axis=-1)
70
 
71
  acc = accuracy_metric.compute(predictions=predictions, references=labels)
72
+ f1_macro = f1_metric.compute(
73
+ predictions=predictions, references=labels, average="macro"
74
+ )
75
+ f1_weighted = f1_metric.compute(
76
+ predictions=predictions, references=labels, average="weighted"
77
+ )
78
+ precision = precision_metric.compute(
79
+ predictions=predictions, references=labels, average="weighted"
80
+ )
81
+ recall = recall_metric.compute(
82
+ predictions=predictions, references=labels, average="weighted"
83
+ )
84
 
85
  return {
86
  "accuracy": acc["accuracy"],
 
96
  # ---------------------------------------------------------------------------
97
  # Tokenization
98
  # ---------------------------------------------------------------------------
99
+ def tokenize_dataset(
100
+ dataset_dict: DatasetDict, tokenizer, label2id: dict, max_length: int = MAX_LENGTH
101
+ ):
102
  """Tokenize all splits and encode labels as integers."""
103
 
104
  def preprocess(examples):
 
176
  logger.info(f"Model: {model_name}")
177
  logger.info(f"Output: {output_dir}")
178
  logger.info(f"Epochs: {epochs}, Batch size: {batch_size}, LR: {learning_rate}")
179
+ logger.info(
180
+ f"Device: {'CUDA' if torch.cuda.is_available() else 'MPS' if torch.backends.mps.is_available() else 'CPU'}"
181
+ )
182
  logger.info(f"FP16: {fp16}")
183
 
184
  # ------------------------------------------------------------------
 
221
  label2id=label2id,
222
  )
223
  logger.info(f" Parameters: {sum(p.numel() for p in model.parameters()):,}")
224
+ logger.info(
225
+ f" Trainable: {sum(p.numel() for p in model.parameters() if p.requires_grad):,}"
226
+ )
227
 
228
  # ------------------------------------------------------------------
229
  # 4. Training
 
267
 
268
  callbacks = []
269
  if early_stopping_patience > 0:
270
+ callbacks.append(
271
+ EarlyStoppingCallback(early_stopping_patience=early_stopping_patience)
272
+ )
273
 
274
  trainer = Trainer(
275
  model=model,
 
297
 
298
  logger.info("\nTest Results:")
299
  for key, value in test_results.items():
300
+ logger.info(
301
+ f" {key}: {value:.4f}" if isinstance(value, float) else f" {key}: {value}"
302
+ )
303
 
304
  # ------------------------------------------------------------------
305
  # Save artifacts
 
383
  )
384
 
385
  # Model & output
386
+ parser.add_argument(
387
+ "--model-name",
388
+ type=str,
389
+ default=MODEL_NAME,
390
+ help="Pretrained model name or path",
391
+ )
392
+ parser.add_argument(
393
+ "--output-dir",
394
+ type=str,
395
+ default=DEFAULT_OUTPUT_DIR,
396
+ help="Output directory for model and artifacts",
397
+ )
398
 
399
  # Training hyperparameters
400
+ parser.add_argument(
401
+ "--epochs", type=int, default=4, help="Number of training epochs"
402
+ )
403
+ parser.add_argument(
404
+ "--batch-size", type=int, default=16, help="Training batch size per device"
405
+ )
406
+ parser.add_argument(
407
+ "--learning-rate", type=float, default=2e-5, help="Peak learning rate"
408
+ )
409
+ parser.add_argument(
410
+ "--weight-decay", type=float, default=0.01, help="Weight decay for AdamW"
411
+ )
412
+ parser.add_argument(
413
+ "--warmup-ratio",
414
+ type=float,
415
+ default=0.1,
416
+ help="Fraction of total steps for linear warmup",
417
+ )
418
+ parser.add_argument(
419
+ "--max-length",
420
+ type=int,
421
+ default=MAX_LENGTH,
422
+ help="Maximum token sequence length",
423
+ )
424
+ parser.add_argument(
425
+ "--gradient-accumulation-steps",
426
+ type=int,
427
+ default=1,
428
+ help="Number of gradient accumulation steps",
429
+ )
430
 
431
  # Data
432
+ parser.add_argument(
433
+ "--examples-per-category",
434
+ type=int,
435
+ default=80,
436
+ help="Base synthetic examples per category",
437
+ )
438
+ parser.add_argument(
439
+ "--augmented-copies",
440
+ type=int,
441
+ default=2,
442
+ help="Augmented copies per base example",
443
+ )
444
+ parser.add_argument(
445
+ "--seed", type=int, default=42, help="Random seed for reproducibility"
446
+ )
447
 
448
  # Training config
449
+ parser.add_argument(
450
+ "--fp16", action="store_true", default=None, help="Force FP16 training"
451
+ )
452
+ parser.add_argument("--no-fp16", action="store_true", help="Disable FP16 training")
453
+ parser.add_argument(
454
+ "--early-stopping-patience",
455
+ type=int,
456
+ default=3,
457
+ help="Early stopping patience (0 to disable)",
458
+ )
459
 
460
  # Hub
461
+ parser.add_argument(
462
+ "--push-to-hub",
463
+ action="store_true",
464
+ help="Push trained model to HuggingFace Hub",
465
+ )
466
+ parser.add_argument(
467
+ "--hub-model-id",
468
+ type=str,
469
+ default=HUB_MODEL_ID,
470
+ help="HuggingFace Hub model ID",
471
+ )
472
 
473
  args = parser.parse_args()
474