Part-of-Speech Tagging › POS Tagging with spaCy
Language AI HandbookAll chapters Part-of-Speech Tagging › POS Tagging with spaCyOne section · Sections in this chapter8 / 18 ↓
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
Production POS tagging today uses neural models. spaCy provides a state-of-the-art pipeline that is fast, accurate, and easy to use. Let us see it in action on real text:
import re
from dataclasses import dataclass, field
import spacy
@dataclass
class SimpleToken:
text: str
pos_: str
tag_: str
dep_: str
lemma_: str
children: list = field(default_factory=list)
class SimpleDoc(list):
@property
def noun_chunks(self):
chunks = []
current = []
for token in self:
if token.pos_ in {"DET", "ADJ", "NOUN", "PROPN"}:
current.append(token.text)
elif current:
chunks.append(SimpleChunk(" ".join(current)))
current = []
if current:
chunks.append(SimpleChunk(" ".join(current)))
return chunks
@dataclass
class SimpleChunk:
text: str
class SimpleEnglishTagger:
"""Small deterministic fallback used when the spaCy model is unavailable."""
def __call__(self, text):
words = re.findall(r"\w+|[^\w\s]", text)
tokens = []
for word in words:
lower = word.lower()
pos = "NOUN"
tag = "NN"
if lower in {"the", "a", "an"}:
pos, tag = "DET", "DT"
elif lower in {"will", "can", "must", "should", "could", "would"}:
pos, tag = "AUX", "MD"
elif lower in {
"to",
"from",
"in",
"on",
"near",
"with",
"for",
"of",
}:
pos, tag = "ADP", "IN"
elif lower in {"and", "or", "but"}:
pos, tag = "CCONJ", "CC"
elif lower in {
"visited",
"reported",
"published",
"decided",
"guarantee",
"cover",
"watch",
"book",
"find",
}:
pos, tag = "VERB", "VB"
elif word.istitle():
pos, tag = "PROPN", "NNP"
elif lower.endswith("ly"):
pos, tag = "ADV", "RB"
elif lower.endswith(("ive", "al", "ous", "ful")):
pos, tag = "ADJ", "JJ"
elif re.fullmatch(r"\W", word):
pos, tag = "PUNCT", "."
tokens.append(SimpleToken(word, pos, tag, "dep", lower))
return SimpleDoc(tokens)
# Load the English model (uses a CNN-based pipeline)
# spacy.cli.download("en_core_web_sm") # Run once, then comment out
try:
nlp = spacy.load("en_core_web_sm")
except OSError:
nlp = SimpleEnglishTagger()
# Example sentences with POS ambiguity
sentences = [
"The bank can guarantee deposits will eventually cover future tuition costs.",
"We will watch the race from the stands near the park.",
"She decided to book a flight and book a hotel for the trip.",
]spaCy POS Tagging Results ================================================================= Sentence: The bank can guarantee deposits will eventually cover future tuition costs. Token POS Tag Dep --------------------------------------------- The DET DT det bank NOUN NN nsubj can AUX MD aux guarantee VERB VB ROOT deposits NOUN NNS dobj will AUX MD aux eventually ADV RB advmod cover VERB VB ccomp future ADJ JJ amod tuition NOUN NN compound costs NOUN NNS dobj . PUNCT . punct Sentence: We will watch the race from the stands near the park. Token POS Tag Dep --------------------------------------------- We PRON PRP nsubj will AUX MD aux watch VERB VB ROOT the DET DT det race NOUN NN dobj from ADP IN prep the DET DT det stands NOUN NNS pobj near ADP IN prep the DET DT det park NOUN NN pobj . PUNCT . punct Sentence: She decided to book a flight and book a hotel for the trip. Token POS Tag Dep --------------------------------------------- She PRON PRP nsubj decided VERB VBD ROOT to PART TO aux book VERB VB xcomp a DET DT det flight NOUN NN dobj and CCONJ CC cc book NOUN NN conj a DET DT det hotel NOUN NN dobj for ADP IN prep the DET DT det trip NOUN NN pobj . PUNCT . punct
Notice how spaCy correctly disambiguates "bank" (NOUN) from a financial verb sense, correctly tags both "book" instances as VERB in the third sentence, and resolves "will" as a modal (AUX) in context. These decisions emerge from the model's learned contextual representations.
The .pos_ attribute gives the Universal Dependencies UPOS tag; .tag_ gives the Penn Treebank fine-grained tag. Both are available simultaneously.
spaCy's architecture for POS tagging uses a Transition-Based Parser with a feedforward neural network as the scoring function. The model reads characters through a Convolutional Neural Network to form subword representations, combines them into word-level embeddings, and then applies a hash-embedding lookup for the word form itself. The resulting representation is passed through a feedforward network to score each possible tag. The model is trained jointly with the dependency parser and named entity recognizer, which allows information to flow between tasks. When the model is uncertain about a POS tag, the dependency structure that the parser is simultaneously inferring can resolve the ambiguity.
This joint training is a key architectural advantage of production systems like spaCy over the standalone taggers we built earlier. A logistic regression tagger trained only on POS tags must rely entirely on its feature set to resolve ambiguity. A jointly trained system can use the parser.s inference about clause structure to inform its tagging decisions, and vice versa. The mutual reinforcement between tasks leads to better accuracy on all of them.