The problem
A bigram table cannot look further back than one token, and a bigger table grows out of control. Bengio et al. (2003) replaced the table with a small neural network, an MLP (multilayer perceptron), that reads the last tokens and outputs one score (logit) for every token of the vocabulary. You will write its forward pass, its loss, and the function that cuts a text into training examples.
The forward pass
For a batch of contexts, ids of shape (each row holds token ids, oldest first):
- Embed. Look up each token in the embedding table of shape . Result: shape , one vector of numbers per token.
- Concatenate. Put the vectors of each context side by side, oldest first. Result: , shape .
- Hidden layer. , with of shape : numbers per context.
- Logits. , with of shape : one score per token of the vocabulary.
where , , , are the weights and biases of the two layers.
Example. tokens, , , , and a batch of contexts,
ids = [[0, 2], [1, 0]].
- With =
[[1, 0], [0, 1], [1, 1]], context 0 looks up rows 0 and 2:[[1, 0], [1, 1]]. Context 1 looks up rows 1 and 0:[[0, 1], [1, 0]].- Concatenated: =
[[1, 0, 1, 1], [0, 1, 1, 0]], shape . The first two numbers of each row belong to the oldest token.- With
[[1, 0], [0, 1], [1, 0], [0, 1]]and : =[[1, 0.5], [0.5, 0.5]], and of it[[0.762, 0.462], [0.462, 0.462]].- With =
[[1, 0, -1], [0, 1, 1]]and :[[0.762, 0.462, -0.299], [0.462, 0.462, 0]], shape .
The loss
The loss is the mean cross-entropy of the logits against targets, the right next token of each context (section 2): for each row, , averaged over the batch.
Example. With
targets = [2, 1]: row 0 has loss about (the model gave token 2 the lowest score), row 1 about , and the mean is about .
Training examples from a text
To train, a long text is cut into windows: every run of consecutive tokens is a context, and the token right after it is its target.
Example.
build_contexts([5, 6, 7, 8, 9], n=2)returnsids = [[5, 6], [6, 7], [7, 8]]andtargets = [7, 8, 9]. A text of tokens gives examples.
What to implement
mlp_lm_logits(ids, params):paramsis a dict with keys"C","W1","b1","W2","b2". Return the logits.mlp_lm_loss(ids, targets, params): the mean cross-entropy of the logits againsttargets(shape ), computed stably.build_contexts(tokens, n): return(ids, targets), where row ofidsistokens[t : t + n]andtargets[t]istokens[t + n], for every that fits.
Requirements
- Concatenation order matters: the first columns of are the oldest token.
- No Python loops in
mlp_lm_logitsandmlp_lm_loss.build_contextsmay use a loop, orsliding_window_viewfromnp.lib.stride_tricks.
Hints
- Step 2 is a single
reshapeof the lookup to : reshape reads the last axis fastest, so each token's numbers stay together, in order. - You already wrote every piece: the embedding lookup (section 4), a linear layer (section 1) and cross-entropy from logits (section 2).