How Do Language Models Work? I Built One Small Enough to Watch, article cover in AI Basics on learnai24.com

How Do Language Models Work? I Built One Small Enough to Watch

The usual one-sentence explanation of a language model is that it predicts the next word. That is correct, and on its own it settles nothing, because it skips the two parts that would tell you how the thing behaves. What does a prediction look like before a word comes out of it? And what does training change, step by step?

So I built one and measured it. It is small enough to train in about twenty minutes on two CPU threads, which means every number below came out of a model I can rerun, and so can you: the script is here, and it prints every figure in this article. One dependency, PyTorch.

It has 824,897 parameters and a vocabulary of 65 characters, and it was trained on one megabyte of Shakespeare. For scale, one large model that does publish its size is gpt-oss-120b, at 117 billion parameters with 5.1 billion active per token. Mine is about 1/142,000 of the first number and about 1/6,200 of the second.

A prediction is a whole distribution, and it moves every character

The model does not output a character. It outputs a score for every entry in its vocabulary at once, all 65, and those scores are then turned into probabilities that add up to 100 percent. Only after that does anything get picked.

Here is that process walked through a single line, one character at a time. The model has seen the text up to that point and nothing after it. I have written down its top guess and how sure it was.

Text so farTop guessIts probabilityWhat actually came next
First Citize99.7%e
First Citizen100.0%n
First Citizen:68.5%:
First Citizen:line break81.5%line break
First Citizen: (line break)T11.7%B
…hear me spea55.7%a
…hear me speak97.3%k

I measured 56 positions in that opening speech, the speaker name and the sentence under it. The top guess ranges from 11.6 percent to 100.0 percent. Two of those swings are worth sitting with. Inside a word the model is almost certain, because there is only one way to finish “Citize”. One character after the line break it is down to 11.7 percent, and its best guess is wrong, because a new line of dialogue could start with anything.

Confidence is not a property the model has or lacks. It is a property of the position: three characters take it from a top guess that rounds to 100.0 percent down to one at 11.7 percent.

The full distribution at one of those positions makes the shape clearer. After hear me spea:

Next characterProbability
k97.34%
r1.70%
d0.50%
c0.29%
t0.10%
the other 60 characters0.07% combined

And after (line break)MENENIUS:(line break)The , where many words could follow:

Next characterProbability
w9.09%
s7.87%
p7.84%
c6.84%
m5.51%
h5.45%

Same weights, same machinery, two completely different shapes. This part is the same in a large model. A production system runs the operation over a list of word fragments instead of single characters: OpenAI’s o200k_base vocabulary has just under 200,000 entries, which I counted entry by entry. The list is longer. The shape of the answer is the same.

What 5,000 training steps actually change

Training is scored with cross entropy loss, and it has two useful reference points. A model that ignores the text and guesses uniformly among 65 characters scores 4.174. A model that ignores the text and knows nothing but how often each character occurs scores 3.317 on the training text and 3.344 on the held-out block. Lower is better. Here is my model, measured on text it trained on and on a block of the corpus it never saw.

StepTraining lossHeld-out loss
04.34724.3459
1002.49992.5107
5001.97272.0504
2,0001.45161.6891
5,0001.28691.5827

Two things in that table. Before training the model scores 4.3472, which is worse than uniform guessing, because its starting numbers are random and a randomly tilted distribution is punished harder than a flat one. And by step 100 both columns are already below the character-frequency baseline, which means it has learned something that letter frequency alone does not give you.

The samples say more than the losses. All five were generated from the same starting character with the same random seed, at temperature 1.0, and each is cut to exactly 180 characters, so the only difference between them is how much training had happened.

Step 0:

Dt!'mamAeRcLs:vJoYSkyMG!t'eSUQSNsSEBVEXQuG
RmabfjPBh?F mfdrZtzjOTRkKThUkswKbO,h'ZOVS TOvcE'HZ3jbIJ;JgAKp3URUXD'NSIH:tb;Sx!Y&TkZNL;aEBx'uKAMxBlg RxvNISJwQJw&tOuTchu?Psxn ESafivr;XkT

Step 100:

AGN'Tanthocos: lorok l ftse y Saseres
Ane hitabe mee t mer s but bof hUCTheem, the is ave, the bIUsegateay we maldr:
G he ol kit, than't fon
Ag trvee cwilwithuranursson thasivr;
ke

Step 500:

DUS's mastcos: lord, layteest Saser hemour
Rrabe my thymer stout bord besater, a shis a chall.

IUSERAD:
You Dull Hath herel kit, the 'thewn
Yor rage own with your sson thasivr;
ke

Step 2,000:

At 'tine, come lord, letteen with thereof.

Citizen:
What we besere done weep, for is a chard.

ISABELLA:
Awe mader:
I have thou hast 'twaster and good low thus
Aress not afigriman

Step 5,000:

At 'manishcome lord, letters with any more
Reach of thy forsulet bear be at my father.
My lord believe take everlarge; here's mine
Earl of the warrants.

QUEEN:
Are son that vriver

Watch the order things arrive in. Spacing and line breaks first. Then strings with the length and shape of English words. Then the layout of the source text, a name in capitals followed by a colon and a new line. Then real words in orders that look like sentences, although the sentences fall apart as soon as you read them. Meaning does not arrive.

“Word-shaped” can be counted rather than asserted. I generated 2,000 characters from each checkpoint and checked what share of the word-like strings in it appear anywhere in the Shakespeare corpus, which contains 12,631 distinct ones:

StepShare that appear in the corpus
10013.5%
50045.6%
2,00075.9%
5,00083.8%

By step 5,000, five out of six word-like strings are in the corpus. The sixth looks like “forsulet”: built out of English spelling patterns and not in the text it learned from.

That order is not in the code. There is no stage that teaches punctuation before spelling, and no list of rules anywhere. The only instruction the model ever gets is: given this stretch of text, put probability on what came next.

One caveat on the gap between the two loss columns. At step 100 they are nearly equal, and at step 5,000 the model is 0.30 better on text it trained on than on text it did not. That is a generalization gap, and part of it is memorization. It is not all memorization, because my held-out block is the last ten percent of the file rather than a random sample, so it contains different plays and different speakers. Splitting it that way is standard and it confounds the two effects, which is exactly the kind of detail that gets dropped when a number like this is quoted on its own.

Temperature, and what it actually does

Temperature is usually introduced as a creativity setting. Mechanically it divides the model’s scores by a number before they are turned into probabilities. Below 1 the gaps between candidates widen, so the leader wins more often. Above 1 the gaps shrink, so the tail gets a real chance. The model’s opinion does not change. Only how much of it gets used.

That has a measurable consequence. I generated five samples of 1,000 characters at each of four settings, twenty samples in total, and counted how often a 12-character window repeats inside the same sample, plus the share of words in each sample that are distinct.

TemperatureRepeated 12-character windowsShare of words that are distinct
0.29.76%43.5%
0.50.61%61.6%
1.00.02%79.9%
1.50.00%91.8%

At 0.2 the model repeats itself roughly five hundred times as often as at 1.0. Here is what that looks like, three settings, same seed, 200 characters each. The middle one is the step 5,000 sample from earlier, because temperature 1.0 is what produced it.

Temperature 0.2:

GLOUCESTER:
And so the tread of the world that we will not to the prince
That which should be the content to the people.

KING EDWARD IV:
Well, then were be to be so much for the world.

KING EDWARD I

Temperature 1.0:

At 'manishcome lord, letters with any more
Reach of thy forsulet bear be at my father.
My lord believe take everlarge; here's mine
Earl of the warrants.

QUEEN:
Are son that vrivery; basing me.

KING 

Temperature 1.5:

At 'manishcom: lord, lettees wisder's
mouthstable vess men: to through what have shing
My 'gudge!

ESCALUS:
No, SLY:
God! Your brattle's goodly arroonswor:
Is your sson that vrifked; bacvildied!
Laure

The low setting sticks to common words and ordinary sentence shapes, and starts looping: in 200 characters it uses the same speaker name twice and ends “the world” twice. The high setting produces strings that are not words. Neither setting is more correct than the other. Low temperature is more predictable, which is not the same thing as more accurate.

What does not transfer: a production system does not sample the way my model does. The major APIs expose further controls alongside temperature, among them top-p, which cuts off the unlikely tail, and frequency and presence penalties, which act on tokens that have already appeared. Those are separate mechanisms. The table shows what temperature alone does, not what your chat window does.

Is it just quoting the training text?

The obvious suspicion about a model trained on one megabyte of Shakespeare is that it is quoting. That is checkable, so I checked it. For each of five 600-character samples I searched the 1,003,854 characters the model was trained on for the longest passage that appears in both.

The five answers are 14, 24, 25, 26 and 28 characters. The longest, at 28, is this: heart.\n\nHENRY BOLINGBROKE:\n. Four of the five are the same shape: the end of a line, a blank line, a speaker name in capitals, a colon, and the first word or two of the next speech. What the model reproduces exactly is mostly the scaffolding of a play script, the part of the text that comes back on almost every page.

I want to be careful about what that does and does not prove. It does not prove the model could not have stored the text. 824,897 float parameters are about 3.3 megabytes of raw storage, which is more than the training split needs, so the arithmetic does not settle it in either direction. What settles it is the measurement, for this model, at this size. Whether a system with a hundred billion parameters reproduces training text verbatim is a different question with different evidence, and a character-level toy cannot answer it.

How to read a claim about how a model works

Restate it as a claim about a probability distribution. “The model knows the capital of France” becomes “at that position it puts most of its probability on Paris”. The second version is checkable, the first is not.

Ask which of the three steps it belongs to. Producing the distribution, sampling from it, and being trained to behave like an assistant are separate mechanisms. Assigning something to the wrong one is an easy way to end up with the wrong explanation for what a model did.

Check whether the example numbers were measured. Explainers often illustrate next-word prediction with figures like “60 percent mat, 20 percent roof”. Those are illustrations, which is fine until an argument gets built on them. Measured ones cost a few lines of code.

What the small model does not have

The prediction mechanism transfers to a large system. The behavior does not, and the gaps are specific.

Scale, and a different shape. My model is dense: every parameter is used for every character. gpt-oss-120b reports 117 billion parameters with 5.1 billion active per token, which indicates a model that routes each token to a fraction of itself. Beyond a size difference, that is a structural one.

Preference training. A model trained only to continue text produces text that continues plausibly, which is not the same as answering a question. The step in between is training on human preferences. Ouyang and colleagues report that in human evaluations on their own prompt distribution, outputs from a 1.3 billion parameter model given that treatment were preferred to those from a 175 billion parameter model without it, “despite having 100x fewer parameters”. That is labeler preference on one prompt set, not a general statement about capability, and the treatment includes a supervised fine-tuning stage as well as the ranking step. My model went through none of it.

Everything bolted on afterward. Web search, retrieval from a document store, code execution, memory between sessions. None of that is the language model. It is machinery around the language model, and it is why a chatbot can cite a source the model itself never saw.

The reason to build the small one anyway is that at 824,897 parameters nothing has to be taken on trust. Every figure above comes out of one script, and the script is short enough to read.

FAQ

Is a character-level model really the same thing as ChatGPT?

The prediction step, the sampling step and the temperature dial are the same. The vocabulary is not: mine picks one of 65 characters, a production model picks one of tens or hundreds of thousands of word fragments. Nor is the size, the routing, or the training that comes after pretraining. Same mechanism, different machine.

What is a token, concretely?

An entry in a fixed list the model was built with. In mine the list is the 65 distinct characters in the training text. In a production model it is a list of word fragments, and how text gets cut into them depends heavily on the language it is written in, and on whether it contains things like emoji.

Why do models make things up?

Because nothing in the pretraining objective checks whether a statement is true. The signal rewards putting probability on what came next in the text, and a plausible false statement scores well on that. Preference training adds a check of a kind, and retrieval adds another from outside, but both sit on top of the prediction rather than inside it. My model writes “vrivery” and “forsulet”. A large model writes something fluent that can be wrong in ways that are far harder to spot. I am not claiming those two failures are the same size or have the same causes, only that neither is a lookup going wrong.

Can I actually rebuild this?

Yes. The script is here, the corpus is one download, and the only dependency is PyTorch. It trains the model and then prints every table on this page. Every seed is fixed, so the first evaluation you see should read 4.3472 and 4.3459, exactly as above. About twenty minutes on two CPU threads.

The short version

A language model turns a stretch of text into a probability for every entry in its vocabulary. A sampler picks one. The whole thing runs again with that pick appended. Training is repetition of being scored on that prediction and nudged, and the structure you see in the output is what falls out of doing it enough times. Temperature decides how much of the distribution gets used. What a chat assistant does on top of that is a separate set of mechanisms, and worth keeping separate when you are trying to work out why it did something.

Sources and method, 14 September 2026

Every number in this article was measured on a model trained for it, and the script that produces all of them is published, training run and measurements together, so the whole thing can be repeated. Training text: the standard tinyshakespeare corpus, 1,115,394 bytes, md5 6fb458f1232090904fb40fe944165e91, from karpathy/char-rnn, split into the first 90 percent for training (1,003,854 characters) and the last 10 percent held out. Vocabulary: the 65 distinct characters in that file.

Model: decoder-only transformer in the GPT-2 style, 4 layers, 4 attention heads, embedding width 128, context 128 characters, learned position embeddings, pre-layer-norm, ReLU feedforward, no dropout, 824,897 parameters. Training: AdamW, learning rate 0.001, batch size 32, 5,000 steps, PyTorch 2.14, two CPU threads, 1,162 seconds wall clock including the evaluation pauses, seed 1337. Losses are the mean over 20 batches at a fixed evaluation seed. The reference points are the natural logarithm of 65, which is 4.1744, and a model of nothing but character frequencies, fitted on the training split: 3.3166 scored on the training split and 3.3437 scored on the held-out block, both over the same 20 batches at the same evaluation seed as the model losses. Samples use a fixed generation seed, so the only difference between them is training progress or temperature; each is cut to a fixed length, stated where it appears. The repetition table is 5 samples of 1,000 characters per setting, counting 12-character windows that occur more than once in the same sample. The real-word share is 2,000 characters generated per checkpoint at the same seed and temperature, matching each word-like string against the set of word-like strings in the full corpus, case-insensitively. The verbatim check used five 600-character samples and searched only the training split.

Architecture: Vaswani et al., Attention Is All You Need, 2017, which built a sequence model out of attention alone, in an encoder-decoder arrangement; the decoder-only version used here and in GPT-style systems came later. Attention itself is older, from Bahdanau, Cho and Bengio, 2014. Preference training and the quoted comparison: Ouyang et al., Training language models to follow instructions with human feedback, 2022. Parameter figures from the gpt-oss-120b model card, which states 117B parameters with 5.1B active.

What this page does not claim: that a model of this size behaves like a production system, that its failure modes scale in any simple way, or that character-level results carry over to word-fragment models without qualification. It shows one mechanism at a size where the whole thing is visible.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *