nikkie-ftnextの日記

イベントレポートや読書メモを発信

rouge-scoreライブラリが日本語テキストからROUGEを算出できないのはなぜ? デフォルトでは英数字以外を除く挙動でした

はじめに

ちはやぶる 神代も聞かず 竜田川、nikkieです。

ライブラリrouge-scoreをそのまま使うと、日本語テキストのROUGEは算出できないことを知りました。

目次

rouge-score

評価指標ROUGEについてはこちらをどうぞ

大規模言語モデル入門』では rouge-score というライブラリが紹介されています。
Google ResearchによるROUGEの実装です。

サンプルコード

README(やPyPIのプロジェクトページ)の例を元にしています

  • Python 3.11.8
  • rouge-score 0.1.2
>>> from rouge_score.rouge_scorer import RougeScorer
>>> scorer = RougeScorer(["rouge1", "rouge2", "rougeL", "rougeLsum"])
>>> scorer.score("The quick brown fox jumps over the lazy dog", "The quick brown dog jumps on the log.")
{'rouge1': Score(precision=0.75, recall=0.6666666666666666, fmeasure=0.7058823529411765), 'rouge2': Score(precision=0.2857142857142857, recall=0.25, fmeasure=0.26666666666666666), 'rougeL': Score(precision=0.625, recall=0.5555555555555556, fmeasure=0.5882352941176471), 'rougeLsum': Score(precision=0.625, recall=0.5555555555555556, fmeasure=0.5882352941176471)}

score()メソッドは、先に来る引数がtarget(正解)で、後の引数がprediction(予測)です。
https://github.com/google-research/google-research/blob/bbb2376225c54168e44c2f749aa1173ee0ed5991/rouge/rouge_scorer.py#L110

Hugging Face Spacesで触る

rouge-scoreはhuggingface/evaluateでも使われています1

上の例を入力してみます。
※テキストはダブルクォートで囲む必要があります2

ROUGEのFスコアが計算されました!

分かち書きされた日本語テキストを入れる

半角スペースで区切ったテキストが入力されているので、分かち書きした日本語テキストを入力してみます。
predictionsとreferencesが一致する状態で試します。

predictionsとreferencesが一致するので、ROUGEのFスコアとしては1を期待していました。
ところが、算出されたのは0。

これは手元の環境でも再現します

>>> scorer.score("いぬ ねこ", "いぬ ねこ")
{'rouge1': Score(precision=0.0, recall=0.0, fmeasure=0.0), 'rouge2': Score(precision=0.0, recall=0.0, fmeasure=0.0), 'rougeL': Score(precision=0, recall=0, fmeasure=0), 'rougeLsum': Score(precision=0, recall=0, fmeasure=0)}

『大規模言語モデル入門』7.3.1より

執筆時現在、ROUGE算出の実装が日本語文字に対応していないため、(略) (Kindle 版 p.360)

rouge-scoreは英数字でない文字を半角スペースに置き換える

なぜこのような挙動になるのか、実装を見るとつかめました。

RougeScorerのscore()メソッドでは、targetもpredicitionもトークンに分割します。
https://github.com/google-research/google-research/blob/bbb2376225c54168e44c2f749aa1173ee0ed5991/rouge/rouge_scorer.py#L129-L130
RougeScorerはトークナイザインスタンスを持っているんですね。
上記の呼び出しではDefaultTokenizerを持ちます
https://github.com/google-research/google-research/blob/bbb2376225c54168e44c2f749aa1173ee0ed5991/rouge/rouge_scorer.py#L83

DefaultTokenizerですが、tokenize()メソッドではtokenizeモジュール中のtokenize()関数を呼び出します
https://github.com/google-research/google-research/blob/bbb2376225c54168e44c2f749aa1173ee0ed5991/rouge/tokenizers.py#L50-L51

tokenize()関数にて、英数字でない文字を半角スペースに置き換えます。
https://github.com/google-research/google-research/blob/bbb2376225c54168e44c2f749aa1173ee0ed5991/rouge/tokenize.py#L51-L52

non-alpha-numeric characters with spaces.

つまり"いぬ ねこ"" "となり、ROUGEは0と算出されたわけです。

>>> scorer.score("   ", "   ")
{'rouge1': Score(precision=0.0, recall=0.0, fmeasure=0.0), 'rouge2': Score(precision=0.0, recall=0.0, fmeasure=0.0), 'rougeL': Score(precision=0, recall=0, fmeasure=0), 'rougeLsum': Score(precision=0, recall=0, fmeasure=0)}

対応案

分かち書きを整数IDに置き換える

『大規模言語モデル入門』で採用しているのがこちらの方法。
例えば、いぬ -> 1、ねこ -> 2のように置き換えてみます3

>>> scorer.score("1 2", "1 2")
{'rouge1': Score(precision=1.0, recall=1.0, fmeasure=1.0), 'rouge2': Score(precision=1.0, recall=1.0, fmeasure=1.0), 'rougeL': Score(precision=1.0, recall=1.0, fmeasure=1.0), 'rougeLsum': Score(precision=1.0, recall=1.0, fmeasure=1.0)}

日本語のまま入力しないということですね

tokenizer引数を指定する

Hugging FaceのSpaceで案内されています。

One can also pass a custom tokenizer which is especially useful for non-latin languages.

これはhuggingface/evaluateの様式なので、rouge-scoreをそのまま使う場合はインターフェースが異なります。

RougeScorerの__init__のtokenizer引数はtokenize()メソッドを持ったTokenizerインスタンスを受け取ります。
https://github.com/google-research/google-research/blob/bbb2376225c54168e44c2f749aa1173ee0ed5991/rouge/rouge_scorer.py#L74C7-L74C65
なので、日本語テキストを分かち書きするTokenizerを実装して、そのインスタンスをtokenizer引数に指定することで、日本語テキストも扱えそうですね(別途手を動かそう。宿題)

分かち書きしてある前提での実装はこちらです

終わりに

rouge-scoreライブラリで日本語テキストのROUGEが算出できない(0になってしまう)ことを見てきました。

  • デフォルトのTokenizerは、入力された文から英数字以外を除く(=英数字だけを残す)挙動
  • 英数字以外を除かないTokenizerを定義して、RougeScorerに渡そう

  1. 『大規模言語モデル入門』がきっかけで知りました
  2. ページ内に「make sure to wrap you input in double quotes.
  3. 書籍の実装は分かち書きしたテキストを入力し、単語ごとに整数IDを振って置き換えます。ref: https://github.com/ghmagazine/llm-book/blob/4990a3e69effd794847a6240b8386d3073d78ed4/chapter7/7-summarization-generation.ipynbconvert_words_to_ids関数