Semantic Processing Part 2 - Entities, Roles, and Coreference
Semantic Processing Part 2 - Entities, Roles, and Coreference
Where We Left Off
Part 1 explained how computers understand the meaning of individual words—through lexical semantics, word sense disambiguation, and co-occurrence patterns.
Now we zoom out to the full sentence. The question changes from “what does this word mean?” to:
- Who or what is mentioned? → Named Entity Recognition
- Who did what to whom? → Semantic Role Labelling
- When two words refer to the same thing? → Coreference Resolution
These are the tools that turn raw text into structured, queryable knowledge.
Section 1: Semantic Role Labelling — Who Did What to Whom?
The Core Question
Read this sentence:
“The chef carefully sliced the tomatoes with a sharp knife in the kitchen.”
As a human you instantly know:
- Who performed the action? The chef
- What action was performed? sliced
- What was acted upon? the tomatoes
- How was it done? carefully, with a sharp knife
- Where did it happen? in the kitchen
Computers need to learn this too. Semantic Role Labelling (SRL) is the task of automatically identifying these roles.
Semantic Roles Explained Simply
Each participant in an event plays a role. The key roles are:
| Role | Plain English | Example from above |
|---|---|---|
| Agent | Who/what did the action | the chef |
| Patient / Theme | Who/what received the action | the tomatoes |
| Instrument | What was used to do it | a sharp knife |
| Location | Where it happened | in the kitchen |
| Manner | How it was done | carefully |
| Time | When it happened | (not in this sentence) |
| Beneficiary | Who benefited | (e.g. “for the guests”) |
Why SRL Matters
Without SRL, a computer cannot tell the difference between:
“The dog bit the man.” “The man bit the dog.”
Both sentences contain the same three entities. SRL tells you which one is the biter (Agent) and which is the bitten (Patient)—which completely changes the meaning.
Real-world uses:
- Question answering: “Who made the decision?” → find the Agent of “decided”
- Information extraction: Pull structured facts from news articles
- Machine translation: Preserve who does what across languages
Python Example with spaCy
import spacy
nlp = spacy.load("en_core_web_sm")
sentence = "The chef sliced the tomatoes with a sharp knife."
doc = nlp(sentence)
print(f"{'Token':<15} {'Dep Label':<15} {'Head':<15} {'Role Hint'}")
print("-" * 65)
role_hints = {
"nsubj": "→ likely Agent",
"dobj": "→ likely Patient/Theme",
"pobj": "→ Instrument / Location",
"ROOT": "→ Main verb (the event)",
"advmod":"→ Manner",
}
for token in doc:
hint = role_hints.get(token.dep_, "")
print(f"{token.text:<15} {token.dep_:<15} {token.head.text:<15} {hint}")
Output:
Token Dep Label Head Role Hint
-----------------------------------------------------------------
The det chef
chef nsubj sliced → likely Agent
sliced ROOT sliced → Main verb (the event)
the det tomatoes
tomatoes dobj sliced → likely Patient/Theme
with prep sliced
a det knife
sharp amod knife
knife pobj with → Instrument / Location
. punct sliced
Section 2: Named Entity Recognition — Spotting the Important Nouns
What Is a Named Entity?
A named entity is a real-world object with a proper name: a person, a place, a company, a date, a currency amount. Not just any noun—specifically the ones that refer to something specific and identifiable.
| Not a Named Entity | Named Entity |
|---|---|
| “the city” | “London” |
| “the CEO” | “Elon Musk” |
| “a bank” | “Barclays” |
| “last year” | “2023” |
| “a large sum” | “$4.5 billion” |
Entity Types
The standard set of entity types used in most NLP systems:
| Label | Meaning | Examples |
|---|---|---|
| PERSON | People’s names | Tim Cook, Marie Curie |
| ORG | Organisations | Apple, WHO, Oxford University |
| GPE | Countries, cities, states | India, New York, California |
| LOC | Non-political locations | the Sahara, Mount Everest |
| DATE | Dates and periods | 14 June 2026, last Tuesday |
| TIME | Times of day | 3pm, midnight |
| MONEY | Monetary values | $500, £1.2 million |
| PERCENT | Percentages | 45%, a third |
| PRODUCT | Products | iPhone 15, Tesla Model S |
| EVENT | Named events | the World Cup, World War II |
A Real Example
“Apple CEO Tim Cook announced that the company earned $94.8 billion in revenue during Q4 2023 at an event in Cupertino.”
Tim Cook → PERSON
Apple → ORG
$94.8 billion → MONEY
Q4 2023 → DATE
Cupertino → GPE
This is the kind of structured information you can extract automatically from thousands of news articles—building knowledge databases without any human reading.
Python Example with spaCy
import spacy
nlp = spacy.load("en_core_web_sm")
text = """
Apple CEO Tim Cook announced that the company earned $94.8 billion
in revenue during Q4 2023 at an event in Cupertino, California.
"""
doc = nlp(text)
print(f"{'Entity':<25} {'Label':<12} {'Explanation'}")
print("-" * 60)
for ent in doc.ents:
print(f"{ent.text:<25} {ent.label_:<12} {spacy.explain(ent.label_)}")
Output:
Entity Label Explanation
------------------------------------------------------------
Apple ORG Companies, agencies, institutions
Tim Cook PERSON People, including fictional
$94.8 billion MONEY Monetary values, including unit
Q4 2023 DATE Absolute or relative dates or periods
Cupertino GPE Countries, cities, states
California GPE Countries, cities, states
Section 3: IOB Labelling — How NER Tags Words in Sequence
The Problem with Simple Tagging
Imagine you have this sentence and you want to tag “Tim Cook” as a PERSON:
Tim Cook announced the results
If you just label each word, you get:
Tim=PERSON Cook=PERSON announced=O the=O results=O
But this does not tell you whether “Tim” and “Cook” are one entity (a full name) or two separate entities. What if you had “Tim Cook and Sundar Pichai”?
Tim=PERSON Cook=PERSON and=O Sundar=PERSON Pichai=PERSON
You cannot tell which words group together.
IOB Tagging Solves This
IOB stands for Inside, Outside, Beginning.
Each word gets one of three prefixes:
- B- (Beginning): This word starts a named entity
- I- (Inside): This word is inside (continues) a named entity
- O (Outside): This word is not part of any named entity
Example:
Tim → B-PERSON (Begins a PERSON entity)
Cook → I-PERSON (Inside the same PERSON entity)
announced → O (Not an entity)
the → O
results → O
from → O
Apple → B-ORG (Begins an ORG entity)
Now it is crystal clear: “Tim Cook” is one entity, “Apple” is another.
A More Complex Example
“Sundar Pichai joined Google in 2004 and became CEO in August 2015.”
Sundar → B-PERSON
Pichai → I-PERSON
joined → O
Google → B-ORG
in → O
2004 → B-DATE
and → O
became → O
CEO → O
in → O
August → B-DATE
2015 → I-DATE
Notice “August 2015” is correctly identified as one DATE entity (B then I), while “2004” is a standalone DATE (just B, no I follows).
Python: Applying IOB Tags
import spacy
nlp = spacy.load("en_core_web_sm")
text = "Sundar Pichai joined Google in 2004 and became CEO in August 2015."
doc = nlp(text)
print(f"{'Token':<12} {'IOB Tag':<10} {'Entity Type'}")
print("-" * 40)
for token in doc:
iob = token.ent_iob_ # 'B', 'I', or 'O'
etype = token.ent_type_ or "-"
label = f"{iob}-{etype}" if iob != "O" else "O"
print(f"{token.text:<12} {label:<10} {etype}")
Output:
Token IOB Tag Entity Type
----------------------------------------
Sundar B-PERSON PERSON
Pichai I-PERSON PERSON
joined O -
Google B-ORG ORG
in O -
2004 B-DATE DATE
and O -
became O -
CEO O -
in O -
August B-DATE DATE
2015 I-DATE DATE
. O -
Section 4: Conditional Random Fields — Teaching the Sequence
Why Word-by-Word Classification Is Not Enough
You could train a simple classifier that looks at each word in isolation and decides its IOB tag. But this ignores something crucial: the tags of neighbouring words constrain each other.
Think about these rules that a good tagger should follow:
- An
I-PERSONtag should only follow aB-PERSONor anotherI-PERSON, never anOorB-ORG - After a
B-DATE, you would expect eitherI-DATEorO, notB-DATEagain - Two consecutive
B-tags of the same type usually means two separate entities
A word-by-word classifier has no way to enforce these constraints. Conditional Random Fields (CRFs) solve this.
CRFs in Plain English
A Conditional Random Field is a model that considers the entire sequence of labels at once, rather than labelling each word independently.
Think of it like this:
Simple classifier (word-by-word):
“I see the word ‘Cook’. Based on this word alone, is it PERSON, ORG, or O?”
CRF (sequence-aware):
“I see the word ‘Cook’. The previous word ‘Tim’ was tagged B-PERSON. What is the most likely tag for ‘Cook’ given both the word itself AND the previous tag?”
The CRF learns transition probabilities:
P(I-PERSON | previous=B-PERSON)is very highP(B-ORG | previous=B-PERSON)is very low
Features a CRF Uses
A CRF does not just look at the raw word—it uses carefully crafted features:
| Feature | Example for word “Apple” |
|---|---|
| The word itself | “Apple” |
| Is it capitalised? | Yes |
| Previous word | “bought” |
| Next word | “shares” |
| Does it end in common suffix? | No (-ing, -ed, -ly) |
| Is it in a company name list? | Yes |
| Part-of-speech tag | NN (noun) |
The CRF combines all these features to make a decision about the full tag sequence.
Python: CRF for NER with sklearn-crfsuite
# pip install sklearn-crfsuite
import sklearn_crfsuite
from sklearn_crfsuite import metrics
def word_features(sentence, i):
"""Extract features for word at position i"""
word = sentence[i][0] # the word
postag = sentence[i][1] # its POS tag
features = {
'word.lower': word.lower(),
'word.isupper': word.isupper(),
'word.istitle': word.istitle(), # Is it capitalised?
'word.isdigit': word.isdigit(),
'postag': postag,
'postag[:2]': postag[:2], # First 2 chars of POS tag
'word[-3:]': word[-3:], # Last 3 chars (suffix)
'word[-2:]': word[-2:], # Last 2 chars
}
# Features from previous word
if i > 0:
prev_word = sentence[i-1][0]
features['prev_word.lower'] = prev_word.lower()
features['prev_word.istitle'] = prev_word.istitle()
else:
features['BOS'] = True # Beginning of sentence
# Features from next word
if i < len(sentence) - 1:
next_word = sentence[i+1][0]
features['next_word.lower'] = next_word.lower()
features['next_word.istitle'] = next_word.istitle()
else:
features['EOS'] = True # End of sentence
return features
def sentence_features(sentence):
return [word_features(sentence, i) for i in range(len(sentence))]
def sentence_labels(sentence):
return [label for _, _, label in sentence]
# Example training data: (word, pos_tag, iob_label)
train_sentences = [
[("Tim", "NNP", "B-PERSON"), ("Cook", "NNP", "I-PERSON"),
("joined", "VBD", "O"), ("Apple", "NNP", "B-ORG")],
[("London", "NNP", "B-GPE"), ("is", "VBZ", "O"),
("a", "DT", "O"), ("city", "NN", "O")],
]
X_train = [sentence_features(s) for s in train_sentences]
y_train = [sentence_labels(s) for s in train_sentences]
# Train CRF
crf = sklearn_crfsuite.CRF(algorithm='lbfgs', max_iterations=100)
crf.fit(X_train, y_train)
Section 5: Coreference Resolution — When Two Words Mean the Same Thing
The Problem
Read this paragraph:
“Marie Curie was a physicist. She conducted research in Paris. Her work on radioactivity won her two Nobel Prizes. The scientist changed science forever.”
As a human, you instantly know:
- “She” = Marie Curie
- “Her” = Marie Curie
- “The scientist” = Marie Curie
A computer reading this paragraph in chunks would treat “Marie Curie”, “She”, “Her”, and “The scientist” as four separate entities—and fail to connect them into a unified picture.
Coreference resolution is the task of finding all mentions that refer to the same real-world entity and linking them together.
Key Terms
| Term | Meaning | Example |
|---|---|---|
| Mention | Any reference to an entity | “Marie Curie”, “she”, “the scientist” |
| Coreference cluster | All mentions of the same entity | {Marie Curie, she, her, the scientist} |
| Antecedent | The main, introducing mention | “Marie Curie” |
| Anaphor | A later reference back to it | “she”, “her” |
Why It Matters
Without coreference resolution:
Question: “What did Marie Curie win?”
Computer sees: “She won two Nobel Prizes” — but “she” is not connected to “Marie Curie” in the computer’s memory, so it cannot answer correctly.
With coreference resolution:
All mentions are linked → the computer knows “she” = “Marie Curie” → can answer: “two Nobel Prizes.”
Other applications:
- Summarisation: Avoid repeating full names repeatedly
- Information extraction: Build complete profiles of entities
- Chatbots: Remember that “it” in a follow-up question refers to the product mentioned earlier
Types of References
| Type | Description | Example |
|---|---|---|
| Pronoun | Personal pronouns | she, he, it, they |
| Definite description | “the + noun” | “the scientist”, “the company” |
| Proper name repetition | Repeating the name | “Curie” after “Marie Curie” |
| Demonstrative | this, that, these | “This discovery proved…” |
How Coreference Resolution Works
Step 1: Mention detection Find all noun phrases that could be entity references.
Mentions: [Marie Curie], [a physicist], [She], [Paris], [Her work],
[radioactivity], [her], [two Nobel Prizes], [The scientist]
Step 2: Coreference clustering Group mentions that refer to the same entity.
Cluster 1 (Marie Curie): {Marie Curie, a physicist, She, her, The scientist}
Cluster 2 (Paris): {Paris}
Cluster 3 (Nobel Prizes):{two Nobel Prizes}
Step 3: Resolution For downstream tasks, replace all mentions in a cluster with the canonical name.
Python Example with spaCy
# pip install spacy
# python -m spacy download en_core_web_sm
# Note: full coreference requires neuralcoref or spacy-experimental
import spacy
# Simple example using spaCy's built-in token references
nlp = spacy.load("en_core_web_sm")
text = "Marie Curie was a physicist. She worked in Paris. Her research was groundbreaking."
doc = nlp(text)
# Print all noun phrases (candidate mentions)
print("Candidate mentions (noun phrases):")
for chunk in doc.noun_chunks:
print(f" '{chunk.text}' — root: {chunk.root.text}, dep: {chunk.root.dep_}")
# Print pronouns (likely anaphors)
print("\nPronouns (likely referring back to something):")
for token in doc:
if token.pos_ == "PRON":
print(f" '{token.text}' at position {token.i}")
A Simple Rule-Based Approach
def simple_coref_resolve(text):
"""
Very simple rule-based coreference:
Replace pronouns with the most recent named entity of matching gender.
(Simplified - real systems are far more sophisticated)
"""
nlp = spacy.load("en_core_web_sm")
doc = nlp(text)
last_person = None
resolved = []
for token in doc:
if token.ent_type_ == "PERSON":
last_person = token.text
resolved.append(token.text)
elif token.text.lower() in ["she", "her", "he", "his", "him"] and last_person:
resolved.append(f"{token.text}[={last_person}]")
else:
resolved.append(token.text)
return " ".join(resolved)
text = "Marie Curie was a physicist. She worked tirelessly. Her research changed science."
print(simple_coref_resolve(text))
# Marie Curie was a physicist. She[=Curie] worked tirelessly. Her[=Curie] research changed science.
Section 6: Putting It All Together
Here is how the semantic layer builds up across a single sentence:
“Apple CEO Tim Cook announced record profits in Cupertino last Tuesday, and he said the company would invest in AI.”
Named Entity Recognition:
Tim Cook → PERSON
Apple → ORG
Cupertino → GPE
last Tuesday → DATE
IOB Tagging:
Apple/B-ORG CEO/O Tim/B-PERSON Cook/I-PERSON announced/O
record/O profits/O in/O Cupertino/B-GPE last/B-DATE Tuesday/I-DATE
Semantic Role Labelling:
Agent: Tim Cook (who announced)
Action: announced
Theme: record profits (what was announced)
Location: Cupertino
Time: last Tuesday
Coreference Resolution:
"he" → Tim Cook
"the company" → Apple
Result: A structured, queryable fact:
WHO: Tim Cook (Agent, from Apple)
DID WHAT: announced
WHAT: record profits
WHERE: Cupertino
WHEN: last Tuesday
FOLLOW-UP: Tim Cook said Apple would invest in AI
This is how modern systems turn raw news text into structured knowledge databases.
Section 7: Practice Questions
1. What is the difference between an Agent and a Patient in semantic roles?
Answer
The Agent performs the action (the doer). The Patient receives or is affected by the action (the target). In "The chef sliced the tomatoes," the chef is the Agent and the tomatoes are the Patient.2. What do B-, I-, and O- mean in IOB tagging?
Answer
B- = Beginning of a named entity. I- = Inside (continuation of) the same entity. O = Outside (not part of any entity).3. Why do CRFs outperform word-by-word classifiers for NER?
Answer
CRFs consider the full sequence of labels, learning which tag transitions are valid (e.g. I-PERSON should follow B-PERSON) instead of treating each word independently.4. What is coreference resolution? Why does it matter for question answering?
Answer
Coreference resolution links all mentions of the same entity (e.g. "Marie Curie", "she", "the scientist"). Without it, a QA system cannot connect "What did she win?" to "Marie Curie won two Nobel Prizes."5. In the sentence “Amazon fired 9,000 workers. The company cited economic pressures.”, what is the coreference cluster?
Answer
{Amazon, The company} — both refer to the same organisation.Summary
| Technique | Question It Answers | Output |
|---|---|---|
| Semantic Role Labelling | Who did what to whom, how, where, when? | Agent, Patient, Instrument, Location, Time labels |
| Named Entity Recognition | What real-world things are mentioned? | PERSON, ORG, GPE, DATE, MONEY labels |
| IOB Tagging | Which words belong to the same entity? | B-/I-/O prefix labels |
| Conditional Random Fields | What is the best tag sequence overall? | Full IOB tag sequence |
| Coreference Resolution | Which mentions refer to the same entity? | Coreference clusters |
What Comes Next?
Now you understand how computers extract meaning from text. The next guide covers Classical Text Generation—how computers produce new text using statistical methods like N-gram language models and Markov chains, before the era of neural networks.