Unblock all features
Sign in to track progress, get AI help and save conversations.
There is another problem with our model.
Currently it treats each position in the sequence separately. The same token gets different parameters depending on where it appears in the sequence.
That causes two problems:
United means when it appears at position 3, it still has to relearn it when United appears at a different position.United followed by Kingdom at positions 3 and 4 is a country, it still has to relearn it for every other position pair they can appear in.Both of these issues can be solved by the attention mechanism.
It introduces parameters that are shared across all tokens and lets every token look at every other token using the same parameters. So the token's meaning and its relationships are computed the same way wherever it appears.
Attention produces a contextual embedding for each token that carries information about all the other tokens in the sequence and its relationships to them.
Let's see how these contextual embeddings are calculated.
First we need to calculate three vectors for each token: Key (k), Query (q), and Value (v) by multiplying each token's embedding by shared parameters:
Then for each token we look at every previous token in the sequence and decide how much attention to pay to each one.
E.g. the relationship of ? to What's is probably more important than to others.
These values are called the attention score and they are calculated using this formula.
Now we can use all the scores for each token to get the contextual embeddings.
First, we turn the raw scores into attention weights by applying the softmax across each token's scores, so they become positive and add up to 1:
Then for each token we multiply every previous token's Value by its attention weight and sum them up.
Here is the whole attention computation. I have decreased the context window to two tokens, to make it easier to visualise.
Note how the parameters are shared across all tokens and the relationships are learned per token pair.
We solved the problem with per-position parameters, but now we lost any information about the token's position. If we reorder the sequence, each token will get the exact same values of contextual embedding, but position still matters.
To fix this we will introduce another table (positional encoding) that carries values per position. The input of the attention for each token is a sum of the token's embedding and its positional encoding.
Now the input carries both position and meaning at once. The positional encoding values are learned during training, the same way embeddings are learned.
There is also an option to run multiple attention heads () in parallel. Each head with its own , , values.
At the end all heads' outputs are concatenated to a single contextual embedding per token.
This can help catch different kinds of relationships per attention head. But for our small LLM we will stick with .
Once we have the contextual embeddings per token we can pass them to an MLP and get the next token predictions.
Here is how the complete transformer architecture for our LLM looks like with the context window increased to 32 tokens:
Here is how the transformer neural network trained on the same TinyStories corpus behaves for any input text:
Loading model…
We have built our small LLM. It might not behave exactly like popular frontier models do, but the main mechanism is the same.
If we want to improve its behaviour, the main thing needed is scaling: larger corpus, bigger embeddings, more parameters, etc. However, that scaling comes with a performance cost. Some frontier models use trillions of parameters, which makes them hard to run on one machine.
Even our small model has ~0.7 million parameters.
So we will leave the model as it is and implement it in code in the next section. Once we have the core architecture implementation, we can experiment more with the model and its hyperparameters.