【機械学習】Hugging faceの評価指標計算ライブラリ「Evaluate」を使ってみた。

本記事のGoogleColabで動かせるコードをこちらで公開中です。

Hugging faceの新ライブラリ「Evaluate」を使ってみた。

こんにちは。PlayGroundのデータコースに所属している安藤太一です。
NLPモデルのライブラリ「transformers」などで有名なHugging face社が最近新しいライブラリ、「Evaluate」を発表したので、使ってみようと思います。

目次

  • Evaluateとは
  • 基本的な評価値の計算
  • Evaluatorを使う
  • まとめ
  • 参考文献

Evaluateとは

Evaluateはモデルの評価や比較、性能のレポートをより簡単に、標準的に行うためのライブラリです。
既存の評価指標(メトリクス)はNLP(自然言語処理)からCV(Computer Vision)まで幅広く対応しているそうです。(datasetsやevaluateなどhuggingfaceのライブラリは名前がシンプルで、これからの機械学習のスタンダードを担おうという意志が感じられます。実際このevaluateも先にPyPiで登録されていたライブラリがありましたが、名前を譲ってもらったそうです。)

早速使いながら説明していきます。

pipでインストール

(google colabでは、scipyのバージョンを1.7.1以上に上げる必要があります。)

!pip install scipy==1.7.1
Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/
Requirement already satisfied: scipy==1.7.1 in /usr/local/lib/python3.7/dist-packages (1.7.1)
Requirement already satisfied: numpy<1.23.0,>=1.16.5 in /usr/local/lib/python3.7/dist-packages (from scipy==1.7.1) (1.21.6)
!pip install -qqq evaluate
import evaluate

公式の現在使用可能なメトリクス(評価指標)を確認してみます。

evaluate.list_evaluation_modules(module_type="metric", include_community=False, with_details=True)
[{'community': False, 'likes': 0, 'name': 'precision', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'code_eval', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'roc_auc', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'cuad', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'xnli', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'rouge', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'pearsonr', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'mse', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'super_glue', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'comet', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'cer', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'sacrebleu', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'mahalanobis', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'wer', 'type': 'metric'},
 {'community': False,
  'likes': 0,
  'name': 'competition_math',
  'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'f1', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'recall', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'coval', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'mauve', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'xtreme_s', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'bleurt', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'ter', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'accuracy', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'exact_match', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'indic_glue', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'spearmanr', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'mae', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'squad', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'chrf', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'glue', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'perplexity', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'mean_iou', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'squad_v2', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'meteor', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'bleu', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'wiki_split', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'sari', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'frugalscore', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'google_bleu', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'bertscore', 'type': 'metric'},
 {'community': False,
  'likes': 1,
  'name': 'matthews_correlation',
  'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'seqeval', 'type': 'metric'},
 {'community': False, 'likes': 2, 'name': 'trec_eval', 'type': 'metric'},
 {'community': False, 'likes': 0, 'name': 'rl_reliability', 'type': 'metric'}]

よく使われる基本的な評価指標であるMAEやaccuracyから、自然言語処理で使われるBLEU、BERTScoreのような専門的な指標まで使えるようです。

ここでは、例としてMAE(Mean Abusolute Error)を計算するモジュールをロードしてみましょう。(できたてほやほやのライブラリであるからか、'mae'と名のつくディレクトリがないと言われていますが、GitHubのマスターブランチから持ってきてくれるみたいです。)

mae = evaluate.load("mae")
Couldn't find a directory or a metric named 'mae' in this version. It was picked from the master branch on github instead.

mae.descriptionで評価指標の簡単な説明を見ることができます。また、mae.reference_urlsでReferenceのURLも出力することもできます。

使用している指標の性質や、詳細が知りたくなってググるのはよくあることなのでちょっと嬉しいです。

mae.description
'Mean Absolute Error (MAE) is the mean of the magnitude of difference between the predicted and actual\nvalues.\n'
mae.reference_urls
['https://scikit-learn.org/stable/modules/generated/sklearn.metrics.mean_absolute_error.html']

基本的な評価値の計算

では早速評価値を計算していきましょう。
ドキュメントによると、評価値を計算する方法は

  1. All-in-one
  2. Incremental

の2つがあるようです。
All-in-oneは、compute()に必要な引数を一度に渡して計算する方法で、Incrementalはadd()add_batch()を使って、予測値を保存しておき、最後にcompute()に渡して計算する方法です。
深層学習モデルなど、バッチサイズでfor文を用いて繰り返し予測値を構築し、一度に評価するときにこの方法が有効になってきます。

まずは、All-in-one方式を試してみます。compute()の引数referencesに正解データを、predictionsに予測データを設定します。

mae.compute(references=[1, 2, 3, 4], predictions=[1, 1, 1, 1])
{'mae': 1.5}

辞書型でmaeが返ってきました。

次に、Incremental方式を試します。
以下のコードでは、for文で正解データと予測データを回し、add()で予測値を保持しておき最後に評価を実行しています。

for ref, pred in zip([1, 2, 3, 4], [1, 1, 1, 1]):
  mae.add(references=ref, predictions=pred)
mae.compute()
{'mae': 1.5}

Evaluatorを使う

上記の2つの方法では、予測値を自分で計算して用意する必要がありましたが、evaluate.evaluator()を使うと、モデルとデータセットを用意し、メトリクスを設定するだけで内部で推論を実行し評価値を出力することができます。ここでは、チュートリアルに従い、テキスト分類を行うモデルを用意してevaluate.evaluator()を使ってみます。transformersのpipelineに従う限り、好きなフレームワークを使用することができるようです。(pipelineは複雑なモデルを抽象化し、たった数行で推論APIを構築できるライブラリです。)

!pip install -qqq transformers datasets
!pip install -qqq evaluate[evaluator]
from transformers import pipeline
from datasets import load_dataset
from evaluate import evaluator

#テキスト分類を行うパイプラインをロード
pipe = pipeline("text-classification", model="lvwerra/distilbert-imdb", device=0)
#テストデータをロード
data = load_dataset("imdb", split="test").shuffle().select(range(1000))
#メトリクスを設定
metric = evaluate.load("accuracy")

#evaluatorを定義
eval = evaluator("text-classification")
#スコアを計算
results = eval.compute(model_or_pipeline=pipe, data=data, metric=metric,
                       label_mapping={"NEGATIVE": 0, "POSITIVE": 1},)

print(results)
ERROR: Could not find a version that satisfies the requirement transformersscipy>=1.7.1; extra == "evaluator" (from evaluate[evaluator]) (from versions: none)
ERROR: No matching distribution found for transformersscipy>=1.7.1; extra == "evaluator"


Reusing dataset imdb (/root/.cache/huggingface/datasets/imdb/plain_text/1.0.0/2fdd8b9bcadd6e7055e742a706876ba43f19faee861df134affd7a3f60fc38a1)
Couldn't find a directory or a metric named 'accuracy' in this version. It was picked from the master branch on github instead.


{'accuracy': 0.927}

評価値を得られることができました。また、モデル間の比較をするために、ブートストラップ法を用いてスコアの信頼区間や標準誤差を計算することもできます。

#引数strategyにboostrapを設定し、n_resamplesに復元抽出をする際のサンプルサイズを設定
results = eval.compute(model_or_pipeline=pipe, data=data, metric=metric,
                       label_mapping={"NEGATIVE": 0, "POSITIVE": 1},
                       strategy="bootstrap", n_resamples=200)

print(results)
{'accuracy': {'confidence_interval': (0.908781865486257, 0.9418386604232667), 'standard_error': 0.009033716854152826, 'score': 0.927}}

まとめ

以上がEvaluateの基本的な使い方になります。
Evaluateにはこれ以外にも、オリジナルの評価指標を作成して、Hugging face Hubにアップロードし、共有する機能もあるので今度はその機能をブログに書こうと思います。読んでいただきありがとうございました。

参考文献

Evaluate:https://huggingface.co/docs/evaluate/index