ROUGE
Recall-Oriented Understudy for Gisting Evaluation
ROUGE stands for Recall-Oriented Understudy for Gisting Evaluation, a metric that measures how much a model’s generated text overlaps with the reference. It was originally built for evaluating summarisation, but it is used across text generation generally — QA, translation, and so on.
The core idea is simple: how many of the words in the reference show up in the prediction?
The shared formulas: precision, recall, F1
Every ROUGE variant follows the same framework. Only the unit in which overlap is counted changes.
\[\text{Recall} = \frac{|\text{overlap}|}{|\text{Reference}|}\] \[\text{Precision} = \frac{|\text{overlap}|}{|\text{Prediction}|}\] \[F_1 = \frac{2 \times P \times R}{P + R}\]- Recall: how much of the reference did we capture (did we miss anything)
- Precision: what fraction of the prediction was actually in the reference (is there any nonsense)
- F1: the harmonic mean of the two. This is what HuggingFace
evaluatereports by default.
The running example
We will compute every ROUGE variant on the example below.
Reference: "Seoul has about ten million residents and is the capital"
Prediction: "Seoul is the capital and has about nine million residents"
Tokenised on whitespace:
R = [Seoul, has, about, ten, million, residents, and, is, the, capital] -> m = 10
P = [Seoul, is, the, capital, and, has, about, nine, million, residents] -> n = 10
ROUGE-1 (unigram)
Formula
\[\text{ROUGE-1}_{recall} = \frac{\sum_{w \in R} \min(\text{count}_R(w),\ \text{count}_P(w))}{\sum_{w \in R} \text{count}_R(w)}\]For each unigram $w$, take the smaller of its count in the reference and its count in the prediction, and sum.
Counting
| Reference token | In the prediction? |
|---|---|
| Seoul | ✅ |
| has | ✅ |
| about | ✅ |
| ten | ❌ (the prediction says “nine”) |
| million | ✅ |
| residents | ✅ |
| and | ✅ |
| is | ✅ |
| the | ✅ |
| capital | ✅ |
Overlapping tokens: everything but “ten” → $|\text{overlap}| = 9$
Computation
\[R = \frac{9}{10} = 0.9\] \[P = \frac{9}{10} = 0.9\] \[F_1 = \frac{2 \times 0.9 \times 0.9}{0.9 + 0.9} = 0.9\]ROUGE-2 (bigram)
Formula
\[\text{ROUGE-2}_{recall} = \frac{\sum_{b \in R_{bigram}} \min(\text{count}_R(b),\ \text{count}_P(b))}{\sum_{b \in R_{bigram}} \text{count}_R(b)}\]Overlap is counted over pairs of adjacent words (bigrams) instead of unigrams.
Counting
Reference bigrams (9):
(Seoul, has) (has, about) (about, ten)
(ten, million) (million, residents) (residents, and)
(and, is) (is, the) (the, capital)
Prediction bigrams (9):
(Seoul, is) (is, the) (the, capital)
(capital, and) (and, has) (has, about)
(about, nine) (nine, million) (million, residents)
Overlapping bigrams:
(has, about) ✅ (million, residents) ✅ (is, the) ✅ (the, capital) ✅
everything else ❌
$|\text{overlap}| = 4$
Computation
\[R = \frac{4}{9} \approx 0.4444\] \[P = \frac{4}{9} \approx 0.4444\] \[F_1 \approx 0.4444\]ROUGE-2 comes out lower than ROUGE-1 because a word only counts once it appears adjacent to the right neighbour. “Seoul” and “and” overlap as unigrams, but their surrounding tokens differ, so they contribute to no bigram at all.
ROUGE-L (longest common subsequence)
Formula
This one uses the length of the LCS (longest common subsequence).
\[R_{lcs} = \frac{\text{LCS}(R, P)}{m}\] \[P_{lcs} = \frac{\text{LCS}(R, P)}{n}\] \[F_{lcs} = \frac{(1 + \beta^2) \times R_{lcs} \times P_{lcs}}{R_{lcs} + \beta^2 \times P_{lcs}}\]$m$ = reference length, $n$ = prediction length, $\beta = P_{lcs} / R_{lcs}$ (setting it this way makes $F_{lcs}$ identical to $F_1$).
The key point: a subsequence does not have to be contiguous. It can skip, as long as the original order is preserved.
Finding the LCS (the DP table)
R = [Seoul, has, about, ten, million, residents, and, is, the, capital]
P = [Seoul, is, the, capital, and, has, about, nine, million, residents]
DP table $L[i][j]$ = the LCS length of $R[0..i]$ and $P[0..j]$:
| Seoul | is | the | capital | and | has | about | nine | million | residents | |
|---|---|---|---|---|---|---|---|---|---|---|
| Seoul | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| has | 1 | 1 | 1 | 1 | 1 | 2 | 2 | 2 | 2 | 2 |
| about | 1 | 1 | 1 | 1 | 1 | 2 | 3 | 3 | 3 | 3 |
| ten | 1 | 1 | 1 | 1 | 1 | 2 | 3 | 3 | 3 | 3 |
| million | 1 | 1 | 1 | 1 | 1 | 2 | 3 | 3 | 4 | 4 |
| residents | 1 | 1 | 1 | 1 | 1 | 2 | 3 | 3 | 4 | 5 |
| and | 1 | 1 | 1 | 1 | 2 | 2 | 3 | 3 | 4 | 5 |
| is | 1 | 2 | 2 | 2 | 2 | 2 | 3 | 3 | 4 | 5 |
| the | 1 | 2 | 3 | 3 | 3 | 3 | 3 | 3 | 4 | 5 |
| capital | 1 | 2 | 3 | 4 | 4 | 4 | 4 | 4 | 4 | 5 |
$\text{LCS}(R, P) = 5$ → [Seoul, has, about, million, residents]
“and”, “is”, “the” and “capital” all exist on both sides, but in the reference they come after “residents” while in the prediction they come before “has”, so they cannot be part of the LCS at the same time as [has, about, million, residents].
Computation
\[R_{lcs} = \frac{5}{10} = 0.5\] \[P_{lcs} = \frac{5}{10} = 0.5\] \[F_1 = 0.5\]Here ROUGE-L < ROUGE-1. ROUGE-1 ignores order and counts 9 tokens; ROUGE-L counts only 5 because of the ordering constraint.
An extreme case where ROUGE-1 = 1.0 but ROUGE-L is low
R = [A, B, C]
P = [C, B, A]
ROUGE-1: overlap = {A, B, C} -> 3/3 = 1.0
ROUGE-L: LCS = [A] or [B] or [C] -> 1/3 = 0.33
Every word is present, but the order is completely reversed, so ROUGE-L falls sharply. This is exactly why ROUGE-L exists.
ROUGE-Lsum
Formula
For multi-sentence text, compute the LCS per reference sentence and sum.
When the reference consists of sentences $r_1, r_2, \ldots, r_k$:
\[R_{lsum} = \frac{\sum_{i=1}^{k} \text{LCS}(r_i, P)}{m}\]Example
Reference:
r1: "Seoul has about ten million residents" (6 tokens)
r2: "Seoul is the capital of South Korea" (7 tokens)
Prediction:
"Seoul is the capital and has about nine million residents"
LCS(r1, P) = [Seoul, has, about, million, residents] -> 5
LCS(r2, P) = [Seoul, is, the, capital] -> 4
Recall = (5 + 4) / (6 + 7) = 9/13 ~ 0.6923
Applying ROUGE-L once over the whole text ignores sentence boundaries, so a match in one sentence can get in the way of another sentence’s score. ROUGE-Lsum computes each sentence independently, which is fairer for tasks like summarisation where there are several sentences.
For a single sentence, ROUGE-L = ROUGE-Lsum.
Overall comparison
| Metric | Overlap unit in the formula | F1 on the example | Character |
|---|---|---|---|
| ROUGE-1 | unigram | 0.9 | most lenient; ignores order |
| ROUGE-2 | bigram | 0.4444 | phrase similarity; requires adjacency |
| ROUGE-L | LCS | 0.5 | order-aware; allows gaps |
| ROUGE-Lsum | sum of per-sentence LCS | - | improves ROUGE-L for multi-sentence text |
Generally: ROUGE-1 >= ROUGE-L >= ROUGE-2
The limits of ROUGE
It cannot catch synonyms
Reference: "glad"
Prediction: "happy"
-> ROUGE = 0 (same meaning, different token)
Writing at length dilutes precision
Reference: "symphony" -> m = 1
Prediction: "Wagner wrote a symphony" -> n = 4
Recall = 1/1 = 1.0 (caught every reference token)
Precision = 1/4 = 0.25 (75% of the prediction is unnecessary)
F1 = 2 x (1.0 x 0.25) / (1.0 + 0.25) = 0.4
It does not see semantic accuracy
Reference: "2023"
Prediction: "2024"
-> ROUGE-1 = 0, even though it is essentially almost right
Because of limits like these, practitioners use ROUGE alongside BERTScore (embedding similarity), exact match, and human evaluation rather than on its own.
Computing it in Python
import evaluate
rouge = evaluate.load("rouge")
predictions = ["Seoul is the capital and has about nine million residents"]
references = ["Seoul has about ten million residents and is the capital"]
results = rouge.compute(predictions=predictions, references=references)
print(results)
# {'rouge1': 0.9, 'rouge2': 0.4444, 'rougeL': 0.5, 'rougeLsum': 0.5}