GPT: Generative Pre-trained Transformers (GPT-1 to GPT-3)
Three papers, one while loop. Explained as a program, because the program is the whole idea.
Download gpt_from_scratch.py — everything below runs from it. NumPy, plus a tiny CPU PyTorch model for the training step.
I'm an AI Engineer. I've shipped models, done the MLOps, and I've called the GPT API more times than I've called my mother. I still couldn't have told you what the three papers actually claim, or why one title says "unsupervised multitask" and the next says "few-shot". Nobody told me they are one idea with three headlines, and that the idea is a loop I've written a hundred times.
Autocomplete that got so good it became an interface
You know this:
text = "the model "
while len(text) < 100:
text += most_likely_next_char(text) # feed the output back in
That's GPT. All three of them. The only thing that changed between 2018 and 2020 is what
lives inside most_likely_next_char — a stack of decoder blocks from the
attention page, causal mask on — and how many of them:
def most_likely_next_char(text):
ctx = encode(text)[-block_size:] # crop to the context window
logits, _ = gpt_forward(ctx, p) # (T, vocab): a guess for EVERY row
return decode([argmax(logits[-1])]) # keep only the last row
Type a question and it types the answer, because the answer is the likeliest continuation of a question. The model doesn't know it's answering. It's finishing your sentence.
| Term | Means | Programmer's analogy |
|---|---|---|
| Token | The unit of text. A character in my script, a word-piece in the real thing | The key type of the dict |
| Context window | The most tokens the model can see at once (block_size) | A ring buffer. Old tokens fall off the back |
| Causal mask | No position may read a later position | A read-lock on the future |
| Temperature | Divide the logits before softmax | A knob between argmax and dice |
| In-context learning | Worked examples in the prompt, weights frozen | Configuration by input, not by redeploy |
One idea, three headlines
Before GPT-1 you built one architecture per task and paid for a labelled dataset for each. GPT-1's pitch: the labels are free if the task is "predict the next token". GPT-2 dropped the fine-tuning. GPT-3 put the examples in the prompt. Same block, same loss, same loop, more of everything. Two details you'll meet in any GPT codebase: position embeddings are learned, not sine waves, and from GPT-2 on the LayerNorm sits at the input of each sub-layer. Neither is the idea.
| Paper | Size | Data | The headline claim |
|---|---|---|---|
| GPT-1, 2018 | 12 layers, dmodel 768, 117M params | BooksCorpus, ~7,000 unpublished books | Pretrain on next-token, then fine-tune per task by rewriting the task's inputs as one token sequence with delimiters. Pretraining transfers. |
| GPT-2, 2019 | 48 layers, dmodel 1600, 1.5B params | WebText, ~40 GB of pages linked from Reddit | No fine-tuning at all. Phrase the task as text ("TL;DR:") and read the continuation. Zero-shot from scale. |
| GPT-3, 2020 | 96 layers, dmodel 12288, 96 heads, 175B params | ~300B tokens: filtered Common Crawl, WebText2, books, Wikipedia | Put a few worked examples in the prompt and the frozen model uses them. In-context learning, no gradient updates. |
The one formula
GPT-1's Equation 1 with the sign flipped, so it's a loss to minimise rather than a likelihood to maximise. k is the context window. Everything else in three papers is this sum, made bigger. Read it as pipeline stages:
| Step | Shape | What it does |
|---|---|---|
tokens | (T,) | Integers. The text is its own label — nobody annotated anything |
tok_emb[tokens] + pos_emb[:T] | (T, d) | What each token is, plus where it sits. Both learned |
decoder_block × N | (T, d) | Causal attention (tokens talk, backwards only), then an MLP (each token thinks alone). Shape unchanged → stackable |
layer_norm @ W_out | (T, V) | Logits. One guess for every position, all at once — one matrix multiply scores T rows. The mask makes that legal: row t is scored on tokens[t+1] and never saw it |
softmax, row-wise | (T, V) | Percentages |
−log p[target] | (T,) | How surprised each row was by the real next token. 0 if certain and right |
mean | scalar | The loss. ln(V) for a uniform guess; the floor is the data's own randomness |
The loop: feed the output back in
Demo 1 runs the NumPy block with random weights. Nothing is learned; I just want you to see the shapes. Four tokens in, a (4, 28) matrix out — one guess per position — and the loop keeps only the last row:
Don't take my word for it. Here's the actual output:
logits : (4, 28) (one row PER position)
we keep only logits[-1] : (28,) (the next-token guess)
the loop, 5 steps, greedy:
step 0: T=4 tokens -> logits(4, 28) -> argmax of last row = 23 = 't'
step 1: T=5 tokens -> logits(5, 28) -> argmax of last row = 16 = 'l'
step 2: T=6 tokens -> logits(6, 28) -> argmax of last row = 0 = '\n'
step 3: T=7 tokens -> logits(7, 28) -> argmax of last row = 23 = 't'
step 4: T=8 tokens -> logits(8, 28) -> argmax of last row = 23 = 't'
result: 'the tl\ntt'
causal: logits for position 0 identical at ctx=4 and ctx=8? True
no mask: logits for position 0 identical at ctx=4 and ctx=8? False
The last two lines are a bonus the mask gives you for free. With it on, row 0's output never changes as the sequence grows, so a server can cache every earlier row instead of recomputing it. That's the KV cache. Without it, nothing can be cached.
The context window
The loop has no memory past block_size; GPT-3's is 2048 tokens. Demo 5 gets
it wrong both ways, on the trained model:
torch: 33 tokens into the block_size=32 trained model, no crop:
IndexError: index out of range in self (pos_emb has only 32 rows)
crop from the wrong end -- idx[:, :block_size] instead of idx[:, -block_size:]:
the model hides the answer again......................................
every token after the window filled is the same one? True
vs the right crop, same model, same prompt:
the model hides the answer again. the prompt hides the answer again. t
Forget to crop and the crash is the good outcome. Crop from the head and the model re-reads the same stale prefix forever, never sees what it just wrote, and prints one character until you kill it.
The shift: the label is the next character
Inputs are tokens[:-1], targets are tokens[1:]. That one-token
shift is the entire labelling budget of GPT-3:
text : autocomplete
inputs : autocomplet <- tokens[:-1]
targets : utocomplete <- tokens[1:], shifted one left
pairs : a->u u->t t->o o->c c->o o->m m->p p->l l->e e->t t->e
Now break it. Set targets = inputs and train 200 steps next to the honest version:
honest (targets = tokens[1:]), 200 steps:
step 1 loss 3.536
step 50 loss 1.446
step 200 loss 0.313
BROKEN (targets = inputs) , 200 steps:
step 1 loss 3.719
step 50 loss 0.016
step 200 loss 0.002 ← learned output[i] = input[i], nothing else
Train it, then let it talk
Demo 3 trains a two-layer, 105,728-parameter GPT on my laptop's CPU in about ten seconds. The corpus is 400 sentences I generate from four word lists — subject, verb, object, adjunct, each drawn uniformly — so the model has a grammar to learn and can't memorise. That also means I know the floor: four choices of six per sentence is 4·ln 6 nats over about 38 characters, roughly 0.19 per character.
step 1 loss 3.536
step 150 loss 0.363
step 300 loss 0.288
step 600 loss 0.305
a uniform guess scores ln(28) = 3.332; the corpus's own
randomness puts the floor near 0.191 (4 choices of 6 per sentence)
temperature 0.8, 100 chars:
the model feeds the future by hand. the model feeds the next token again
. the prompt feeds the answer at scale
Same loop as demo 1. The random weights printed 'the tl\ntt'; the trained
weights print sentences in the corpus's grammar. Nothing in the loop changed. It learned
the structure, not the meaning — there is no meaning in my corpus to learn, and that's the
honest version of what a language model does.
Temperature
One number between the logits and the dice. Divide the last row by it before softmax:
below 1 sharpens toward argmax, above 1 flattens toward uniform. I print the
entropy of the distribution each character was drawn from, in nats; ln 28 ≈ 3.33 means
"no idea":
temperature 0.2: mean entropy 0.096 nats
feeds the answer again. the mask feeds the answer at scale. the mask cop
ies the next token at scale.
temperature 2.0: mean entropy 1.100 nats
feeds its own oly.\nthefix by hagan. tby\naby hok fedsy . the feeds k\nt
he prefix slowly.\nfeefcashi rec
temperature 0 for 400 chars -- does it loop?
yes: the tail repeats every 35 chars: 'r again. the prompt hides the answe'
That last line is the "repetition" bug everyone hits. Greedy decoding is deterministic, so the moment it revisits a state it's in a cycle forever. Temperature 0 isn't "the most accurate setting"; it's a loop with no exit condition.
The mask is what makes the loop honest
The attention page said: remove the mask and you have BERT. Here's what that costs if you
wanted to generate. Demo 6 trains the identical model with causal=False,
same data, same 600 steps:
same model, same data, same steps, mask removed:
step 1 loss 3.540
step 600 loss 0.013 ← vs 0.305 with the mask on
loss per position: mean over positions 0..30, then position 31,
the one row that has no next column to peek at:
causal : 0.280 last position: 0.190
no mask: 0.001 last position: 0.296
short prompt 'the model ', temperature 0.8:
causal :
the model feeds the future by hand. the model feeds the next token again
. the prompt feeds the answer at scale
no mask:
the model ooooooomoooooooooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooooooooooooooooo
Without the mask, position i can attend to position i+1 — which is its target. So it copies, and the loss collapses to 0.001 at every row but the last, the only row with nothing to copy. At sampling time the last row is the only row that matters, so 31 of 32 rows of training went on a trick that never fires in production, and the one honest row got 1/32 of the gradient: 0.296 against the masked model's 0.190. Give it a full 32-character window so the last row is the honest one and it limps — run-on sentences, a made-up word; the script prints that too. Train/serve skew, controlled by one flag.
In-context learning: the prompt is the program
GPT-3's headline. Say it plainly, because interviewers will ask: in-context learning means the worked examples are in the prompt and the weights are frozen. No fine-tuning, no gradient step, nothing written to disk. The paper's few-shot format is exactly this shape — a few input→output pairs, then an input with no output:
english: cat -> french: chat
english: dog -> french: chien
english: house -> french: maison
english: cheese -> french:
that prompt is 118 tokens; the model sees one integer array:
[10, 18, 12, 16, 14, 22, 13, 4, 1, 8, 6, 23] ... [1, 11, 21, 10, 18, 8, 13, 4]
logits shape: (1, 32, 28) gradient updates: 0
Nothing in the code knows what an "example" is. The three worked pairs are just more
prefix; few-shot is longer input, full stop. GPT-3's claim is that at 175B parameters the
likeliest continuation is fromage, and that this improves with more examples
and more parameters — the results section is that curve, task by task. My model has
105,728 parameters and a six-noun vocabulary. It won't do this, and I'm not going to
pretend it does. The mechanism is identical. The capability is what scale bought. What
came after — making the autocomplete want to follow instructions rather than
merely continue them — is InstructGPT.
நான் ஒரு AI Engineer. Models ship செய்திருக்கிறேன், MLOps செய்திருக்கிறேன், என் அம்மாவைக் கூப்பிட்டதை விட GPT API-ஐ அதிக முறை கூப்பிட்டிருக்கிறேன். இருந்தும் அந்த மூன்று papers உண்மையில் என்ன சொல்கின்றன என்று என்னால் சொல்ல முடியாது; ஒரு தலைப்பு "unsupervised multitask" என்றும் அடுத்தது "few-shot" என்றும் ஏன் சொல்கிறது என்பதும் தெரியாது. அவை மூன்றும் ஒரே கருத்துக்கு மூன்று தலைப்புகள் என்றும், அந்தக் கருத்து நான் நூறு முறை எழுதியிருக்கும் ஒரு loop தான் என்றும் யாரும் சொல்லவில்லை.
ரொம்ப நன்றாகிவிட்டதால் interface ஆகிவிட்ட autocomplete
இது உங்களுக்குத் தெரியும்:
text = "the model "
while len(text) < 100:
text += most_likely_next_char(text) # output-ஐ மீண்டும் உள்ளே கொடு
இதுதான் GPT. மூன்றுமே இதுதான். 2018-க்கும் 2020-க்கும் இடையே மாறியது ஒன்றே
ஒன்று — most_likely_next_char-க்கு உள்ளே என்ன இருக்கிறது
(attention page-ன் decoder blocks அடுக்கு,
causal mask போட்டு), மற்றும் அவை எத்தனை:
def most_likely_next_char(text):
ctx = encode(text)[-block_size:] # context window அளவுக்கு வெட்டு
logits, _ = gpt_forward(ctx, p) # (T, vocab): ஒவ்வொரு row-க்கும் ஊகம்
return decode([argmax(logits[-1])]) # கடைசி row மட்டும் வைத்துக்கொள்
ஒரு கேள்வியை type செய்யுங்கள், அது விடையை type செய்யும் — ஏனெனில் ஒரு கேள்விக்குப் பிறகு வர வாய்ப்புள்ள தொடர்ச்சிதான் அந்த விடை. தான் பதில் சொல்கிறோம் என்று model-க்குத் தெரியாது. அது உங்கள் வாக்கியத்தை முடிக்கிறது.
GPT-nu solradhu oru while loop dhaan: adutha token-ai guess pannu, adhaye thirumba input-la kudu. Model badhil solradhu illa — unga sentence-ai mudikkiradhu, adhu dhaan ellame.
| சொல் | அர்த்தம் | Programmer உதாரணம் |
|---|---|---|
| Token | Text-ன் அலகு. என் script-ல் ஒரு எழுத்து, நிஜ model-ல் ஒரு word-piece | Dict-ன் key type |
| Context window | Model ஒரே நேரத்தில் பார்க்கக்கூடிய அதிகபட்ச tokens (block_size) | ஒரு ring buffer. பழைய tokens பின்னால் விழுந்துவிடும் |
| Causal mask | எந்த position-ம் தனக்குப் பின் வரும் position-ஐப் படிக்கக் கூடாது | எதிர்காலத்தின் மேல் ஒரு read-lock |
| Temperature | Softmax-க்கு முன் logits-ஐ வகுப்பது | argmax-க்கும் பகடைக்கும் இடையே ஒரு knob |
| In-context learning | Prompt-ல் செய்து காட்டிய உதாரணங்கள், weights உறைந்தபடி | Redeploy இல்லாமல், input மூலம் configuration |
ஒரே கருத்து, மூன்று தலைப்புகள்
GPT-1-க்கு முன் ஒவ்வொரு task-க்கும் ஒரு architecture கட்டி, ஒவ்வொன்றுக்கும் labelled dataset-க்குக் காசு கொடுக்க வேண்டியிருந்தது. GPT-1-ன் வாதம்: task "அடுத்த token-ஐக் கணி" என்றால் labels இலவசம். GPT-2 fine-tuning-ஐயே கைவிட்டது. GPT-3 உதாரணங்களை prompt-க்குள் வைத்தது. அதே block, அதே loss, அதே loop — எல்லாமே அதிகம். எந்த GPT codebase-லும் நீங்கள் சந்திக்கும் இரண்டு விவரங்கள்: position embeddings sine அலைகள் அல்ல, அவை கற்கப்படுபவை; GPT-2 முதல் LayerNorm ஒவ்வொரு sub-layer-ன் input-ல் அமர்கிறது. இரண்டுமே மையக் கருத்து அல்ல.
| Paper | அளவு | Data | தலைப்புச் செய்தி |
|---|---|---|---|
| GPT-1, 2018 | 12 layers, dmodel 768, 117M params | BooksCorpus, ~7,000 வெளியிடப்படாத புத்தகங்கள் | Next-token மேல் pretrain செய்து, பிறகு ஒவ்வொரு task-க்கும் fine-tune — task-ன் inputs-ஐ delimiters-உடன் ஒரே token sequence ஆக மாற்றி. Pretraining கடத்தப்படுகிறது. |
| GPT-2, 2019 | 48 layers, dmodel 1600, 1.5B params | WebText, Reddit-ல் link ஆன ~40 GB pages | Fine-tuning சுத்தமாக இல்லை. Task-ஐ text ஆகவே எழுது ("TL;DR:"), தொடர்ச்சியைப் படி. Scale-ல் இருந்து zero-shot. |
| GPT-3, 2020 | 96 layers, dmodel 12288, 96 heads, 175B params | ~300B tokens: வடிகட்டிய Common Crawl, WebText2, புத்தகங்கள், Wikipedia | Prompt-ல் சில செய்து காட்டிய உதாரணங்களை வையுங்கள், உறைந்த model அவற்றைப் பயன்படுத்தும். In-context learning, gradient updates இல்லை. |
அந்த ஒரு சூத்திரம்
GPT-1-ன் Equation 1, குறி மாற்றப்பட்டது — அதிகப்படுத்த வேண்டிய likelihood அல்ல, குறைக்க வேண்டிய loss. k என்பது context window. மூன்று papers-ல் இருக்கும் மற்ற எல்லாமே இந்தக் கூட்டல், பெரிதாக்கப்பட்டது. இதை pipeline stages ஆகப் படியுங்கள்:
| படி | Shape | என்ன செய்கிறது |
|---|---|---|
tokens | (T,) | Integers. Text-ஏ தன் label — யாரும் எதையும் annotate செய்யவில்லை |
tok_emb[tokens] + pos_emb[:T] | (T, d) | ஒவ்வொரு token என்ன, மேலும் அது எங்கே அமர்கிறது. இரண்டுமே கற்கப்படுபவை |
decoder_block × N | (T, d) | Causal attention (tokens பேசுகின்றன, பின்னோக்கி மட்டும்), பிறகு ஒரு MLP (ஒவ்வொரு token-ம் தனியே யோசிக்கிறது). Shape மாறவில்லை → அடுக்கலாம் |
layer_norm @ W_out | (T, V) | Logits. ஒவ்வொரு position-க்கும் ஒரு ஊகம், ஒரே நேரத்தில் — ஒரே matrix multiply T rows-ஐ score செய்கிறது. Mask அதைச் சட்டப்படி ஆக்குகிறது: row t, tokens[t+1] மேல் score செய்யப்படுகிறது, ஆனால் அதை ஒருபோதும் பார்த்ததில்லை |
softmax, row-wise | (T, V) | சதவீதங்கள் |
−log p[target] | (T,) | நிஜமான அடுத்த token-ஐப் பார்த்து ஒவ்வொரு row-ம் எவ்வளவு அதிர்ச்சி அடைந்தது. உறுதியாகவும் சரியாகவும் இருந்தால் 0 |
mean | scalar | Loss. சீரான ஊகத்திற்கு ln(V); தரை என்பது data-வின் சொந்த randomness |
Loop: output-ஐ மீண்டும் உள்ளே கொடு
Demo 1, random weights-உடன் NumPy block-ஐ ஓட்டுகிறது. எதுவும் கற்கப்படவில்லை; நீங்கள் shapes-ஐப் பார்க்க வேண்டும், அவ்வளவே. நான்கு tokens உள்ளே, ஒரு (4, 28) matrix வெளியே — ஒரு position-க்கு ஒரு ஊகம் — loop கடைசி row-ஐ மட்டும் வைத்துக்கொள்கிறது:
என் வார்த்தையை நம்ப வேண்டாம். இதோ நிஜமான output:
logits : (4, 28) (one row PER position)
we keep only logits[-1] : (28,) (the next-token guess)
the loop, 5 steps, greedy:
step 0: T=4 tokens -> logits(4, 28) -> argmax of last row = 23 = 't'
step 1: T=5 tokens -> logits(5, 28) -> argmax of last row = 16 = 'l'
step 2: T=6 tokens -> logits(6, 28) -> argmax of last row = 0 = '\n'
step 3: T=7 tokens -> logits(7, 28) -> argmax of last row = 23 = 't'
step 4: T=8 tokens -> logits(8, 28) -> argmax of last row = 23 = 't'
result: 'the tl\ntt'
causal: logits for position 0 identical at ctx=4 and ctx=8? True
no mask: logits for position 0 identical at ctx=4 and ctx=8? False
கடைசி இரண்டு வரிகள் mask இலவசமாகத் தரும் ஒரு போனஸ். அது இருந்தால், sequence வளர வளர row 0-ன் output ஒருபோதும் மாறாது — எனவே ஒரு server முந்தைய ஒவ்வொரு row-ஐயும் மீண்டும் கணக்கிடாமல் cache செய்யலாம். அதுதான் KV cache. Mask இல்லாமல் எதையும் cache செய்ய முடியாது.
Context window
block_size-க்கு அப்பால் loop-க்கு நினைவே இல்லை; GPT-3-ன் அளவு
2048 tokens. Demo 5, பயிற்சி பெற்ற model-ல் இதை இரண்டு வழிகளிலும் தவறாகச்
செய்கிறது:
torch: 33 tokens into the block_size=32 trained model, no crop:
IndexError: index out of range in self (pos_emb has only 32 rows)
crop from the wrong end -- idx[:, :block_size] instead of idx[:, -block_size:]:
the model hides the answer again......................................
every token after the window filled is the same one? True
vs the right crop, same model, same prompt:
the model hides the answer again. the prompt hides the answer again. t
வெட்ட மறந்தால், crash ஆவதுதான் நல்ல முடிவு. தலைப்பக்கத்திலிருந்து வெட்டினால், model அதே பழைய prefix-ஐயே எப்போதும் மீண்டும் படிக்கும், தான் இப்போது எழுதியதைப் பார்க்கவே பார்க்காது, நீங்கள் நிறுத்தும் வரை ஒரே எழுத்தை அச்சடிக்கும்.
அந்த shift: label என்பது அடுத்த எழுத்து
Inputs என்பது tokens[:-1], targets என்பது tokens[1:].
அந்த ஒரு token shift-தான் GPT-3-ன் முழு labelling budget:
text : autocomplete
inputs : autocomplet <- tokens[:-1]
targets : utocomplete <- tokens[1:], shifted one left
pairs : a->u u->t t->o o->c c->o o->m m->p p->l l->e e->t t->e
இப்போது அதை உடையுங்கள். targets = inputs என வைத்து, நேர்மையான
version-ஐ ஒட்டி 200 steps train செய்யுங்கள்:
honest (targets = tokens[1:]), 200 steps:
step 1 loss 3.536
step 50 loss 1.446
step 200 loss 0.313
BROKEN (targets = inputs) , 200 steps:
step 1 loss 3.719
step 50 loss 0.016
step 200 loss 0.002 ← learned output[i] = input[i], nothing else
Inputs tokens[:-1], targets tokens[1:] — antha oru token shift dhaan GPT-3-oda mothha labelling budget. Loss romba kammiya irundha, first shift-ai check pannu.
Train பண்ணு, பிறகு பேச விடு
Demo 3, என் laptop-ன் CPU-வில் இரண்டு layers, 105,728 parameters கொண்ட ஒரு GPT-ஐ சுமார் பத்து நொடிகளில் train செய்கிறது. Corpus என்பது நான்கு word lists-ல் இருந்து — subject, verb, object, adjunct, ஒவ்வொன்றும் சீராக எடுக்கப்பட்டு — நான் உருவாக்கும் 400 வாக்கியங்கள். அதனால் model-க்கு ஒரு grammar கற்க இருக்கிறது, மனப்பாடம் செய்ய முடியாது. அதன் பொருள் தரை எனக்குத் தெரியும்: ஒரு வாக்கியத்திற்கு ஆறில் நான்கு தேர்வுகள் என்பது சுமார் 38 எழுத்துகளுக்கு 4·ln 6 nats, அதாவது ஒரு எழுத்துக்கு ஏறக்குறைய 0.19.
step 1 loss 3.536
step 150 loss 0.363
step 300 loss 0.288
step 600 loss 0.305
a uniform guess scores ln(28) = 3.332; the corpus's own
randomness puts the floor near 0.191 (4 choices of 6 per sentence)
temperature 0.8, 100 chars:
the model feeds the future by hand. the model feeds the next token again
. the prompt feeds the answer at scale
Demo 1-ன் அதே loop. Random weights 'the tl\ntt' என்று
அச்சடித்தன; பயிற்சி பெற்ற weights corpus-ன் grammar-ல் வாக்கியங்களை
அச்சடிக்கின்றன. Loop-ல் எதுவும் மாறவில்லை. அது கட்டமைப்பைக் கற்றது, அர்த்தத்தை
அல்ல — என் corpus-ல் கற்க அர்த்தமே இல்லை, அதுதான் ஒரு language model என்ன
செய்கிறது என்பதன் நேர்மையான பதில்.
Temperature
Logits-க்கும் பகடைக்கும் இடையே ஒரே ஒரு எண். Softmax-க்கு முன் கடைசி row-ஐ
அதனால் வகுங்கள்: 1-க்குக் கீழே என்றால் argmax பக்கம் கூர்மையாகும்,
1-க்கு மேலே என்றால் சீரான பக்கம் தட்டையாகும். ஒவ்வொரு எழுத்தும் எந்த
distribution-ல் இருந்து எடுக்கப்பட்டதோ அதன் entropy-ஐ nats-ல் அச்சடிக்கிறேன்;
ln 28 ≈ 3.33 என்றால் "எதுவுமே தெரியவில்லை":
temperature 0.2: mean entropy 0.096 nats
feeds the answer again. the mask feeds the answer at scale. the mask cop
ies the next token at scale.
temperature 2.0: mean entropy 1.100 nats
feeds its own oly.\nthefix by hagan. tby\naby hok fedsy . the feeds k\nt
he prefix slowly.\nfeefcashi rec
temperature 0 for 400 chars -- does it loop?
yes: the tail repeats every 35 chars: 'r again. the prompt hides the answe'
அந்தக் கடைசி வரிதான் எல்லோரும் சந்திக்கும் "repetition" bug. Greedy decoding தீர்மானகரமானது (deterministic), எனவே அது ஒரு state-ஐ மீண்டும் அடைந்த கணமே என்றென்றும் ஒரு சுழற்சிக்குள் விழுந்துவிடும். Temperature 0 என்பது "மிகச் சரியான setting" அல்ல; அது exit condition இல்லாத ஒரு loop.
Temperature-nu solradhu logits-ai softmax-kku munnadi divide panradhu dhaan. 0 vechaa deterministic — oru state thirumba vandha, loop-la maatikkum, exit illa.
Loop-ஐ நேர்மையாக வைப்பது mask தான்
Attention page சொன்னது: mask-ஐ எடுத்துவிட்டால் அது BERT. நீங்கள் generate
செய்ய விரும்பினால் அது என்ன விலை என்பதை இதோ பாருங்கள். Demo 6, அதே model-ஐ
causal=False வைத்து, அதே data, அதே 600 steps train செய்கிறது:
same model, same data, same steps, mask removed:
step 1 loss 3.540
step 600 loss 0.013 ← vs 0.305 with the mask on
loss per position: mean over positions 0..30, then position 31,
the one row that has no next column to peek at:
causal : 0.280 last position: 0.190
no mask: 0.001 last position: 0.296
short prompt 'the model ', temperature 0.8:
causal :
the model feeds the future by hand. the model feeds the next token again
. the prompt feeds the answer at scale
no mask:
the model ooooooomoooooooooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooooooooooooooooo
Mask இல்லாமல், position i, position i+1-ஐப் பார்க்க முடியும் — அதுதான் அதன் target. எனவே அது நகலெடுக்கிறது, கடைசி row தவிர ஒவ்வொரு row-லும் loss 0.001-க்கு சரிகிறது; நகலெடுக்க எதுவும் இல்லாத ஒரே row அதுதான். Sampling நேரத்தில் கடைசி row மட்டுமே முக்கியம், எனவே 32-ல் 31 rows-ன் பயிற்சி production-ல் ஒருபோதும் வேலைக்கு வராத ஒரு தந்திரத்திற்குப் போய்விட்டது; நேர்மையான அந்த ஒரு row-க்கு gradient-ல் 1/32 மட்டுமே கிடைத்தது: masked model-ன் 0.190-க்கு எதிராக 0.296. கடைசி row நேர்மையானதாக இருக்க முழு 32-எழுத்து window கொடுங்கள் — அது தள்ளாடும்: முடிவில்லாத வாக்கியங்கள், ஒரு புனையப்பட்ட வார்த்தை; script அதையும் அச்சடிக்கிறது. ஒரே ஒரு flag-ஆல் கட்டுப்படுத்தப்படும் train/serve skew.
In-context learning: prompt தான் program
GPT-3-ன் தலைப்புச் செய்தி. நேராகச் சொல்லிவிடுகிறேன், ஏனெனில் interviewers கேட்பார்கள்: in-context learning என்றால் செய்து காட்டிய உதாரணங்கள் prompt-ல் இருக்கின்றன, weights உறைந்திருக்கின்றன. Fine-tuning இல்லை, gradient step இல்லை, disk-க்கு எதுவும் எழுதப்படவில்லை. Paper-ன் few-shot format சரியாக இதே வடிவம்தான் — சில input→output ஜோடிகள், பிறகு output இல்லாத ஒரு input:
english: cat -> french: chat
english: dog -> french: chien
english: house -> french: maison
english: cheese -> french:
that prompt is 118 tokens; the model sees one integer array:
[10, 18, 12, 16, 14, 22, 13, 4, 1, 8, 6, 23] ... [1, 11, 21, 10, 18, 8, 13, 4]
logits shape: (1, 32, 28) gradient updates: 0
"உதாரணம்" என்றால் என்ன என்று code-ல் எதற்கும் தெரியாது. அந்த மூன்று ஜோடிகளும்
இன்னும் கொஞ்சம் prefix, அவ்வளவே; few-shot என்பது நீளமான input, முற்றுப்புள்ளி.
175B parameters-ல் வாய்ப்புள்ள தொடர்ச்சி fromage என்பதும், அதிக
உதாரணங்கள் மற்றும் அதிக parameters-உடன் இது மேம்படுகிறது என்பதும்தான் GPT-3-ன்
கூற்று — results பகுதி என்பது task வாரியாக அந்த வளைவு. என் model-க்கு 105,728
parameters, ஆறு nouns கொண்ட vocabulary. இது அதைச் செய்யாது, செய்வது போல் நான்
நடிக்கப் போவதும் இல்லை. இயங்குமுறை ஒன்றுதான். திறமைதான் scale வாங்கிக்
கொடுத்தது. அதற்குப் பிறகு வந்தது — autocomplete-ஐ வெறுமனே தொடராமல்,
அறிவுறுத்தல்களைப் பின்பற்ற விரும்ப வைப்பது —
InstructGPT.
मैं एक AI Engineer हूँ। मैंने models ship किए हैं, MLOps किया है, और GPT API को अपनी माँ से भी ज़्यादा बार call किया है। फिर भी मैं आपको यह नहीं बता सकता था कि वे तीन papers असल में दावा क्या करते हैं, या एक title में "unsupervised multitask" और अगले में "few-shot" क्यों लिखा है। किसी ने नहीं बताया कि वे एक ही विचार की तीन सुर्ख़ियाँ हैं, और वह विचार एक loop है जो मैंने सौ बार लिखा है।
वह autocomplete जो इतना अच्छा हो गया कि interface बन गया
यह आप जानते हैं:
text = "the model "
while len(text) < 100:
text += most_likely_next_char(text) # output को वापस अंदर डालो
यही GPT है। तीनों। 2018 और 2020 के बीच सिर्फ़ इतना बदला कि
most_likely_next_char के अंदर क्या है —
attention page वाले decoder blocks का ढेर, causal
mask चालू — और वे कितने हैं:
def most_likely_next_char(text):
ctx = encode(text)[-block_size:] # context window तक काटो
logits, _ = gpt_forward(ctx, p) # (T, vocab): हर row के लिए एक अंदाज़ा
return decode([argmax(logits[-1])]) # सिर्फ़ आख़िरी row रखो
कोई सवाल type कीजिए और यह जवाब type कर देता है, क्योंकि जवाब ही सवाल की सबसे संभावित निरंतरता है। Model को पता नहीं कि वह जवाब दे रहा है। वह आपका वाक्य पूरा कर रहा है।
| शब्द | मतलब | Programmer की उपमा |
|---|---|---|
| Token | Text की इकाई। मेरी script में एक अक्षर, असली model में एक word-piece | Dict का key type |
| Context window | सबसे ज़्यादा कितने tokens model एक साथ देख सकता है (block_size) | एक ring buffer। पुराने tokens पीछे से गिर जाते हैं |
| Causal mask | कोई position बाद वाली position नहीं पढ़ सकती | भविष्य पर एक read-lock |
| Temperature | Softmax से पहले logits को भाग देना | argmax और पासे के बीच एक knob |
| In-context learning | Prompt में हल किए हुए उदाहरण, weights जमे हुए | Redeploy से नहीं, input से configuration |
एक विचार, तीन सुर्ख़ियाँ
GPT-1 से पहले आप हर task के लिए एक architecture बनाते थे और हर एक के लिए labelled dataset का ख़र्च उठाते थे। GPT-1 की बात: अगर task "अगला token बताओ" है तो labels मुफ़्त हैं। GPT-2 ने fine-tuning ही छोड़ दी। GPT-3 ने उदाहरण prompt में रख दिए। वही block, वही loss, वही loop, बस हर चीज़ ज़्यादा। दो बातें जो आपको किसी भी GPT codebase में मिलेंगी: position embeddings sine तरंगें नहीं, सीखे हुए होते हैं, और GPT-2 से आगे LayerNorm हर sub-layer के input पर बैठता है। इनमें से कोई भी मूल विचार नहीं है।
| Paper | आकार | Data | मुख्य दावा |
|---|---|---|---|
| GPT-1, 2018 | 12 layers, dmodel 768, 117M params | BooksCorpus, ~7,000 अप्रकाशित किताबें | Next-token पर pretrain, फिर हर task के लिए fine-tune — task के inputs को delimiters के साथ एक ही token sequence में लिखकर। Pretraining transfer होती है। |
| GPT-2, 2019 | 48 layers, dmodel 1600, 1.5B params | WebText, Reddit से linked ~40 GB pages | Fine-tuning बिलकुल नहीं। Task को text की तरह लिखिए ("TL;DR:") और निरंतरता पढ़िए। Scale से zero-shot। |
| GPT-3, 2020 | 96 layers, dmodel 12288, 96 heads, 175B params | ~300B tokens: छना हुआ Common Crawl, WebText2, किताबें, Wikipedia | Prompt में कुछ हल किए उदाहरण रखिए और जमा हुआ model उन्हें इस्तेमाल कर लेता है। In-context learning, कोई gradient update नहीं। |
वह एक सूत्र
GPT-1 का Equation 1, चिह्न पलटा हुआ — ताकि यह बढ़ाने वाली likelihood नहीं, घटाने वाला loss हो। k context window है। तीन papers में बाक़ी सब कुछ यही जोड़ है, बस बड़ा किया हुआ। इसे pipeline stages की तरह पढ़िए:
| चरण | Shape | क्या करता है |
|---|---|---|
tokens | (T,) | Integers. Text ख़ुद अपना label है — किसी ने कुछ annotate नहीं किया |
tok_emb[tokens] + pos_emb[:T] | (T, d) | हर token क्या है, और कहाँ बैठा है। दोनों सीखे हुए |
decoder_block × N | (T, d) | Causal attention (tokens बात करते हैं, सिर्फ़ पीछे की ओर), फिर एक MLP (हर token अकेले सोचता है)। Shape वही → stack हो सकता है |
layer_norm @ W_out | (T, V) | Logits. हर position के लिए एक अंदाज़ा, एक साथ — एक matrix multiply T rows को score करता है। Mask इसे जायज़ बनाता है: row t को tokens[t+1] पर score मिलता है और उसने उसे कभी देखा नहीं |
softmax, row-wise | (T, V) | प्रतिशत |
−log p[target] | (T,) | असली अगले token से हर row कितनी चौंकी। पक्का और सही हो तो 0 |
mean | scalar | Loss. समान अंदाज़े के लिए ln(V); फ़र्श data की अपनी randomness है |
Loop: output को वापस अंदर डालो
Demo 1 random weights के साथ NumPy block चलाता है। कुछ सीखा नहीं गया; मैं बस चाहता हूँ आप shapes देखें। चार tokens अंदर, एक (4, 28) matrix बाहर — हर position के लिए एक अंदाज़ा — और loop सिर्फ़ आख़िरी row रखता है:
मेरी बात पर भरोसा मत कीजिए। यह रहा असली output:
logits : (4, 28) (one row PER position)
we keep only logits[-1] : (28,) (the next-token guess)
the loop, 5 steps, greedy:
step 0: T=4 tokens -> logits(4, 28) -> argmax of last row = 23 = 't'
step 1: T=5 tokens -> logits(5, 28) -> argmax of last row = 16 = 'l'
step 2: T=6 tokens -> logits(6, 28) -> argmax of last row = 0 = '\n'
step 3: T=7 tokens -> logits(7, 28) -> argmax of last row = 23 = 't'
step 4: T=8 tokens -> logits(8, 28) -> argmax of last row = 23 = 't'
result: 'the tl\ntt'
causal: logits for position 0 identical at ctx=4 and ctx=8? True
no mask: logits for position 0 identical at ctx=4 and ctx=8? False
आख़िरी दो lines mask का मुफ़्त बोनस हैं। Mask चालू हो तो sequence बढ़ने पर भी row 0 का output कभी नहीं बदलता, इसलिए server हर पुरानी row दोबारा गिनने के बजाय cache कर सकता है। यही KV cache है। इसके बिना कुछ भी cache नहीं हो सकता।
Context window
block_size के आगे loop की कोई याददाश्त नहीं; GPT-3 का
2048 tokens है। Demo 5 trained model पर इसे दोनों तरह से ग़लत करता है:
torch: 33 tokens into the block_size=32 trained model, no crop:
IndexError: index out of range in self (pos_emb has only 32 rows)
crop from the wrong end -- idx[:, :block_size] instead of idx[:, -block_size:]:
the model hides the answer again......................................
every token after the window filled is the same one? True
vs the right crop, same model, same prompt:
the model hides the answer again. the prompt hides the answer again. t
काटना भूल गए तो crash होना अच्छा नतीजा है। सिर से काटा तो model वही बासी prefix हमेशा दोबारा पढ़ता रहता है, जो उसने अभी लिखा वह कभी नहीं देखता, और आप रोकने तक एक ही अक्षर छापता रहता है।
वह shift: label अगला अक्षर है
Inputs हैं tokens[:-1], targets हैं tokens[1:]।
वही एक-token shift GPT-3 का पूरा labelling budget है:
text : autocomplete
inputs : autocomplet <- tokens[:-1]
targets : utocomplete <- tokens[1:], shifted one left
pairs : a->u u->t t->o o->c c->o o->m m->p p->l l->e e->t t->e
अब इसे तोड़िए। targets = inputs कर दीजिए और ईमानदार version के
साथ-साथ 200 steps train कीजिए:
honest (targets = tokens[1:]), 200 steps:
step 1 loss 3.536
step 50 loss 1.446
step 200 loss 0.313
BROKEN (targets = inputs) , 200 steps:
step 1 loss 3.719
step 50 loss 0.016
step 200 loss 0.002 ← learned output[i] = input[i], nothing else
Train कीजिए, फिर बोलने दीजिए
Demo 3 मेरे laptop के CPU पर दो layers और 105,728 parameters वाला एक GPT क़रीब दस सेकंड में train करता है। Corpus वे 400 वाक्य हैं जो मैं चार word lists से बनाता हूँ — subject, verb, object, adjunct, हर एक समान रूप से चुना गया — ताकि model के पास सीखने को grammar हो और वह रट न सके। इसका मतलब यह भी है कि फ़र्श मुझे पता है: हर वाक्य में छह में से चार चुनाव यानी लगभग 38 अक्षरों पर 4·ln 6 nats, यानी प्रति अक्षर लगभग 0.19।
step 1 loss 3.536
step 150 loss 0.363
step 300 loss 0.288
step 600 loss 0.305
a uniform guess scores ln(28) = 3.332; the corpus's own
randomness puts the floor near 0.191 (4 choices of 6 per sentence)
temperature 0.8, 100 chars:
the model feeds the future by hand. the model feeds the next token again
. the prompt feeds the answer at scale
Demo 1 वाला ही loop। Random weights ने 'the tl\ntt' छापा था;
trained weights corpus की grammar में वाक्य छापते हैं। Loop में कुछ नहीं
बदला। उसने संरचना सीखी, अर्थ नहीं — मेरे corpus में सीखने लायक़ अर्थ है ही
नहीं, और यही एक language model के काम का ईमानदार वर्णन है।
Temperature
Logits और पासे के बीच एक संख्या। Softmax से पहले आख़िरी row को इससे भाग
दीजिए: 1 से कम argmax की ओर तेज़ करता है, 1 से ज़्यादा समान
वितरण की ओर सपाट। जिस distribution से हर अक्षर निकाला गया, मैं उसकी entropy
nats में छापता हूँ; ln 28 ≈ 3.33 का मतलब है "कुछ पता नहीं":
temperature 0.2: mean entropy 0.096 nats
feeds the answer again. the mask feeds the answer at scale. the mask cop
ies the next token at scale.
temperature 2.0: mean entropy 1.100 nats
feeds its own oly.\nthefix by hagan. tby\naby hok fedsy . the feeds k\nt
he prefix slowly.\nfeefcashi rec
temperature 0 for 400 chars -- does it loop?
yes: the tail repeats every 35 chars: 'r again. the prompt hides the answe'
वह आख़िरी line ही वह "repetition" bug है जिससे सब टकराते हैं। Greedy decoding deterministic है, इसलिए जिस पल वह किसी state पर दोबारा पहुँचता है, वह हमेशा के लिए एक चक्र में है। Temperature 0 "सबसे सटीक setting" नहीं है; वह बिना exit condition वाला loop है।
Loop को ईमानदार mask बनाता है
Attention page ने कहा था: mask हटा दीजिए तो वह BERT है। अगर आप generate करना
चाहते हैं तो उसकी क़ीमत यह है। Demo 6 वही model causal=False के
साथ, वही data, वही 600 steps train करता है:
same model, same data, same steps, mask removed:
step 1 loss 3.540
step 600 loss 0.013 ← vs 0.305 with the mask on
loss per position: mean over positions 0..30, then position 31,
the one row that has no next column to peek at:
causal : 0.280 last position: 0.190
no mask: 0.001 last position: 0.296
short prompt 'the model ', temperature 0.8:
causal :
the model feeds the future by hand. the model feeds the next token again
. the prompt feeds the answer at scale
no mask:
the model ooooooomoooooooooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooooooooooooooooo
Mask के बिना position i, position i+1 को देख सकती है — और वही उसका target है। तो वह नक़ल करता है, और आख़िरी row को छोड़कर हर row पर loss 0.001 तक गिर जाता है; वही एक row है जिसके पास नक़ल करने को कुछ नहीं। Sampling के समय सिर्फ़ आख़िरी row मायने रखती है, यानी 32 में से 31 rows की training एक ऐसी चालाकी पर गई जो production में कभी नहीं चलती, और वह एक ईमानदार row gradient का 1/32 ही पा सकी: masked model के 0.190 के मुक़ाबले 0.296। पूरी 32-अक्षर window दीजिए ताकि आख़िरी row ईमानदार हो, तो वह लड़खड़ाता है — बेसिर-पैर लंबे वाक्य, एक गढ़ा हुआ शब्द; script वह भी छापती है। एक flag से नियंत्रित train/serve skew।
In-context learning: prompt ही program है
GPT-3 की सुर्ख़ी। साफ़-साफ़ कह देता हूँ, क्योंकि interviewer पूछेंगे: in-context learning का मतलब है कि हल किए हुए उदाहरण prompt में हैं और weights जमे हुए हैं। कोई fine-tuning नहीं, कोई gradient step नहीं, disk पर कुछ नहीं लिखा जाता। Paper का few-shot format ठीक इसी आकार का है — कुछ input→output जोड़े, फिर बिना output वाला एक input:
english: cat -> french: chat
english: dog -> french: chien
english: house -> french: maison
english: cheese -> french:
that prompt is 118 tokens; the model sees one integer array:
[10, 18, 12, 16, 14, 22, 13, 4, 1, 8, 6, 23] ... [1, 11, 21, 10, 18, 8, 13, 4]
logits shape: (1, 32, 28) gradient updates: 0
Code में कहीं यह नहीं लिखा कि "उदाहरण" क्या होता है। वे तीन हल किए जोड़े बस
और prefix हैं; few-shot का मतलब लंबा input, बस इतना ही। GPT-3 का दावा यह है
कि 175B parameters पर सबसे संभावित निरंतरता fromage है, और यह
ज़्यादा उदाहरणों और ज़्यादा parameters के साथ बेहतर होती जाती है — results
section वही curve है, task दर task। मेरे model में 105,728 parameters और छह
संज्ञाओं की vocabulary है। यह ऐसा नहीं करेगा, और मैं बहाना भी नहीं करूँगा कि
करता है। तंत्र वही है। क्षमता वह है जो scale ने ख़रीदी। इसके बाद जो आया —
autocomplete को केवल आगे बढ़ाने के बजाय निर्देश मानने के लिए
इच्छुक बनाना — वह InstructGPT है।
The code
NumPy for the mechanism. A tiny PyTorch model, CPU only, trains in about ten seconds for
the one part that needs gradients. Every function under 16 lines. Attention, the multi-head
wrapper and the causal mask are the attention page's, with a
causal switch added so a demo can cheat.
Download the full script — 130 lines of mechanism plus seven runnable demos. Then:
python3 gpt_from_scratch.py
def decoder_block(x, p, causal=True):
a, weights = multi_head_attention(layer_norm(x), p, causal)
x = x + a # tokens talk (backwards only)
x = x + gelu(layer_norm(x) @ p["W_1"]) @ p["W_2"] # each token thinks alone
return x, weights # shape unchanged -> stackable
def gpt_forward(tokens, p, causal=True):
T = len(tokens)
assert T <= p["block_size"], f"context {T} > block_size {p['block_size']}"
x = p["tok_emb"][tokens] + p["pos_emb"][:T] # (T, d): what + where
x, weights = decoder_block(x, p, causal) # a real GPT stacks 12..96
logits = layer_norm(x) @ p["W_out"] # (T, vocab): a guess PER row
return logits, weights
def generate(p, prefix, n_new, temperature=0.0, rng=None, trace=False):
tokens = list(prefix)
for step in range(n_new):
ctx = tokens[-p["block_size"]:] # crop: no memory past this
logits, _ = gpt_forward(np.array(ctx), p)
row = logits[-1] # only the LAST row matters
if temperature == 0:
nxt = int(np.argmax(row)) # greedy
else:
nxt = int(rng.choice(len(row), p=softmax(row / temperature)))
tokens.append(nxt) # feed it back in
if trace:
print(f" step {step}: T={len(ctx)} tokens -> logits{logits.shape}"
f" -> argmax of last row = {nxt:2d} = {decode([nxt])!r}")
return tokens
def get_batch(data, block_size, batch_size, gen, shift=True):
ix = torch.randint(len(data) - block_size - 1, (batch_size,), generator=gen)
x = torch.stack([data[i:i + block_size] for i in ix])
if shift:
y = torch.stack([data[i + 1:i + block_size + 1] for i in ix]) # THE shift
else:
y = x.clone() # the break: label == input
return x, y
Now break it
This is the part that actually makes it stick. Reading won't do it. You debug for a living — every row below is one edit to the script and one thing to watch.
| Break this | Watch for |
|---|---|
In demo_3_train_and_sample, build the model with causal=False | Training loss 0.013 instead of 0.305 — it's reading the answer off the next column. Sample from a short prompt: ooooooo… forever. |
In get_batch, replace the shifted y with y = x.clone() | Loss 0.002 by step 200. It learned output[i] = input[i] and nothing about the text. |
Sample with temperature=0.0 for 400 characters | The tail repeats every 35 characters — "the prompt hides the answer again." — until you stop it. Deterministic loop, no exit. |
In sample, delete the crop: model(idx) instead of model(idx[:, -model.block_size:]) | IndexError: index out of range in self on the 33rd token. pos_emb has 32 rows. The crash is the good outcome. |
Crop from the head instead: idx[:, :model.block_size] | Re-reads the same stale prefix every step and prints . forever once the window fills. No crash, just wrong. |
In demo_1_the_loop, call gpt_forward(..., causal=False) and print weights[0] | The upper triangle fills in; row 0 now reads row 3, and position 0's logits change every time you append a token, so nothing can be cached. That's an encoder block. It's BERT. |
On the maths anxiety
Honestly: the whole thing is one −log. Cross-entropy is minus the log of the
probability the model gave the right answer — 0 when it was sure and right, big when it was
sure and wrong. Softmax turns scores into percentages. Temperature divides before the
softmax. Entropy is "how surprised do I expect to be", the same −log averaged.
That's every symbol on this page.
You don't need to derive anything; loss.backward() and
F.cross_entropy are library calls. What I'd want you to do at a whiteboard is
write the shift, write the mask, and say why removing either one makes the loss go down
and the model go bad. What separates AI engineers here isn't research maths: it's knowing
that in-context learning is a longer input, that the KV cache exists because of the mask,
that context length is a hard ring buffer, and that temperature 0 loops.
Where to go next
This page gave you one thing: three papers as one loop you can break. It skipped byte-pair tokenisation, the fine-tuning recipe, top-k and nucleus sampling, and the entire results section. These cover what I cut.
-
Language Models are Few-Shot Learners
Now that you have the loop: §2 and Figure 2.1 first — the in-context learning definition is one figure. Then §3 with a ruler: pick one task and follow its zero-, one- and few-shot lines up the model sizes. Then §5, where the authors list what it can't do, which is more useful than what it can.
-
The Illustrated GPT-2
Where I gave you
.shape, this gives you the tensors as boxes flowing through the decoder stack one token at a time. Best picture anywhere of masked self-attention and of why the KV cache is what serving actually does. -
Let's build GPT: from scratch, in code, spelled out
My torch model is a compression of this. He writes the same thing — character-level, causal, tiny — in front of you, every
triland every shape said aloud, then scales it to Shakespeare. Go here when my forty lines of torch feel like a magic trick.