TAI BUI
Back to curriculum
LearnPython

T5, BART — Encoder-Decoder Models

Encoders read. Decoders write. Put them together and you get a model built for input-to-output tasks: translate, summarize, rewrite, transcribe.

TIME: ~45 minutesPREREQUISITES: Phase 7 · 05 (Full Transformer), Phase 7 · 06 (BERT), Phase 7 · 07 (GPT)

Learning Objectives

  • Describe how an encoder-decoder transformer carries information from the encoder to the decoder through cross-attention.
  • Build T5-style span corruption and verify that the corrupted source and the target round-trip to the original sentence.
  • Contrast T5's span-corruption objective with BART's five denoising noise functions, including why BART's pretraining costs more compute.
  • Decide whether a new task calls for an encoder-decoder or a decoder-only model.

The Problem

An encoder-decoder model is a transformer built from two stacks. A bidirectional encoder reads the whole input and produces a dense representation of it. A causal decoder generates the output one token at a time, cross-attending to that representation as it goes. Training is shift-by-one on the output side: the same loss as GPT, conditioned on the encoder output.

GPT and BERT each strip the 2017 architecture down for a different goal. Plenty of tasks are naturally input-to-output, though:

  • Translation: English → French.
  • Summarization: 5,000-token article → 200-token summary.
  • Speech recognition: audio tokens → text tokens.
  • Structured extraction: prose → JSON.

For these, encoder-decoder is the cleanest fit. The source gets its own stack, and the output gets its own.

Two papers set the modern playbook:

  1. T5 (Raffel et al. 2019), the "Text-to-Text Transfer Transformer." NLP tasks are reframed as text in, text out, with a single architecture, a single vocabulary, and a single loss. Pretraining is masked span prediction: corrupt spans in the input, decode them in the output.
  2. BART (Lewis et al. 2019), the "Bidirectional and Auto-Regressive Transformer." A denoising autoencoder: corrupt the input in several ways (shuffle, mask, delete, rotate) and ask the decoder to reconstruct the original.

In 2026 the format survives where input structure matters:

  • Whisper (speech → text).
  • Google's translation stack.
  • Some code-completion and repair models that have distinct context-and-edit structures.
  • Flan-T5 and variants for structured reasoning tasks.

Decoder-only took the spotlight, but encoder-decoder stayed in use.

Warm-up0/2
1. Which of these models lets every token attend to tokens on both sides of it?Warm-up
Two panels, GPT and BERT, each showing the tokens The, cat, sat, on, the, mat with the third token sat highlighted and a question mark under the line asking which positions sat can attend to.
Same sentence in both panels. The question mark is what the stem asks about.
2. In the original transformer's decoder, where do the keys and values in cross-attention come from?Warm-up
An encoder on the left producing an encoder output, and a decoder block on the right with masked self-attention, cross-attention and FFN layers. A dashed stub labelled K, V with a question mark enters cross-attention from the left and is not connected to anything.
Where the keys and values come from is left open.
Check: The Problem0/2
1. What is an encoder-decoder transformer?Define
Four input-to-output tasks from the lesson: English to French, a 5,000-token article to a 200-token summary, audio tokens to text tokens, and prose to JSON. All four feed into one central box marked with a question mark.
Task pairs from the lesson's Problem section.
2. What is teacher forcing when training the decoder?Define
A table of four decoder steps for the target starting with extra_id_0, brown, extra_id_1, over. The decoder input column shows a start token at step 1 and question marks at steps 2 to 4. The label column lists the target tokens.
First four target tokens of the lesson's span-corruption example. The question marks are the inputs the stem asks about.

The Concept

Encoder-decoder with cross-attention

The forward loop

source tokens ─▶ encoder ─▶ (N_src, d_model)  ──┐
                                                 │
target tokens ─▶ decoder block                   │
                 ├─▶ masked self-attention       │
                 ├─▶ cross-attention ◀───────────┘
                 └─▶ FFN
                ↓
              next-token logits

The encoder runs once per input. The decoder runs autoregressively but cross-attends to the same encoder output at each step, so you can cache that output. For long inputs it's a free speedup.

T5 pretraining: span corruption

Span corruption is T5's pretraining objective. Pick random spans of the input and replace each with a unique sentinel token, then train the decoder to output the missing spans. The spans average 3 tokens and cover 15% of the input in total (the T5 paper's rate). The sentinels are <extra_id_0>, <extra_id_1>, and so on. The decoder outputs only the corrupted spans, each with its sentinel prefix:

source: The quick <extra_id_0> fox jumps <extra_id_1> dog
target: <extra_id_0> brown <extra_id_1> over the lazy

That's a cheaper signal than predicting the whole sequence, and it was competitive with MLM (BERT) and prefix-LM (UniLM) in the T5 paper's ablation.

BART pretraining: multi-noise denoising

Denoising is BART's objective. Apply a noise function to the input, then train the decoder to reconstruct the original text. BART tries five noising functions:

  1. Token masking.
  2. Token deletion.
  3. Text infilling (mask a span, decoder inserts the right length).
  4. Sentence permutation.
  5. Document rotation.

Text infilling combined with sentence permutation gave the best downstream numbers. The decoder's target is the full original sequence, not just the corrupted spans, so pretraining compute is higher than T5's.

Inference

Generation works the same way as in GPT, and greedy, beam, and top-p sampling all apply. Beam search (width 4–5) is standard for translation and summarization because the output distribution is narrower than chat.

When to pick each variant in 2026

TaskEncoder-decoder?Why
TranslationYes, usuallyClear source sequence; fixed output distribution; beam search works
Speech-to-textYes (Whisper)Input modality differs from output; encoder shapes audio features
Chat / reasoningNo, decoder-onlyNo persistent "input"; the conversation is the sequence
Code completionUsually noDecoder-only with long context wins; code models like Qwen 2.5 Coder are decoder-only
SummarizationEither worksBART, PEGASUS beat earlier decoder-only baselines; modern decoder-only LLMs match them
Structured extractionEitherT5 is clean because "text → text" absorbs any output format

Since about 2022, decoder-only has taken over tasks that encoder-decoder used to own, for three reasons: (a) instruction-tuned decoder-only LLMs generalize to new tasks through prompting, (b) one architecture scales more easily than two, and (c) RLHF assumes a decoder. Encoder-decoder holds on where the input modality differs (speech, images) or where beam search quality matters.

Check: The Concept0/18
1. Which statement about cross-attention in an encoder-decoder is correct?Define
An encoder with its output box on the left and a decoder block on the right with masked self-attention, cross-attention and FFN layers. A dashed line with a question mark runs from the encoder output toward the decoder block boundary without touching any layer.
How encoder information enters the decoder block is left open.
2. In T5's span corruption pretraining, what does the decoder output?Define
The corrupted encoder input The quick extra_id_0 fox jumps extra_id_1 dog flows into an encoder and decoder box, and from there to a decoder output box containing a question mark.
Source line from the lesson's span-corruption example; the target is not shown.
3. What is the denoising objective that BART pretrains with?Define
A noised 14-token excerpt with four mask tokens highlighted flows into an encoder and decoder box, then to a decoder target box containing a question mark.
Encoder input is the output of the lesson's token_mask(rate=0.2) with seed 1 on the first 14 tokens.
4. What does a sentinel token such as `<extra_id_0>` do in T5 span corruption?Define
The source row The quick extra_id_0 fox jumps extra_id_1 dog with the extra_id_0 chip highlighted and an arrow from it to a box asking for its role, followed by a question mark.
Source line from the lesson's span-corruption example.
5. What does beam search do while generating the output?Define
A bar chart of four candidate first words with illustrative probabilities Le 0.40, La 0.30, Les 0.20, Un 0.10, and below it a box asking which partial sequences are carried on to the next step, with a question mark.
Probabilities are made up for illustration, not from a model.
6. Which combination of BART's five noising functions gave the best downstream numbers?Define
Five rows, one per BART noising function: token masking, token deletion, text infilling, sentence permutation and document rotation, each with a short mechanism and a question mark at the right end for its downstream result.
Mechanisms summarized from the lesson's list; downstream results are not shown.
7. Why is caching the encoder output a free speedup for encoder-decoder models?Explain
A source and encoder box above a row of four decoder steps, with no links drawn between them, and a box asking how many encoder passes happen over the source during the four steps, with a question mark.
Four steps are an arbitrary example length.
8. How does cross-attention in a decoder block differ from its masked self-attention?Explain
On the left a target prefix box and an encoder output box. On the right a decoder block with a masked self-attention layer and a cross-attention layer, each marked reads: question mark. No arrows connect the left boxes to the layers.
The target prefix is a typed example of a decoder input, not captured output.
9. Why is T5's span corruption pretraining cheaper than BART's denoising objective?Explain
Two rows, T5 span corruption and BART denoising. Each takes a corrupted or noised version of the same 25-token sentence through an encoder and decoder box to a decoder emits question mark.
The 25-token sentence is the lesson's demo sentence.
10. How does BART's text infilling differ from plain token masking?Explain
Fourteen tokens with indices 0 to 13 in two rows, tokens 1 to 3 (quick brown fox) highlighted. Below, two rows for token masking and text infilling, each asking for the encoder input at that span with a question mark.
Excerpt is the first 14 tokens of the lesson's demo sentence.
11. What beam width is standard for translation and summarization, and why?Explain
A code panel with the lesson's translate call and a generate call that has max_new_tokens=32 and num_beams set to a question mark.
Lesson's example call with a num_beams argument added (typed, not captured).
12. Why does encoder-decoder remain the right choice for speech-to-text like Whisper?Explain
A waveform box labelled audio in, an arrow to a box asking which architecture, and an arrow to a row of four token chips labelled text out.
The waveform is a schematic, not real audio.
13. Which of these is one of the reasons decoder-only has taken over tasks encoder-decoder used to own since about 2022?Explain
A two-column table of six tasks and whether encoder-decoder fits: translation yes usually, speech-to-text yes (Whisper), chat and reasoning no decoder-only, code completion usually no, summarization either works, structured extraction either.
Columns from the Concept section's task table; the Why column is left out.
14. A translation service re-runs the encoder on the same source at every decoding step. Output is correct but slow. What did the authors miss?Analyze
Four columns, each with an encoder box labelled same source above a decoder step box, numbered 1 to 4, with an arrow from the encoder down to the decoder step in every column.
The service as the stem describes it.
15. After a refactor, the decoder blocks keep masked self-attention and the FFN but drop cross-attention. Training loss still falls, yet outputs ignore the source. Why?Analyze
An encoder on the left with an encoder output box, and a decoder block on the right that has only masked self-attention and FFN layers. A dashed line with a question mark joins the encoder output to the decoder side.
Cross-attention has been removed from the block.
16. You are building a support chatbot where the whole job is one running conversation. Which starting point fits best?Design
A chat transcript of four turns between a user and a bot, and below it a box asking for the best starting point, with a question mark.
Turns are a typed example.
17. You must ship an English-to-French translator. The source is a clear sequence and the output distribution is narrow. Which setup fits best?Design
A list of three requirements: English to French, source is a clear sequence, output distribution is narrow. Below, two rows for architecture and decoding, each with a question mark.
Requirements restate the stem.
18. You are building a code-completion assistant that must use a long context of the surrounding repository. Where should you start?Design
Six file boxes inside a bracket labelled surrounding repository, a long context, an arrow to a cursor box, and below it a box asking where the model design should start, with a question mark.
Schematic; the number of files is arbitrary.

Build It

See code/main.py. It implements T5-style span corruption on a toy corpus. This is the piece of the lesson worth building yourself, because span corruption is the core of T5's pretraining.

Step 1: span corruption

python
def corrupt_spans(tokens, mask_rate=0.15, mean_span=3.0, rng=None):
    """Pick spans summing to ~mask_rate of tokens. Return (corrupted_input, target)."""
    n = len(tokens)
    n_mask = max(1, int(round(n * mask_rate)))
    n_spans = max(1, int(round(n_mask / mean_span)))
    ...

The default mask_rate is the paper's 0.15, but the demo in main() passes 0.20. The target format is the T5 convention: each <extra_id_N> sentinel followed by its span, then one closing sentinel after the last span (<extra_id_0> span0 <extra_id_1> span1 <extra_id_2>). The Concept example leaves that closing sentinel out for brevity. The corrupted input interleaves unchanged tokens with the same sentinels at the span locations.

Step 2: verify round-trip

Given the corrupted input and target, reconstruct the original sentence. If the corruption is reversible, the forward pass is well-defined. This is a sanity check. Real training doesn't do it, but it's cheap and catches off-by-one bugs in your span bookkeeping.

Step 3: BART noising

Five functions: token_mask, token_delete, text_infill, sentence_permute, document_rotate. main() demonstrates each one on its own, so run it and compare each output against the original.

Check: Build It0/9
1. How many noising functions does Build It list for BART-style corruption?Define
Three rows for Build It steps: span corruption, verify round-trip, and BART noising. In the third row the list of function names is covered by one grey bar followed by a question mark and the words functions in total.
Step titles from the lesson; the list in Step 3 is covered.
2. What does the round-trip step in Build It check?Explain
A terminal showing python main.py output: the corrupted source with extra_id_0 and extra_id_1, the decoder target extra_id_0 brown extra_id_1 stitch in extra_id_2, and the line reconstruction matches original: YES.
Captured by running the lesson's main.py; the long source line is wrapped for display.
3. In the demo, `corrupt_spans` gets 25 tokens, `mask_rate=0.20` and `mean_span=3.0`. What do `n_mask` and `n_spans` come out to?Apply
A call panel corrupt_spans(tokens, mask_rate=0.20, mean_span=3.0), a grid of 25 numbered token cells from 0 to 24, and a box reading n_mask equals question mark, n_spans equals question mark.
Call as in the stem; the sentence is the demo's 25-token sentence.
4. Given source `["a", "<extra_id_0>", "d", "e"]` and target `["<extra_id_0>", "b", "c", "<extra_id_1>"]`, what does `round_trip` return?Apply
A source list a, extra_id_0, d, e and a target list extra_id_0, b, c, extra_id_1, with an arrow labelled round_trip(source, target) pointing to a box that says returns followed by a question mark.
Lists from the stem.
5. `corrupt_spans` picks 3 spans. How many sentinel tokens appear in the source and in the target?Apply
The 25-token demo sentence with three separate spans highlighted in three colours, and a box reading sentinels in the source question mark, in the target question mark.
Spans chosen by the lesson's corrupt_spans (mask_rate=0.4, seed 0); the spans are the three coloured runs.
6. `document_rotate` runs on `["a", "b", "c", "d", "e"]` and draws a pivot of 2. What does it return?Apply
Five boxes a, b, c, d, e with indices 0 to 4, the note pivot = 2, and an arrow labelled document_rotate(tokens) to five empty boxes marked with question marks.
Input and pivot from the stem.
7. A 200-token document goes through T5-style corruption at a 15% rate with a mean span of 3. About how many spans and masked tokens result?Apply
A grid of 200 empty token cells, ten rows of twenty, labelled 200 tokens, and a box reading mask rate 15%, mean span 3: spans question mark, masked tokens question mark.
Each cell is one token.
8. Your `round_trip` check prints NO for a corrupted pair from `corrupt_spans`. What is the most likely cause?Analyze
A panel with the three lines of main() that call round_trip and print YES or NO, followed by a python main.py command and the output reconstruction matches original: NO.
Check code from the lesson's main(); the NO outcome is the stem's scenario (typed, not captured).
9. You run the demo and the corrupted source looks heavier than the 15% the paper uses. What explains it?Analyze
An abridged main.py showing the corrupt_spans signature with mask_rate=0.15 as its default, and a call inside main() with its arguments elided, plus a box asking why the demo's corrupted source looks heavier than 15%.
Signature from the lesson; the call's arguments are elided.

Use It

HuggingFace reference:

python
from transformers import T5ForConditionalGeneration, T5Tokenizer
tok = T5Tokenizer.from_pretrained("google/flan-t5-base")
model = T5ForConditionalGeneration.from_pretrained("google/flan-t5-base")
 
inputs = tok("translate English to French: Attention is all you need.", return_tensors="pt")
out = model.generate(**inputs, max_new_tokens=32)
print(tok.decode(out[0], skip_special_tokens=True))

The T5 trick is to put the task name in the input text. One model handles dozens of tasks because each task is text in, text out. Instruction-tuned decoder-only models have since generalized the pattern, but T5 codified it first.

Check: Use It0/3
1. What is Flan-T5?Define
A code panel loading google/flan-t5-base with T5Tokenizer and T5ForConditionalGeneration, and below it a chain: T5 with span-corruption pretraining, an unknown step marked with a question mark, then Flan-T5.
Code lines from the lesson's HuggingFace reference.
2. What is 'the T5 trick' that lets one model handle dozens of tasks?Define
Three text inputs for translation, summarization and structured extraction feed one T5 model box that outputs text. A caption below asks how the model knows which task to do, with a question mark.
Task types from the lesson's Problem section.
3. You loaded `flan-t5-base` and now want it to summarize a paragraph instead of translating. What changes in the call?Apply
A code panel with the lesson's flan-t5-base loading lines, a tok call on the translate English to French input and a generate call, and below it a box asking what changes to summarize a paragraph instead, with a question mark.
Code from the lesson's HuggingFace reference; the input line is wrapped.

Ship It

See outputs/skill-seq2seq-picker.md. The skill picks between encoder-decoder and decoder-only for a new task, given its input-output structure, latency, and quality targets.

Check: Ship It0/1
1. What does the seq2seq-picker skill do?Define
Three input boxes, input-output structure, latency target and quality target, feed a box named seq2seq-picker skill, which points to an output box marked with a question mark.
Inputs as the Ship It section describes them; the output is what the stem asks about.
Author's Note

I'd read corrupt_spans and round_trip in main.py before going back to either paper. They're short, and running them shows what the decoder target actually contains: sentinels and the missing spans, not the whole sentence. Once that's concrete, the compute gap between T5 and BART is easy to see. T5's decoder emits only the spans, and BART's rebuilds the full sequence.

My bet is that the task table matters more than the architecture history. The question I'd ask about a new task is whether the input has a structure the output doesn't share, like audio going in and text coming out. If it does, a separate encoder is earning its place. If the whole job is one running conversation, I'd start with a decoder-only model.

One thing to watch when you run the script: the demo calls corrupt_spans with mask_rate=0.20, not the 0.15 default in Step 1, so the corrupted source will look a bit heavier than the 15% I described above.

Exercises

  1. Easy. Run code/main.py, read the span-corruption output for its 25-token demo sentence, and verify that concatenating the non-sentinel source tokens with the decoded target spans reproduces the original.
  2. Medium. main.py already implements BART's text_infill, which replaces each span with a single <mask> token and draws span lengths from a Gaussian. Extend it to draw lengths from a Poisson distribution with mean 3, as the BART paper does, and show one example.
  3. Hard. Fine-tune flan-t5-small on a tiny English → pig-Latin corpus (200 pairs). Measure BLEU on a held-out 50-pair set. Compare against fine-tuning Llama-3.2-1B on the same data with the same compute.

Key Terms

TermWhat people sayWhat it actually means
Encoder-decoder"Seq2seq transformer"Two stacks: a bidirectional encoder for the input and a causal decoder with cross-attention for the output.
Cross-attention"Where source talks to target"The decoder's Q against the encoder's K/V. It's the only path by which encoder information reaches the decoder.
Span corruption"T5's pretraining trick"Replace random spans with sentinel tokens; the decoder outputs the spans.
Denoising objective"BART's game"Apply a noise function to the input and train the decoder to reconstruct the clean sequence.
Sentinel token"The <extra_id_N> placeholder"A special token that tags a corrupted span in the source and re-tags it in the target.
Flan"Instruction-tuned T5"T5 fine-tuned on >1,800 tasks; made encoder-decoder competitive at instruction-following.
Beam search"Decoding strategy"Keep the top-k partial sequences at each step; standard for translation and summarization.
Teacher forcing"Training-time input"During training, feed the decoder the true previous output token instead of the sampled one.

Further Reading

Code

Ships With