Seq2Seq (spec model) #
Encoder-decoder models for sequence generation.
This file supports both bounded token indices and differentiable token distributions. A bounded
index has type Fin vocabularySize, so embedding lookup cannot silently substitute a value for an
invalid token. A token distribution uses a vector of length vocabularySize and realizes embedding
as a matrix multiplication.
PyTorch analogue:
- encoder:
nn.RNN/nn.LSTM(ornn.TransformerEncoder) over source token embeddings - decoder:
nn.RNNover target embeddings (teacher forcing in training), then a finalnn.linearto vocabulary logits
Scope of this baseline:
- the optional attention in
Seq2SeqDecoderSpecis causal self-attention over decoder inputs. Training and inference use the same attention parameters and RNN recurrence. The baseline receives the encoder's final hidden state; it does not attend to the encoder's output sequence. - for cross-attention style mechanisms, we include a small additive/Bahdanau-style attention at the
bottom of the file (
computeAttentionWeightsSpec/applyAttentionSpec).
The transformer encoder blocks used by the transformer variant come from
NN/Spec/Models/Transformer.lean.
References:
- Sutskever et al., "Sequence to Sequence Learning with Neural Networks" (NeurIPS 2014).
- Bahdanau et al., "Neural Machine Translation by Jointly Learning to Align and Translate" (2015).
- Hochreiter and Schmidhuber, "Long Short-Term Memory" (1997).
- Cho et al., "Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation" (2014).
- Vaswani et al., "Attention Is All You Need" (2017) for the transformer encoder variant.
PyTorch docs (for API intuition, not semantics):
torch.nn.Embedding: https://pytorch.org/docs/stable/generated/torch.nn.Embedding.htmltorch.nn.RNN: https://pytorch.org/docs/stable/generated/torch.nn.RNN.htmltorch.nn.LSTM: https://pytorch.org/docs/stable/generated/torch.nn.LSTM.htmltorch.nn.Linear: https://pytorch.org/docs/stable/generated/torch.nn.Linear.htmltorch.nn.MultiheadAttention: https://pytorch.org/docs/stable/generated/torch.nn.MultiheadAttention.htmltorch.nn.TransformerEncoderLayer: https://pytorch.org/docs/stable/generated/torch.nn.TransformerEncoderLayer.html
Implementation status #
No API builder implements an encoder-decoder model. nn.rnn, nn.lstm, and nn.gru build single
recurrent layers and NN/API/Models/Recurrent.lean builds sequence models with a per-step linear
head, neither of which is this architecture. NN/Spec/Module/Seq2seq.lean wraps this file as a
Spec.Module. No theorem is proved about it.
Training + gradients (one-hot inputs) #
Most of this file focuses on architecture variants and forward passes (teacher-forcing, inference-time decoding, optional self-attention in the decoder, etc.).
To make Seq2Seq usable as a first-class baseline, we also provide an explicit training objective and reverse-mode gradients for the differentiable path:
- inputs are one-hot / token distributions (so embedding lookup is a matrix multiply),
- teacher forcing is used in the decoder,
- the loss is per-timestep cross-entropy between
softmax(logits)and the target distribution, - gradients flow through embeddings, encoder RNN, decoder RNN, output projection, and (optionally) the decoder self-attention block.
Bounded token indices are intentionally treated as non-differentiable.
Small gradient records #
Gradients for a token embedding table E : (vocabularySize × embedDim).
PyTorch analogue: nn.Embedding.weight.grad.
- embedding : TorchLean.Tensor α [vocabularySize, embedDim]
Gradient of the embedding matrix.
Instances For
End-to-end gradient record for the differentiable Seq2Seq baseline.
This bundles gradients for:
- source/target embeddings,
- encoder RNN,
- decoder RNN,
- decoder output projection,
- optional decoder self-attention (if enabled in the decoder spec).
- sourceEmbedding : Seq2SeqEmbeddingGrads α srcVocabSize embedDim
Gradients for the source embedding table.
- targetEmbedding : Seq2SeqEmbeddingGrads α tgtVocabSize embedDim
Gradients for the target embedding table.
- encoder : RNNParameterGradients α embedDim hiddenDim
Gradients for the encoder RNN parameters.
- decoderRnn : RNNParameterGradients α embedDim hiddenDim
Gradients for the decoder RNN parameters.
- outputProjection : LinearParameterGradients α hiddenDim tgtVocabSize
Gradients for the decoder output projection (
hiddenDim -> tgtVocabSize). - decoderAttention : Option ((numHeads : ℕ) × MultiHeadAttentionParameterGradients numHeads embedDim (embedDim / numHeads) α)
Gradients for optional decoder self-attention parameters.
Instances For
Seq2Seq token embedding specification.
Parameters:
embedding: a lookup tableE : (vocabularySize × embedDim).
PyTorch analogue: nn.Embedding(vocabularySize, embedDim).
- embedding : TorchLean.Tensor α [vocabularySize, embedDim]
Embedding table
E : (vocabularySize × embedDim).
Instances For
Embedding forward pass for discrete token ids.
Inputs:
tokenIds : (seqLen), a tensor of indices bounded by the vocabulary size.
Output:
y : (seqLen × embedDim), where each timestep selects a row of the embedding table.
PyTorch analogue: nn.Embedding on an integer tensor. The Fin vocabularySize element type
expresses the lookup precondition directly, rather than assigning an arbitrary meaning to an invalid
token.
Instances For
Seq2Seq embedding forward pass for one-hot / token distributions.
This is the usual "embedding lookup as a matrix multiply":
- if
E : (vocabularySize × embedDim)is the embedding table, - and
x_t : (vocabularySize)is a one-hot / probability vector for time stept, - then the embedded vector is
y_t = x_tᵀ · E : (embedDim).
PyTorch analogy: y = x @ E where x is one-hot / a distribution; this matches nn.Embedding
when the input is exactly one-hot.
Instances For
Backward pass for Seq2SeqEmbeddingSpec.forwardOneHot.
This is just a time-distributed linear layer:
y_t = token_tᵀ · E
So:
dE = Σ_t token_t ⊗ dY_tdToken_t = E · dY_t(not usually needed, but included for completeness)
Instances For
RNN-based encoder specification for Seq2Seq.
This models an nn.RNN-style encoder over embedded tokens:
- input is a sequence of embeddings
(seqLen × embedDim), - output is the full hidden-state sequence plus the final hidden state.
PyTorch analogue: nn.RNN(..., batch_first=True) (ignoring the batch axis), returning (output, h_n).
- rnn : RNNSpec α embedDim hiddenDim
RNN cell parameters.
Instances For
Forward pass for Seq2SeqRNNEncoderSpec.
Inputs:
x : (seqLen × embedDim), embedded source tokens,h0, optional initial hidden state (hiddenDim).
Returns:
(outputs, final_h)whereoutputs : (seqLen × hiddenDim)is the per-timestep hidden sequence.
Instances For
LSTM-based encoder specification for Seq2Seq.
This models an nn.LSTM-style encoder over embedded tokens, returning the full hidden sequence,
final hidden state, and final cell state.
PyTorch analogue: nn.LSTM(..., batch_first=True) (ignoring the batch axis), returning
(output, (h_n, c_n)).
- lstm : LSTMSpec α embedDim hiddenDim
LSTM cell parameters.
Instances For
Forward pass for Seq2SeqLSTMEncoderSpec.
Inputs:
x : (seqLen × embedDim), embedded source tokens,h0, optional initial hidden state (hiddenDim),c0, optional initial cell state (hiddenDim).
Returns:
(outputs, final_h, final_c)whereoutputs : (seqLen × hiddenDim)is the per-timestep hidden sequence.
Instances For
Transformer-based encoder specification for Seq2Seq.
This wrapper applies exactly numLayers TransformerEncoderLayers from
NN.Spec.Models.Transformer as a left fold.
PyTorch analogue: nn.TransformerEncoder(nn.TransformerEncoderLayer(...), num_layers=...)
(ignoring dropout and most configuration knobs).
- layers : TorchLean.Tensor (TransformerEncoderLayer numHeads embedDim (embedDim * 4) α) [numLayers]
Encoder layer stack. Its length is part of the type.
Instances For
Forward pass for Seq2SeqTransformerEncoderSpec.
Input/output shape: (seqLen × embedDim).
This uses post-norm transformer layers from NN.Spec.Models.Transformer and does not model
dropout; it is meant as a clean semantic reference rather than a full training-ready implementation.
Instances For
RNN decoder specification for Seq2Seq.
This decoder consumes a sequence of target-side embeddings and produces vocabulary logits:
- an
RNNSpeccell updates the hidden state per timestep, - a time-distributed
LinearSpecmaps hidden states to logits, - optionally, causal self-attention transforms the decoder input embeddings before the RNN.
Position
iattends to inputs0, ..., i, in both teacher forcing and greedy decoding.
PyTorch analogue: a hand-rolled decoder using nn.RNN and nn.linear, optionally preceded by
nn.MultiheadAttention over the target embeddings (note: this is not encoder-decoder
cross-attention).
- rnn : RNNSpec α embedDim hiddenDim
Decoder RNN cell parameters.
- attention : Option ((numHeads : ℕ) × MultiHeadAttention α numHeads embedDim (embedDim / numHeads))
Optional causal self-attention over decoder inputs, shared by training and inference.
- outputProjection : LinearSpec α hiddenDim vocabularySize
Output projection (
hiddenDim -> vocabularySize) producing per-timestep logits.
Instances For
Prepare the RNN inputs from a nonempty sequence of decoder embeddings.
With attention enabled, row i uses only rows 0, ..., i. The hard mask gives later positions
zero weight, including in the attention backward pass. Without attention, the embeddings pass
through unchanged. Teacher forcing and single-step decoding both use this function, so attention
has the same parameters, projection order, and mask convention in both paths.
Instances For
Teacher-forcing logits for a sequence of decoder inputs.
targetEmbeddings has shape (tgtSeqLen × embedDim) and contains the tokens fed to the decoder.
For next-token prediction, the caller supplies the start token followed by the preceding target
tokens; the labels are one position ahead of these inputs. Causal self-attention prepares each RNN
input, then the recurrence starts at h0 and the output projection produces vocabulary logits.
Changing a later decoder input cannot create an attention edge into an earlier position.
Instances For
Decoder backward (teacher forcing) #
The decoder is: (optional self-attention) → RNN → time-distributed linear projection.
We compute gradients by:
- recomputing the attended embeddings (if any),
- recomputing the decoder hidden sequence,
- backpropagating through the output projection per timestep,
- backpropagating through the RNN sequence,
- optionally backpropagating through self-attention.
Gradients for the decoder parameters, target embeddings, and initial hidden state.
The attention field is present exactly when the decoder has an attention block. The final two fields keep the input sequence gradient separate from the gradient passed back to the encoder through its final hidden state.
- rnn : RNNParameterGradients α embedDim hiddenDim
Gradients for the decoder RNN parameters.
- outputProjection : LinearParameterGradients α hiddenDim vocabularySize
Gradients for the time-distributed output projection.
- attention : Option ((numHeads : ℕ) × MultiHeadAttentionParameterGradients numHeads embedDim (embedDim / numHeads) α)
Gradients for the optional decoder self-attention parameters.
- targetEmbeddings : TorchLean.Tensor α [tgtSeqLen, embedDim]
Gradient with respect to the target embedding sequence.
- initialHidden : TorchLean.Tensor α [hiddenDim]
Gradient with respect to the initial hidden state
h0.
Instances For
Backward pass for Seq2SeqDecoderSpec.forwardTeacherForcing.
Returns a Seq2SeqDecoderGradients record.
The attended embeddings and hidden sequence are recomputed with the forward pass's causal mask. The same mask is passed to the attention VJP. An upstream gradient supported on an initial target prefix therefore cannot flow through an attention edge to a later decoder input.
Instances For
Advance the decoder once using a nonempty prefix of input embeddings.
previousHidden is the RNN state after processing all but the last prefix token. Attention sees the
whole prefix, and its last row supplies the current RNN input. The earlier RNN steps are not
replayed. The result contains the new hidden state and this step's vocabulary logits. Keeping the
prefix explicit also lets a caller compare a teacher-forced prefix with a single inference step.
Instances For
Greedy autoregressive decoding from startToken and the initial hidden state h0.
Each step appends the current token embedding to the input prefix, applies forwardStep, and
feeds the argmax token back as the next input. Optional self-attention therefore sees the same
prefix as teacher forcing with those input tokens. The RNN state advances once per emitted token.
Attention projections are recomputed from the stored prefix; this specification has no key/value
cache.
The result contains logits of shape (maxLen × vocabularySize) and maxLen predicted token ids.
When maxLen = 0, both outputs are empty and no decoder step runs.
Instances For
Complete Seq2Seq model specification (baseline).
This bundles:
- source and target embedding tables,
- an RNN encoder,
- an RNN decoder with output projection (and optional decoder self-attention).
PyTorch analogue: a small encoder-decoder model built from nn.Embedding, nn.RNN, and
nn.linear.
- sourceEmbedding : Seq2SeqEmbeddingSpec α srcVocabSize embedDim
Source embedding table.
- targetEmbedding : Seq2SeqEmbeddingSpec α tgtVocabSize embedDim
Target embedding table.
- encoder : Seq2SeqRNNEncoderSpec α embedDim hiddenDim
Encoder RNN parameters.
- decoder : Seq2SeqDecoderSpec α embedDim hiddenDim tgtVocabSize
Decoder parameters (RNN + output projection + optional self-attention).
Instances For
Teacher-forcing logits from discrete source and decoder input tokens.
sourceTokens supplies the encoder sequence. targetTokens supplies the decoder inputs: for
next-token prediction, these are the start token followed by the preceding target tokens. The
caller pairs the returned (tgtSeqLen × tgtVocabSize) logits with labels one position ahead.
The function embeds these inputs as given; it does not insert a start token or shift the sequence.
The encoder's final hidden state initializes the decoder. Optional decoder self-attention is causal, and embedding lookup treats the bounded token ids as discrete inputs without token-id gradients.
Instances For
Encode the source once and generate maxTgtLen target tokens greedily.
The encoder's final hidden state initializes the decoder, and startToken supplies its first input.
Each predicted token becomes the next decoder input. Optional causal self-attention uses that
growing input prefix, with the same attention parameters as teacher forcing. The returned pair
contains (maxTgtLen × tgtVocabSize) logits and maxTgtLen bounded token ids.
Instances For
Differentiable training + backward (one-hot inputs) #
This is the “full” training interface for the Seq2Seq baseline.
Differentiable forward pass for training (teacher forcing) using one-hot/token-distribution inputs.
This is the same computation as Seq2SeqSpec.forwardTraining, except that embedding lookup is
expressed as a matrix multiplication (forwardOneHot), so gradients can flow into the embedding
tables and back into upstream token distributions. tgtOneHot contains the decoder inputs, in the
same start-token/preceding-token order as the discrete path. For next-token prediction, the labels
must be supplied separately to the loss, one position ahead of these inputs.
Instances For
Per-timestep cross-entropy loss for the differentiable Seq2Seq baseline.
Computes:
- logits via
Seq2SeqSpec.forwardTrainingOneHot, - probabilities via
softmax, - cross-entropy against the target token distribution at each timestep.
PyTorch analogue: nn.CrossEntropyLoss applied per timestep (with probabilities represented as
one-hot).
Instances For
Compute (loss, grads) for the Seq2Seq baseline under per-timestep cross-entropy.
This returns gradients for:
- both embedding tables,
- the encoder RNN,
- the decoder RNN,
- the decoder output projection,
- and decoder self-attention (if present).
Instances For
Attention-augmented Seq2Seq specification (simple encoder-output attention).
This record extends the baseline with an additional projection matrix used by the helper
attention functions below (computeAttentionWeightsSpec / applyAttentionSpec).
Note: this file includes these attention helpers as a building block; the main baseline forward passes above do not integrate encoder-decoder cross-attention by default.
- sourceEmbedding : Seq2SeqEmbeddingSpec α srcVocabSize embedDim
Source embedding table.
- targetEmbedding : Seq2SeqEmbeddingSpec α tgtVocabSize embedDim
Target embedding table.
- encoder : Seq2SeqRNNEncoderSpec α embedDim hiddenDim
Encoder RNN parameters.
- decoder : Seq2SeqDecoderSpec α embedDim hiddenDim tgtVocabSize
Decoder parameters (RNN + output projection + optional self-attention).
- attentionWeights : TorchLean.Tensor α [hiddenDim, hiddenDim]
Attention projection matrix used to score encoder outputs against the decoder hidden state.
Instances For
Compute attention weights over encoder outputs for a single decoder hidden state.
This is a simple dot-product style attention:
- project the decoder hidden state (
attention_weights · decoder_hidden), - score each encoder hidden vector by an elementwise product + sum,
- normalize scores with
softmaxover the sequence axis.
It is inspired by classic encoder-decoder attention mechanisms (Bahdanau-style), and this spec keeps the scoring rule compact.
Instances For
Apply attention weights to encoder outputs (weighted sum / context vector).
Given attention weights a : (seqLen) and encoder outputs H : (seqLen × hiddenDim), returns the
context vector c = Σ_i a_i · H_i : (hiddenDim).