Named Entity Recognition
Named entity recognition finds name spans in text, people, places, organizations, dates, and labels each span so later steps can use it as a fact.
Why Does This Exist?
Names carry the facts. "Tim Cook visited New York" is useful only once you know which words are the person and which are the place. Search, knowledge graphs and event tracking all start from that step: find the spans, name their types, hand structured pairs downstream.
Early systems kept long lists of known names and capitalisation rules. A new startup, a new spelling or lowercase chat text broke them. Contextual models fixed this by reading surrounding words, so an unseen name in a familiar pattern still gets caught.
This page covers span finding plus typing: the BIO labelling, the encoder that scores tags, and the layer that keeps tag order valid. For the full chain this step sits in, see the NLP pipeline. For the word-level grammar step that runs just before it, see part-of-speech tagging. For turning these spans into relations between entities, see information extraction.
The one idea you need first
Text must be split into pieces before any piece can carry a tag. A token is that piece, usually a word or subword. The full treatment is in tokenization in NLP.
Think of It Like This
Highlighting a newspaper with four markers
You read a newspaper with four markers: yellow for people, blue for organizations, green for places, orange for dates. You mark "Tim Cook" yellow across two words as one span, skip "visited", then mark "New York City" green across three words as one span. One-word names get a start mark, continuations get a continue mark, everything else stays unmarked.
The colours are the entity types. Start versus continue marks are B versus I tags. Leaving words unmarked is the O tag.
Where it stops: your eyes use the whole world to decide, including photos and memory. The tagger sees only nearby words and the tag patterns it learned, so sarcasm, new domains and missing capitals fool it faster than they fool you.
How It Actually Works
Give the sentence tokens , where each is one token such as "Tim". The tagger must output one tag per token. Tag comes from the BIO set: starts a span of some type, continues that same span, marks no entity. Type is the entity kind such as PER for person or LOC for location, so -PER means this token starts a person span.
A contextual encoder turns each token plus its neighbours into a vector , a list of numbers that knows "Apple" near "CEO" differs from "apple" near "pie". A linear layer maps to one score per possible tag; call that vector , where is the score of tag at position . A conditional random field adds a transition table , where is the learned score of tag following tag . The sentence score is the sum of the chosen tag scores plus the transition scores, and decoding picks the valid path with the top total, which forbids broken orders such as -ORG right after -PER.
The BIO tags on a real sentence
"Tim Cook visited New York City" has six tokens. The correct tags are -PER, -PER, , -LOC, -LOC, -LOC. Merging gives two spans: ("Tim Cook", PER) and ("New York City", LOC). One opens each span, extends it, contributes nothing.
Why the transition table matters
Independent per-token picks can output -PER then -LOC, which is nonsense: one span cannot change type halfway. The transition table learns that -LOC after -PER scores very low, so the decoder prefers a globally valid path even when a local score tempts it otherwise. That's the whole job of the final layer.
Worked example
Keep the two spans above as the truth. Suppose an evaluation batch has correct spans, predicted spans that match nothing, and true spans the model missed. Here counts hits, counts false alarms, counts misses.
Precision . Recall . The span is their harmonic mean:
So the model finds most entities at recall with fairly clean output at precision, combined as . The same three numbers appear above the diagram.
Code
This snippet recomputes precision, recall and span F1 from the worked counts.
def prf(tp: int, fp: int, fn: int) -> tuple[float, float, float]: p = tp / (tp + fp) r = tp / (tp + fn) f = 2 * p * r / (p + r) return (p, r, f)
p, r, f = prf(45, 9, 6)print(round(p, 4), round(r, 4), round(f, 4)) # -> 0.8333 0.8824 0.8571Watch Out For
Counting a half span as a hit
Tagging "New York" when the truth is "New York City" feels close, but span scoring calls it one false alarm plus one miss. The symptom is token accuracy near 95 percent while entity F1 sits near 70. The fix is to score exact spans per type during development, and to review boundary words such as "City", "Inc" and titles before blaming the model size.
Running news models on specialist text
A tagger trained on news misses gene names, drug names and statute cites because the shapes and contexts differ. The symptom is near-zero recall on the new domain while news tests still look fine. The fix is a domain encoder or fresh annotated data plus an entity set that matches the domain, not a threshold tweak on the old model.
The Quick Version
- Named entity recognition outputs (span, type) pairs such as ("Tim Cook", PER) for downstream use.
- BIO framing turns span finding into per-token tagging: B starts, I continues, O marks nothing.
- A contextual encoder scores tags per token and a transition table keeps the whole tag order valid.
- The worked batch at TP 45, FP 9, FN 6 gives precision 0.833, recall 0.882 and F1 0.857.
- Score exact spans per type, because half spans count as a miss plus a false alarm.
- News models fail on specialist text, so change the encoder or the training data for a new domain.