Part XI — Sequence Models: RNNs, LSTMs, Attention & The Transformer Block · Chapter 5

Sequence-to-Sequence models & encoder-decoders

Hook

Every RNN — short for Recurrent Neural Network — and gated cell so far has produced one output per input step — same length in, same length out. Translation doesn't work that way: a 3-word sentence in one language might need 5 words in another. How do you connect two sequences that don't even agree on length?

Intuition

One context vector, decoded out to however many steps you ask for. The encoder that produced it never runs again — everything the decoder knows about the input has to already be packed into that single number.

Formalize

An encoder RNN reads the entire input sequence and keeps only its final hidden state — the context vector. A separate decoder RNN is initialized from that context and unrolls its own sequence, feeding each output back in as the next step's input, for as many steps as the task needs:

c=Encoder(x1,,xn),y1,,ym=Decoder(c)c = \text{Encoder}(x_1, \dots, x_n), \qquad y_1, \dots, y_m = \text{Decoder}(c)
  • cc — the context vector: the encoder's final hidden state, and the only channel of information passed to the decoder.
  • x1,,xnx_1, \dots, x_n — the input sequence, of length nn.
  • y1,,ymy_1, \dots, y_m — the output sequence, of length mm, produced by the decoder.
  • Encoder\text{Encoder} — the RNN that reads the entire input sequence and compresses it down to the context vector.
  • Decoder\text{Decoder} — the RNN that unrolls its own sequence starting from the context vector.
  1. Input and output lengths are decoupled

    Nothing ties nn and mm together — the decoder can unroll for however many steps the task needs, independent of how long the input sequence was.

  2. One network compresses, the other generates

    The encoder's job is compression; the decoder's job is generation — and the context vector is the only channel between them.

Play

Three input steps compress down to one number. That same number then drives four decoder steps, each depending only on the context and the decoder's own previous output — the input sequence itself is gone the moment the context vector is formed.

Worked example

Input sequence x=(1,1,1)x = (1, -1, 1), encoder recurrence ht=tanh(xt+0.5ht1)h_t=\tanh(x_t + 0.5\,h_{t-1}):

  1. Encoding compresses three steps into one number
    • h1=tanh(1)0.7616h_1=\tanh(1)\approx 0.7616
    • h2=tanh(1+0.5(0.7616))0.5506h_2=\tanh(-1+0.5(0.7616))\approx -0.5506
    • h3=tanh(1+0.5(0.5506))0.6198h_3=\tanh(1+0.5(-0.5506))\approx 0.6198

    The context vector is c=h30.6198c=h_3\approx 0.6198 — everything about the 3-token input, folded into one scalar.

  2. Decoding starts from the context, not the input

    y1=tanh(c)0.5510y_1=\tanh(c)\approx 0.5510. From here the decoder only sees its own last output: y2=tanh(0.5y1)0.2687y_2=\tanh(0.5\,y_1)\approx 0.2687.

  3. The same context, unrolled further or not at all

    Running the decoder for 4 steps instead of 2, continuing the exact same recurrence from y2y_2:

    • y3=tanh(0.5y2)=tanh(0.5×0.2687)0.1336y_3=\tanh(0.5\,y_2)=\tanh(0.5\times0.2687)\approx 0.1336
    • y4=tanh(0.5y3)=tanh(0.5×0.1336)0.0667y_4=\tanh(0.5\,y_3)=\tanh(0.5\times0.1336)\approx 0.0667

    Each output smaller than the last, since nothing new is feeding the recurrence. The output length was never determined by the input length; it's just how many times you choose to run the decoder.

Checkpoint

Find the decode length whose final output has decayed below 0.1 in magnitude.

Pick a decode length to try it
Summary
c=Encoder(x1:n),y1:m=Decoder(c)— n and m need not matchc = \text{Encoder}(x_{1:n}), \qquad y_{1:m} = \text{Decoder}(c) \qquad \text{— } n \text{ and } m \text{ need not match}

Encoder-decoder splits "understand the input" from "produce the output" into two separate networks connected by a single bottleneck vector. That bottleneck is also the design's biggest weakness: every detail of a long input has to survive being squeezed through one fixed-size vector, no matter how long the sequence was. This is exactly the problem attention was built to solve, back in Part IV — letting the decoder look back at every encoder state individually, instead of trusting one number to carry everything. The next chapter turns to a different bottleneck, one inside a single deep network rather than between two of them: what happens to a signal after it passes through far more layers than any network in this course has used so far.