はじめに
ちはやぶる 神代も聞かず 竜田川、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に渡そう