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

Transformers & the Path to Large Language Models

This is where the whole module lands. The transformer replaced recurrence with attention, unlocked parallel training at massive scale, and — through pretraining and fine-tuning — became the LLM you've used all course. Understand this chapter and claude-opus-4-8 stops being a black box.

⏱️ ~55 min🏛️ Foundations🎯 Intermediate→Advanced

Learning objectives

  • Explain self-attention intuitively — every token looking at every other.
  • Name the transformer's key parts: attention, multi-head, positional encoding, feed-forward, stacked layers.
  • Explain why the transformer scaled where RNNs couldn't.
  • Walk the path from architecture → pretraining → fine-tuning/alignment → the LLM.
  • Connect transformer facts to LLM behavior you already know (context, tokens, hallucination).
The payoff of the whole arcK1 (why language is hard) → K2 (text as vectors) → K3 (classifying it) → K4 (RNNs and their walls) all lead here. The transformer keeps the good ideas — next-token prediction (K4), embeddings (K2) — and replaces recurrence with attention, fixing both RNN walls at once. This chapter closes the loop back to C1: the models you call are transformers.

Self-attention: the core idea advanced

The RNN's flaw (K4) was forcing information through a single hidden state, one step at a time. Self-attention throws that out: for each token, the model looks directly at every other token in the sequence and decides how much each one matters for understanding this one. No chain, no bottleneck — every position can reach every other in a single step.

Start with the moment this idea clicks. Read the sentence "the animal didn't cross the street because it was tired," then ask yourself: what does "it" mean? You knew instantly — the animal. But notice what your brain just did. It didn't read left-to-right and hope to still be holding "animal" in working memory eight words later; it reached back across the whole sentence at once and connected "it" to the noun that fits. Every model before the transformer struggled with exactly this — the further apart two related words sat, the more likely the connection was lost. Self-attention is the mechanism that lets a machine do what you just did, without a chain to walk down.

Here is the plain idea, before any math. Imagine each word in the sentence gets to ask a question about the others and receive weighted answers back. "It" asks, roughly, "which earlier word am I standing in for?" — and every other word answers with a relevance score. "Animal" scores high; "street" and "tired" score low. The word's new, richer meaning is then a blend of all the others, mixed in proportion to those scores. Run that for every word simultaneously and each token walks away understanding its own place in the sentence's web of relationships. That is the entire trick, and it is why we call it self-attention: the sequence is paying attention to itself.

Why this is a leap and not just a tweak. The RNN of K4 was like reading a book through a mail-slot — one word slides past at a time, and to remember page one by page three hundred you must cram everything into a single fixed-size note (the hidden state). Two things go wrong: it is slow (you cannot start word three until you have finished word two), and it forgets (early words fade as the note is overwritten). Self-attention removes the mail-slot entirely. Every token sees every other token directly, in one parallel step — no chain to traverse, no bottleneck note to overflow. That single change fixes both of K4's walls at once, which is the drum this whole chapter beats.

The common misconception to kill early: attention is not the model "looking things up in a database" or running a search. It is a learned, weighted average. Nothing is fetched and copied; instead every value vector is blended in, with the most weight going to the most relevant. And the weights are not hand-coded rules — they are learned from data, which is why a well-trained model discovers on its own that pronouns tend to attend to nouns and verbs to their subjects. Hold that framing — a differentiable, learned blend of the whole context, computed for every token in parallel — and the diagram below will read like confirmation of something you already understand.

"the animal didn't cross the street because it was tired" it animal (0.7) street (0.1) tired (0.1) "it" attends strongly to "animal" — resolving the reference across distance Every token weighs every other. To represent "it", the model attends across the whole sentence and learns that "animal" matters most — resolving the pronoun regardless of distance. That's self-attention: a learned, weighted look at the entire context for each token. It directly solves K1's context problem and K4's long-range fade.
🗺️ How to read this diagram

This is the single most important picture in the whole chapter. It shows self-attention: how the model figures out what the word "it" refers to. The sentence is at the top; the coloured word it sits in the middle box, and arrows drop from it down to the other words it is "looking at".

  • The box in the middle is the word the model is currently trying to understand: it. Think of it as the word asking a question: "which earlier word do I stand for?"
  • Each arrow is it looking at another word and giving it a weight — a score for how relevant that word is. Every token looks at every other token; the arrows just show the ones that matter here.
  • The numbers in the lower boxes are those weights: animal (0.7) is much bigger than street (0.1) or tired (0.1). So the model decides it mostly means animal — even though animal sits far back in the sentence.
  • The whole row of weights adds up to about 1 — the model is splitting 100% of its attention across the words, spending most of it on the one that matters. That is the "weighted look at the whole sentence" the caption calls self-attention.

In short: each word gets to look directly at every other word and decide how much each one matters. No word is too far away to reach — which is exactly the long-distance problem the older models could not solve.

Query, key, value — the mechanism in one lineEach token produces a query ("what am I looking for?"), and every token offers a key ("what do I contain?") and a value ("what I'll contribute"). A token's new representation is a weighted sum of all values, weighted by how well its query matches each key (a dot product, then softmax). It's a differentiable, learned lookup — "for this token, retrieve and blend the relevant context." The math is dot products and softmax over vectors (A4).

The transformer's parts advanced

The transformer wraps self-attention with a few more components. You don't need to implement them, but naming them makes model behavior legible.

Here's why this section is worth more than a quick skim. Attention on its own is powerful but incomplete — like a brilliant engine with no chassis, no transmission, and no steering. Left alone, raw self-attention has two glaring gaps that would make a real language model useless. First, it is completely order-blind: to attention, "dog bites man" and "man bites dog" are the same bag of words attending to each other (the exact failure that sank bag-of-words in K2). Second, attention only mixes information between tokens — it doesn't do much thinking with the information once gathered. The extra parts in the table below are the chassis and transmission that turn the engine into a working car.

Read the table as a set of answers to "what would go wrong if this piece were missing?" Positional encoding answers the order-blindness gap — it stamps each token with a signal of where it sits so the model can tell first from last. Multi-head attention answers "one kind of relationship isn't enough": rather than a single attention pass, the model runs several in parallel, and each head is free to specialize — one might track grammatical subject-verb links, another might track which pronoun refers to which noun, another topic. It's the difference between one person reading a contract and a whole team of specialists reading it at once. Feed-forward layers are where per-token "processing" happens after attention has gathered the context — a small neural network applied to each position, and, perhaps surprisingly, where a large share of the model's factual knowledge is stored.

The last two rows are pure engineering, but they are the reason the whole thing is trainable. Residual connections add each layer's input back to its output — a shortcut that lets gradients flow cleanly through dozens of layers instead of vanishing (the very problem that capped RNN depth in K4). Normalization keeps the numbers in each layer in a sane range so training doesn't blow up. And then you stack the block — attention plus feed-forward, wrapped in residuals and norm — dozens or hundreds of times. Depth is what builds abstraction: early layers pick up surface patterns (word shapes, simple syntax), and deeper layers assemble those into meaning, intent, and long-range structure. The mental model to keep: one transformer block is a small, repeatable unit; a transformer is that unit stacked deep, and an LLM is that stack made enormous.

ComponentJob
Self-attentionEach token gathers relevant context from all others — the core
Multi-head attentionRun attention several times in parallel, each "head" learning a different kind of relationship (syntax, coreference, topic…)
Positional encodingAttention alone is order-blind (like bag-of-words, K2!) — positional info is added so the model knows token order
Feed-forward layersPer-token processing after attention mixes context — where much "knowledge" lives
Residuals + normalizationEngineering that makes very deep stacks trainable and stable
Stacked layersDozens+ of attention+FFN blocks — depth builds abstraction, shallow→deep meaning
Two parts worth rememberingMulti-head is why one attention layer captures many relationship types at once — different heads specialize. Positional encoding exists because pure attention has no sense of order (the exact order-blindness that made bag-of-words fail in K2) — so position is injected explicitly. These two, stacked deep, are most of what a transformer is.

Why the transformer won advanced

The 2017 paper's title — "Attention Is All You Need" — was the claim that you could drop recurrence entirely. It won because it solved both RNN walls (K4) simultaneously.

The stakes here are the entire modern AI industry, so it's worth understanding why this architecture and not another. Plenty of clever sequence models came before it, and many were more elaborate. The transformer won not because it was the smartest design but because it was the one you could scale — and in this field, scalability beats cleverness almost every time. The story is really about a single word: parallelism. An RNN is fundamentally sequential; its recurrence means step n literally cannot begin until step n−1 finishes. That is a death sentence on modern hardware, because a GPU is a machine with thousands of cores built to do thousands of things at the same time. Feeding it a strictly one-at-a-time computation is like hiring a thousand-person crew and letting only one work while the rest watch.

Self-attention has no such ordering constraint. Because every token attends to every other in one shot, you can compute all positions simultaneously — the whole sequence goes through the GPU at once, saturating those thousands of cores. That is the left-vs-right contrast in the diagram below: a chain you must walk versus a block you process in parallel. And the practical consequence is staggering: a training run that would have taken an RNN months could be done in days, which meant researchers could afford to train on far more data with far bigger models. Scale stopped being impossible and became merely expensive — and "expensive but possible" is exactly the regime where breakthroughs happen.

The second win rides along for free. Because any token reaches any other directly — one attention hop, not a hundred recurrent steps — the "long-range fade" that made RNNs forget the start of a paragraph by its end simply disappears. A word at position 500 looks straight back at position 1 with no signal loss. So the transformer didn't trade one weakness for another; it removed both of K4's walls at once. The lesson worth carrying forward: when the field bet everything on transformers, it wasn't betting on a uniquely brilliant idea — it was betting that parallel + direct-access would let scale do the heavy lifting. That bet paid off so completely that the next section's pipeline (architecture → pretrain → align) is really just "take this scalable engine and pour in as much data and compute as money allows."

RNN: sequential one step at a time Transformer: parallel all positions at once → uses GPUs fully + any token attends directly to any other → no long-range fade both RNN walls gone → train on trillions of tokens at scale Parallel + direct access = scale. Attention processes all positions at once (fixing K4's sequential wall → trains on massive GPU clusters) and lets any token reach any other directly (fixing K4's long-range wall). Removing both limits unlocked training on trillions of tokens — and scale is what turned a good architecture into a startlingly capable one.
🗺️ How to read this diagram

This diagram contrasts the old way (left, red) with the new way (right, green) of reading a sentence, and shows why the transformer won. Read it as two side-by-side pictures, then the two summary lines at the bottom.

  • Left, red — "RNN: sequential." The little boxes are words, joined by arrows in a chain. The arrows mean the model must process one word at a time, in order — it can't start word 3 until it finishes word 2. Slow, and it forgets things that happened far back in the chain.
  • Right, green — "Transformer: parallel." The boxes have no chain between them: the model reads all the words at once. That is what "all positions at once" means, and it is why a transformer can use a big GPU (many calculations at the same time) instead of waiting in line.
  • The first bottom line adds the other win: any token attends directly to any other — so a word at the end can look straight back at a word at the start, with no "long-range fade" (nothing gets forgotten along a chain).
  • The last blue line is the payoff: with both old limits gone, you can train the model on trillions of words. Speed + memory-across-distance = the ability to train at massive scale.

In short: the transformer beat the older model by fixing two problems at once — it reads everything in parallel (fast) and lets any word see any other word directly (no forgetting). That combination is what made huge LLMs possible.

The cost of attention: it's quadraticAttention's power has a price — every token attending to every other is O(n²) in sequence length. Double the context, quadruple the attention cost. This is why context windows are finite and long context is expensive (C1, Ch 3 chunking), and why efficient-attention research is active. The trait that gave transformers their power is also their main scaling constraint — and it directly explains a cost you've felt as an API user.

Lab K5.1 · Architecture → pretraining → the LLM expert

The transformer is an architecture. Turning it into an LLM takes two more stages. This pipeline is the answer to "where does an LLM come from?"

This is the section that clears up the most common confusion beginners carry about LLMs. People conflate three very different things — the architecture, the trained-on-everything base model, and the helpful assistant you chat with — and treat them as one magical object. They are not. They are three distinct stages, built one on top of the next, and separating them explains almost every "why does it do that?" question you will ever ask. Think of it like a car factory: first you design the engine (the architecture), then you build and run it in raw form (pretraining), then you tune it, add the interior, and make it pleasant to drive (alignment). Each stage produces something real but unfinished until the last.

Stage one, the architecture, is everything you learned above — attention, multi-head, positional encoding, stacked blocks. On its own it is an empty engine: the wiring is there, but the millions of weights are random, so it knows nothing. Stage two, pretraining, is where all the raw capability comes from, and the surprising part is how simple the task is. The model plays one game, trillions of times: given the text so far, predict the next token — the exact next-token objective from K4, just run at unimaginable scale. Nobody labels the data; the text is its own answer key (the next word is always sitting right there). To get good at that one game across the whole internet, the model is forced to absorb grammar, facts, styles, reasoning patterns, even a rough world-model — because all of those help it guess the next word. The output is a base model: fluent, knowledgeable, and completely unhelpful as an assistant, because it only knows how to continue text, not to answer you.

Stage three, alignment, is what turns that raw predictor into Claude. Two things happen: instruction tuning shows it thousands of examples of "a request, and a good response," teaching it to treat your input as something to answer rather than continue; then preference-based training (RLHF, and Anthropic's Constitutional-AI-style methods) shapes it toward being helpful, honest, and harmless (C1). The subtle but crucial point — and a great myth-buster — is that alignment mostly surfaces and steers capability that pretraining already created; it rarely teaches new knowledge. That's why the assistant's personality is a thin, changeable layer over a vast base, and why the same base model can be aligned into very different-feeling products. Keep the three stages separate in your head and the whole rest of the chapter — base capability vs. assistant behavior, hallucination vs. persona — falls neatly into place.

Transformerarchitecture Pretrainpredict next token Fine-tune /align (RLHF) LLMClaude base capability from scale · then shaped to be helpful, honest, harmless (C1) Three stages to an LLM. Start with the transformer architecture; pretrain it on enormous text with the K4 next-token objective (this is where raw capability comes from — it's just prediction at scale); then fine-tune & align (instruction tuning, RLHF, and Anthropic's Constitutional-AI-style methods) to make it follow instructions and be helpful/honest/harmless (C1). The result is the model you call.
🗺️ How to read this diagram

This is a pipeline: read it left to right, following the arrows. It answers "where does an LLM like Claude actually come from?" — the transformer you just learned about is only the first box; two more stages turn it into a helpful assistant.

  • Box 1 — "Transformer architecture." The blueprint from this chapter — attention, stacked layers. On its own it knows nothing yet; it's an empty engine.
  • Arrow, then Box 2 — "Pretrain: predict next token." The engine is fed enormous amounts of text and practises one game over and over: guess the next word. Doing this at huge scale is where its raw knowledge and fluency come from.
  • Arrow, then Box 3 — "Fine-tune / align (RLHF)." The fluent-but-raw model is then trained on examples and human/AI feedback to follow instructions and be helpful, honest, and harmless — turning a text-predictor into an assistant.
  • Arrow, then Box 4 — "LLM · Claude." The finished product you actually call. The bottom caption sums the flow up: capability comes from scale, then it's shaped to be helpful.

In short: an LLM is built in three steps — build the transformer, pretrain it to predict the next word on massive text, then align it to be a helpful assistant. Each arrow is one step forward in that recipe.

StageWhat happensProduces
PretrainingNext-token prediction (K4) on trillions of tokens of textA "base model" — fluent, knowledgeable, but not a helpful assistant
Instruction tuningFine-tune on examples of following instructionsA model that responds to requests, not just continues text
Alignment (RLHF/CAI)Train on human/AI preferences toward helpful, honest, harmlessThe assistant behavior you interact with (C1)
"Large" is doing real work in the nameThe transformer architecture is a decade old; what makes an LLM is scale — billions/trillions of parameters, trained on internet-scale text with massive compute. Capabilities like in-context learning and multi-step reasoning emerged from scaling the same next-token objective, not from new architecture. That's the empirical bet the whole field made: bigger transformer + more data + more compute → qualitatively more capable. It's why C1's model tiers exist and why they cost what they do.

Encoder, decoder, both expert

The original transformer had an encoder and a decoder (for translation, K4). Different LLM families use different slices — worth knowing because it explains what a model is good at.

This is the section that stops you from reaching for the wrong tool. A recurring beginner mistake is assuming "transformer" means "chatbot like Claude" — so people try to make a chat model produce search embeddings, or expect an embedding model to hold a conversation, and are baffled when it goes badly. The fix is realizing the transformer is a Lego brick, not a finished toy: the same self-attention block can be assembled three different ways, and each assembly is genuinely good at a different job. Knowing which is which turns "why doesn't this model do what I want?" into an architecture-selection decision you can make on purpose.

The dividing line is one deceptively simple question: can a token look at the tokens that come after it, or only the ones before? An encoder reads bidirectionally — every token attends to the entire input, both left and right — which is perfect for understanding a fixed piece of text you already have in full. That's why encoder-only models (BERT-style) power the embedding and classification jobs behind RAG (K2, K3): to summarize a sentence into one meaning vector, it helps to see the whole sentence at once. A decoder reads causally — each token may only attend to what came before it, never ahead — because its job is to generate the next token, and peeking at the future would be cheating (during training the answer would leak in). That left-to-right constraint is exactly the K4 next-token setup, which is why every chat and coding LLM you use, Claude included, is decoder-only.

The third assembly, encoder-decoder, bolts both together: an encoder fully digests an input sequence, then a decoder generates a fresh output sequence while attending back to that digest. This shines when the output is a transformation of a specific input rather than an open-ended continuation — translation (read the French, write the English) and some summarization models being the classic cases. The reason generative LLMs mostly abandoned this two-piece design in favor of pure decoders is scale and simplicity: it turns out a big enough decoder-only model, trained on enough text, can learn to translate and summarize just by predicting the next token, so the extra encoder machinery wasn't worth the complexity. Same brick, three assemblies — and now the mismatch surprises disappear, because you can name what each shape was built to do.

TypeReads…Best atExample use
Encoder-onlyThe whole input at once (bidirectional)Understanding: classification, embeddingsBERT-style; the embedding models behind RAG (K2, Ch 3)
Decoder-onlyLeft-to-right, predicting next tokenGenerationThe GPT/Claude family — chat, code, the LLMs you use
Encoder-decoderEncode input, decode outputTransforming one sequence to anotherTranslation, some summarization models
Why generative LLMs are decoder-onlyThe chat/coding LLMs you use (Claude included) are decoder-only: they're built to predict the next token given everything so far — exactly the K4 objective. Encoder-only models like BERT are built to understand a fixed input, which is why they power embeddings and classifiers (K2/K3) rather than generation. Same transformer building block, different assembly for different jobs.

Transformer facts explain LLM behavior expert

The whole point of this module: knowing the machine explains the behavior you've worked with all course. Every LLM quirk traces to something here.

This is the section that earns the whole chapter its keep. Everything before it was mechanism; this is the payoff — the moment the machine stops being abstract and starts explaining things you have actually felt as a user of these models. The reason it matters so much is that mystery breeds superstition. Engineers who don't understand the transformer end up treating the LLM like a temperamental oracle — muttering incantations, adding "please" and "you are an expert," hoping. Engineers who do understand it reason about behavior mechanically and fix problems at the root. The table below is a Rosetta Stone between the two views: every column-two entry is a fact from this chapter; every column-one entry is a behavior you've bumped into.

Walk a couple of the rows to feel the pattern, because the pattern is the real lesson. Take the finite, costly context window: it's not an arbitrary product limit someone chose to annoy you — it falls straight out of attention being O(n²) in length, so doubling the input quadruples the work, and at some point that's simply too expensive to serve. Or take the biggest one, hallucination: the model was built and trained to do exactly one thing — predict a plausible next token — and a plausible continuation is not the same thing as a true one. The model has no built-in sense of "I don't actually know this"; it will confidently produce fluent text either way. That's not a bug you can prompt away; it's the defining trait of a next-token predictor, and it is why the course keeps insisting on grounding (RAG), evaluation, and guardrails.

The mental habit to build here — and to carry into every chapter that follows — is the "trace it to the machine" reflex. Whenever a model does something surprising, don't stop at "it's smart" or "it's dumb." Ask: which mechanism produced this? Prompt-order sensitivity? That's every token attending to every other, so rephrasing genuinely changes the whole representation. Few-shot examples working? That's attention letting the examples in your prompt directly steer the next-token prediction. The helpful, careful persona? That's the thin alignment layer (C1), not the base model. Once you can complete that chain from observed behavior back to a concrete transformer fact, the LLM stops being magic and becomes an engineering component with knowable failure modes — which is precisely the mindset the rest of this course is built to give you.

LLM behavior you knowTransformer explanation
Finite, costly context windowAttention is O(n²) in length — more tokens cost quadratically (C1, Ch 3)
Billed per token; sees subwordsInput is tokenized to subwords (A5), then embedded (K2); compute scales with token count
HallucinationIt's a next-token predictor (K4) — it generates plausible continuations, not verified facts (why RAG & citations, Ch 3)
In-context learning (few-shot)Attention lets examples in the prompt directly influence next-token prediction (Ch 2)
Sensitive to prompt wording/orderEvery token attends to every other; phrasing changes the whole context representation
Helpful/honest/harmless personaFrom alignment (RLHF/CAI), not the base model (C1)
The most important takeaway: it predicts, it doesn't verifyBecause an LLM is fundamentally a next-token predictor (K4) scaled up, it produces fluent, plausible text — which is not the same as true text. Fluency is not accuracy. This is the root cause of hallucination and exactly why the course insists on grounding (RAG, Ch 3), evaluation (Ch 5), guardrails (I5), and the safety gate on actions (L5). Understanding the transformer isn't academic — it's why every production discipline in this course exists.

Common pitfalls expert

PitfallFix
Thinking the transformer "understands" like a personIt's a scaled next-token predictor; capability ≠ comprehension
Assuming attention knows word orderIt's order-blind; positional encoding adds order
Believing bigger context is freeAttention is O(n²) — long context costs quadratically
Confusing pretraining with the assistant behaviorAlignment (RLHF/CAI) creates the helpful persona (C1)
Treating fluency as accuracyPrediction ≠ truth — ground & verify (Ch 3, Ch 5)
Thinking architecture alone made LLMsScale (data + compute + params) is the decisive factor

Exercises expert

Exercise K5.1 — Attention by intuition

Context: The Winograd-style sentence "The trophy didn't fit in the suitcase because it was too big" is the classic test of whether a model can resolve a pronoun using the whole sentence — the leap self-attention makes that n-grams cannot.

Your task: Explain what "it" refers to in that sentence, how the reference flips when "big" becomes "small", and why self-attention resolves it while an n-gram model (from K4) cannot.

Requirements:

  • State the referent for both the "big" and the "small" version
  • Explain how self-attention lets "it" weigh the whole sentence, adjective included
  • Explain why a fixed-window n-gram has no way to reach back to the nouns
  • Frame this as the concrete "context leap" the chapter is about

💡 Hint: Ask yourself what information the pronoun needs to see, and how far away in the sentence that information sits.

Show what to look for

"big" → it = trophy; "small" → it = suitcase. Attention lets "it" weigh the whole sentence including the adjective, resolving the reference. An n-gram sees only a tiny fixed window and has no way to reach back to "trophy"/"suitcase" or reason about the adjective. That's the context leap.

Exercise K5.2 — Explain the O(n²) cost

Context: Self-attention compares every token with every other token, so its cost grows with the square of the sequence length. That single fact explains a lot of the economics you have met elsewhere in the course.

Your task: In two sentences, explain to a peer why doubling the context window more than doubles attention cost, and connect it to pricier long-context API calls (C1) and to why RAG chunks documents (Ch 3).

Requirements:

  • Name the quadratic (O(n²)) relationship between length and cost explicitly
  • Show why 2× the tokens is ~4× the pairwise comparisons
  • Tie it to why long-context calls cost more
  • Tie it to why RAG splits documents into chunks instead of stuffing everything

💡 Hint: Count the pairwise interactions, not the tokens — that is where the squaring comes from.

Exercise K5.3 — Trace one behavior to the machine

Context: The whole point of this chapter is the "it's not magic" muscle: every LLM behaviour you have hit traces down to a mechanical fact about transformers.

Your task: Pick one LLM behaviour you have experienced in this course (hallucination, few-shot working, a prompt-order effect, or the token bill) and trace it all the way down to a transformer fact from this chapter.

Requirements:

  • Name a specific behaviour you actually observed
  • Connect it to a concrete mechanism — attention, softmax, context length, or position
  • Make the causal chain explicit, from mechanism to observed behaviour
  • Avoid hand-waving: the link should be mechanical, not "the model is smart"

💡 Hint: Prompt-order effects and the token bill both point straight at attention over a finite, quadratic context — good places to start.

🪜 Practice ladder beginner → industry

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

Exercise 1 · Softmax over attention scoresBeginner

Context: Attention weights are a probability distribution over tokens, and that distribution comes from a softmax over raw compatibility scores. Getting the softmax numerically right is the first brick in the whole transformer.

Your task: Implement a numerically stable softmax(scores) and confirm the returned weights form a valid probability distribution.

Requirements:

  • Subtract the max score before exponentiating so large inputs don't overflow
  • Normalize by the sum of exponentials so the output sums to 1.0
  • Work on a plain Python list of floats using only the stdlib
  • Demonstrate on a few scores and print that the weights sum to ~1.0
  • Handle negative and positive scores in the same call

💡 Hint: The stability trick is a single subtraction — shifting every score by the maximum leaves the softmax unchanged but keeps exp() in range.

Show solution

Subtract the max for stability, exponentiate, normalize. Runnable, stdlib:

import math

def softmax(scores):
    m = max(scores)
    exps = [math.exp(s - m) for s in scores]   # subtract max -> no overflow
    Z = sum(exps)
    return [e / Z for e in exps]

w = softmax([2.0, 1.0, 0.1, -1.0])
print([round(x, 3) for x in w])
print("sum =", round(sum(w), 6))   # 1.0 -- a valid probability distribution

Softmax turns raw compatibility scores into a distribution — the attention weights that decide how much each token contributes.

Exercise 2 · Scaled dot-product attention (one query)Intermediate

Context: Scaled dot-product attention is the core operation of every transformer: softmax(q·kᵀ / √d) · V. Building it for one query over a handful of key/value pairs makes the mechanism concrete.

Your task: Implement single-query attention that scores the query against each key, scales, softmaxes, and returns the weighted sum of the values.

Requirements:

  • Score each key by the dot product with the query
  • Divide every score by √d where d is the query dimension
  • Convert scores to weights with your stable softmax
  • Return both the attention weights and the blended output vector
  • Show that a query aligned with one key pulls the output toward that key's value

💡 Hint: The output is a convex combination of the value vectors — weight each value by its softmax score and sum component-wise.

Show solution

Score against each key, scale by √d, softmax, weight the values. Runnable:

import math

def softmax(scores):
    m = max(scores); exps = [math.exp(s - m) for s in scores]; Z = sum(exps)
    return [e / Z for e in exps]

def attention(q, keys, values):
    d = len(q)
    scores = [sum(qi * ki for qi, ki in zip(q, k)) / math.sqrt(d) for k in keys]
    w = softmax(scores)
    out = [0.0] * len(values[0])
    for wi, v in zip(w, values):
        for j in range(len(v)):
            out[j] += wi * v[j]
    return w, out

q = [1.0, 0.0]
keys   = [[1.0, 0.0], [0.0, 1.0], [1.0, 1.0]]
values = [[10.0, 0.0], [0.0, 10.0], [5.0, 5.0]]
w, out = attention(q, keys, values)
print("weights:", [round(x, 3) for x in w])
print("output :", [round(x, 3) for x in out])
# the query aligns with key 0 (and 2), so the output leans toward their values

The scaling by √d keeps scores from growing with dimension (which would saturate softmax) — a small but load-bearing correctness detail.

Exercise 3 · Why √d: score variance without scalingAdvanced

Context: The √d divisor in attention looks arbitrary until you watch score magnitude grow with dimension. That growth pushes softmax into saturation, which is exactly why the transformer paper added the scaling.

Your task: Show numerically that unscaled dot-product scores grow with dimension and drive softmax toward a one-hot distribution, while dividing by √d keeps it usable.

Requirements:

  • Generate random query/key vectors at several dimensions (e.g. 4, 64, 512)
  • Report the magnitude of the largest unscaled score at each dimension
  • Compare the peak softmax weight with and without the √d scaling
  • Make it reproducible with a fixed random seed
  • Draw the conclusion: score magnitude scales roughly with √d

💡 Hint: Watch the maximum softmax weight as d grows — unscaled it creeps toward 1.0 (saturation), scaled it stays moderate.

Show solution

Compare unscaled score magnitude at small vs large d, and softmax sharpness. Runnable:

import math, random

def softmax(scores):
    m = max(scores); e = [math.exp(s - m) for s in scores]; Z = sum(e)
    return [x / Z for x in e]

def demo(d, seed=0):
    rng = random.Random(seed)
    q = [rng.gauss(0, 1) for _ in range(d)]
    ks = [[rng.gauss(0, 1) for _ in range(d)] for _ in range(3)]
    raw = [sum(a*b for a, b in zip(q, k)) for k in ks]
    scaled = [s / math.sqrt(d) for s in raw]
    return max(abs(s) for s in raw), softmax(raw), softmax(scaled)

for d in (4, 64, 512):
    mag, un, sc = demo(d)
    print(f"d={d:>3}: max|score|={mag:7.2f}  "
          f"unscaled max-w={max(un):.3f}  scaled max-w={max(sc):.3f}")
# score magnitude grows ~sqrt(d); unscaled softmax saturates toward 1.0

Unscaled scores grow with √d, so softmax collapses onto one token and gradients vanish; dividing by √d keeps the distribution usable — exactly why the transformer paper added it.

Exercise 4 · Positional encoding: order without recurrenceExpert

Context: A transformer processes all tokens in parallel with no recurrence, so position must be injected explicitly. Sinusoidal encodings give the model a continuous, parameter-free notion of where each token sits.

Your task: Implement sinusoidal positional encoding for a position and model dimension, then show that nearby positions produce more similar vectors than distant ones.

Requirements:

  • Even dimensions use sine, odd dimensions use cosine
  • Frequencies are geometrically spaced across the dimensions
  • The function returns a vector of length d_model
  • Use cosine similarity to compare a few positions
  • Demonstrate that adjacent positions are more similar than far-apart ones

💡 Hint: The frequency for dimension i follows 1 / 10000**(2*(i//2)/d_model) — geometric spacing is what makes the position signal smooth and extrapolatable.

Show solution

Even dims use sin, odd dims cos, at geometrically spaced frequencies. Runnable:

import math

def positional_encoding(pos, d_model):
    pe = []
    for i in range(d_model):
        freq = 1.0 / (10000 ** (2 * (i // 2) / d_model))
        pe.append(math.sin(pos * freq) if i % 2 == 0 else math.cos(pos * freq))
    return pe

def cos_sim(a, b):
    dot = sum(x*y for x, y in zip(a, b))
    na = math.sqrt(sum(x*x for x in a)); nb = math.sqrt(sum(y*y for y in b))
    return dot / (na * nb)

d = 16
p0, p1, p10 = (positional_encoding(p, d) for p in (0, 1, 10))
print(f"sim(pos0, pos1)  = {cos_sim(p0, p1):.3f}")
print(f"sim(pos0, pos10) = {cos_sim(p0, p10):.3f}")
# adjacent positions are more similar than distant ones -- a smooth position signal

Sinusoidal encodings give the model a continuous, extrapolatable notion of position without any learned parameters or recurrence.

Exercise 5 · Encoder, decoder, or both: architecture routerProfessional

Context: The lesson maps a task to a transformer family: encoder-only for understanding, decoder-only for generation, encoder-decoder for transduction. Knowing which to reach for is a routine architecture decision.

Your task: Build a selector that, given a task name, returns the right architecture family and a representative model.

Requirements:

  • Cover classification/embedding → encoder-only
  • Cover generation/chat → decoder-only
  • Cover translation/summarization → encoder-decoder
  • Return both the family and an example model per task
  • Fall back to a sensible default for an unknown task

💡 Hint: A dictionary keyed by task name is enough — the teaching point is the mapping itself, and that modern LLMs are overwhelmingly decoder-only.

Show solution

Route the task to the architecture family and a representative model. Runnable:

def architecture_for(task):
    table = {
        "classification":   ("encoder-only",    "BERT-style"),
        "embedding/search": ("encoder-only",    "sentence encoders"),
        "text generation":  ("decoder-only",    "GPT/Llama-style"),
        "chat":             ("decoder-only",    "GPT/Llama-style"),
        "translation":      ("encoder-decoder", "T5/BART-style"),
        "summarization":    ("encoder-decoder", "T5/BART-style"),
    }
    return table.get(task, ("decoder-only", "default for open-ended tasks"))

for t in ["classification", "text generation", "translation"]:
    fam, model = architecture_for(t)
    print(f"{t:>16} -> {fam:<16} ({model})")

Encoders read (bidirectional, great for understanding); decoders write (causal, great for generation); encoder-decoder transduces one sequence into another. Modern LLMs are overwhelmingly decoder-only.

Exercise 6 · Multi-head attention, assembledIndustry scenario

Context: Multi-head attention is the transformer's core mixing step: several attention heads run in parallel over different learned projections, and their outputs concatenate. Modeling it in stdlib shows why heads can attend differently.

Your task: Assemble multi-head attention: project the query/keys/values per head, run scaled dot-product attention in each head, and concatenate the head outputs.

Requirements:

  • Each head has its own projection applied to q, k, and v
  • Run your single-query scaled dot-product attention inside each head
  • Use different projections so the heads attend to different dimensions
  • Concatenate the per-head outputs into one vector
  • Print each head's weights to show they differ, then the concatenated output

💡 Hint: Give two toy heads projections that isolate different input dimensions — you'll see them place attention on different keys before you concatenate.

Show solution

Each head projects q/k/v, attends, and the outputs concatenate. Runnable (toy projections, stdlib):

import math

def softmax(s):
    m = max(s); e = [math.exp(x - m) for x in s]; Z = sum(e)
    return [x / Z for x in e]

def head_attention(q, keys, values):
    d = len(q)
    scores = [sum(a*b for a, b in zip(q, k)) / math.sqrt(d) for k in keys]
    w = softmax(scores)
    out = [sum(wi * v[j] for wi, v in zip(w, values)) for j in range(len(values[0]))]
    return w, out

def project(vec, W):
    return [sum(vec[i] * W[i][j] for i in range(len(vec))) for j in range(len(W[0]))]

# two heads with different projections -> they attend to different things
Wq = {0: [[1, 0], [0, 0]], 1: [[0, 0], [0, 1]]}   # head 0 sees dim0, head 1 sees dim1
q = [1.0, 1.0]
keys   = [[1.0, 0.0], [0.0, 1.0]]
values = [[9.0, 0.0], [0.0, 9.0]]
concat = []
for h in (0, 1):
    qh = project(q, Wq[h])
    kh = [project(k, Wq[h]) for k in keys]
    w, out = head_attention(qh, kh, values)
    print(f"head {h}: weights={[round(x,2) for x in w]}")
    concat += out
print("concat output:", [round(x, 2) for x in concat])

Multiple heads let the model attend to different relationships in parallel (syntax, coreference, position); concatenating their outputs is the transformer's core mixing step. A trained model learns the projections — labeled needs-libs/GPU.

✓ Checkpoint — you can move on when you can…

  • Explain self-attention as every token weighting every other.
  • Name the transformer's parts and what multi-head & positional encoding do.
  • Say why it scaled where RNNs couldn't (parallel + direct access).
  • Walk architecture → pretraining → alignment → the LLM.
  • Trace real LLM behavior (context cost, hallucination) to transformer facts.
🏗️ The arc closes — and the whole course connectsYou started this module asking "why do LLMs behave the way they do?" and ended at the machine itself: a transformer, pretrained on next-token prediction (K4), representing meaning as vectors (K2), aligned into an assistant (C1). Every discipline in the course now has a root cause — RAG and citations exist because prediction ≠ truth; context windows are finite because attention is O(n²); the safety gate exists because a fluent predictor will confidently do the wrong thing. The AI DevOps Engineer capstone rests on all of it. You've walked the full path from "what is NLP" to "why the model I deploy works" — that's the foundation under everything you build. See the capstone → · Back to course home →

Knowledge check check yourself

✓ Knowledge check

Explain self-attention using the query/key/value mechanism, and say which two prior-chapter problems it solves.

Show answer
Each token emits a query ('what am I looking for?'), every token offers a key ('what do I contain?') and a value; a token's new representation is a weighted sum of all values, weighted by query·key match (softmax). It's a learned lookup that lets any token reach any other directly — solving K1's context problem and K4's long-range fade.
✓ Knowledge check

Attention is O(n²) in sequence length. What practical LLM realities does this quadratic cost directly explain?

Show answer
Doubling the context quadruples attention cost, which is why context windows are finite and long-context calls are expensive (driving RAG chunking) and why efficient-attention research is active. The trait giving transformers their power is also their main scaling constraint.
© 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