milvus-logo
LFAI
フロントページへ
  • 統合

DeepEvalによる評価

Open In Colab GitHub Repository

このガイドでは、Milvusをベースに構築されたRAG(Retrieval-Augmented Generation)パイプラインを評価するためにDeepEvalを使用する方法を示します。

RAGシステムは、検索システムと生成モデルを組み合わせて、与えられたプロンプトに基づいて新しいテキストを生成します。このシステムは、まずMilvusを使用してコーパスから関連文書を検索し、次に生成モデルを使用して検索された文書に基づいて新しいテキストを生成する。

DeepEvalは、RAGパイプラインの評価を支援するフレームワークである。このようなパイプラインの構築を支援する既存のツールやフレームワークはありますが、パイプラインを評価し、パイプラインのパフォーマンスを定量化することは困難です。そこでDeepEvalの出番です。

前提条件

このノートブックを実行する前に、以下の依存関係がインストールされていることを確認してください:

$ pip install --upgrade pymilvus openai requests tqdm pandas deepeval

Google Colabを使用している場合、インストールしたばかりの依存関係を有効にするには、ランタイムを再起動する必要があるかもしれません(画面上部の "Runtime "メニューをクリックし、ドロップダウンメニューから "Restart session "を選択してください)。

この例では、LLMとしてOpenAIを使います。api key OPENAI_API_KEY を環境変数として用意してください。

import os

os.environ["OPENAI_API_KEY"] = "sk-*****************"

RAGパイプラインの定義

Milvusをベクトルストアとして、OpenAIをLLMとして使用するRAGクラスを定義します。このクラスには、テキストデータをMilvusにロードするload メソッド、与えられた質問に最も類似したテキストデータを検索するretrieve メソッド、検索された知識を用いて与えられた質問に回答するanswer メソッドが含まれます。

from typing import List
from tqdm import tqdm
from openai import OpenAI
from pymilvus import MilvusClient


class RAG:
    """
    RAG(Retrieval-Augmented Generation) class built upon OpenAI and Milvus.
    """

    def __init__(self, openai_client: OpenAI, milvus_client: MilvusClient):
        self._prepare_openai(openai_client)
        self._prepare_milvus(milvus_client)

    def _emb_text(self, text: str) -> List[float]:
        return (
            self.openai_client.embeddings.create(input=text, model=self.embedding_model)
            .data[0]
            .embedding
        )

    def _prepare_openai(
        self,
        openai_client: OpenAI,
        embedding_model: str = "text-embedding-3-small",
        llm_model: str = "gpt-4o-mini",
    ):
        self.openai_client = openai_client
        self.embedding_model = embedding_model
        self.llm_model = llm_model
        self.SYSTEM_PROMPT = """
            Human: You are an AI assistant. You are able to find answers to the questions from the contextual passage snippets provided.
        """
        self.USER_PROMPT = """
            Use the following pieces of information enclosed in <context> tags to provide an answer to the question enclosed in <question> tags.
            <context>
            {context}
            </context>
            <question>
            {question}
            </question>
        """

    def _prepare_milvus(
        self, milvus_client: MilvusClient, collection_name: str = "rag_collection"
    ):
        self.milvus_client = milvus_client
        self.collection_name = collection_name
        if self.milvus_client.has_collection(self.collection_name):
            self.milvus_client.drop_collection(self.collection_name)
        embedding_dim = len(self._emb_text("demo"))
        self.milvus_client.create_collection(
            collection_name=self.collection_name,
            dimension=embedding_dim,
            metric_type="IP",
            consistency_level="Strong",
        )

    def load(self, texts: List[str]):
        """
        Load the text data into Milvus.
        """
        data = []
        for i, line in enumerate(tqdm(texts, desc="Creating embeddings")):
            data.append({"id": i, "vector": self._emb_text(line), "text": line})
        self.milvus_client.insert(collection_name=self.collection_name, data=data)

    def retrieve(self, question: str, top_k: int = 3) -> List[str]:
        """
        Retrieve the most similar text data to the given question.
        """
        search_res = self.milvus_client.search(
            collection_name=self.collection_name,
            data=[self._emb_text(question)],
            limit=top_k,
            search_params={"metric_type": "IP", "params": {}},  # inner product distance
            output_fields=["text"],  # Return the text field
        )
        retrieved_texts = [res["entity"]["text"] for res in search_res[0]]
        return retrieved_texts[:top_k]

    def answer(
        self,
        question: str,
        retrieval_top_k: int = 3,
        return_retrieved_text: bool = False,
    ):
        """
        Answer the given question with the retrieved knowledge.
        """
        retrieved_texts = self.retrieve(question, top_k=retrieval_top_k)
        user_prompt = self.USER_PROMPT.format(
            context="\n".join(retrieved_texts), question=question
        )
        response = self.openai_client.chat.completions.create(
            model=self.llm_model,
            messages=[
                {"role": "system", "content": self.SYSTEM_PROMPT},
                {"role": "user", "content": user_prompt},
            ],
        )
        if not return_retrieved_text:
            return response.choices[0].message.content
        else:
            return response.choices[0].message.content, retrieved_texts

RAGクラスをOpenAIとMilvusクライアントで初期化してみよう。

openai_client = OpenAI()
milvus_client = MilvusClient(uri="./milvus_demo.db")

my_rag = RAG(openai_client=openai_client, milvus_client=milvus_client)

MilvusClient の引数については以下の通り:

  • uri の引数をローカルファイル、例えば./milvus.db に設定するのが最も便利である。
  • データ規模が大きい場合は、dockerやkubernetes上に、よりパフォーマンスの高いMilvusサーバを構築することができます。このセットアップでは、サーバの uri、例えばhttp://localhost:19530uri として使用してください。
  • MilvusのフルマネージドクラウドサービスであるZilliz Cloudを使用する場合は、Zilliz CloudのPublic EndpointとApi keyに対応するuritoken を調整してください。

RAGパイプラインの実行と結果の取得

Milvusの開発ガイドをRAGのプライベートナレッジとして使用します。

ダウンロードし、RAGパイプラインにロードする。

import urllib.request
import os

url = "https://raw.githubusercontent.com/milvus-io/milvus/master/DEVELOPMENT.md"
file_path = "./Milvus_DEVELOPMENT.md"

if not os.path.exists(file_path):
    urllib.request.urlretrieve(url, file_path)
with open(file_path, "r") as file:
    file_text = file.read()

text_lines = file_text.split("# ")
my_rag.load(text_lines)
Creating embeddings: 100%|██████████| 47/47 [00:20<00:00,  2.26it/s]

開発ガイドのドキュメントの内容に関するクエリの質問を定義します。そして、answer メソッドを使用して、答えと取得したコンテキストテキストを取得します。

question = "what is the hardware requirements specification if I want to build Milvus and run from source code?"
my_rag.answer(question, return_retrieved_text=True)
('The hardware requirements specification to build and run Milvus from source code is as follows:\n\n- 8GB of RAM\n- 50GB of free disk space',
 ['Hardware Requirements\n\nThe following specification (either physical or virtual machine resources) is recommended for Milvus to build and run from source code.\n\n```\n- 8GB of RAM\n- 50GB of free disk space\n```\n\n##',
  'Building Milvus on a local OS/shell environment\n\nThe details below outline the hardware and software requirements for building on Linux and MacOS.\n\n##',
  "Software Requirements\n\nAll Linux distributions are available for Milvus development. However a majority of our contributor worked with Ubuntu or CentOS systems, with a small portion of Mac (both x86_64 and Apple Silicon) contributors. If you would like Milvus to build and run on other distributions, you are more than welcome to file an issue and contribute!\n\nHere's a list of verified OS types where Milvus can successfully build and run:\n\n- Debian/Ubuntu\n- Amazon Linux\n- MacOS (x86_64)\n- MacOS (Apple Silicon)\n\n##"])

それでは、いくつかの質問とそれに対応するグランドトゥルースの答えを用意しましょう。RAGパイプラインから回答とコンテキストを取得します。

from datasets import Dataset
import pandas as pd

question_list = [
    "what is the hardware requirements specification if I want to build Milvus and run from source code?",
    "What is the programming language used to write Knowhere?",
    "What should be ensured before running code coverage?",
]
ground_truth_list = [
    "If you want to build Milvus and run from source code, the recommended hardware requirements specification is:\n\n- 8GB of RAM\n- 50GB of free disk space.",
    "The programming language used to write Knowhere is C++.",
    "Before running code coverage, you should make sure that your code changes are covered by unit tests.",
]
contexts_list = []
answer_list = []
for question in tqdm(question_list, desc="Answering questions"):
    answer, contexts = my_rag.answer(question, return_retrieved_text=True)
    contexts_list.append(contexts)
    answer_list.append(answer)

df = pd.DataFrame(
    {
        "question": question_list,
        "contexts": contexts_list,
        "answer": answer_list,
        "ground_truth": ground_truth_list,
    }
)
rag_results = Dataset.from_pandas(df)
df
/Users/eureka/miniconda3/envs/zilliz/lib/python3.9/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm
Answering questions: 100%|██████████| 3/3 [00:03<00:00,  1.06s/it]
質問 コンテキスト 答え 真実
0 ハードウェア要件とは何ですか? [Hardware Requirementsn以下の仕様がある。 Milvusをビルドするためのハードウェア要件は何ですか? Milvusをビルドし、ソースから実行する場合、...
1 Milvusをビルドしてソースコードから実行したいのですが、プログラミング言語は何ですか? [Milvusのアルゴリズムライブラリは、CMakeとConan... Knowherを記述するために使用されるプログラミング言語... Knowherを記述するために使用されるプログラミング言語...
2 コードカバレッジを実行する前に確認すべきことは? [Code coveragenBefore submitting your pull ... コードカバレッジを実行する前に、... コードカバレッジを実行する前に、...

レトリバーの評価

大規模言語モデル(LLM)システムでレトリバーを評価する場合、以下の点を評価することが重要だ:

  1. ランキングの妥当性:リトリーバーが、関連性のないデータよりも関連性のある情報をいかに効果的に優先させるか。

  2. 文脈検索:入力に基づき、文脈に関連する情報を捕捉し、検索する能力。

  3. バランス:テキストチャンクのサイズと検索範囲をいかにうまく管理し、関連性のない情報を最小限に抑えるか。

これらの要素を組み合わせることで、リトリーバがどのように優先順位を付け、最も有用な情報を取得し、提示しているかを包括的に理解することができます。

from deepeval.metrics import (
    ContextualPrecisionMetric,
    ContextualRecallMetric,
    ContextualRelevancyMetric,
)
from deepeval.test_case import LLMTestCase
from deepeval import evaluate

contextual_precision = ContextualPrecisionMetric()
contextual_recall = ContextualRecallMetric()
contextual_relevancy = ContextualRelevancyMetric()

test_cases = []

for index, row in df.iterrows():
    test_case = LLMTestCase(
        input=row["question"],
        actual_output=row["answer"],
        expected_output=row["ground_truth"],
        retrieval_context=row["contexts"],
    )
    test_cases.append(test_case)

# test_cases
result = evaluate(
    test_cases=test_cases,
    metrics=[contextual_precision, contextual_recall, contextual_relevancy],
    print_results=False,  # Change to True to see detailed metric results
)
/Users/eureka/miniconda3/envs/zilliz/lib/python3.9/site-packages/deepeval/__init__.py:49: UserWarning: You are using deepeval version 1.1.6, however version 1.2.2 is available. You should consider upgrading via the "pip install --upgrade deepeval" command.
  warnings.warn(
DeepEvalの最新のContextual Precision Metricを実行しています!(gpt-4o、 strict=Falseasync_mode=True を使用 )...
DeepEval の最新のコンテキスト・リコール測定を実行しています!(gpt-4o、 strict=Falseasync_mode=True使用 )...
DeepEval の最新のContextual Relevancy Metric を実行しています!(gpt-4o を使用、 strict=Falseasync_mode=True)...
Event loop is already running. Applying nest_asyncio patch to allow async execution...


Evaluating 3 test case(s) in parallel: |██████████|100% (3/3) [Time Taken: 00:11,  3.91s/test case]
テストが終了しましたConfident AIで評価結果を見るには、'deepeval login'を実行してください。 
‼️ 注意:代わりにConfident AI上でdeepevalの全メトリクスの評価を直接実行することもできます。

生成の評価

大規模言語モデル(LLM)で生成された出力の品質を評価するには、2つの重要な側面に注目することが重要です:

  1. 関連性:プロンプトがLLMを効果的に誘導し、有益で文脈に適した応答を生成するかどうかを評価する。

  2. 忠実性:モデルが事実上正しく、幻覚や矛盾のない情報を生成することを確認し、出力の正確さを測定する。生成されたコンテンツは、検索コンテキストで提供された事実情報と一致していなければならない。

これらの要素を組み合わせることで、出力が適切かつ信頼できるものになります。

from deepeval.metrics import AnswerRelevancyMetric, FaithfulnessMetric
from deepeval.test_case import LLMTestCase
from deepeval import evaluate

answer_relevancy = AnswerRelevancyMetric()
faithfulness = FaithfulnessMetric()

test_cases = []

for index, row in df.iterrows():
    test_case = LLMTestCase(
        input=row["question"],
        actual_output=row["answer"],
        expected_output=row["ground_truth"],
        retrieval_context=row["contexts"],
    )
    test_cases.append(test_case)

# test_cases
result = evaluate(
    test_cases=test_cases,
    metrics=[answer_relevancy, faithfulness],
    print_results=False,  # Change to True to see detailed metric results
)
DeepEval の最新の回答関連性メトリックを実行しています!(gpt-4o、 strict=Falseasync_mode=True を使用 )...
DeepEval の最新の忠実度メトリックを実行しています!(gpt-4o を使用、 strict=Falseasync_mode=True)...
Event loop is already running. Applying nest_asyncio patch to allow async execution...


Evaluating 3 test case(s) in parallel: |██████████|100% (3/3) [Time Taken: 00:11,  3.97s/test case]
テストが終了しましたConfident AIで評価結果を見るには'deepeval login'を実行する。 
‼️ 注意: 代わりに Confident AI 上で直接、deepeval のすべてのメトリクスに対する評価を実行することもできます。

翻訳DeepL

Try Managed Milvus for Free

Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.

Get Started
フィードバック

このページは役に立ちましたか ?