Azure AI Studio には LLM アプリケーションの評価に使える様々な組み込み評価機能が搭載されており、作成中のチャットアシスタントのフローや外部アシスタントからの出力結果をインポートして評価することができます。
プロンプトフローギャラリーで選択できる評価フローには 8 種類ありますが、その中に QnA F1 Score Evaluation というようなメトリクスがあります。F1 スコアは機械学習で用いられるメトリクスで、計算には Precision と Recall の調和平均を計算しますが、このフローでは入力値 answer
と ground_truth
間のトークンの重複数をカウントして一致率を評価するために使用されます。
現在は LLM アプリケーションの評価は複数のメトリクスを組み合わせて評価されているかと思いますが、そのうち以下の一番左に対応する評価方法となります。LLM を使用しない堅牢な評価方法ですが、言い換え、順序、意図の類似性などは考慮されません。
デフォルトは英語用
フロー内でテキストをトークンに分割する処理を見ると、、、まぁ英語圏用ですよね。空白で分けるのみのやつです。これでは日本語には使用できません。
これを日本語用に置き換えちゃいましょう。
形態素解析器 Sudachi を使用してトークン分割+正規化
Sudachi は AI Challenge Day でワークスアプリケーションズさんに教えてもらいました。なんと自社のワークス徳島人工知能NLP研究所で開発している形態素解析器!とても軽量でいいですね。
from sudachipy import tokenizer
from sudachipy import dictionary
# Sudachiを使用して日本語テキストを形態素解析する関数
def tokenize(text):
tokenizer_obj = dictionary.Dictionary().create()
mode = tokenizer.Tokenizer.SplitMode.C
tokens = []
for m in tokenizer_obj.tokenize(text, mode):
if m.part_of_speech()[0] in ["名詞", "代名詞", "動詞"]:
tokens.append(m.normalized_form())
return tokens
こんな感じで、分かち書きして必要な品詞だけ抽出します。
tokenize("吾輩は猫である。名前はまだない。")
['我が輩', '猫', '有る', '名前']
normalized_form
ええやん
normalized_form
を出力することで、自動的にトークンを正規化して比較することができます!これは便利。
tokenize("吾輩はねこで在る。なまえはまだない。")
['我が輩', '猫', '有る', '名前']
ライブラリの導入
Sudachi の実行に必要な SudachiPy ライブラリをプロンプトフローのコンピューティングセッションにインストールするには、「ファイル」ペインの requirements.txt
に追加したいライブラリを書き込んで保存すれば導入されます。
requirements.txt
sudachipy
sudachidict_core
自動評価(バッチ)の実行
今回は事前に用意されたテスト用 CSV を準備して評価します。
プロンプトフロー上からカスタム評価を選択
評価用 CSV のアップロード
先ほど作成したカスタム評価フローを選択
F1 スコアの比較
データを変えて複数回評価を実行した場合、このように精度を可視化しながら比較できます。なるほど、Aggregate フローの log_metric
の出力値は一番上のところに各レコードの平均値として表示してくれていることがわかりますね。
とても簡単ですね。
生成 AI アプリケーションの評価で独自の評価メトリクスを使ってる方、プロンプトフローで良いカスタム評価ライフを。
参考
全コード
QnA F1 Score Evaluation メトリクスをクローンして、f1_score.py
ノードを以下で置き換えます。元々のやつを拡張しただけです。
from promptflow import tool
from collections import Counter
import string
import re
from sudachipy import tokenizer
from sudachipy import dictionary
@tool
def compute_f1_score(ground_truth: str, answer: str) -> float:
# Sudachiを使用して日本語テキストを形態素解析する関数
def tokenize(text):
tokenizer_obj = dictionary.Dictionary().create()
mode = tokenizer.Tokenizer.SplitMode.C
tokens = []
for m in tokenizer_obj.tokenize(text, mode):
if m.part_of_speech()[0] in ["名詞", "代名詞", "動詞"]:
tokens.append(m.normalized_form())
return tokens
def normalize_text(text) -> str:
"""Lower text and remove punctuation, articles and extra whitespace."""
def remove_articles(text):
return re.sub(r"\b(a|an|the)\b", " ", text)
def white_space_fix(text):
return " ".join(text.split())
def remove_punc(text):
exclude = set(string.punctuation)
return "".join(ch for ch in text if ch not in exclude)
def lower(text):
return text.lower()
return white_space_fix(remove_articles(remove_punc(lower(text))))
# 正規化された予測トークンと参照トークンを取得
prediction_tokens = tokenize(normalize_text(answer))
reference_tokens = tokenize(normalize_text(ground_truth))
# 集合を使用して共通トークンをカウント
prediction_set = set(prediction_tokens)
reference_set = set(reference_tokens)
common_tokens = prediction_set & reference_set
num_common_tokens = len(common_tokens)
# F1スコアを計算
if num_common_tokens == 0:
f1 = 0
else:
# 精度を計算
precision = num_common_tokens / len(prediction_tokens)
# 再現率を計算
recall = num_common_tokens / len(reference_tokens)
# F1スコアを計算
f1 = (2 * precision * recall) / (precision + recall)
return f1
Sudachi