Master the essential techniques for cleaning and transforming raw text into structured input for NLP models. From tokenization and stemming to normalization and encoding, these preprocessing steps form the foundation of every NLP pipeline.
Raw text is messy. It contains inconsistent casing, special characters, HTML artifacts, contractions, and encoding issues. Before any NLP model can extract meaning from text, we need a systematic preprocessing pipeline to clean, normalize, and structure the input.
Text preprocessing is not a one-size-fits-all process. The right pipeline depends on your task: sentiment analysis benefits from keeping exclamation marks and emoticons, while topic modeling usually removes stopwords and punctuation. A search engine needs stemming for recall, but a chatbot needs lemmatization for grammatical responses.
This chapter covers the six core preprocessing techniques every NLP practitioner must master:
Click any topic to jump in
Splitting text into tokens — word, subword (BPE), and character strategies and their trade-offs.
Normalizing word forms and removing noise
Reducing words to base forms — fast heuristic stemming vs. linguistically correct lemmatization.
Filtering low-information words — standard lists, domain-specific words, and frequency-based detection.
Pattern-based extraction and format normalization
Pattern matching and substitution — extracting emails, URLs, and cleaning HTML artifacts.
Case folding, unicode normalization, contraction expansion — standardizing text before modeling.
ASCII, UTF-8, and multilingual byte sequences — ensuring every character survives the pipeline.
Tokenization is the process of splitting text into smaller, meaningful units called tokens. It is the very first step in virtually every NLP pipeline. The choice of tokenization strategy has profound effects on model vocabulary size, sequence length, and ability to handle unseen words.
At the word level, tokenization seems trivial---split on whitespace. But real text is full of edge cases: contractions ("don't"), hyphenated words ("state-of-the-art"), URLs, email addresses, and languages without whitespace (Chinese, Japanese). Modern NLP has largely moved to subword tokenization methods like Byte Pair Encoding (BPE) and WordPiece, which balance vocabulary size against token granularity.
The tokenization choice directly impacts downstream performance. Character-level tokenization produces long sequences that are expensive to process ( for transformers), while word-level tokenization creates enormous vocabularies and cannot handle out-of-vocabulary words. Subword methods sit in the sweet spot: manageable vocabulary sizes (32K--128K tokens) with graceful handling of rare and unseen words.
The simplest approach: split text on whitespace and punctuation boundaries. Python's str.split() handles basic whitespace splitting, while NLTK's word_tokenize() uses the Penn Treebank rules to handle punctuation, contractions, and special cases. SpaCy's tokenizer is rule-based with exception lists for abbreviations and special tokens.
Word tokenization splits on whitespace and punctuation boundaries. For a string of length characters, a regex-based tokenizer runs in time. The resulting vocabulary for a corpus follows Heaps' law: where is total tokens, , and . A 1M-word corpus typically has unique words.
Tokenize the sentence: "Dr. Smith's car costs $3,500.00!" using NLTK's word_tokenize.
Byte Pair Encoding (BPE) starts with individual characters and iteratively merges the most frequent adjacent pairs until a target vocabulary size is reached. WordPiece uses a similar approach but merges pairs that maximize the training data likelihood. Both produce a vocabulary of common words and frequent subword units, so rare words decompose into known pieces rather than becoming unknown tokens.
BPE starts with byte-level tokens and performs merges, giving . Each merge greedily picks the most frequent adjacent pair and replaces all occurrences with . The entropy of the token distribution decreases with each merge: , meaning tokens become more uniformly distributed. GPT-4 uses merges for a vocabulary of tokens.
Explain why subword tokenization handles the unseen word "unhappiest" gracefully.
Splitting text into individual sentences is essential for tasks like summarization, translation, and document processing. The challenge is distinguishing sentence-ending periods from abbreviations ("Dr.", "U.S.A."), decimal numbers ("3.14"), and ellipses ("..."). NLTK's sent_tokenize() uses the Punkt algorithm, which is trained on large corpora to learn abbreviation patterns.
The Punkt sentence tokenizer models abbreviation detection as a classification problem. It estimates using features like word length, final period, and capitalization of the next word. The decision boundary is: if , classify as abbreviation (not sentence boundary). This handles 'Dr.', 'U.S.', and 'etc.' without a hand-curated list.
Split into sentences: "Dr. Smith arrived at 3 p.m. He said, 'Hello!' Ms. Jones replied."
Each character becomes a token. The vocabulary is tiny (around 256 for byte-level, ~100 for ASCII letters + digits + punctuation) and can represent any text without unknown tokens. The downside is dramatically longer sequences: a 10-word sentence becomes 50+ tokens, and transformers with attention cost become expensive quickly.
Character-level tokenization has vocabulary (byte-level) but sequence length . For transformers with attention, this means the compute cost. A 512-word document becomes characters — attention needs operations vs for word-level, a increase.
Compare token counts for "Machine Learning" at word vs character level. What is the cost implication for a transformer?
A search engine processes queries in 47 languages. You must choose a tokenization strategy. Word-level tokenization fails for Chinese (no spaces) and agglutinative languages like Turkish (one word = one sentence). Compare word, subword (BPE), and character tokenization for this use case. Which do you recommend and why?
Write a Python function that tokenizes text using three methods (whitespace, NLTK word_tokenize, and character-level) and compares the token counts.