BLEU 指標

Eval

BLEU 指標

まとめ
  • BLEU は候補文と参照文の n-gram 一致度を使って翻訳品質を測る指標です。
  • n-gram の重なりと brevity penalty を実装し、スコアの出方を確認します。
  • 語順や同義語に弱い特性と複数参照で補う方法を整理します。

1. BLEU の考え方 #

  1. 1-gram〜n-gram(通常は 4-gram まで)について、候補文と参照文の一致度(modified precision)を計算する。
  2. 各 precision の対数平均を取り、幾何平均に変換する。
  3. 候補文が参照文より短い場合は brevity penalty を掛け、過度な省略を罰する。

BLEU スコアは 0〜1 の範囲を取り、値が大きいほど参照文に近い翻訳と見なされます。


2. Python 3.13 での実装例 #

標準ライブラリのみで BLEU を実装し、候補文と参照文のサンプルに適用します。

from __future__ import annotations

import math
from collections import Counter
from collections.abc import Iterable, Sequence


def ngram_counts(tokens: Sequence[str], n: int) -> Counter[tuple[str, ...]]:
    """トークン列から n-gram の頻度を数える。"""
    return Counter(tuple(tokens[i : i + n]) for i in range(len(tokens) - n + 1))


def modified_precision(
    candidate: Sequence[str],
    references: Iterable[Sequence[str]],
    n: int,
) -> tuple[int, int]:
    """候補文の n-gram が参照文に出現した件数と候補文の n-gram 総数を返す。"""
    cand_counts = ngram_counts(candidate, n)
    max_ref: Counter[tuple[str, ...]] = Counter()
    for ref in references:
        max_ref |= ngram_counts(ref, n)
    overlap = {ng: min(count, max_ref[ng]) for ng, count in cand_counts.items()}
    return sum(overlap.values()), max(1, sum(cand_counts.values()))


def brevity_penalty(candidate_len: int, reference_lens: Iterable[int]) -> float:
    """候補文が短すぎる場合に課す brevity penalty を計算する。"""
    if candidate_len == 0:
        return 0.0
    closest_ref_len = min(reference_lens, key=lambda r: (abs(r - candidate_len), r))
    ratio = candidate_len / closest_ref_len
    if ratio > 1:
        return 1.0
    return math.exp(1 - 1 / ratio)


def bleu(candidate: str, references: Sequence[str], max_n: int = 4) -> float:
    """候補文と参照文の集合から BLEU スコアを計算する。"""
    candidate_tokens = candidate.split()
    reference_tokens = [ref.split() for ref in references]
    precisions: list[float] = []
    for n_value in range(1, max_n + 1):
        overlap, total = modified_precision(candidate_tokens, reference_tokens, n_value)
        precisions.append(overlap / total)
    if min(precisions) == 0:
        return 0.0
    geometric_mean = math.exp(sum(math.log(p) for p in precisions) / max_n)
    penalty = brevity_penalty(len(candidate_tokens), (len(ref) for ref in reference_tokens))
    return penalty * geometric_mean


if __name__ == "__main__":
    candidate_sentence = "the cat is on the mat"
    reference_sentences = [
        "there is a cat on the mat",
        "the cat sits on the mat",
    ]
    score = bleu(candidate_sentence, reference_sentences)
    print(f"BLEU = {score:.3f}")

出力例:

BLEU = 0.638

3. 長所 #

  • 実装が容易で高速。翻訳ベンチマークの比較指標として長く使われている。
  • 参照文を複数用意すると、言い換えに対する耐性が上がる。

4. 注意点 #

  • 同義語や語順の違いに弱く、意味が同じでもスコアが低く出ることがある。
  • 長文要約では人手評価との相関が低くなる場合がある。
  • 日本語など語境界が曖昧な言語では、分かち書きなどで適切にトークン化してから計算する。

まとめ #

  • BLEU は n-gram 一致度と brevity penalty で翻訳の品質を推定する自動評価指標。
  • Python 3.13 では標準ライブラリで容易に実装でき、型ヒント付きにしておくと再利用性が高まる。
  • 語彙の多様性や語順の違いを評価したい場合は、ROUGE や METEOR などの指標も併用して総合的に判断しよう。