Technical Blog テクニカルブログ
  1. HOME
  2. テクニカルブログ
  3. RAGの精度評価ツール「RAGAS」を試してみた(watsonx.ai)

RAGの精度評価ツール「RAGAS」を試してみた(watsonx.ai)

投稿者:泉

こんにちは!
日本情報通信の泉です。

最近、生成AIの普及に伴い、Retrieval-Augmented Generation(RAG)の活用ケースが増えてきていますが、活用にあたってRAGの精度評価を手動で実施することに苦戦している方も多いのではないでしょうか。そんな中、RAGの評価を手動ではなく自動で実施したい、定量的に評価したいという要望もあるかと思います。そこで、本TechBlogでは、RAGASを利用してIBM社のwatsonx.aiを使用したRAGの評価を実施してみたいと思います。

RAGASとは

RAGASとは、RAGを評価/テストするためのツールを提供するライブラリになります。
RAGとは生成AIが学習していないデータから情報を検索し、回答を生成するAIの自然言語処理の技術のことを指します。
※RAGについては前回執筆したTechBlogでも解説しておりますので、ご興味のある方はこちらをご参照ください。

RAGASにはRAGを評価するために必要なパラメータが4つあります。

パラメータ概要
questionユーザーが入力した質問
ground_truthquestionに対する正解の回答
answerquestionに対して、実際にLLMから返された回答
contexts回答するにあたって参照した情報/データ


また、RAGのパフォーマンスを測定するために利用できる評価指標が主に4つあります。
指標のfaithfulnessとAnswer Relevancyは文章生成(genaration)に関する評価に利用され、指標のContext PrecisionとContext Recallは文章検索(retrival)に関する評価に利用されます。

評価指標概要
Faithfulness生成された回答が与えられた文脈とどれだけ事実的に一致しているかを測定します。answerと取得されたcontextを基に算出され、評価スコアは0から1の範囲で示されます。スコアが高いほど、一貫性が高いことを意味します。
Answer Relevancy生成された回答が与えられた質問に対してどれだけ関連しているかを評価します。不完全な回答や冗長な情報を含む回答には低いスコアが与えられ、一方で適切な回答には高いスコアが与えられます。この指標は、question、contextおよびanswerを用いて算出されます。
Context Precisioncontext内にground-truthが含まれ、それが上位にランク付けされているかどうかを評価します。この指標はquestion、ground_truth、およびcontextを使用して算出され、スコアは0から1の範囲で示されます。ground-truthが上位に含まれているとスコアが高くなります。
Context Recallcontextがground truthとどれだけ一致しているかを測定します。この指標は、question、ground truth、contextを用いて算出され、スコアは0から1の範囲で示されます。スコアが高いほど、パフォーマンスが良いことを意味します。

実装手順

ここから実際にRAGASを使用してwatsonx.aiを使用したRAGの評価を実施していきたいと思います。

■環境準備

今回Pythonの実行環境にはVisual Stadio Codeを使用します。
まずは以下のコマンドを実行し、仮想環境を構築します。

python -m venv venv

続いて作成した仮想環境をアクティベートします。

.\venv\Scripts\activate

■データソースの準備

RAGに与えるデータソースを準備します。
今回はRAGに与えるデータソースとして、私の地元である福岡県に関する観光スポットやグルメ、歴史、文化などの情報をLLMに作成してもらいました。

data.txt

福岡市は、日本の九州地方に位置する主要都市であり、福岡県の県庁所在地です。福岡市は歴史的にも地理的にも重要な位置にあり、古代からアジアとの交易の拠点として栄え、多文化が交錯するダイナミックな都市です。人口は約160万人を超え、九州最大の都市として経済・文化の中心地となっています。
福岡市は交通の便が非常に良く、福岡空港から市内中心部へのアクセスは国内外からの観光客にとっても便利です。地下鉄やバスなどの公共交通機関も充実しており、都市内外への移動がスムーズです。市内には天神や博多といった商業エリアがあり、ショッピングやグルメを楽しむ人々で賑わっています。特に博多ラーメンやもつ鍋、明太子などの地元料理は福岡を訪れる観光客に人気で、食の街としても知られています。
また、福岡市は自然と都市が調和した魅力的な都市です。市中心部には大濠公園や舞鶴公園などの広大な緑地が広がり、市民や観光客がリラックスできる場所を提供しています。さらに、福岡市の周辺には能古島や志賀島といった自然豊かなスポットも多く、行楽地としても人気です。
歴史的な側面を見ても、福岡市は興味深い場所です。太宰府天満宮や櫛田神社、福岡城跡など歴史的な名所が点在しており、歴史好きにはたまらないスポットが多いです。また、福岡市は祭りの街としても有名で、毎年夏に開催される博多祇園山笠は、700年以上の歴史を誇る伝統的な祭りで、多くの観光客が訪れます。
学術・研究の面でも福岡市は活況を呈しています。九州大学をはじめとする多くの高等教育機関や研究機関が集積しており、教育・研究都市としての側面も持っています。これにより、若者が多く集まり、常に新しいエネルギーとアイデアが生まれる都市としても注目されています。
総じて、福岡市は歴史と現代が融合し、自然と都市が調和した、多彩で魅力的な都市です。多様な文化、豊富な自然、市民の活気が一体となった福岡市は、訪れた人々に忘れられない体験を提供しています。

■必要なパッケージのインストール

以下のコマンドを実行し、必要なパッケージをインストールします。

pip install langchain_community
pip install ragas==0.2.1
pip install langchain_ibm
pip install ibm_watson_machine_learning
pip install ibm_watsonx_ai
pip install langchain_core
pip install nltk

※最新バージョンのragasライブラリではバグにより以下のエラーが発生するため、バージョンを指定してインストールしています。

LLMDidNotFinishException(The LLM generation was not completed. Please increase try increasing the max_tokens and try again.

■Pythonプログラムの構築

続いてPythonプログラムでRAGASを実装していきます。

1.環境変数の設定と読み込み

import os
from dotenv import load_dotenv

load_dotenv()

WATSONX_URL = os.getenv("WATSONX_URL")
WATSONX_APIKEY = os.getenv("WATSONX_APIKEY")
WATSONX_PROJECT_ID = os.getenv("WATSONX_PROJECT_ID")

envファイルの内容は以下になります。

"""
WATSONX_URLは使用するリージョンに合わせて以下から選択:
- Dallas:    https://us-south.ml.cloud.ibm.com 
- London:    https://eu-gb.ml.cloud.ibm.com 
- Frankfurt: https://eu-de.ml.cloud.ibm.com 
- Tokyo:     https://jp-tok.ml.cloud.ibm.com 
"""

WATSONX_URL = "WATSONX_URL" 
WATSONX_APIKEY = "WATSONX_APIKEY"
WATSONX_PROJECT_ID = "WATSONX_PROJECT_ID"

2.クラス定義:WatsonxLLM

通常Ragasの評価は、デフォルトでOpen AIのモデルを使用しています。そのため、watsonx.aiのモデルをRagasで使用するために、Ragasが必要とする特定のプロパティ(temperature)を追加したり、Ragasが求める結果の形式に合わせて、ラッパーのメソッドをカスタマイズします。

from langchain_community.llms import WatsonxLLM as _WatsonxLLM
from langchain.callbacks.manager import CallbackManagerForLLMRun
from langchain.schema import LLMResult
from typing import List, Optional, Any

class WatsonxLLM(_WatsonxLLM):
    temperature: float = 0.05
    
    def generate(
        self,
        prompts: List[str],
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        stream: Optional[bool] = None,
        **kwargs: Any,
    ) -> LLMResult:
        result: LLMResult = super()._generate(prompts, stop, run_manager, stream, **kwargs)
        if not result.llm_output or "token_usage" not in result.llm_output:
            return result
        usage = result.llm_output["token_usage"]
        if not isinstance(usage, dict):
            return result
        result.llm_output["token_usage"] = {
            "prompt_tokens": usage["input_token_count"],
            "completion_tokens": usage["generated_token_count"],
            "total_tokens": usage["input_token_count"] + usage["generated_token_count"],
        }
        return result

3.LangchainLLMWrapperのインスタンス化と設定

LangchainLLMWrapperを作成します。
このラッパーを使用し、Ragasとwatsonx.aiモデルが互換性を持つようにします。

from ragas.llms import LangchainLLMWrapper
from ibm_watsonx_ai.metanames import GenTextParamsMetaNames as GenParams

watsonx_llm = LangchainLLMWrapper(
    langchain_llm = WatsonxLLM(
        model_id = "meta-llama/llama-3-1-70b-instruct",
        url = WATSONX_URL,
        apikey = WATSONX_APIKEY,
        project_id = WATSONX_PROJECT_ID,
        params = {
            GenParams.MAX_NEW_TOKENS: 200,
            GenParams.MIN_NEW_TOKENS: 1,
            GenParams.TEMPERATURE: 0.2,
            GenParams.TOP_K: 50,
            GenParams.TOP_P: 1,
        }
    )
)

4.WatsonxEmbeddingsのインスタンス化

embeddingモデルの設定を行います。
今回embeddingモデルにはintfloat/multilingual-e5-largeを使用します。

from langchain_ibm import WatsonxEmbeddings

watsonx_embeddings = WatsonxEmbeddings(
    model_id = "intfloat/multilingual-e5-large",
    url = WATSONX_URL,
    apikey = WATSONX_APIKEY,
    project_id = WATSONX_PROJECT_ID
)

5.質問に対する回答生成関数の実装

LLMを使って質問に対する回答を生成する関数を定義します。
この関数は、questionとcontextをLLMに与え、生成された回答を返します。

def generate_answer(llm_wrapper, question, context):
    combined_prompt = f"質問: {question}\nコンテキスト: {context}\n\n回答:"
    llm_result = llm_wrapper.langchain_llm.generate([combined_prompt])
    return llm_result.generations[0][0].text.strip()

6.ファイルからコンテキストを読み込む関数の実装

Pythonプログラムと同ディレクトリに配置しているデータソース(data.txt)を読み込む関数を実装します。

def read_context_from_file(filepath):
    with open(filepath, 'r', encoding='utf-8') as file:
        context = file.read()
    return context

7.データの準備と評価用データセットの生成

RAGを評価するために必要なパラメータを設定します。今回各パラメータには以下の情報を入れております。

question:コマンドライン上で入力した値
contexts:data.txtの情報
ground_truth:入力する質問に対する想定回答
answer:実際にLLMから返ってきた回答

import pandas as pd
from datasets import Dataset

question = input("質問を入力してください: ")
context = read_context_from_file('data.txt')
ground_truth = "博多ラーメン" 
answer = generate_answer(watsonx_llm, question, context)

data = {
    "question": [question],
    "ground_truth": [ground_truth],
    "answer": [answer],
    "contexts": [[context]] 
}

df = pd.DataFrame(data)
dataset = Dataset.from_pandas(df)

8.評価の実行

evaluate関数を実行し、評価を実施します。

from ragas import evaluate
from ragas.metrics import answer_relevancy, context_precision, context_recall, faithfulness

result = evaluate(
    dataset,
    metrics=[context_precision, faithfulness, answer_relevancy, context_recall],
    llm=watsonx_llm,
    embeddings=watsonx_embeddings
)

9.評価結果の表示

LLMから返された回答と評価結果をターミナル上で出力します。

print(f"生成された回答: {answer}")
print("評価結果:")
print(result)

評価結果

構築が完了したのでプログラムを実行し、以下の質問を投げてみます。

質問を入力してください: 福岡県で有名な食べ物は?

回答と評価結果は以下のようになりました。

生成された回答: 博多ラーメンです。
評価結果:
{'context_precision': 1.0000, 'faithfulness': nan, 'answer_relevancy': 0.7495, 'context_recall': 1.0000}

context_precisionとanswer_relevancy、context_recallともに高い評価となりました。ただfaithfulnessが何回実行してもnanになり、日本語データ使用する際に発生することがあるようです。こちらは原因調査中のため、分かり次第本TechBlogも更新したいと思います。

まとめ

今回のTechBlogではRAGASについてご紹介しました。
RAGASを利用することで、人の手を使わずに定量的にRAGの精度を評価することができます。自動的に評価することで工数の削減が見込まれ、また人の主観が入らないため一貫性のある評価が可能です。RAGを実装している場合は、一度試してみてはいかがでしょうか。

また、弊社では生成AIにIBM社のwatsonx.aiを活用したり、検索エンジンにベクトルDB(Milvus)を活用したRAG構築を行っております。是非ご興味のある方はご連絡いただけますと幸いです。
ご覧いただき、ありがとうございました。

ページのトップへ