HardNLP

BLEU Score

NLP · Metrics & Evaluation

Hard

Problem

BLEU (Bilingual Evaluation Understudy) is the standard metric for evaluating machine translation quality. It measures how many n-grams in the candidate translation appear in the reference, with a penalty for translations that are too short.

Given a candidate translation, a reference translation (both as token lists), and a maximum n-gram order, compute the BLEU score.

Algorithm

  1. For each n-gram order from 1 to max_n, compute modified precision by clipping candidate n-gram counts by reference counts:

p_n = \frac{\sum_{ng} \min(C_{ng},\; R_{ng})}{\sum_{ng} C_{ng}}

  1. Compute the brevity penalty to penalize short translations:

BP = \begin{cases} 1 & \text{if } c \ge r \\ e^{1 - r/c} & \text{if } c < r \end{cases}

  1. Combine into the BLEU score using the geometric mean of precisions:

\text{BLEU} = BP \cdot \exp\left(\frac{1}{N} \sum_{n=1}^{N} \log p_n\right)

If any precision is zero, BLEU is zero.

Return the BLEU score as a float between zero and one.

Theory

BLEU (Bilingual Evaluation Understudy) is the standard automatic metric for evaluating machine translation quality. It measures how similar a candidate translation is to one or more reference translations.

The core idea: count how many n-grams (sequences of n consecutive words) in the candidate also appear in the reference. More matching n-grams means a better translation.


Why BLEU Exists

Before BLEU, evaluating translation quality required expensive human judgments. BLEU provided a fast, automatic metric that correlates reasonably well with human assessments.

BLEU is now used beyond translation:


The Components of BLEU

BLEU combines three elements:

1. N-gram precision: What fraction of candidate n-grams appear in the reference?

2. Modified precision (clipping): Prevents gaming by repeating words.

3. Brevity penalty: Penalizes translations that are too short.


Modified N-gram Precision

Simple precision would count how many candidate n-grams appear in the reference:

p_n = \frac{\text{matching n-grams}}{\text{total candidate n-grams}}

But this is gameable. If the candidate is just "the the the the", it might match many references since "the" is common.

Modified precision clips counts:

For each n-gram, count its occurrences in the candidate, but cap at its maximum count in any reference:

p_n = \frac{\sum_{\text{n-gram}} \min(C_{\text{n-gram}}, R_{\text{n-gram}})}{\sum_{\text{n-gram}} C_{\text{n-gram}}}

Where:


A Detailed Example

Reference: "the cat sat on the mat" Candidate: "the the the cat mat"

Unigram counts:

Candidate counts: the=3, cat=1, mat=1 (total: 5) Reference counts: the=2, cat=1, sat=1, on=1, mat=1

Modified precision for unigrams:

Clipped matches: 2 + 1 + 1 = 4 Total candidate unigrams: 5

p_1 = 4/5 = 0.8

Without clipping, we would count all three "the"s, getting 5/5 = 1.0, which is misleading.


Brevity Penalty

A short translation can have high precision by only including words it is confident about. To counter this, BLEU adds a brevity penalty (BP):

BP = \begin{cases} 1 & \text{if } c \geq r \\ e^{1 - r/c} & \text{if } c < r \end{cases}

Where:

Example:

The score is multiplied by 0.368, heavily penalizing the too-short translation.

If the candidate is longer than the reference, BP = 1 (no penalty). BLEU does not explicitly penalize verbosity, though low precision on longer outputs naturally reduces the score.


Combining Into the Final Score

BLEU uses the geometric mean of precisions across different n-gram orders:

\text{BLEU} = BP \cdot \exp\left(\frac{1}{N} \sum_{n=1}^{N} \log p_n\right)

Where N is the maximum n-gram order (typically 4, written as BLEU-4).

The geometric mean ensures that a zero precision at any level makes the entire score zero. This prevents translations that only get unigrams right but fail on longer phrases.


Handling Zero Counts

If any p_n = 0 (no matching n-grams of that order), the log is undefined (negative infinity).

Smoothing techniques:

For single sentences, smoothing is important since zero n-gram matches are common.


Corpus-Level vs. Sentence-Level

Corpus-level BLEU:

Sentence-level BLEU:


Interpreting BLEU Scores

BLEU scores range from 0 to 1 (often reported as 0 to 100):

BLEU = 0.60-1.00: Very high quality, near human level BLEU = 0.40-0.60: Understandable, good quality BLEU = 0.20-0.40: The gist is clear but many errors BLEU = 0.00-0.20: Poor quality

These ranges are rough. BLEU scores are most meaningful when comparing systems on the same test set.


Limitations of BLEU

Synonyms not rewarded: "fast" and "quick" mean the same thing, but BLEU treats them as completely different words.

Word order flexibility: Some languages allow flexible word order. BLEU penalizes valid reorderings.

No semantic understanding: "The cat sat on the mat" and "The mat was sat on by a cat" have different n-grams but the same meaning.

Gaming: Systems can be optimized for BLEU without actually improving translation quality.


Alternatives to BLEU

METEOR: Includes synonyms and stemming, correlates better with human judgment.

ROUGE: Used for summarization, focuses on recall rather than precision.

BERTScore: Uses neural embeddings for semantic similarity.

Human evaluation: Still the gold standard, but expensive.

BLEU remains popular due to its simplicity, speed, and widespread adoption despite its limitations.

Examples

Example 1

Input
candidate = ["the", "cat", "sat", "on", "the", "mat"], reference = ["the", "cat", "sat", "on", "the", "mat"], max_n = 4
Output
1.0
Explanation
Every modified precision equals one and no brevity penalty applies.

Example 2

Input
candidate = ["the", "cat", "is", "here"], reference = ["the", "cat", "was", "here"], max_n = 2
Output
0.5

Hints

  1. Use tuples as n-gram keys and clip each candidate count by its reference count.
  2. Return zero for any zero precision; otherwise combine log precisions and the brevity penalty.

Requirements

Constraints

Starter Code

import math
from collections import Counter

def bleu_score(candidate: list, reference: list, max_n: int) -> float:
    """
    Returns the unsmoothed BLEU score.
    """
    # Write code here
    pass

Test Cases

CaseMatches
Perfect matchpublic
Partial matchpublic