メインコンテンツへスキップ
見出し画像

Weave による RAGシステム のLLM変更時の精度検証手順

    「Weave」による「RAGシステム」のLLM変更時の精度検証手順を解説します。

    この入門記事は、「Weights & Biases」のご支援により提供されています。Weights & Biases JapanのNoteでは他にも多くの有用な記事が掲載されていますので是非ご覧ください。

    画像

    1. Weave

    「Weave」は、RAGなどのLLMアプリケーション開発時、および運用時に記録・実験・評価を行い、その性能を高めていくために使われるLLMOpsツールです。「Weights & Biases」が提供する機能の1つになります。

    主な機能は、次のとおりです。

    ・記録 : LLMとのあらゆるやり取りを記録。
    ・実験 : 様々なパラメータを試して結果を確認。
    ・評価 : 評価を実行してモデルが改善されたかどうかを測定。

    2. Weave の使い方

    はじめに、基本的な「Weave」の使い方を紹介します。

    (1) パッケージのインストール。

    # パッケージのインストール
    !pip install weave

    (2) 「Weave」の初期化。
    実行すると、wandbのAPIキーを要求されるので入力してください。はじめて「wandb」を使用する場合はアカウント登録も必要になります。

    import weave
    
    # weaveの初期化
    weave.init("hello-weave")
    画像

     weave.init() を呼ぶことで、以降のLLMライブラリの入出力を自動的に記録するようになります。OpenAIやAnthropicなど多くのLLMライブラリの入出力は、後述する@weave.op()を利用せずとも、「Weave」を使用して自動的に追跡され、LLMのメタデータ、トークンの使用量、コストなども記録されます。

    (3) 環境変数の準備。
    左端の鍵アイコンで「OPENAI_API_KEY」を設定してから、以下のセルを実行してください。

    画像
    import os
    from google.colab import userdata
    
    # 環境変数の準備 (左端の鍵アイコンでOPENAI_API_KEYを設定)
    os.environ["OPENAI_API_KEY"] = userdata.get("OPENAI_API_KEY")

    (4) LLM呼び出し。
    「OpenAI API」によるLLM呼び出しを行います。
    出力結果に、「Weave」のサイトへのリンクも出力されます。

    from openai import OpenAI
    
    # LLMの準備
    client = OpenAI()
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": "入力の日本語を英語に翻訳してください。"},
            {"role": "user", "content": "吾輩は猫である"},
        ],
        temperature=0,
    )
    
    # 推論の実行
    generation = response.choices[0].message.content
    print(generation)
    画像

    (5) 「Weave」のサイトへのリンクをクリックして記録を確認。
    「Traces」で、「OpenAI API」のLLMの入出力 (chat.completions.create) が自動的に記録されていることを確認できます。

    画像

    3. Weave の記録方法

    「Weave」には、先程紹介したLLMライブラリの自動記録も含めて記録方法が3つあります。

    (1) LLMライブラリの自動記録
    ・LLMライブラリの入出力を自動記録

    (2)関数の記録 - @weave.op()
    ・特定の関数の入出力を記録
    ・関数レベルでのバージョン管理

    (3) モデルの記録 - weave.Model
    ・モデルの属性と関数の記録
    ・モデルレベルでのバージョン管理

    3-1. LLMライブラリの自動記録

    weave.init() のみで、LLMライブラリの入出力を自動記録する方法です。
    以下の「LLMプロバイダ」および「LLMフレームワーク」に対応しています。

    ・LLMプロバイダ

    ・OpenAI
    ・
    Anthropic
    ・
    Cerebras
    ・
    Cohere
    ・
    MistralAI
    ・
    Google Gemini
    ・
    Together AI
    ・
    Groq
    ・
    Open Router
    ・
    LiteLLM

    ・LLMフレームワーク

    ・LangChain
    ・
    LlamaIndex
    ・
    DSPy

    3-2. 関数の記録 - @weave.op()

    関数に @weave.op() を付加して、関数の入出力を記録します。

    (1) 関数に @weave.op を付加。
    今回は、関数 translation() に @weave.op を付加しています。

    # 関数の準備
    @weave.op()
    def translation(user_input):
        client = OpenAI()
        response = client.chat.completions.create(
            model="gpt-4o-mini", # 変更
            messages=[
                {"role": "system", "content": "入力の日本語を英語に翻訳してください。"},
                {"role": "user", "content": user_input},
            ],
            temperature=0,
        )
        return response.choices[0].message.content
    
    # 推論の実行
    result = translation("吾輩は猫である。")
    print(result)
    画像

    (2) 「Weave」のサイトへのリンクをクリックして記録を確認。
    関数 translation() の入出力が記録されていることを確認できます。

    画像

    3-3. モデルの記録 - weave.Model

    (1) クラスの継承元に weave.Model を指定。
    今回は、クラス TranslationModel に weave.Model を継承させています。

    from openai import OpenAI
    
    # モデルの準備
    class TranslationModel(weave.Model):
        system_instruction: str
    
        @weave.op()
        def translation(self, user_input):
            client = OpenAI()
            response = client.chat.completions.create(
                model="gpt-4o-mini",
                messages=[
                    {"role": "system", "content": self.system_instruction},
                    {"role": "user", "content": user_input},
                ],
                temperature=0,
            )
            return response.choices[0].message.content
    
    # 推論の実行
    model = TranslationModel(
        system_instruction="入力の日本語を英語に翻訳してください。",
    )
    result = model.translation("国境の長いトンネルを抜けると雪国であった。")
    print(result)
    画像

    (2) 「Weave」のサイトへのリンクをクリックして記録を確認。
    「Models」で、クラス TranslationModel の属性 (system_instruction) と関数 (translation()) が記録されていることを確認できます。

    画像

    4. RAGシステム のLLM変更時の精度検証手順

    「RAGシステム」には、定期的にLLM変更が必要になる場面がおとずれます。しかし、LLM変更した後も、「RAGシステム」が正しく動く保証はありません。回答精度だけでなく、処理時間や使用料金も変わります。そのため、RAGシステムの精度検証が必要になります。

    「Weave」によるRAGシステムの精度検証手順は、次のとおりです。

    4-1. RAGシステムの準備

    はじめに、練習用のシンプルなRAGシステムを構築します。

    (1) ドキュメントの準備。

    # ドキュメントの準備
    documents = [
        "OpenAIは、イーロン・マスク氏らが設立した研究機関で、汎用人工知能の開発を目指しています。GPT-4やDALLEなどの言語モデルや画像生成モデルを開発しました。",
        "DeepMindは、Google傘下の人工知能研究企業です。強化学習や深層学習の研究で知られ、AlphaGoやAlphaFoldなどの画期的なプロジェクトを手がけています。",
        "Anthropicは、「Constitutional AI」という概念を提唱し、安全で倫理的な人工知能の開発を目指すスタートアップです。言語モデルのClaudeを開発しました。",
        "Hugging Faceは、自然言語処理に特化したオープンソースのライブラリやモデルを提供するスタートアップです。Transformersライブラリが有名で、多くの研究者や開発者に利用されています。",
        "Stability AIは、Stable Diffusionなど高品質な画像生成モデルを開発し、オープンソースで公開することで知られるAIスタートアップです。",
        "Cohereは、自然言語処理技術を利用してテキスト生成や理解を可能にするAIモデルを提供する企業です。Command R plusやRerankなどが有名です。"
    ]

    (2) ドキュメントからの埋め込みの生成。

    from openai import OpenAI
    
    # ドキュメントからの埋め込みの生成の関数
    def docs_to_embeddings(docs: list) -> list:
        openai = OpenAI()
        document_embeddings = []
        for doc in docs:
            response = (
                openai.embeddings.create(
                    input=doc,
                    model="text-embedding-3-small"
                )
                .data[0]
                .embedding
            )
            document_embeddings.append(response)
        return document_embeddings
    
    # ドキュメントからの埋め込みの生成
    docs_embeddings = docs_to_embeddings(documents)

    (3) ユーザー入力に最も関連性の高いドキュメントの取得する関数の準備。

    import numpy as np
    
    # 最も関連性の高いドキュメントの取得
    @weave.op()
    def get_most_relevant_document(query):
        # クエリからの埋め込みの生成
        openai = OpenAI()
        query_embedding = (
            openai.embeddings.create(
                input=query,
                model="text-embedding-3-small"
            )
            .data[0]
            .embedding
        )
    
        # 類似評価の実行
        similarities = [
            np.dot(query_embedding, doc_emb)
            / (np.linalg.norm(query_embedding) * np.linalg.norm(doc_emb))
            for doc_emb in docs_embeddings
        ]
    
        # 類似評価最大のドキュメントを返す
        most_relevant_doc_index = np.argmax(similarities)
        return documents[most_relevant_doc_index]

    (4) モデルの定義。
    「Weave」でRAGシステムの評価に必要な情報を収集するモデルを定義します。

    ・属性 :  model_name
    ・関数 : predict()
     入力に「質問」、出力に「回答」「コンテキスト」を指定

    # モデルの定義
    class RAGModel(weave.Model):
        model_name: str = "gpt-4o"
    
        # 質問応答
        @weave.op()
        def predict(self, question: str) -> dict:
            # 最も関連性の高いドキュメントの取得
            context = get_most_relevant_document(question)
    
            # 回答の生成
            client = OpenAI()
            query = f"""次のコンテキストのみを使って質問に答えてください。
    答えが見つからない場合は「わかりません」と答えてください。"
    
    コンテキスト:"
    ```
    {context}
    ```
    
    質問:
    {question}"""
            response = client.chat.completions.create(
                model=self.model_name,
                messages=[
                    {"role": "user", "content": query},
                ],
                temperature=0.0,
                response_format={"type": "text"},
            )
            answer = response.choices[0].message.content
            return {"answer": answer, "context": context}

    (5) 推論の実行。

    # 推論の実行
    model = RAGModel(
        model_name="gpt-4o"
    )
    model.predict("GPT-4を開発したのは?")
    画像

    4-2. 評価データセットと評価関数の準備

    回答精度を計算するには、「評価データセット」と「評価関数」が必要になります。

    (1) 評価データセットの準備
    今回は、「質問」を6個準備しました。

    # 評価データセットの準備
    questions = [
        {"question": "GPT-4を開発したのは?"},
        {"question": "Google傘下の人工知能研究企業は?"},
        {"question": "「Constitutional AI」という理念を提唱しているのは?"},
        {"question": "Hugging Faceが開発したライブラリは?"},
        {"question": "Stable Diffusionで有名なスタートアップは?"},
        {"question": "Cohereはどんな会社?"}
    ]

    (2) 評価関数の準備。
    今回は、RAG評価のためのフレームワーク「RAGAS」の評価指標の1つ「Faithfulness」 に習って、「コンテキストが与えられた回答に到達するのに役立ったかどうか」をLLMに評価してもらいます。

    import json
    
    # 評価関数の準備
    @weave.op()
    async def context_precision_score(question, model_output):
        context_precision_prompt = """与えられた質問、回答、コンテキストにより、
    コンテキストが与えられた回答に到達するのに役立ったかどうかを確認します。
    JSON出力で、有用であれば {{verification: 1}}、
    有用でない場合は {{verification: 0}} と出力します。
    有効なJSON形式でのみ出力してください。
    
    コンテキスト:
    ```
    {context}
    ```
    
    回答:
    {answer}
    """
        client = OpenAI()
        prompt = context_precision_prompt.format(
            question=question,
            context=model_output["context"],
            answer=model_output["answer"],
        )
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[{"role": "user", "content": prompt}],
            response_format={ "type": "json_object" }
        )
        response_message = response.choices[0].message
        response = json.loads(response_message.content)
        return {
            "verdict": int(response["verification"]) == 1,
        }


    ・RAGAS の評価指標
    「RAGAS」には、「Faithfulness」以外にも、次のような評価指標が提供されています。「RAGAS」のプロンプトを参考に、対象となるRAGシステムを評価するためのプロンプトを記述すると良いでしょう。

    画像

    ・Faithfulness : コンテキストに基づいて回答しているか
     ・入力 : 質問, コンテキスト, 回答
     ・評価対象 : 生成モデル
    ・Answer Relevancy : 質問に対して簡潔かつ適切に回答しているか
     ・入力 : 質問, 回答
     ・評価対象 : 生成モデル
    ・Context Recall : 教師データからコンテキストをどの程度再現できるか
     ・入力 : コンテキスト, 正解
     ・評価対象 : 検索モデル
    ・Context Precision : コンテキストを正確に取得できているか
     ・入力 : 質問, コンテキスト
     ・評価対象 : 検索モデル
    ・Answer Semantic Similarity : 回答が正解とどの程度類似しているか
     ・入力 : 回答, 正解
     ・評価対象 : End to End
    ・Answer Correctness : 回答がどの程度正確か
     ・入力 : 回答, 正解
     ・評価対象 : End to End

    4-3. 実験・評価

    (1) 「Google Colab」での asyncio 利用の有効化。
    「Google Colab」で非同期処理を行う場合のみ必要な手順になります。

    # Google Colabでのasyncio利用の有効化
    import nest_asyncio
    nest_asyncio.apply()

    (2) weave.Evaluation の準備。
    weave.Evaluation  は、「Weave」で評価するためのクラスです。評価データセットと評価関数を保持します。

    # weave.Evaluation の準備
    evaluation = weave.Evaluation(
        dataset=questions,  # 評価データセット
        scorers=[context_precision_score]  # 評価関数
    )

    (3) LLM変更前のRAGシステムの評価。
    今回は、「gpt-4o」のRAGシステムの評価を行います。

    # LLM変更前のRAGシステムの評価
    model = RAGModel(
        model_name="gpt-4o"
    )
    await evaluation.evaluate(model)

    (4) LLM変更後のRAGシステムの評価。
    今回は、「gpt-4o-mini」のRAGシステムの評価を行います。

    # LLM変更後のRAGシステムの評価
    model = RAGModel(
        model_name="gpt-4o-mini"
    )
    await evaluation.evaluate(model)

    (5) 「Weave」のサイトへのリンクをクリックして評価結果を確認。「Datasets」で、使用したデータセットを確認できます。

    画像

    「Evaluations」で、評価結果を確認できます。複数の Evaluation を選択して Compare を押すことで、評価結果をグラフなどで比較できます。

    画像


    今回の評価では、「gpt-4」を「gpt-4-mini」に変更してもRAGシステムの精度が落ちていないことがわかります。

    画像


    評価データの質問ごとの結果も比較して確認することができます。

    画像



     
     

    npaka

     
     
    プログラマー。iPhone / Android / Unity / ROS / AI / AR / VR / RasPi / ロボット / ガジェット。年2冊ペースで技術書を執筆。アニソン / カラオケ / ギター / 猫 twitter : @npaka123

    あなたへのおすすめ