Thomas Piacentini
← Workshop

Python exercise

Train a character bigram language model

Original text · English40 min

A bigram language model predicts the next character from the current character only. It is the smallest model that uses context, and its loss is the same next-token cross-entropy that trains a GPT. In this task you train a table of next-character logits with gradient descent. The checks then measure it on a held-out text that your code never sees.

Task

Write two functions.

  1. train(ids, vocab_size, rng) trains the model on one long sequence of character ids and returns the model.
  2. next_log_probs(model, prev_ids) returns \log P(\text{next} \mid \text{prev}) for each context in prev_ids.

The model is a matrix W of shape (V, V). Row a holds the logits of the character that follows character a, so P(\cdot \mid a) = \mathrm{softmax}(W_a). The training pairs are (x_t, x_{t+1}) for t = 0, \dots, T - 2. Train W with gradient descent on the mean negative log-likelihood

L = -\frac{1}{T - 1} \sum_{t=0}^{T-2} \log P(x_{t+1} \mid x_t).

The training text is TEXT in support.py. It holds the "What it actually means" lines of the first 26 entries of the course glossary, glossary/terms.md, from "Activation Checkpointing" to "Benchmark Contamination". The text is lower case. Every character outside VOCAB is a space. VOCAB has 29 characters: the space, the letters a to z, the full stop and the comma. encode(text) gives the ids and decode(ids) gives the text back.

During training, call report(step, loss) from practice_check once for each update step. The first step is 0. The page draws these values as a live loss curve, and the checks count them.

Input and output

Name Type Shape Meaning
ids int array (T,) the character ids of the training text, from 0 to vocab_size - 1
vocab_size int the number of characters, 29
rng np.random.Generator the only source of random numbers
model any the value that train returns
prev_ids int array (M,) the context characters
  • train returns the model. The model can be any Python value, for example the matrix W.
  • next_log_probs returns a float array of shape (M, 29). Row i holds the log probabilities of the 29 characters after prev_ids[i].

Constraints

  • Budget: at most 300 update steps, so at most 300 calls to report.
  • Target: a mean negative log-likelihood of at most 2.55 nats per character on a held-out text from other glossary entries. This page does not show that text. A uniform model gets \log 29 = 3.367. A unigram model that ignores the context gets about 2.9.
  • Train with gradient descent. Pairs that never occur in the training text must keep a finite log probability.
  • Take every random number from rng. The same seed must give the same model.
  • Do not change ids.
  • Use numpy only. One full submit has a limit of 15 seconds.

Examples

python
ids = encode(TEXT)
model = train(ids, len(VOCAB), np.random.default_rng(1))
np.exp(next_log_probs(model, encode("q"))[:, encode("u")])
text
[[0.9663]]

The output is rounded to 4 decimals. After q, the model gives the letter u a probability of 0.9663. The reference starts with logits near 0, so its first reported loss is 3.3666, close to \log 29. After 300 steps its loss on the training text is 2.3148. A unigram model gets 2.9110 on the same text.

Code and artifacts 15
What you take away

Saved in your notebook on this device.

Sources and limits

Reading checkpoints are self-reported. Test results are recorded separately and apply to the executed cases.

AI Engineering from Scratch · 3f9715c · MIT

Download this file ↓