Re:ゼロから始めるML生活

どちらかといえばエミリア派です

RAGの評価をいい感じにできるようにしたい

最近こんな記事を見かけました。

zenn.dev

zenn.dev

zenn.dev

自分もRAGとかちょろっと勉強してたりしてLLMアプリケーションの評価周りはずっと気になるところではあったので、上記の記事を見てちょっと勉強してみる気になりました。

せっかくなので、色々作りながら評価について自分で考えてみようかと思います。

LLMアプリケーションの評価

評価の3レイヤー

LLMアプリケーションの評価と一口に言っても、複数のレイヤーに分けて考えることができるそうです。

評価指標にはレイヤーの概念があることも念頭においておく必要があるでしょう。

  • レベル1: LLM機能・アプリケーションそのものに対する評価
    • 出力に対するの評価
      • 期待するアウトプット=Grand Truthと実際のアウトプットの比較
      • 出力の妥当性の評価(LLM as a Judgeで扱う)
    • レイテンシーなどの非機能要件の評価
  • レベル2: LLM機能・アプリケーションに対するユーザーの反応や挙動に対する評価
    • ユーザーからの直接的なフィードバック(Good/Badボタンでの評価など)
    • ユーザーの利用状況(クリック率や受入れ率など)
  • レベル3: KPIが向上したかどうかの評価 LLMアプリケーションの評価入門〜基礎から運用まで徹底解説〜

LLM機能・アプリケーションそのものに対する評価を行ったうえで、レベル2、レベル3のレイヤーについても評価する必要があるそうです。

レベル2、レベル3はどうしてもユーザーに対して出してみないとわからない部分も多く、試行回数としては少なくなりがちです。そのため、なるべくレベル1の段階で「レベル2, レベル3のテストまでいかなくてもわかる問題」は見つけきりたいですね。

LLM機能・アプリケーションそのものに対する評価

レベル2、レベル3まで評価が大事だからこそ、レベル1の「LLM機能・アプリケーションそのものに対する評価」を品質・効率良く実行していきたいわけです。

「LLM機能・アプリケーションそのものに対する評価」をどうやるのか考えます。

LLMアプリケーションの出力に対するの評価をどのように行うべきなのかを考えていきましょう。

上記でも述べたように、出力に対するの評価には大きく分けて2つの評価方法があって、

  • 期待するアウトプット(Grand Truth)と実際のアウトプットを比較してスコアリングする
  • 定義した評価基準に基づいて、システムの出力の妥当性をスコアリング(合格/不合格を判定)するというものがあります。 LLMアプリケーションの評価入門〜基礎から運用まで徹底解説〜

よく言う"精度"ってやつを測定するわけで、これを算出するには「期待するアウトプット」を用意する必要があります。 決まった形式の回答であれば文字列一致やハミング距離などで一文字単位で一致を判定するのもありでしょう。

そうではなく、「ある程度の自由回答」を許しつつも「期待するアウトプット」と意味が一致しているかどうかを判断するには、どうしても文字列レベルの判定では難しく別の方法を用いる必要があります。

LLM-as-a-Judge

もちろん人間の目視で判定するでも良いのですが、人間が毎回目視確認していては開発効率は当然上がりません。 ということで、LLMの出力した回答が期待するアウトプットと意味的に合致しているかどうかを判定するために"LLM-as-a-Judge"というやり方が多くの場合取られています。

LLM-as-a-Judgeをどう実現するかの細かいやり方は様々考えられるでしょうが、個人的にはこのやり方が良いなと感じました。

zenn.dev

こんな感じのプロンプトを使って採点を行っていきます。

system-message

あなた(evaluation-assistant)には別のアシスタント(suggestion-assistant)のメッセージを評価していただきます。

## suggestion-assistantの前提
suggestion-assistantは~~~~

user-message

suggestion-assistantの最後の返答がどの程度下記の文書作成マニュアルに従っているかで0〜100点でscoreをつけてください

### 文書ライティングの方針
- 丁寧に対応する
- ~~~

これらを

{ "evaluated_text": {評価対象の文章}, "reason": {判断理由}, "score": {score} }

のような形式で応答させることで、アプリケーションの良し悪しを確認できるようにしているようです。 こういうふうに書けば確かに基準に沿った判定ができそうですね。

試しに作ってみる

おおよそ概要がわかったところで、実際に評価をいい感じにすることを考えていきます。

評価観点

ちゃんと作るなら言い回しとか言葉遣いとか気にしたほうが良いんですが、今回は内容が一致しているかだけで評価しようと思います。

評価にあたって下記の情報を評価用のLLMに実施させようと思います。

  • score: LLMの出力と期待する回答との乖離の度合い
  • reason: scoreの判断理由

scoreについて、基準は下記のようにしようと思います。

  • 1.0: LLMの出力と期待する回答が合致している
  • 0.5: 期待する回答に部分的に一致している
  • 0.0: LLMの出力と期待する回答が完全に異なっている

後は判断理由を一緒に出力させてchain of thoughtを用いてやらせようと思います。

プロンプト

この評価を行うプロンプトは一旦こんな感じにしてみました。 (もっと良いのがあれば誰かこっそり教えてください)

system message

あなた(evaluation-assistant)には別のアシスタント(suggestion-assistant)のメッセージを評価していただきます。

以下に示す質問に対してコンテキストの情報をもとに、期待する回答をsuggestion-assistantが出力することが期待しています。

### 質問
{question}

### コンテキスト
{context}

### 期待する回答
{answer}

user message

suggestion-assistantの出力がどの程度期待する回答に合致しているかで0.0〜1.0の範囲でscoreをつけてください。

## 点数の基準
- 1.0: 期待する回答の内容と合致している
- 0.8: 期待する回答の内容とおおよそ合致していて、わずかに異なっている 
- 0.5: 期待する回答の内容と部分的に一致している
- 0.2: 期待する回答の内容とおおよそ異なっていて、ごく一部だけ合致している
- 0.0: 期待する回答の内容と完全に異なっている

## suggestion-assistantの出力
{output}

## Output
score: 
reason: 

作ってみる

ちょっと前にテストデータセットを試しに作成するというのをやっていたので、これの延長で今回の評価をやってみたいと思います。

www.nogawanogawa.com

書いたコードはこの後に置いとくとして、最終的なモニタリングの画面はこんな感じになってました。

今回は評価の詳細について見てみると、回答精度はQA Correctness : 0.33って事になってました。 これは事前に決めた基準を踏まえると、「期待する回答の内容とおおよそ異なっていて、ごく一部だけ合致している」よりちょっとマシってレベルのRAGになっているということがわかりました。

各回答の採点理由についてもfeedbackを見ると確認できるようになっており、こんな感じです。

retrievalの精度にかんしてはこんな感じです。

検索結果に対するコメントとかあるので、欲しい情報が入っているかどうかも確認できますね。

今回の実装では、中身を見てみる感じとしては

  • 回答の精度があまり良くない
    • 部分的に回答できている程度で、十分な内容の回答はできていない模様
  • 検索の方で正しい箇所を参照できていない
    • その影響なのか、最終的な回答があんまり正しくない

という状況なことがわかりました。

精度はあまり良くはありませんが、今回評価のコードを加えることで今作ってるRAGが

  • どれくらいの精度なのか
  • 個別要素がどれくらいの精度で組み合わさっているのか

が説明できるようになりました。

作ったコード

今回作ったコードはこちらにあります。

gist.github.com

参考文献

下記の文献を参考にさせていただきました。

感想

以上、RAGの評価どうやってやったら良いんだ?と思っていたところにいい感じのブログを見かけたのでやってみた記事でした。 LLM-as-a-judgeってどうやってやったら良いんだ?って長い間ずっと考えてましたがようやくちゃんとできるようになった気がします。