AI EngineeringZero to ProductionHome·About·What’s new·Contact
NLP & Transformers · Chapter K1

NLP Foundations & Text Processing

LLMs didn't appear from nowhere — they're the current peak of decades of natural language processing. This module walks that arc so the models you already use stop being magic. It starts at the base: what NLP is, why human language is genuinely hard for computers, and the text-processing pipeline that underlies everything after.

⏱️ ~45 min🧭 Foundations🎯 Beginner→Expert
🌱 Start here — from zero NLP & transformers, from scratch — how the model actually works under the hood — the 'why', for the curious and the interview-bound.

This section opens the black box: how text becomes numbers a model can process, and how the transformer architecture (the 'T' in GPT) turns those numbers into predictions. It's the theory behind everything else — useful for debugging, for interviews, and for genuinely understanding the tools you've been using.

The words you'll hear (in plain terms):

TermWhat it actually means
NLPNatural Language Processing — getting computers to work with human language.
token / embeddingtext split into chunks (tokens), each turned into a vector of numbers (embedding).
transformerthe neural-network architecture behind modern LLMs.
attentionthe mechanism that lets a model weigh which words matter to each other.
neural networklayers of math that learn patterns from data.

What you need before starting:

  • This is the most theoretical section — helpful but not required to build apps.
  • Comfort with basic math (vectors) and Python; NumPy (DA1) helps.
  • Curiosity about 'why', not just 'how'.

New to the topic? Read this box, then take the chapters in order — each section is tagged essential → expert so you always know the depth you're at.

Learning objectives

  • Define NLP and the classic task families it covers.
  • Explain why natural language is hard — ambiguity, context, scale.
  • Run the classic preprocessing pipeline: normalize → tokenize → stem/lemmatize → stop-words.
  • Know which classic steps LLMs made optional — and which still matter.
  • Place this module's arc: rules → statistics → neural → transformers → LLMs.
Why learn "old" NLP when LLMs exist?Because the ideas didn't die — they got absorbed. Tokenization, embeddings, sequence modeling, attention: every one is a classic NLP concept the transformer perfected. Understanding the lineage tells you why LLMs behave as they do (context windows, token costs, why they see subwords not letters) and when a lighter classical tool still beats a frontier model. This chapter is the base; K2–K5 climb to the transformer.

What NLP is essential

Start with the moment the abstraction leaks. You paste a paragraph into an LLM, ask it to "pull out the invoice number and the due date," and it just… does it. No training, no model file, no rules — it feels like one seamless magic trick. But the day that request returns the wrong date, or costs ten times what you budgeted, or has to run a million times an hour, the single trick splinters into a dozen distinct problems — and each of those problems has a name that predates the LLM by decades. That catalog of names is NLP, and knowing it is what turns "the model did something" into "I know exactly which job I asked it to do and how to check it."

Here's the analogy that makes the field click. Think of NLP as medicine and the tasks below as its specialties — cardiology, dermatology, oncology. For fifty years you saw a different specialist for each complaint: a spam classifier here, a named-entity extractor there, a translation system somewhere else, each a separate model trained on separate data. The LLM is the eerily capable general practitioner who handles most of the specialties from one conversation. That's a genuine revolution — but the specialties didn't disappear. You still diagnose the problem in the old vocabulary ("this is a classification task, that is extraction"), because that framing is what tells you how to prompt it, how to evaluate it, and when to send it to a specialist after all.

The common mistake here is skipping the diagnosis. Beginners treat every text problem as one undifferentiated "ask the AI" blob, then can't explain why it works or fails. If you can't name the task family, you can't pick an eval for it (Ch 5), you can't reason about whether a cheaper tool would do the job, and you can't tell whether a wrong answer is a prompting bug or a genuinely hard case. Naming is not academic bookkeeping — it's the first move of every competent practitioner. So the definition, plainly: Natural Language Processing is getting computers to work with human language — understanding it, generating it, and transforming it. It's a decades-old field; LLMs are its latest, most general tool. Classic NLP is a catalog of tasks, and it helps to see them because an LLM now does most of them with one model.

Task familyExampleAn LLM does this via…
ClassificationSpam? Sentiment? Topic?A prompt or structured output (Ch 2, K3)
ExtractionFind names, dates, entities (NER)Schema-constrained extraction (Ch 2)
GenerationSummarize, translate, writeIts core capability
Retrieval / searchFind relevant documentsEmbeddings + RAG (Ch 3)
Sequence labelingTag each word (POS, chunks)Rarely needed directly now
One model, many tasks — that's the shiftThe historic pattern was "one specialized model per task": a spam classifier, a separate NER model, a separate translation system, each trained on task-specific data. The LLM's breakthrough is generality: a single model handles all of these from a prompt. Knowing the task families still matters — it's how you frame a problem and pick evals (Ch 5) — but you no longer build a bespoke model for each.

Why language is hard for computers essential

Here's a question that should nag at you: if computers beat us at chess in 1997, why did fluent conversation take until the 2020s? Chess has more possible games than atoms in the universe — vastly "bigger" than a sentence. The answer is that chess is hard but crisp: fixed board, legal moves, one clear goal. Language is the opposite — it's small but slippery. Every rule has exceptions, every word bends with context, and the "correct" answer depends on who's speaking, to whom, about what, and what everyone already knows. That slipperiness, not size, is why language sat unsolved for half a century. This section is the honest tour of why — and once you feel each difficulty, the entire design of modern LLMs reads as a direct response to it.

The useful mental model is this: a computer is superb with things that are precise and stable — a bank balance means one thing, everywhere, forever. Language is neither. Take the sentence "I saw her duck." Is she lowering her head, or does she own a waterfowl? Same eight letters, two worlds apart, and nothing in the sentence itself decides it — only the surrounding situation does. A computer that reads text as fixed symbols is helpless here in a way it never is with numbers, because the symbol and the meaning have come apart. Everywhere you look in language you find this same gap: one form, many meanings (ambiguity); meaning that lives sentences away (context); many forms for one meaning (variation); meaning that flatly contradicts the literal words (idiom).

The mistake to avoid is thinking these are edge cases you could patch away. Early NLP tried exactly that — hand-write a rule for "duck," another for "bank," another for sarcasm — and drowned, because the exceptions are the language, not a fringe of it. The breakthrough was to stop fighting ambiguity and instead let a model use context to resolve it, which is precisely what "attention" (K5) does: weigh every other word to decide what this one means right now. Read the table below not as a list of annoyances but as the design brief for the transformer — each row is a problem so stubborn that solving it reshaped the whole field. The difficulties below are exactly what made NLP a decades-long research field — and what the transformer's context-handling finally tackled at scale.

ChallengeExampleWhy it's hard
Ambiguity"I saw her duck" (bird? crouch?)Same words, multiple meanings — needs context
Context dependence"It" refers to what, three sentences back?Meaning spans distance (why context windows matter)
Variation"big" / "large" / "huge" / "enormous"Many surface forms, similar meaning
Idiom & implication"It's raining cats and dogs"Meaning ≠ the literal words
Scale & sparsityInfinite possible sentencesCan't enumerate; must generalize
These challenges explain LLM behavior you've already seenContext dependence is why a bigger context window helps and why "meaning spans distance" pushed the field toward attention (K5). Ambiguity is why the same prompt can yield different readings. Variation is why embeddings (K2) beat exact keyword match. The hard parts of language are the design pressures that shaped modern models.

The historical arc essential

Why does a history lesson belong in a hands-on AI course? Because the past isn't behind the LLM — it's inside it. Every era of NLP left a fossil in the model you use today: tokenization came from the rules era, "how likely is this next word" is the statistical era's core idea, learned representations came from the neural era, and attention is the transformer era's one big move. When an LLM surprises you — bills per token, miscounts letters, forgets something from earlier in a long chat — the explanation almost always lives in one of these layers. Knowing the arc means you're never reasoning from magic; you're reasoning from a lineage you can name.

The cleanest way to hold this in your head is that each era hit a ceiling, and the ceiling of one era became the founding problem of the next. Rules were precise but couldn't scale — nobody can hand-write a grammar for all of English, so they broke on the first sentence they hadn't anticipated. Statistics fixed brittleness by counting real usage instead of prescribing it, but counting words treats "big" and "large" as unrelated symbols and drowns in the sheer number of possible word combinations. Neural networks fixed that by learning that similar words should have similar representations — but the early ones (RNNs) had to read a sentence strictly left-to-right, one word at a time, and would forget the beginning by the end. Transformers fixed that by letting the model look at the whole sequence at once and weigh what matters. That's not four disconnected techniques; it's one long conversation, each answer exposing the next question.

The trap to sidestep is treating the earlier eras as "obsolete" and skipping them. They're not obsolete — they're the concepts the transformer absorbed and perfected, and the later modules (K2 statistical, K3 classification, K4 neural, K5 transformers) only make sense once you can see the ceiling each was built to break. Think of it like learning arithmetic before calculus: you don't use long division much anymore, but the ideas underneath it never left. This module is a walk up that ladder, and the payoff is that "why does the LLM behave like this?" almost always has an answer you can point to on the timeline below. NLP moved through four eras, each fixing the previous one's ceiling. This module is a walk up that ladder — knowing where you are on it makes every technique make sense.

Rules Statistical Neural (RNN) Transformers→ LLMs hand-written count & probability learn features attention Four eras, rising capability. Rules (hand-written grammars) → statistical (count words, model probabilities — K2/K3) → neural (learn representations; RNNs process sequences — K4) → transformers (attention over the whole sequence — K5), which scaled into LLMs. Each era's ceiling became the next era's starting problem.
🗺️ How to read this diagram

This is a timeline, read left to right. Each rising block is an era in how computers handled language, and each one got better than the one before it. Height roughly means "how capable" — the boxes climb as you move right in time.

  • Rules (leftmost, shortest) — the earliest approach: humans hand-wrote grammar rules by hand ("if the sentence looks like X, do Y"). Powerful but brittle — you can't write a rule for every sentence.
  • Statistical — instead of rules, count words and model probabilities ("how often does this word follow that one?"). This is the era of counting, covered in K2/K3.
  • Neural (RNN) — a neural network learns the useful features itself from data instead of being told them. RNNs read a sentence one word at a time, in order (K4).
  • Transformers → LLMs (rightmost, tallest) — the breakthrough is attention: the model looks at the whole sentence at once and weighs which words matter to each other. Scaled up, this became the LLMs you use today (K5).
  • The line along the bottom is the shared timeline; the small labels under each box (hand-written, count & probability, learn features, attention) name the one big idea of each era.

In short: Each era hit a ceiling that the next era was invented to break. You are climbing this same ladder as you go through modules K1 → K5.

Lab K1.1 · The classic preprocessing pipeline intermediate

Picture the problem a 1990s model actually faced. It's a spam filter, and it wants to learn that the word "free" signals junk mail. But in the raw inbox that word arrives as "Free", "FREE!", "free,", "free." and " free " — to a computer comparing exact strings, those are five different words, and the signal it needs gets shattered across all of them. The model can't learn anything until someone melts those five variants back into one. That melting-down is preprocessing, and this pipeline is the assembly line that does it: it takes text as humans carelessly type it and forges it into the clean, consistent units a counting model can actually work with.

The analogy that makes each stage obvious is a recycling plant. Mixed rubbish comes in the front; the plant washes it, sorts it into bins, throws out the worthless bits, and crushes each material down to a standard form so it can be reused. Preprocessing does the same four moves to text. Normalize is the wash — lowercase everything and strip punctuation so "Run", "run", and "run!" stop being strangers. Tokenize is the sort — chop the cleaned string into individual units (words) you can count. Stop-word removal is the throwing-out — words like "the" and "is" appear everywhere and so carry almost no signal about what a document is about, so they're discarded. Stemming/lemmatization is the crushing — "running", "runs", and "runner" all get pressed down toward one root so the model sees them as the same feature. Out the far end comes a tidy token list, and everything downstream (K2's representations, K3's classifiers) eats that output.

Now the honest caveat, because this is where the chapter turns. Every one of these steps throws information away on purpose — that was a feature when your model was too weak to use capitalization or punctuation, and it's a bug when your model is an LLM that reads exactly those cues for meaning. So learn this pipeline as history and as a still-useful tool for search and lightweight classical models — but hold the warning in your other hand: the aggressive cleaning you're about to run is precisely what you should not do before an LLM (the warning box later spells out why). Watch what each stage discards; that lost signal is the whole point of the lesson. Before statistical/ML models could touch text, they needed it cleaned into consistent units. This pipeline is foundational NLP — and you still use pieces of it for search, classical models, and data cleaning (B1).

raw text normalize tokenize stop-words /stem·lemma tokens Turn messy text into consistent units. Lowercase & strip punctuation (normalize), split into words (tokenize), drop filler words (stop-words), and reduce forms to a root (stem/lemmatize). The output — a clean token list — is what classic representations (K2) and models (K3) consume.
🗺️ How to read this diagram

This is a pipeline — a conveyor belt. Messy text enters on the left, flows through four cleaning stations (the arrows show the direction), and comes out on the right as a tidy list of tokens (word-chunks) that older NLP models can work with.

  • raw text (start) — the original sentence, exactly as a human wrote it, with capitals, punctuation, and filler words.
  • normalize — make everything consistent: lowercase the letters and strip punctuation, so "Run", "run", and "run!" all become the same thing.
  • tokenize — split the cleaned string into separate units (here, words). Each unit is a token.
  • stop-words / stem·lemma — the last station does two jobs: throw away high-frequency filler words ("the", "is"), and shrink each remaining word to a root form ("running" → "run").
  • tokens (end) — the clean word list that comes out. This is the input that classic text representations (K2) and models (K3) actually consume.

In short: Follow one word through the belt: "Runners!" → normalize → "runners" → tokenize → a token → stem → "runner". The lab below runs exactly these four steps in Python.

Example code for learning — review, test, and adapt it before running against real or production systems. Commands can create, change, or delete resources. See the Terms & Disclaimer.
Lab K1.1
preprocess.pytext = "The Runners were running quickly through the streets!"

# 1 · normalize — lowercase, strip punctuation
norm = "".join(c for c in text.lower() if c.isalnum() or c.isspace())

# 2 · tokenize — split into words (real tools handle edge cases)
tokens = norm.split()
# ['the','runners','were','running','quickly','through','the','streets']

# 3 · remove stop-words — drop low-signal filler
STOP = {"the", "were", "through", "a", "is"}
content = [t for t in tokens if t not in STOP]
# ['runners','running','quickly','streets']

# 4 · stem / lemmatize — reduce to a root form
# stemming (crude): 'runners'->'runner', 'running'->'run'  (chops suffixes)
# lemmatizing (smart): 'running'->'run', 'better'->'good'   (uses a dictionary)
▶ How this works

This is the classic NLP preprocessing pipeline in ~10 lines of plain Python — the same four stations from the diagram above, turning one messy sentence into a clean list of word-roots. There's no library and no AI here; it's deliberately simple so you can see each step happen.

  1. Start with raw text. text = "The Runners were running quickly through the streets!" — note the capital letters, the !, and filler words like the and were. That's the mess we're cleaning up.
  2. Step 1 · normalize. text.lower() makes everything lowercase, then the "".join(c for c in ... if c.isalnum() or c.isspace()) keeps only letters, digits, and spaces — dropping the !. Result: one clean lowercase string.
  3. Step 2 · tokenize. norm.split() breaks that string into a list of words wherever there's a space. The comment shows the result — 8 word tokens, including two copies of the.
  4. Step 3 · remove stop-words. STOP is a set of filler words. The line [t for t in tokens if t not in STOP] keeps only words not in that set, dropping the, were, and through. Down to 4 meaningful words.
  5. Step 4 · stem / lemmatize. These last lines are comments (explanation, not runnable code) because doing it properly needs a library. Stemming crudely chops endings (running → run); lemmatizing uses a dictionary to find the true base (better → good). Stemming is fast but sometimes wrong; lemmatizing is accurate but slower.

What the output means: After steps 1–3 the sentence "The Runners were running quickly through the streets!" becomes the token list ['runners','running','quickly','streets'] — lowercased, de-punctuated, and stripped of filler. Step 4 would then reduce running to its root run.

Try this: Add a word to the STOP set — say "quickly" — and predict how content changes before you run it. This is exactly the kind of aggressive cleaning the warning box later tells you not to do before sending text to an LLM.

StepWhat & why
NormalizeLowercase, strip punctuation/accents so "Run", "run", "run!" match
TokenizeSplit into units (words). The hard part — see the note below
Stop-wordsDrop high-frequency low-meaning words ("the", "is") for count-based models
StemmingCrudely chop suffixes to a root ("running"→"run") — fast, sometimes wrong
LemmatizationUse grammar/dictionary to find the true base ("better"→"good") — accurate, slower

Tokenization: then vs now intermediate

"Just split on spaces" is one of those ideas that sounds finished until you poke it once. Split "don't" on spaces and you get one token — but is that right, or is it hiding a "do" and a "not"? Split "New York" and you get two tokens for one place. Now try a URL, an emoji, a hashtag, or a sentence of Mandarin, which is written with no spaces at all — and the whole "split on spaces" plan collapses. This is the quiet trap of tokenization: it's the very first thing you do to text and the easiest to get subtly wrong, and getting it wrong poisons everything downstream. Every era of NLP had to answer "what is one unit of text?", and the answer kept changing.

Classic NLP answered it with rules — hand-written tokenizers stuffed with special cases for contractions, hyphens, abbreviations, and named entities. It worked, brittly, and it never fully solved the deepest problem: the unknown word. If your tokenizer's vocabulary was built from the training data and a user typed "supercalifragilistic" or a brand-new product name, the model had literally no slot for it — it became a useless "unknown" token, and meaning was lost. Think of it like a printing press with a fixed drawer of whole-word stamps: the day someone needs a word you don't own a stamp for, you're stuck.

Modern LLMs escape the trap with a genuinely clever move: build the units from data, and make them smaller than words. This is subword tokenization (BPE), and the analogy is switching from whole-word stamps to an alphabet of common fragments — token, ization, ing, ##s. With a fixed, modest set of fragments you can spell any word, including ones you've never seen, by gluing pieces together. No more "unknown" cliff. But this efficiency is why several LLM behaviours that puzzle beginners suddenly make sense: you're billed per token (fragment), not per word, so verbose text costs more; the model sometimes miscounts the letters in a word because it never saw the letters, only the chunks; and "token ≠ word" is why your 1,000-word prompt might be 1,300 tokens. The old tokenization problem didn't disappear — BPE just answered it in a way that scales. Splitting text into units sounds trivial and isn't — is "don't" one token or two? What about "New York", URLs, emoji, Chinese (no spaces)? Classic NLP used word-tokenizers with lots of rules. Modern LLMs use subword tokenization (BPE), which sidesteps most of it — and that shift explains real LLM behavior.

Why LLMs see subwords, not words or lettersAn LLM tokenizes "tokenization" into pieces like token+ization, not the whole word and not individual letters. This subword scheme (BPE — built from scratch in A5) handles any word (even unseen ones) with a fixed vocabulary. It's why you're billed per token (Ch 2/C1), why models sometimes miscount letters in a word, and why token ≠ word. The classic tokenization problem didn't vanish — BPE solved it more generally.
Already covered — go deep in A5Tokenization, BPE from scratch, Unicode/encoding, and token counting get full hands-on treatment in A5 (Strings, tokens & parsing). Here you just need: classic NLP split into words with rules; LLMs split into subwords learned from data — a cleaner, more general answer to the same old problem.

What still matters vs what LLMs replaced intermediate

This is the section that stops you from looking like someone who learned NLP from a blog post in 2015. When LLMs arrived, two opposite mistakes appeared, and both are expensive. The first is the museum-keeper who lovingly runs the full classic pipeline — lowercase, de-punctuate, strip stop-words, stem — on text before feeding it to an LLM, quietly destroying the exact signals the model was built to read. The second is the demolition crew who declares everything before transformers obsolete and reaches for a frontier model to check whether a string matches ABC-12345 — paying LLM prices and latency for a job a one-line regex does perfectly. This section is the map that keeps you out of both ditches.

The clean way to think about it: ask of each classic step, "does the LLM already do this better, or is this still my job?" Some steps the model absorbed — it handles morphology internally, so stemming is pointless; it uses learned subword tokens, so your hand-written tokenizer is dead weight. Those, you delete. But other steps live outside the model and are as important as ever: cleaning genuine garbage (mojibake, boilerplate, duplicated headers) still follows garbage-in-garbage-out, because a confused input confuses even a brilliant model; and understanding tokens and framing the task aren't preprocessing at all — they're how you control cost, fit the context window, chunk documents for RAG, and design your evals. The table below sorts every classic step into "the model handles this now" versus "still on you," and the distinction is worth memorising because it's a question you'll answer on the job constantly.

The analogy that sticks: the LLM is an expert reader, not a document scanner. You wouldn't hand an expert a photocopy with every third word blacked out and the capital letters removed and expect a good summary — yet that's what over-preprocessing does. You would hand them a clean, readable original. So your job shifts from "grind the text into uniform mush" to "hand over faithful, natural text and spend your energy on framing, tokens, and evals instead." Knowing exactly which old habit to drop and which to keep is the difference between fighting the model and working with it. LLMs made much of the classic pipeline optional — but not all of it. Knowing the difference saves you both from re-inventing what the model already does and from throwing away tools that are still the right choice.

Classic stepStatus with LLMs
Manual stop-word removal, stemmingMostly gone — the model handles morphology; don't strip text before sending it
Word tokenization rulesReplaced by learned subword (BPE) tokenizers
Text normalization / cleaningStill matters — garbage in, garbage out; clean data before RAG/prompts (B1)
Understanding tokensEssential — drives cost, context limits, chunking (Ch 3, C1)
Task framing (classify/extract/etc.)Essential — it's how you structure prompts & evals (Ch 2, Ch 5)
Don't pre-process text to death before an LLMA common mistake from a classical-NLP habit: lowercasing, stripping punctuation, and removing stop-words before sending text to an LLM. Don't — the model uses capitalization, punctuation, and function words for meaning. Aggressive classic preprocessing destroys signal the LLM wants. Clean genuine noise (encoding errors, boilerplate), but otherwise let the model see natural text.

When classic NLP still wins advanced

Here's a scenario that separates engineers from enthusiasts. You're asked to flag every support message containing an order number of the form ABC-12345. It's tempting to reach for the LLM — it's what you've been learning — but pause and do the arithmetic: at ten million messages a day, an API call each is a fortune, adds hundreds of milliseconds of latency per message, and gives a probabilistic answer to a question that has one exact right answer. A regex does the same job in microseconds, for free, deterministically, and you can prove to an auditor exactly why it fired. Using the LLM here isn't sophisticated — it's a rookie move that torches budget on a solved problem. This section is about the judgment that avoids it.

The principle is match the tool to the shape of the task, not to what's fashionable — the same discipline as choosing a model tier (C1) or deciding build-vs-adopt (I1). And the shape that tells you "classic wins" is recognizable: the task is a crisp, enumerable pattern (a fixed format, an exact keyword, a rule you could write down), or a hard non-functional constraint dominates — a brutal latency budget, a tiny per-item cost target at massive volume, or a legal need for full determinism and explainability. In all of those, a regex, a lookup table, or a small linear model isn't the compromise; it's the better engineering. The LLM's expensive superpower — flexible understanding of fuzzy, open-ended meaning — is wasted on a problem that was never fuzzy.

The mistake to guard against is the mirror image, too, so hold both. Flip the same order-number task to "flag frustrated customers" and the whole calculus inverts: frustration has no regex, no enumerable pattern, no fixed spelling — it's exactly the subtle, contextual judgment the LLM was built for, and now a rule-based approach is the naive choice. The skill isn't loyalty to either camp; it's reading which situation you're in. The rule of thumb worth carrying: if you can write the pattern down, write the pattern down; if you can only describe it by example and nuance, that's where the LLM earns its cost. The table below lines up the two columns so the tell-tale signs sit side by side. The LLM isn't always the answer. For some jobs, a classic technique is faster, cheaper, and perfectly adequate — the same "match the tool to the task" judgment as model tiering (C1) and build-vs-adopt (I1).

Reach for classic NLP when…Reach for an LLM when…
Simple keyword search / exact matchMeaning-based search (embeddings, Ch 3)
High-volume, latency-critical, tiny budgetThe task needs real language understanding
A well-defined pattern (regex, rules)Ambiguity, nuance, or generation is involved
You need full determinism & explainabilityFlexibility across varied inputs matters more

Common pitfalls advanced

Every mistake in this table has the same root cause, and naming it once saves you from all five. The root cause is applying a habit from the wrong era — treating an LLM as if it were a fragile 1990s counting model, or treating a regex-simple job as if it needed a frontier brain. Notice that these aren't exotic bugs; they're the comfortable, plausible-sounding defaults that a smart person reaches for precisely because they worked in a neighbouring context. That's what makes them pitfalls rather than obvious errors: each one feels reasonable in the moment.

Read the list as a quick pre-flight check you run whenever you touch text. Two of the rows — over-preprocessing and "the model reads letters/words" — come straight from mistaking the transformer for a bag-of-words model; the middle row comes from forgetting that classic tools still exist; and the last two come from underrating the token concept and the classic-NLP toolkit you just learned. If you internalised the earlier sections, none of these should surprise you — the value here is having them in one place so you can catch yourself before the mistake ships, not in the post-mortem. The fix column is the corrected instinct to build in its place.

PitfallFix
Over-preprocessing text before an LLMSend natural text; clean only real noise
Thinking LLMs "read letters/words"They read subword tokens (A5); token ≠ word
Using an LLM for a regex-simple taskClassic tools are cheaper & deterministic
Ignoring the token conceptIt drives cost, context limits, and chunking
Treating classic NLP as obsoleteIts ideas power LLMs; some tools still win

Exercises advanced

Exercise K1.1 — Run the pipeline

Context: The sharpest way to feel why you don't classically preprocess before an LLM is to run both pipelines on the same text and compare what each keeps.

Your task: Run normalize → tokenize → stopwords → stem on a paragraph, then tokenize the same text with an LLM (subword) tokenizer and compare the units.

Requirements:

  • Apply the full classic pipeline by hand or with a library
  • Tokenize the identical text with a subword LLM tokenizer
  • Note what the classic pipeline discards: case, punctuation, stopwords, word forms
  • Observe that the subword tokenizer preserves all of it

💡 Hint: The contrast is the lesson: classic preprocessing throws away signal an LLM would rather keep.

Show what to look for

The classic pipeline drops case, punctuation, and stop-words and mangles word forms — losing signal. The LLM tokenizer keeps it all as subword tokens. That contrast is exactly why you don't classically preprocess before an LLM.

Exercise K1.2 — Classify the task

Context: Naming the classic task family behind a real problem is what lets you reach for the cheapest tool that solves it.

Your task: For five real problems (route a ticket, pull invoice fields, summarize a doc, find similar articles, tag parts of speech), name the classic NLP task family and how an LLM would handle each.

Requirements:

  • Map each problem to its classic task family (classification, extraction, summarization, similarity/retrieval, sequence tagging)
  • Sketch how an LLM would approach the same task
  • Identify at least one task you would not hand to an LLM
  • Justify that hold-out on cost, latency, or determinism grounds

💡 Hint: The odd one out is usually the high-volume, deterministic task where a classic method is faster and cheaper.

Exercise K1.3 — Classic or LLM?

Context: The same request can flip from a regex job to an LLM job the moment the target shifts from a fixed pattern to a subjective judgement.

Your task: Argue regex vs LLM for flagging messages that contain an order number of form ABC-12345, then redo the argument for flagging frustrated messages.

Requirements:

  • For the fixed format, weigh cost, speed, and reliability of a regex
  • Recognize the structured pattern is a clear regex win
  • Show the decision flips once the target becomes subjective sentiment
  • Explain why fuzzy, contextual meaning is where the LLM earns its cost

💡 Hint: A crisp, enumerable pattern favours regex; open-ended emotional judgement is exactly what a rule cannot capture.

🪜 Practice ladder beginner → industry

Six graded exercises, easy to real-world. Try each before opening its solution.

Exercise 1 · Tokenize, lowercase, strip punctuationBeginner

Context: The classic NLP pipeline begins by turning messy text into clean units. The very first steps — lowercase, split, strip punctuation — decide what signal survives before any model sees the words.

Your task: Write simple_tokens(text) that lowercases, splits on whitespace, and strips surrounding punctuation, then run it on a messy sentence.

Requirements:

  • Lowercase the input before tokenizing
  • Split on whitespace rather than a blunt \W+ split
  • Strip only edge punctuation so a contraction like isn't stays intact
  • Drop any empty tokens from the result

💡 Hint: The choice to strip edge-only punctuation is the whole point — it keeps in-word apostrophes that a naive split would shred.

Show solution

Pure stdlib; no NLTK needed for the basics. Runnable:

import re

def simple_tokens(text):
    text = text.lower()
    raw = text.split()
    toks = [re.sub(r"^[^\w]+|[^\w]+$", "", w) for w in raw]  # strip edge punct
    return [t for t in toks if t]

print(simple_tokens("The QUICK, brown fox! Isn't it fast?"))
# ['the', 'quick', 'brown', 'fox', "isn't", 'it', 'fast']

Note the pipeline choice: stripping only edge punctuation keeps isn't intact, unlike a blunt re.split(r"\W+").

Exercise 2 · Stopwords + a frequency countIntermediate

Context: Stopword removal plus frequency counting is the bones of every classic NLP feature: strip the high-frequency, low-signal words and let the salient terms rise to the top.

Your task: Extend the tokenizer to remove a small stopword set, then count the remaining token frequencies and report the most common content words.

Requirements:

  • Filter tokens against a small stopword set
  • Count frequencies with something like collections.Counter
  • Report the top few content terms (e.g. most_common)
  • Show the stopwords are gone and the signal words dominate

💡 Hint: Removing stopwords before counting is what makes the salient terms — not the/a/is — win the frequency race.

Show solution

Use collections.Counter over filtered tokens. Runnable:

import re
from collections import Counter

STOP = {"the", "a", "an", "is", "it", "in", "of", "to", "and"}

def tokens(text):
    return [re.sub(r"^[^\w]+|[^\w]+$", "", w) for w in text.lower().split()]

def content_freqs(text):
    toks = [t for t in tokens(text) if t and t not in STOP]
    return Counter(toks)

doc = "The cat sat on the mat. A cat is a cat, and the mat is warm."
print(content_freqs(doc).most_common(3))
# [('cat', 3), ('mat', 2), ('sat', 1)] -- stopwords gone, signal kept

Removing stopwords is a classic-NLP move: it strips high-frequency, low-signal words before counting so the salient terms dominate.

Exercise 3 · Stemming with a tiny suffix stripperAdvanced

Context: Stemming collapses inflected forms (running/runs/runner) toward one root so they share a single feature — a crude but effective way to shrink a bag-of-words vocabulary.

Your task: Implement a small rule-based (suffix-stripping) stemmer and show it merges related word forms onto a shared stem.

Requirements:

  • Strip common suffixes, trying the longest suffix first
  • Guard against over-stemming very short stems (keep a minimum stem length)
  • Handle a doubled-consonant case so running → run
  • Demonstrate that inflections of one word collapse together

💡 Hint: This is a miniature Porter-style stripper; order the suffix rules longest-first so ational is tried before s.

Show solution

A miniature Porter-style suffix stripper — order matters (longest suffix first). Runnable:

def stem(word):
    for suf in ("ational", "ization", "ingly", "edly", "ing", "ers", "er", "ed", "es", "s"):
        if word.endswith(suf) and len(word) - len(suf) >= 3:
            base = word[:-len(suf)]
            if suf in ("ing", "ed") and base and base[-1] == base[-2:-1]:
                base = base[:-1]        # runn -> run (undo doubled consonant)
            return base
    return word

for w in ["running", "runs", "runner", "jumped", "jumping", "cats"]:
    print(f"{w:>8} -> {stem(w)}")
# running/runs/runner -> run(n); jumped/jumping -> jump; cats -> cat

Stemming is crude but effective for classic bag-of-words: it shrinks the vocabulary so inflected forms share one feature. Real stemmers (Porter/Snowball) live in NLTK — labeled needs-libs.

Exercise 4 · When classic NLP still wins: a routerExpert

Context: The lesson's core judgement call: classic NLP beats an LLM when you need speed, determinism, cost control, or interpretability. Encoding that as a router makes the trade-off explicit.

Your task: Write pick_method(...) that chooses classic NLP vs an LLM from task constraints — latency budget, need for explanation, daily volume, and whether the meaning is subtle.

Requirements:

  • Subtle / open-ended semantics pushes the choice toward the LLM
  • A hard latency budget, auditability, or huge volume favours classic NLP
  • Return both the recommended method and the reasons that drove it
  • When no constraint dominates, suggest prototyping both

💡 Hint: Let the constraint profile decide: reach for classic NLP when determinism, latency, cost, or interpretability dominate; the LLM earns its keep only on subtle meaning.

Show solution

Match the constraint profile to the method the lesson recommends. Runnable:

def pick_method(latency_ms_budget, needs_explanation, volume_per_day, subtle_semantics):
    reasons = []
    if latency_ms_budget < 20:      reasons.append("hard latency budget")
    if needs_explanation:           reasons.append("must show why (auditable)")
    if volume_per_day > 5_000_000:  reasons.append("cost at scale")
    if subtle_semantics:
        return "LLM (subtle meaning / open-ended)", []
    if reasons:
        return "classic NLP (regex/TF-IDF/linear model)", reasons
    return "either -- prototype both", []

print(pick_method(10, True, 10_000_000, subtle_semantics=False))
print(pick_method(500, False, 1000, subtle_semantics=True))

The lesson's point: reach for classic NLP when determinism, latency, cost, or interpretability dominate — LLMs earn their keep on subtle, open-ended meaning.

Exercise 5 · Language is hard: ambiguity detectorProfessional

Context: Language is hard largely because of ambiguity — a single spelling can carry several senses. A toy detector over a sense dictionary makes that lexical ambiguity visible.

Your task: Build a detector that flags words carrying more than one sense from a small word→senses map, and report the ambiguous tokens in a sentence.

Requirements:

  • Model senses as a word→list-of-senses dictionary
  • Flag a token only when it maps to more than one sense
  • Normalize tokens (case, trailing punctuation) before lookup
  • Report each ambiguous word alongside its competing senses

💡 Hint: This is why bag-of-words plateaus: bank is one feature but two meanings, and only context can disambiguate it.

Show solution

Model ambiguity as a word→senses map; flag tokens with >1 sense. Runnable:

SENSES = {
    "bank":  ["riverside", "financial institution"],
    "bat":   ["animal", "sports equipment"],
    "spring":["season", "coil", "water source"],
}

def ambiguities(sentence):
    out = {}
    for w in sentence.lower().split():
        w = w.strip(".,!?")
        if w in SENSES and len(SENSES[w]) > 1:
            out[w] = SENSES[w]
    return out

s = "I sat on the bank near the spring watching a bat."
for w, senses in ambiguities(s).items():
    print(f"'{w}' is ambiguous: {senses}")

This is why bag-of-words alone plateaus: bank is one feature but two meanings — resolving it needs context, which is what embeddings and transformers add.

Exercise 6 · A reusable preprocessing componentIndustry scenario

Context: In production, preprocessing ships as one configurable component so training and serving stay in lockstep. Two divergent copies of this code is the classic cause of train/serve skew.

Your task: Package the pipeline as a small, testable class with toggles for lowercasing, stopwords, and stemming, plus a method that vectorizes text into token counts.

Requirements:

  • Expose lowercase / stopwords / stem as explicit constructor knobs
  • A single call runs the full tokenize → filter → stem pipeline
  • Provide a vectorize method returning a count vector (bag-of-words)
  • The class is self-contained and reusable as a shared library

💡 Hint: Keep every preprocessing decision behind one interface so the exact same transform runs at train time and at serve time.

Show solution

A small, testable class with explicit knobs — production-shaped. Runnable:

import re
from collections import Counter

class TextPipeline:
    def __init__(self, lower=True, stopwords=None, stem=False):
        self.lower = lower
        self.stop = set(stopwords or [])
        self.stem = stem
    def _stem(self, w):
        for suf in ("ing", "ed", "es", "s"):
            if self.stem and w.endswith(suf) and len(w) - len(suf) >= 3:
                return w[:-len(suf)]
        return w
    def __call__(self, text):
        if self.lower: text = text.lower()
        toks = [re.sub(r"^[^\w]+|[^\w]+$", "", w) for w in text.split()]
        toks = [self._stem(t) for t in toks if t and t not in self.stop]
        return toks
    def vectorize(self, text):
        return Counter(self(text))

pipe = TextPipeline(stopwords={"the", "a", "is"}, stem=True)
print(pipe("The cats are running and the dogs are jumping"))
print(pipe.vectorize("cats cats dogs").most_common())

Shipping preprocessing as one configurable component keeps training and serving in lockstep — the classic cause of train/serve skew is two divergent copies of this code.

✓ Checkpoint — you can move on when you can…

  • Define NLP and name its classic task families.
  • Explain why language is hard: ambiguity, context, variation, scale.
  • Run the normalize→tokenize→stop-words→stem/lemma pipeline.
  • Say which classic steps LLMs replaced and which still matter.
  • Place the rules→statistical→neural→transformer arc.
🏗️ Toward the capstoneEvery LLM feature in this course — the classifier (Ch 2), RAG (Ch 3), the capstone's log-summarization — is a classic NLP task the transformer now does from a prompt. Knowing the task family behind each is how you frame the prompt and design the eval (Ch 5). This module explains the machine you've been driving. Next, K2: how text becomes numbers a model can compute on. Next: text representation →

Knowledge check check yourself

✓ Knowledge check

The lesson warns against aggressive classic preprocessing (lowercasing, stripping punctuation, removing stop-words) before sending text to an LLM. Why is that a mistake?

Show answer
The model uses capitalization, punctuation, and function words for meaning, so that classic cleaning destroys signal the LLM wants. Clean only genuine noise (encoding errors, boilerplate); otherwise let the model see natural text.
✓ Knowledge check

Why do modern LLMs use subword (BPE) tokenization rather than the word-tokenization rules of classic NLP, and what LLM behaviors does that explain?

Show answer
Subword tokens built from data handle any word (even unseen ones) with a fixed vocabulary, sidestepping the rule-heavy classic tokenizer. It explains per-token billing, why models sometimes miscount letters in a word, and why token ≠ word.
© 2026 studybydoing.in · AI Engineering: Zero to Production · All rights reserved. · About · Privacy Policy · Terms · Contact
Educational content, provided as-is and without warranty. Code samples are examples — review, test, and adapt them before using in production. See the Terms of Use & Disclaimer. Use at your own risk.
© studybydoing.in