ちょっと、そこ!今日のアプリケーションの一部が魔法のように賢く見えることを不思議に思ったことはありませんか?その魔法の大部分は、RAG と LLM と呼ばれるものから来ています。 RAG (Retrieval-Augmented Generation) は、AI 世界の賢い本の虫だと考えてください。大量の情報を調べて、質問に必要なものを正確に見つけます。次に、有名な GPT シリーズと同様に、LLM (Large Language Model) があり、その優れたテキスト生成機能に基づいてスムーズな回答を生成します。これら 2 つを組み合わせることで、賢いだけでなく、関連性が高く、コンテキストを認識できる AI が完成します。それは、超高速の研究助手と機知に富んだ会話者を組み合わせたようなものです。このコンボは、特定の情報を素早く見つけることから、驚くほどリアルなチャットをすることまで、あらゆる用途に最適です。
しかし、ここに落とし穴があります。AI が単に派手な専門用語を吐き出すだけでなく、実際に役立つかどうかをどうやって知ることができるのでしょうか?ここで評価が重要になります。これは単にあれば良いというものではなく、非常に重要です。 AI が正確であるだけでなく、関連性があり、有用であり、変な話から逸れていないことを確認する必要があります。結局のところ、スマート アシスタントがユーザーの必要なことを理解できなかったり、的外れな答えを返したりするのであれば、何の意味があるのでしょうか?
RAG + LLM アプリケーションの評価は、現実性のチェックのようなものです。それは、単に技術的に優れているだけでなく、本当に役立つ AI の開発が本当に軌道に乗っているかどうかを示します。そこで、この投稿では、まさにそれを実現する方法、つまり AI が理論上と同じくらい実際に優れていることを確認する方法について詳しく説明します。
開発段階では、典型的な機械学習モデルの評価パイプラインに沿って考えることが重要です。標準的な AI/ML セットアップでは、通常、開発セット、トレーニング セット、テスト セットなどの複数のデータセットを操作し、定量的な指標を使用してモデルの有効性を評価します。ただし、大規模言語モデル (LLM) の評価には特有の課題が伴います。従来の定量的指標は、LLM からの出力の品質を把握するのに苦労しています。これは、これらのモデルが、多様性と創造性の両方を兼ね備えた言語を生成することに優れているためです。したがって、効果的な評価のための包括的なラベルのセットを用意することは困難です。
学術界では、研究者が MMLU などのベンチマークやスコアを使用して LLM をランク付けしたり、人間の専門家が LLM 出力の品質を評価するために協力したりすることがあります。ただし、これらの方法は、開発のペースが速く、実用的なアプリケーションには即時の結果が必要なため、実稼働環境にシームレスに移行することはできません。これは LLM のパフォーマンスだけの問題ではありません。現実の要求には、データの取得、プロンプト構成、LLM の貢献を含むプロセス全体が考慮されます。新しいシステムの反復ごと、またはドキュメントやドメインに変更があった場合に、人間が厳選したベンチマークを作成することは非現実的です。さらに、業界の開発ペースは速いため、展開前に人間のテスターが各アップデートを評価するのに長時間待つという贅沢はありません。したがって、学術界で機能する評価戦略を迅速で結果重視の生産環境に合わせて調整することは、かなりの課題となります。
したがって、このケースに該当する場合は、マスター LLM によって提供される疑似スコアのようなものを考えることができます。このスコアは、自動化された評価指標と人間の判断の抽出されたエッセンスの組み合わせを反映している可能性があります。このようなハイブリッド アプローチは、人間の評価者の微妙な理解と、機械評価のスケーラブルで体系的な分析との間のギャップを埋めることを目的としています。
たとえば、チームが特定のドメインとデータに基づいてトレーニングされた社内 LLM を開発している場合、そのプロセスには通常、開発者、プロンプト エンジニア、データ サイエンティストの共同作業が必要になります。各メンバーは重要な役割を果たします。
開発者はアーキテクトです。これらはアプリケーションのフレームワークを構築し、RAG + LLM チェーンがシームレスに統合され、さまざまなシナリオを簡単にナビゲートできるようにします。
プロンプトエンジニアはクリエイティブです。彼らは、現実世界のユーザー インタラクションをエミュレートするシナリオとプロンプトを考案します。彼らは「もしも」について熟考し、幅広いトピックや質問に対処できるようにシステムを推進します。
データサイエンティストは戦略家です。彼らは応答を分析し、データを詳しく調べ、統計の専門知識を駆使して AI のパフォーマンスが基準を満たしているかどうかを評価します。
ここでのフィードバック ループは不可欠です。 AI がプロンプトに応答すると、チームはすべての出力を精査します。 AIは質問を理解しましたか?回答は正確かつ適切でしたか?言語がもっとスムーズにならないでしょうか?このフィードバックは改善のためにシステムにループバックされます。
これをさらに一歩進めるために、OpenAI の GPT-4 のようなマスター LLM を、自社開発した LLM を評価するためのベンチマークとして使用することを想像してください。堅牢性と多用途性で知られる GPT シリーズのパフォーマンスに匹敵する、あるいはそれを超えることを目指します。次に進む方法は次のとおりです。
関連するデータセットの生成:まず、ドメインのニュアンスを反映するデータセットを作成します。このデータセットは専門家によって厳選されたり、時間を節約するために GPT-4 を利用して合成されたりして、確実にゴールド スタンダードに一致するようになります。
成功のための指標の定義:マスター LLM の強みを活用して、指標の定義を支援します。マスター LLM はより複雑なタスクを処理できるため、目標に最も適したメトリクスを自由に選択できます。コミュニティ標準では、 Langchainやragasなどの他のライブラリの一部の作品を参照することをお勧めします。忠実度、コンテキストの再現度、コンテキストの精度、回答の類似性などの指標がいくつかあります。
評価パイプラインの自動化:急速な開発サイクルに対応するには、自動化されたパイプラインを確立します。これにより、更新または変更のたびに、事前定義されたメトリクスに対してアプリケーションのパフォーマンスが一貫して評価されます。プロセスを自動化することで、評価が徹底的であるだけでなく、効率的に反復できるようになり、迅速な最適化と改良が可能になります。
たとえば、次のデモでは、OpenAI の GPT-4 を使用して、単純なドキュメント検索会話タスクでさまざまなオープンソース LLM を自動的に評価する方法を示します。
まず、OpenAI GPT-4 を利用して、以下に示すようにドキュメントから派生した合成データセットを作成します。
import os import json import pandas as pd from dataclasses import dataclass from langchain.chat_models import ChatOpenAI from langchain.chains import LLMChain from langchain.prompts import PromptTemplate from langchain.document_loaders import PyPDFLoader from langchain.text_splitter import CharacterTextSplitter from langchain.output_parsers import JsonOutputToolsParser, PydanticOutputParser from langchain.prompts import ChatPromptTemplate, HumanMessagePromptTemplate QA_DATASET_GENERATION_PROMPT = PromptTemplate.from_template( "You are an expert on generate question-and-answer dataset based on a given context. You are given a context. " "Your task is to generate a question and answer based on the context. The generated question should be able to" " to answer by leverage the given context. And the generated question-and-answer pair must be grammatically " "and semantically correct. Your response must be in a json format with 2 keys: question, answer. For example," "\n\n" "Context: France, in Western Europe, encompasses medieval cities, alpine villages and Mediterranean beaches. Paris, its capital, is famed for its fashion houses, classical art museums including the Louvre and monuments like the Eiffel Tower." "\n\n" "Response: {{" "\n" " \"question\": \"Where is France and what is it's capital?\"," "\n" " \"answer\": \"France is in Western Europe and it's capital is Paris.\"" "\n" "}}" "\n\n" "Context: The University of California, Berkeley is a public land-grant research university in Berkeley, California. Established in 1868 as the state's first land-grant university, it was the first campus of the University of California system and a founding member of the Association of American Universities." "\n\n" "Response: {{" "\n" " \"question\": \"When was the University of California, Berkeley established?\"," "\n" " \"answer\": \"The University of California, Berkeley was established in 1868.\"" "\n" "}}" "\n\n" "Now your task is to generate a question-and-answer dataset based on the following context:" "\n\n" "Context: {context}" "\n\n" "Response: ", ) OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") if OPENAI_API_KEY is None: raise ValueError("OPENAI_API_KEY is not set") llm = ChatOpenAI( model="gpt-4-1106-preview", api_key=OPENAI_API_KEY, temperature=0.7, response_format={ "type": "json_object" }, ) chain = LLMChain( prompt=QA_DATASET_GENERATION_PROMPT, llm=llm ) file_loader = PyPDFLoader("./data/cidr_lakehouse.pdf") text_splitter = CharacterTextSplitter(chunk_size=1000) chunks = text_splitter.split_documents(file_loader.load()) questions, answers = [], [] for chunk in chunks: for _ in range(2): response = chain.invoke({ "context": chunk }) obj = json.loads(response["text"]) questions.append(obj["question"]) answers.append(obj["answer"]) df = pd.DataFrame({ "question": questions, "answer": answers }) df.to_csv("./data/cidr_lakehouse_qa.csv", index=False)
上記のコードを実行すると、結果として CSV ファイルが取得されます。このファイルには、次のような、入力した文書に関する質問と回答のペアが含まれています。
次に、Langchain を使用して単純な DocumentRetrievalQA チェーンを構築し、Ollama を介してローカルで動作するいくつかのオープンソース LLM で置き換えます。それに関する私の以前のチュートリアルはここにあります。
from tqdm import tqdm from langchain.chains import RetrievalQA from langchain.chat_models import ChatOllama from langchain.vectorstores import FAISS from langchain.embeddings import HuggingFaceEmbeddings vector_store = FAISS.from_documents(chunks, HuggingFaceEmbeddings()) retriever = vector_store.as_retriever() def test_local_retrieval_qa(model: str): chain = RetrievalQA.from_llm( llm=ChatOllama(model=model), retriever=retriever, ) predictions = [] for it, row in tqdm(df.iterrows(), total=len(df)): resp = chain.invoke({ "query": row["question"] }) predictions.append(resp["result"]) df[f"{model}_result"] = predictions test_local_retrieval_qa("mistral") test_local_retrieval_qa("llama2") test_local_retrieval_qa("zephyr") test_local_retrieval_qa("orca-mini") test_local_retrieval_qa("phi") df.to_csv("./data/cidr_lakehouse_qa_retrieval_prediction.csv", index=False)
要約すると、上記のコードは単純なドキュメント検索チェーンを確立します。このチェーンは、Mistral、Llama2、Zephyr、Orca-mini、Phi などのいくつかのモデルを使用して実行します。その結果、各 LLM モデルの予測結果を保存するために、既存の DataFrame に 5 つの列を追加します。
次に、OpenAI の GPT-4 を使用してマスター チェーンを定義し、予測結果を評価しましょう。この設定では、従来の AI/ML 問題で一般的である、近似 F1 スコアと同様の正確性スコアを計算します。これを達成するために、次のように定義される True Positive (TP)、False Positive (FP)、False Negative (FN) などの並行概念を適用します。
これらの定義により、以下の式を使用して適合率、再現率、および F1 スコアを計算できます。
import os import numpy as np import pandas as pd from tqdm import tqdm from langchain.chains import LLMChain from langchain.chat_models import ChatOpenAI from langchain.prompts import PromptTemplate OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") if OPENAI_API_KEY is None: raise ValueError("OPENAI_API_KEY is not set") CORRECTNESS_PROMPT = PromptTemplate.from_template( """ Extract following from given question and ground truth. Your response must be in a json format with 3 keys and does not need to be in any specific order: - statements that are present in both the answer and the ground truth - statements present in the answer but not found in the ground truth - relevant statements found in the ground truth but omitted in the answer Please be concise and do not include any unnecessary information. You should classify the statements as claims, facts, or opinions with semantic matching, no need exact word-by-word matching. Question:What powers the sun and what is its primary function? Answer: The sun is powered by nuclear fission, similar to nuclear reactors on Earth, and its primary function is to provide light to the solar system. Ground truth: The sun is actually powered by nuclear fusion, not fission. In its core, hydrogen atoms fuse to form helium, releasing a tremendous amount of energy. This energy is what lights up the sun and provides heat and light, essential for life on Earth. The sun's light also plays a critical role in Earth's climate system and helps to drive the weather and ocean currents. Extracted statements: [ {{ "statements that are present in both the answer and the ground truth": ["The sun's primary function is to provide light"], "statements present in the answer but not found in the ground truth": ["The sun is powered by nuclear fission", "similar to nuclear reactors on Earth"], "relevant statements found in the ground truth but omitted in the answer": ["The sun is powered by nuclear fusion, not fission", "In its core, hydrogen atoms fuse to form helium, releasing a tremendous amount of energy", "This energy provides heat and light, essential for life on Earth", "The sun's light plays a critical role in Earth's climate system", "The sun helps to drive the weather and ocean currents"] }} ] Question: What is the boiling point of water? Answer: The boiling point of water is 100 degrees Celsius at sea level. Ground truth: The boiling point of water is 100 degrees Celsius (212 degrees Fahrenheit) at sea level, but it can change with altitude. Extracted statements: [ {{ "statements that are present in both the answer and the ground truth": ["The boiling point of water is 100 degrees Celsius at sea level"], "statements present in the answer but not found in the ground truth": [], "relevant statements found in the ground truth but omitted in the answer": ["The boiling point can change with altitude", "The boiling point of water is 212 degrees Fahrenheit at sea level"] }} ] Question: {question} Answer: {answer} Ground truth: {ground_truth} Extracted statements:""", ) judy_llm = ChatOpenAI( model="gpt-4-1106-preview", api_key=OPENAI_API_KEY, temperature=0.0, response_format={ "type": "json_object" }, ) judy_chain = LLMChain( prompt=CORRECTNESS_PROMPT, llm=judy_llm ) def evaluate_correctness(column_name: str): chain = LLMChain( prompt=CORRECTNESS_PROMPT, llm=ChatOpenAI( model="gpt-4-1106-preview", api_key=OPENAI_API_KEY, temperature=0.0, response_format={ "type": "json_object" }, ) ) key_map = { "TP": "statements that are present in both the answer and the ground truth", "FP": "statements present in the answer but not found in the ground truth", "FN": "relevant statements found in the ground truth but omitted in the answer", # noqa: E501 } TP, FP, FN = [], [], [] for it, row in tqdm(df.iterrows(), total=len(df)): resp = chain.invoke({ "question": row["question"], "answer": row[column_name], "ground_truth": row["answer"] }) obj = json.loads(resp["text"]) TP.append(len(obj[key_map["TP"]])) FP.append(len(obj[key_map["FP"]])) FN.append(len(obj[key_map["FN"]])) # convert to numpy array TP = np.array(TP) FP = np.array(FP) FN = np.array(FN) df[f"{column_name}_recall"] = TP / (TP + FN) df[f"{column_name}_precision"] = TP / (TP + FP) df[f"{column_name}_correctness"] = 2 * df[f"{column_name}_recall"] * df[f"{column_name}_precision"] / (df[f"{column_name}_recall"] + df[f"{column_name}_precision"]) evaluate_correctness("mistral_result") evaluate_correctness("llama2_result") evaluate_correctness("zephyr_result") evaluate_correctness("orca-mini_result") evaluate_correctness("phi_result") print("|====Model====|=== Recall ===|== Precision ==|== Correctness ==|") print(f"|mistral | {df['mistral_result_recall'].mean():.4f} | {df['mistral_result_precision'].mean():.4f} | {df['mistral_result_correctness'].mean():.4f} |") print(f"|llama2 | {df['llama2_result_recall'].mean():.4f} | {df['llama2_result_precision'].mean():.4f} | {df['llama2_result_correctness'].mean():.4f} |") print(f"|zephyr | {df['zephyr_result_recall'].mean():.4f} | {df['zephyr_result_precision'].mean():.4f} | {df['zephyr_result_correctness'].mean():.4f} |") print(f"|orca-mini | {df['orca-mini_result_recall'].mean():.4f} | {df['orca-mini_result_precision'].mean():.4f} | {df['orca-mini_result_correctness'].mean():.4f} |") print(f"|phi | {df['phi_result_recall'].mean():.4f} | {df['phi_result_precision'].mean():.4f} | {df['phi_result_correctness'].mean():.4f} |") print("|==============================================================|") df.to_csv("./data/cidr_lakehouse_qa_retrieval_prediction_correctness.csv", index=False)
さて、これでいくつかのモデルの簡単なベンチマークが得られました。これは、各モデルがドキュメント検索タスクをどのように処理するかを示す予備的な指標として考えることができます。これらの数字はスナップショットを提供しますが、それは物語の始まりにすぎません。これらは、特定のコーパスから正確で関連性のある情報を取得するのにどのモデルが優れているかを理解するためのベースラインとして機能します。ソースコードはここにあります。
人間によるループフィードバックを通じて AI を調整する場合、人間のテスターとマスター LLM の間の相乗効果が極めて重要です。この関係は、単にフィードバックを収集することではなく、人間の入力に適応して学習する応答性の高い AI システムを作成することにも関係します。
マスター LLM は、社内で開発された LLM のベンチマークとして、またフィードバック ループへの積極的な参加者として機能します。フィードバックを評価し、プロンプトやモデルのパラメーターを調整し、基本的に人間のやりとりから「学習」します。
このプロセスは変革をもたらします。これにより、AI がリアルタイムで適応できるようになり、より俊敏になり、人間の言語や思考プロセスの複雑さに合わせられるようになります。このようなリアルタイムの適応により、AI の学習曲線は急峻かつ継続的になります。
この対話、フィードバック、適応のサイクルを通じて、私たちの AI は単なるツール以上のものになります。それは学習する存在となり、人間のテスターとのやり取りを通じて改善することができます。このヒューマンインザループモデルにより、AI が停滞することなく、より効率的で直感的なアシスタントに進化することが保証されます。
要約すると、ヒューマンインループ フィードバックは、単に人間の洞察を収集することではなく、ユーザーにより良いサービスを提供するために動作を微調整できる、動的で適応性のある AI を作成することです。この反復的なプロセスにより、当社の RAG + LLM アプリケーションは常に最先端であり、単なる答えではなく、ユーザーのニーズを真に理解した状況を認識した微妙な応答を提供することができます。
簡単なデモについては、ClearML がこの概念を使用して Promptimizer を強化する方法をこのビデオで見ることができます。
作戦フェーズへの移行は、ドレスリハーサルから初日の夜に移行するようなものです。ここでは、RAG + LLM アプリケーションはもはや仮想的な存在ではありません。彼らは実際のユーザーの日々のワークフローに積極的に参加することになります。このフェーズは、開発フェーズで行われるすべての準備と微調整に対するリトマス試験紙です。
このフェーズでは、当社のチーム (運用、製品、アナリスト) が連携してアプリケーションの展開と管理を行い、構築したすべてのものが機能するだけでなく、実際の環境でも正常に動作することを確認します。ここで、制御された方法でアプリケーションの有効性を測定するための A/B テスト戦略の実装を検討できます。
A/B テスト フレームワーク:ユーザー ベースを 2 つのセグメントに分割しました。1 つはアプリケーションの確立されたバージョン (バージョン 1) を使用し続けるコントロール セグメント、もう 1 つはバージョン 2 の新機能を試すテスト セグメントです。複数の A/B テストを同時に実行することもできます)。これにより、ユーザー エクスペリエンス、機能の受容性、全体的なパフォーマンスに関する比較データを収集できます。
運用のロールアウト:運用チームは、インフラストラクチャが堅牢であり、バージョンの移行がユーザーにとってシームレスであることを保証しながら、両方のバージョンをスムーズにロールアウトする任務を負っています。
製品の進化:製品チームは、ユーザーからのフィードバックを常に把握しながら、製品の反復に取り組んでいます。このチームは、新機能がユーザーのニーズと製品全体のビジョンに確実に適合するようにします。
分析的洞察:アナリスト チームは、A/B テストから収集されたデータを厳密に検査します。彼らの洞察は、新しいバージョンが古いバージョンを上回るパフォーマンスを発揮するかどうか、また、より広範なリリースに対応する準備ができているかどうかを判断する上で非常に重要です。
パフォーマンス メトリック:主要パフォーマンス指標 (KPI) は、各バージョンの成功を測定するために監視されます。これらには、ユーザー エンゲージメントの指標、満足度スコア、アプリケーションの出力の精度が含まれます。
運用フェーズは動的であり、継続的なフィードバック ループによって通知され、アプリケーションを改善するだけでなく、ユーザーの関与と満足度も向上します。これは、監視、分析、反復、そして何よりもライブデータからの学習を特徴とするフェーズです。
このフェーズを進めるにあたり、私たちの目標は、開発フェーズで設定された高い基準を維持するだけでなく、それを超えて、RAG + LLM アプリケーションがイノベーションと使いやすさの最前線にあり続けることを保証することです。
要約すると、検索拡張生成 (RAG) と大規模言語モデル (LLM) の統合は、AI の大幅な進歩を示し、詳細なデータ検索と洗練されたテキスト生成を融合させます。ただし、適切かつ効果的な評価方法と反復的な開発戦略が必要です。開発フェーズでは、AI 評価のカスタマイズと人間参加型のフィードバックによる強化に重点を置き、これらのシステムが共感的で現実世界のシナリオに適応できるようにします。このアプローチは、AI が単なるツールから協力的なパートナーへと進化することを強調しています。運用フェーズでは、A/B テストや継続的なフィードバック ループなどの戦略を使用して、現実世界のシナリオでこれらのアプリケーションをテストし、ユーザー インタラクションに基づいた有効性と継続的な進化を保証します。