Information Extraction › A Complete IE Pipeline
Language AI HandbookAll chapters Information Extraction › A Complete IE PipelineOne section · Sections in this chapter6 / 10 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Let us now combine NER, relation extraction, and structured output into an end-to-end pipeline. The pipeline takes raw text, identifies entities, and generates a structured record. This is the pattern used in production IE systems: NER provides entity boundaries and types, a relation classifier or LLM determines relationships, and a post-processing step validates and normalizes the output.
The pipeline we build here is intentionally simple: it uses spaCy for NER and a rule-based relation classifier. In production, the relation classifier would be replaced by a fine-tuned BERT model or an LLM call, and the post-processing step would validate against a JSON Schema. But the overall structure, the composition of modular stages, remains the same.
import re
from typing import Optional
import spacy
# Load spaCy model
nlp = spacy.load("en_core_web_sm")
def extract_entities(text: str) -> list:
"""Extract named entities using spaCy."""
doc = nlp(text)
return [
{
"text": ent.text,
"label": ent.label_,
"start": ent.start_char,
"end": ent.end_char,
}
for ent in doc.ents
]
def find_entity_pairs(entities: list, target_types: list) -> list:
"""Find all pairs of entities with specified types."""
filtered = [e for e in entities if e["label"] in target_types]
pairs = []
for i in range(len(filtered)):
for j in range(i + 1, len(filtered)):
pairs.append((filtered[i], filtered[j]))
return pairs
def classify_relation(text: str, e1: dict, e2: dict) -> Optional[str]:
"""
Simple rule-based relation classifier.
In production, replace with a fine-tuned classifier.
"""
start = min(e1["start"], e2["start"])
end = max(e1["end"], e2["end"])
span = text[start:end].lower()
acquisition_patterns = [
r"\bacquir",
r"\bpurchas",
r"\bbuy\b",
r"\bbought\b",
r"\btakeover\b",
r"\bmerge",
]
for pattern in acquisition_patterns:
if re.search(pattern, span):
return "acquired"
return None
# Run the pipeline
text = (
"Apple acquired Beats Electronics for $3 billion in May 2014. "
"Tim Cook announced the deal at Apple's headquarters in Cupertino, California."
)
entities = extract_entities(text)
org_pairs = find_entity_pairs(entities, target_types=["ORG"])
relations = []
for e1, e2 in org_pairs:
relation = classify_relation(text, e1, e2)
if relation:
relations.append(
{"subject": e1["text"], "predicate": relation, "object": e2["text"]}
)=== Extracted Entities === [ORG ] Apple [ORG ] Beats Electronics [MONEY ] $3 billion [DATE ] May 2014 [PERSON ] Tim Cook [ORG ] Apple [GPE ] Cupertino [GPE ] California === Extracted Relations === (Apple, acquired, Beats Electronics) (Apple, acquired, Apple)
The pipeline correctly identifies the acquisition relationship between Apple and Beats Electronics. The rule-based classifier here is illustrative: production systems use fine-tuned BERT-based classifiers trained on labeled relation examples, which generalize to many more surface forms. The classifier receives the entity types from NER as features alongside the sentence context, making the combination of NER and RE complementary rather than independent. Notice also that the classifier operates only on ORG-ORG pairs: if we had also checked PER-ORG pairs, we might find an employment relation between Tim Cook and Apple. The type-based filtering keeps the search focused and reduces the false positive rate.
Key Parameters
The key parameters and design choices for an IE pipeline are:
- NER model:
en_core_web_sm(fast, lower accuracy) vsen_core_web_trf(slower, higher accuracy). Use the transformer model when precision matters. - Entity type filter for RE: restricting which entity type pairs to consider (ORG-ORG for acquisitions, PER-ORG for employment) reduces false positives.
- Relation classifier threshold: in probabilistic classifiers, lowering the confidence threshold increases recall but also false positives.
- Schema strictness:
requiredfields in JSON Schema enforce that the LLM always populates critical slots; optional fields reduce false "null" values.
Pipeline Architecture Trade-offs
The pipeline approach described above has an important architectural property: each component is independently swappable. If a better NER model becomes available, you can swap it in without touching the relation classifier. If you need to add a new relation type, you can update the classifier's label set without retraining the NER model. This modularity is valuable in production systems that evolve over time.
The cost of modularity is error propagation. An entity boundary error in NER creates a bad input to the relation classifier, which may then either miss the correct entity pair entirely or classify a wrong pair. In practice, the most common NER errors are at entity boundaries, not entity types: the classifier knows that "Beats Electronics" is an organization, but may label the span as "Beats" instead of "Beats Electronics." The relation classifier then sees "Beats" as one entity and tries to relate it to other ORG spans, possibly failing to find the correct pair.
One mitigation is to use soft entity boundaries: instead of passing hard entity spans to the relation classifier, pass the NER model's uncertainty along with the span boundaries. A relation classifier that is aware that "Beats Electronics" might be "Beats" with some probability can account for this uncertainty in its prediction. This fuzzy pipeline approach is less common in practice because it complicates the interface between components, but it can improve end-to-end metrics when NER errors are a significant bottleneck.