Insights
Engineering Insights Jul 2019

What We Learned Building an NLP Resume Parser

Parsing resumes at scale is harder than it looks. Here is what we learned from building an ML-based resume parser - what worked, what failed and where the real complexity hides.

The Problem Looks Simple

A resume is a structured document. It has a name, contact details, work history and education. Extracting those fields programmatically seems like a problem that should be solved in an afternoon.

It isn't.

The surface simplicity hides a field that has absorbed significant research effort - and for good reason. Resumes are the most format-diverse documents in common use. Every candidate has their own interpretation of what a resume should look like. The same information appears in hundreds of different structural patterns, labelled differently, in different sections, in different order.

We built a production resume parser using Python, SpaCy NLP and a supervised learning model. This is what we learned.

Where Rule-Based Approaches Break Down

The first instinct for resume parsing is usually a rule-based approach: find the section headed "Work Experience", extract the content below it, split on company names. For a homogeneous set of resumes - all generated by the same template - this works reasonably well.

In practice, the section doesn't always exist. When it does, it's labelled "Experience", "Work History", "Professional Background", "Career Timeline" or something else entirely. The structure within the section varies: some candidates list company before role, others reverse it. Some include dates, others don't. Some nest achievements under each role, others include them in a single block.

Rule-based parsing degrades rapidly as format diversity increases. You end up in a maintenance loop: every new format that fails generates a new rule, which conflicts with a previous rule, which requires a special case. At production scale across thousands of resumes, this approach breaks down.

What Machine Learning Actually Helps With

The most useful ML application in resume parsing is Named Entity Recognition - identifying spans of text as specific entity types: PERSON, ORG, DATE, SKILL, JOB_TITLE.

SpaCy's NER pipeline is pre-trained on general text and works reasonably well out of the box for common entities like names and organisations. For domain-specific entities - job titles, technical skills, academic credentials - the pre-trained model is inadequate. The training data it learned from wasn't resume text and job titles in particular have an enormous vocabulary that doesn't appear often in general corpora.

The solution is fine-tuning: annotating a domain-specific training set with entity labels and fine-tuning the NER model on that data. This is the labour-intensive part of the project and where most of the project time actually went - not the model architecture, but the data.

The Data Problem

Supervised learning needs labelled data. For NER, that means annotating spans of text in real resumes with the correct entity type. This is manual work and it is slow.

The accuracy ceiling of the model is set by the quality of the training data. Inconsistently labelled training examples produce a model that reflects the inconsistency. We found that inter-annotator agreement - having two people label the same text and measuring how often they agreed - was a better predictor of model quality than the quantity of training examples.

Spending time on annotation guidelines: precise definitions for each entity type, with examples of edge cases, had a measurable impact on training data quality and downstream model accuracy.

PDF and Format Preprocessing

Resumes arrive as PDFs, DOCX files and occasionally plain text. Parsing starts before the NLP does - extracting clean text from these formats is a problem in itself.

PDFs are the worst case. PDF text extraction loses formatting information: columns become mixed text, headers and body text are indistinguishable, reading order is wrong for multi-column layouts. We found that preprocessing quality had more impact on end-to-end accuracy than NLP model quality - a better model cannot recover from corrupted input text.

We invested in PDF preprocessing: identifying and handling two-column layouts, cleaning up extraction artefacts and normalising whitespace before feeding text to the parser. This unglamorous work accounted for a significant portion of the overall accuracy gain.

What the Final System Looked Like

The production pipeline:

  1. Format detection and conversion to plain text with column-aware PDF handling
  2. Section boundary detection using a trained classifier rather than rules
  3. NER for entity extraction within sections using the fine-tuned SpaCy model
  4. Post-processing to resolve entity conflicts and structure the output
  5. Confidence scoring on extracted fields, with low-confidence fields flagged for review

The confidence scoring was important for deployment. The system didn't need to be right 100% of the time - it needed to be reliably right when it said it was confident and to surface its uncertainty when it wasn't.

Where the Real Complexity Lives

Building a resume parser taught us that the hard problem in NLP extraction isn't the model - it's the data pipeline and the edge cases. The model is a component. Getting clean, representative training data, building reliable preprocessing and designing a confidence-aware output format are where the production complexity actually lives.

Anyone evaluating NLP for document extraction should budget at least as much time for data and preprocessing work as for model development. The ratio we'd use now is roughly 60/40 in favour of data work.