著者 |フロリアン・ジューン 編纂者:岳陽 検索拡張生成(RAG)システムでは、ユーザーの元のクエリに関連する問題(例:不正確な語彙や意味情報の不足)がしばしば発生し、RAGシステムの理解が困難になります。例えば、「ロサンゼルス・レイカーズは2020年のNBAチャンピオンシップで優勝しました! langchainフレームワークとは何ですか?」のような質問の場合、ユーザーがこの質問を直接検索すると、LLM(言語モデリング)から誤った、あるいは回答できないモデル応答が返される可能性があります。 したがって、ユーザークエリの意味空間とシステムに保存されているドキュメントの意味空間を統合することが重要です。クエリ書き換え(ユーザークエリを再構築または書き換えて、エラー、曖昧さ、不正確さを修正するプロセス)は、この問題を効果的に解決できます。RAGにおけるその役割は図1に示されています。 図1: RAGにおけるクエリ書き換え手法(赤い破線で囲まれた部分)。画像は著者提供。 RAGシステムにおける位置づけの観点から見ると、クエリ書き換えは検索前の手法です。図は、RAGシステムにおけるクエリ書き換えの位置づけを大まかに示しています。以下では、このプロセスを改善できるいくつかのアルゴリズムを紹介します。 クエリ書き換えは、クエリのセマンティクスをシステムに保存されているドキュメントのセマンティクス空間に整合させるための重要な技術です。例えば、
これらの方法の詳細を詳しく見てみましょう。 01 仮想文書埋め込み(HyDE)論文「関連性ラベルを使用しない高精度ゼロショット高密度検索」[1]では、仮説文書埋め込み(HyDE)に基づく手法が提案されており、その主なプロセスは図2に示されている。 図2:HyDEモデルの概略図。いくつかの文書スニペットを表示。HyDEは、基盤となるGPT-3およびContriever/mContrieverモデルを変更することなく、ユーザーが送信したあらゆる種類のクエリに対応できます。出典:関連性ラベルなしの高精度ゼロショット高密度検索 このプロセスは主に次の 4 つのステップで構成されます。
ユーザーが送信したクエリ q を、特定の問題についての合理的な仮定として考えることもできます。 4.ベクトルvを使用して、ドキュメントライブラリから関連コンテンツを取得します。手順3で説明したように、このベクトルにはユーザーのクエリと期待される具体的な情報が含まれており、再現率を向上させることができます。 HyDE についての私の理解を図 3 に示します。HyDE の目標は、最終的なクエリ ベクトル v がベクトル空間内で実際のドキュメントにできるだけ近くなり、整列するように、仮想ドキュメント (翻訳者注: クエリに基づいてモデル (LLM など) によって生成された偽のドキュメント、クエリに関連するドキュメントをシミュレートするドキュメント) を生成することです。 図3:私の理解では、HyDEの目的は仮想文書を生成することです。これにより、最終的なクエリベクトルvは、ベクトル空間において実際の文書に可能な限り近づき、整合したものになります。画像は原著者提供。 LlamaIndex[2]とLangchain[3]はどちらもHyDEを実装しています。以下ではLlamaIndexを例に説明します。 このファイル[4]をYOUR_DIR_PATHに配置します。テストコードは以下の通りです(インストールしたLlamaIndexのバージョンは0.10.12です)。 インポートOS
os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY" llama_index.coreからVectorStoreIndexとSimpleDirectoryReaderをインポート
llama_index.core.indices.query.query_transform から HyDEQueryTransform をインポートします。
from llama_index.core.query_engine import TransformQueryEngine# ドキュメントをロードし、VectorStoreIndex を構築します。dir_path = "YOUR_DIR_PATH" documents = SimpleDirectoryReader(dir_path).load_data()
インデックス = VectorStoreIndex.from_documents(ドキュメント)
query_str = "ポール・グラハムはRISDに進学した後何をしたのか"# 変換なしのクエリ: 埋め込み検索と要約に同じクエリ文字列が使用されます。query_engine = index.as_query_engine()
レスポンス = query_engine.query(query_str)
印刷('-' * 100)
print("ベースクエリ:")
print(response)# HyDE変換を使用したクエリhyde = HyDEQueryTransform(include_original=True)
hyde_query_engine = TransformQueryEngine(query_engine, hyde)
レスポンス = hyde_query_engine.query(query_str)
印刷('-' * 100)
print("HyDEQueryTransform後:")
印刷(応答) まず、LlamaIndex[5]のデフォルトのHyDEプロンプトを見てみましょう。 ############################################## HYDE###############################################
HYDE_TMPL = (
「質問に答える文章を書いてください」
「できるだけ多くの重要な詳細を含めるようにしてください。\n」
「\n」
「\n」
"{context_str}\n"
「\n」
「\n」
'パッセージ:"""\n'
)
DEFAULT_HYDE_PROMPT = プロンプトテンプレート(HYDE_TMPL、prompt_type=PromptType.SUMMARY) HyDEQueryTransformクラス[6]のコードは以下の通りである。_def run関数の目的は、仮想文書を生成することである。_def run関数には、仮想文書の内容を監視するための3つのデバッグ文が追加されている。 class HyDEQueryTransform(BaseQueryTransform): """Hypothetical Document Embeddings (HyDE) クエリ変換。
LLMを使用して、与えられたクエリに対する仮説的な回答を生成します。
結果のドキュメントを埋め込み文字列として使用します。
「[関連性ラベルなしの高精度ゼロショット高密度検索]」で説明したように、
(https://arxiv.org/abs/2212.10496)
""" デフ__init__(
自己、
llm: オプション[LLMPredictorType] = なし、
hyde_prompt: オプション[BasePromptTemplate] = なし、
include_original: bool = True、
) -> なし: """HyDEQueryTransform を初期化します。
引数:
llm_predictor (オプション[LLM]): 生成用のLLM
仮説文書
hyde_prompt (Optional[BasePromptTemplate]): HyDEのカスタムプロンプト
include_original (bool): 元のクエリを含めるかどうか
埋め込み文字列の1つとして文字列
""" super().__init__()
self._llm = llm または Settings.llm
self._hyde_prompt = hyde_prompt または DEFAULT_HYDE_PROMPT
self._include_original = include_original
def _get_prompts(self) -> PromptDictType: """プロンプトを取得します。""" return {"hyde_prompt": self._hyde_prompt}
def _update_prompts(self, prompts: PromptDictType) -> None: """プロンプトを更新します。""" if "hyde_prompt" in prompts:
self._hyde_prompt = プロンプト["hyde_prompt"]
def _run(self, query_bundle: QueryBundle, metadata: Dict) -> QueryBundle: """クエリ変換を実行します。""" # TODO: 複数の仮想ドキュメントの生成をサポートします。query_str = query_bundle.query_str
hypothetical_doc = self._llm.predict(self._hyde_prompt, context_str=query_str)
埋め込み文字列 = [仮想ドキュメント]
self._include_originalの場合:
埋め込み文字列.拡張(クエリバンドル.埋め込み文字列)
# 次の3行には追加されたデバッグステートメントが含まれています。print('-' * 100)
print("仮想ドキュメント:")
print(埋め込み文字列)
QueryBundleを返す(
query_str=クエリ文字列、
custom_embedding_strs=埋め込み文字列、
) 次のようにテストコードを実行します。 (llamaindex_010) Florian:~ Florian$ python /Users/Florian/Documents/test_hyde.py ------------------------------------------------------------------------------------------------ベースクエリ: ポール・グラハムはRISD卒業後、ニューヨークで以前の生活に戻りました。裕福になり、以前の生活パターンを続けました。しかし、タクシーを簡単に拾ったり、魅力的なレストランで食事をしたりといった新たな機会も得られました。また、新しい静物画の技法を試し始めました。------------------------------------------------------------------------------------------------仮想ドキュメント: ["ロードアイランド・スクール・オブ・デザイン(RISD)卒業後、ポール・グラハムはオンラインストア構築サービスViawebの共同創業者となり、後にYahooに4,900万ドルで買収されました。Viawebの成功を受け、グラハムはテクノロジー業界で影響力を持つ人物となり、2005年にはスタートアップアクセラレーターY Combinatorの共同創業者となりました。Y Combinatorはその後、世界で最も権威があり、成功を収めているスタートアップアクセラレーターの一つとなり、Dropbox、Airbnb、Redditといった企業の立ち上げを支援してきました。グラハムはテクノロジー、スタートアップ、起業家精神に関する著書を多数執筆していることでも知られ、そのエッセイはテクノロジーコミュニティで広く読まれ、高く評価されています。RISD卒業後のポール・グラハムのキャリアは、イノベーション、成功、そしてスタートアップエコシステムへの多大な影響によって彩られています。", 'ポール・グラハムはRISD卒業後何をしたのか']----------------------------------------------------------------------------------------------------------------RISD卒業後、ポール・グラハムはニューヨークで以前の生活に戻りましたが、今や彼は裕福になった彼は、以前の生活パターンを続けながらも、タクシーを簡単に拾ったり、魅力的なレストランで食事をしたりといった新たな機会を得ました。また、絵画にもより力を入れ始め、新しい技法を試しました。さらに、アパートを探し始め、Webアプリ開発のためのWebアプリ開発というアイデアを練り始め、最終的にAspraという新しい会社を設立しました。 この例では、HyDEは、ポール・グラハム(著名な技術起業家、プログラマー、作家であり、Yコンビネータの創設者の一人であり、Lispプログラミング言語の提唱者でもある)がRISD卒業後に何をしたかを正確に想定することで、出力品質を大幅に向上させました(ケーススタディの仮想文書を参照)。これにより、埋め込みベクトルと最終的なモデル出力の品質が向上しました。 もちろん、HyDEにも失敗するケースがあります。興味のある読者は、このリンク[7]にアクセスしてテストすることができます。 HyDE法は教師なし学習のように見えます。つまり、ラベル付きデータでモデルを学習するわけではありません。これには、生成モデル(主なタスクはデータからデータの分布を学習し、それを用いて学習データに類似した新しいデータサンプルを生成することです。これは通常、画像、テキスト、音声などの生成に使用されます)と対照的エンコーダ(対照的な特徴を持つベクトル表現にデータをエンコードするために使用されます。この手法では、類似したサンプルはベクトル空間内で互いに近づけられ、類似しないサンプルは遠ざけられます)が含まれます。 まとめると、HyDEは革新的なクエリ書き換え手法を導入する一方で、いくつかの限界も抱えています。この手法は、クエリ埋め込み間の類似性ではなく、文書間の類似性を重視します。しかし、言語モデルが特定の垂直ドメインに精通していない場合、常に最適な出力を生成できない可能性があり、誤ったコンテンツの増加につながる可能性があります。 02 モデルベース手法この手法は論文「検索強化型大規模言語モデルのためのクエリ書き換え」[8]で提案されました。この論文では、現実世界のシナリオでは、ユーザーが送信した元のクエリの全てがLLMによる直接検索に適しているわけではないと主張しています。 したがって、本論文では、図4(b)に示すように、元のクエリからコンテンツを直接取得してモデル応答を生成するのではなく、まずLLMを使用してクエリを書き換え、次にコンテンツを取得してモデル応答を生成することを提案しています。 図4:左から右へ、(a) 標準的な「取得してから読み込む」方式、(b) LLMを「書き換え・取得・読み込み」パイプラインにおけるクエリ書き換えツールとして使用した場合、(c) パイプラインにおいて学習可能な書き換えツールを使用した場合。出典:Query Rewriting for Retrieval-Augmented Large Language Models[8]。 クエリの書き換えがコンテキストの取得と予測のパフォーマンスにどのように影響するかを説明するために、次のプロンプトの例を考えてみましょう。「2020 年の NBA チャンピオンはロサンゼルス レイカーズです。langchain フレームワークとは何ですか?」これは書き換えによって正確に処理されます。 このプロセスはLangchain[9]を用いて実装できます。必要な基本ライブラリは以下の通りです。 pip で langchain をインストールする pip インストール openai pip で langchainhub をインストール pip で duckduckgo-search をインストールし、pip で langchain_openai をインストールします。 環境設定と Python ライブラリのインポート: インポートOS os.environ["OPENAI_API_KEY"] = "YOUR_OPEN_AI_KEY" langchain_community.utilitiesからDuckDuckGoSearchAPIWrapperをインポート langchain_core.output_parsers から StrOutputParser をインポートします langchain_core.promptsからChatPromptTemplateをインポートします langchain_core.runnables から RunnablePassthrough をインポートします langchain_openaiからChatOpenAIをインポート クエリを処理するプロセス チェーンを構築し、いくつかの簡単なクエリを実行して、プロセス チェーンの機能と有効性をテストします。 june_print(メッセージ、res)を定義します。
印刷('-' * 100)
印刷(メッセージ)
印刷(res)
base_template = """次のコンテキストのみに基づいてユーザーの質問に答えます。
<コンテキスト>
{コンテクスト}
</コンテキスト>
質問: {質問}
"""base_prompt = ChatPromptTemplate.from_template(base_template)
モデル = ChatOpenAI(温度=0)
検索 = DuckDuckGoSearchAPIWrapper() def リトリーバー(クエリ):
search.run(クエリ) を返す
チェーン = (
{"context": リトリーバー、「question": RunnablePassthrough()}
| ベースプロンプト
| モデル
| StrOutputParser()
)
query = "2020年のNBAチャンピオンはロサンゼルス・レイカーズです!langchainフレームワークとは何ですか?" june_print( 'クエリの結果:',
チェーン.invoke(クエリ)
)
june_print( '検索されたコンテキストの結果:',
リトリーバー(クエリ)
) 結果は次のとおりです。 (langchain) フロリアン:~ フロリアン$ python /Users/Florian/Documents/test_rewrite_retrieve_read.py ---------------------------------------------------------------------------------------------------- クエリの結果: 申し訳ありませんが、提供されたコンテキストでは langchain フレームワークについては何も言及されていません。 ---------------------------------------------------------------------------------------------------- 検索されたコンテキストの結果: ロサンゼルス・レイカーズが2020年のNBAチャンピオンに輝きました!優勝を祝う様子はこちらでご覧ください!NBAを登録する:https://on.nba.com/2JX5gSN フルゲームハイライト… 2023年8月4日。2020年のロサンゼルス・レイカーズは、この10年間で最も完成度の高いチームの一つでした。レブロン・ジェームズの4度目の優勝は、彼のキャリアで最も偉大な瞬間の一つでした。2020年のチームからレイカーズに残っているのは、わずか2人だけです。NBAの名高い歴史において、ファンの心を掴み、忘れられない記憶を残したチームはほとんどありません… ジェームズは28得点、14リバウンド、10アシストを記録し、レイカーズは日曜の夜にマイアミ・ヒートを106対93で破り、NBAファイナルを6試合で制しました。ジェームズはNBAの最優秀選手(MVP)にも選出された…ポートランド・トレイルブレイザーズのスター選手、デイミアン・リラードは先日、2020年のNBA「バブル」プレーオフについて語り、最終的に優勝したロサンゼルス・レイカーズが受けた批判について興味深い見解を示した。しかし、2020年のNBAファイナルに関するアデバヨの意見ほど驚くべきものではなかったかもしれない。ヒートはレブロン・ジェームズ率いるロサンゼルス・レイカーズに6試合で敗れた。ミラーは「…について教えてください」と尋ねた。 結果は、検索されたコンテキストに「langchain」に関する情報がほとんどないことを示しています。 それでは、検索クエリ (翻訳者注: ユーザーがシステムに回答してほしい質問、または情報を提供するキーワード) を書き換えるリライターの構築を始めましょう。 rewrite_template = """より良い検索クエリを提供する\
与えられた質問に答えるウェブ検索エンジン、終了\
'**' を含むクエリ。質問: \
{x} 回答:"""rewrite_prompt = ChatPromptTemplate.from_template(rewrite_template)def _parse(text):
text.strip("**") を返す
リライター = rewrite_prompt | ChatOpenAI(温度=0) | StrOutputParser() | _parse
june_print( '書き換えられたクエリ:',
リライター.invoke({"x": クエリ})
) 結果は次のとおりです。 ----------------------------------------------------------------------------------------------------書き換えられたクエリ: langchain フレームワークとは何ですか? どのように機能しますか? rewrite_retrieve_read_chain を構築し、書き換えられたクエリを使用します。 rewrite_retrieve_read_chain = (
{ "context": {"x": RunnablePassthrough()} | リライタ | リトリーバー, "question": RunnablePassthrough(),
}
| ベースプロンプト
| モデル
| StrOutputParser()
)
6月の印刷(
'rewrite_retrieve_read_chainの結果:',
rewrite_retrieve_read_chain.invoke(クエリ)
) 結果は次のとおりです。 ----------------------------------------------------------------------------------------------------rewrite_retrieve_read_chain の結果: LangChainは、言語モデル、特に大規模言語モデル(LLM)を活用したAIアプリケーションの構築を支援するために設計されたPythonフレームワークです。様々な基盤モデルへの汎用インターフェース、プロンプト管理フレームワーク、そして長期記憶、外部データ、他のLLMなどへの中央インターフェースを提供します。LLMとのインタラクションプロセスを簡素化し、ユーザーと自然に対話するチャットボットなど、幅広いアプリケーションの構築に利用できます。 このように、クエリを書き直すことで、正しい答えを得ることができました。 03 ステップバックプロンプト「ステップバックプロンプティング[10]」とは、学習者が多くの具体的な詳細を含む問題から高レベルの概念や基本原理を抽象化し、抽出することを可能にするシンプルなプロンプティング手法です。これにより、大規模言語モデル(LLM)は、具体的な詳細を含む事例から高レベルの概念や基本原理を抽象化し、抽出することが可能になります。この考え方は、「ステップバック問題」を、当初の具体的な問題から派生したより抽象的な問題と定義するものです。 例えば、クエリに多くの詳細が含まれている場合、LLMはタスクの解決に役立つ関連事実を取得するのに苦労する可能性があります。図5の最初の例に示すように、「理想気体の温度が2倍、体積が8倍に増加した場合、圧力Pはどうなるか?」という物理学の質問に対して、この質問について直接推論すると、LLMモデルの応答は理想気体の法則の第一原理から逸脱する可能性があります。 同様に、「エステラ・レオポルドは 1954 年 8 月から 1954 年 11 月までどの学校に通っていましたか?」という質問は、特定の時間枠の制約のため、直接答えるのは非常に困難です。 図5:ステップバックプロンプティングの技術プロセス図。概念と原則を用いて抽象化と推論の2つのステップを導きます。上:MMLU高校物理コースのコンテンツの例。理想気体の法則の第一原理は抽象化によって得られます。下:特定の情報(例えば、教育経験の詳細)は、TimeQAシステムによって抽象化され、「教育歴」という高レベルの概念に要約されます。左:PaLM-2Lは、ユーザーが提起した最初の質問に答えることができませんでした。推論プロセスの中間ステップで、思考連鎖プロンプティングエラーが発生しました(赤でマークされています)。右:PaLM-2Lは、ステップバックプロンプティング技術によって質問に正常に回答しました。出典:TAKE A STEP BACK: EVOKING REASONING VIA ABSTRACTION IN LARGE LANGUAGE MODELS[10]。 どちらの場合も、より広範な質問をすることで、モデルが特定のクエリに効果的に答えられるようになります。「エステラ・レオポルドは特定の時期にどの学校に通っていましたか?」と直接尋ねる代わりに、「エステラ・レオポルドの学歴は?」と尋ねることができます。 このより広範な質問は、ユーザーが提起した元の質問を要約したもので、「エステラ・レオポルドが特定の時期にどの学校に通っていたか」を推測するために必要なすべての情報を提供します。これらのより広範な質問は、元の具体的な質問よりも一般的に答えやすいことに注意してください。 これらの抽象化から導き出された思考の連鎖は、図 5 (左) に示す思考の連鎖の中間ステップにおけるエラーを防ぐのに役立ちます。 要約すると、ステップバック プロンプティングには次の 2 つの基本ステップが含まれます。
ステップバックプロンプトが文脈検索と予測のパフォーマンスにどのように影響するかを説明するために、このステップはLangchain[11]を使用して実装されています。 環境設定と関連ライブラリのインポート: インポートOS os.environ["OPENAI_API_KEY"] = "YOUR_OPEN_AI_KEY" langchain_core.output_parsers から StrOutputParser をインポート langchain_core.prompts から ChatPromptTemplate、FewShotChatMessagePromptTemplate をインポートします。 langchain_core.runnables から RunnableLambda をインポートします。 langchain_openaiからChatOpenAIをインポート langchain_community.utilities から DuckDuckGoSearchAPIWrapper をインポートします クエリを処理するプロセス チェーンを構築し、いくつかの簡単なクエリを実行して、プロセス チェーンの機能と有効性をテストします。 june_print(メッセージ、res)を定義します。
印刷('-' * 100)
印刷(メッセージ)
印刷(res)
question = "トランプが大統領だった頃、chatgptは存在していたのですか?" base_prompt_template = """あなたは世界情勢に精通しています。これから質問をさせていただきます。回答は、関連する内容であれば包括的かつ矛盾のないものでなければなりません。そうでない場合は無視してください。
{通常のコンテキスト}
元の質問: {question}
回答:"""base_prompt = ChatPromptTemplate.from_template(base_prompt_template)
search = DuckDuckGoSearchAPIWrapper(max_results=4)def retriever(query):
search.run(クエリ) を返す
ベースチェーン = (
{ # 通常の質問を使用してコンテキストを取得します(最初の3つの結果のみ)
"normal_context": RunnableLambda(lambda x: x["question"]) | retriever, # 質問を渡す
"質問": lambda x: x["質問"],
}
| ベースプロンプト
| ChatOpenAI(温度=0)
| StrOutputParser()
)
june_print('元の質問の検索されたコンテキスト:', retriever(question))
june_print('base_chainの結果:', base_chain.invoke({"question": question}) ) 結果は次のとおりです。 (langchain) Florian:~ Florian$ python /Users/Florian/Documents/test_step_back.py --------------------------------------------------------------------------------------------------------元の質問の検索されたコンテキスト: ChatGPTは多くの点で印象的ですが、重大な欠陥もいくつかあります。 ... 「[大統領の名前]」は、トランプ前大統領についての詩を書くことを拒否しましたが、バイデン大統領についての詩を書きました... 同社によると、GPT-4は最近、模擬法科大学院の司法試験に合格し、受験者の上位10%程度のスコアを獲得しました。対照的に、以前のバージョンであるGPT-3.5は、下位10%程度のスコアでした。 ... これらの2つの瞬間は、Twitterの選択がトランプ前大統領をどのように助けたかを示しています。 ... 11月下旬に一般公開されたChatGPTを使用すると、ユーザーはエッセイ、ストーリー、歌詞を生成できます... ドナルド・トランプは、1月6日の行動を後悔しているかどうかなどの質問を受け、次のように答えます。「言っておきますが、私以上にこの国を愛している人はいません...------------------------------------------------------------------------------------------------base_chainの結果: はい、ChatGPTはトランプ大統領の在任中に存在していました。ChatGPTはOpenAIが開発したAI言語モデルで、11月下旬に一般公開されました。エッセイ、物語、歌詞を生成する機能を備えています。バイデン大統領についての詩を書くために使われただけでなく、トランプ前大統領を巻き込んだ仮想シナリオからの応答生成など、様々な用途に活用できる可能性があります。 結果は明らかに間違っています。正しいモデル出力を得るために、`step_back_question_chain` と `step_back_chain` の構築を開始してください。 # いくつかのショットの例examples = [
{ "input": "警察のメンバーは合法的な逮捕を行うことができるか?"、 "output": "警察のメンバーは何ができるか?"、
},
{ "input": "Jan Sindel はどの国で生まれましたか?", "output": "Jan Sindel の個人的な経歴は何ですか?",
},
]# これをサンプルメッセージに変換しますexample_prompt = ChatPromptTemplate.from_messages(
[
(「人間」、「{入力}」)、
("ai", "{出力}"),
]
)
few_shot_prompt = FewShotChatMessagePromptTemplate(
example_prompt=例_prompt、
例=例、
)
step_back_prompt = ChatPromptTemplate.from_messages(
[
( "システム", """あなたは世界に関する知識の専門家です。あなたの仕事は、一歩下がって質問をより一般的な、より答えやすい一歩下がった質問に言い換えることです。ここにいくつかの例を示します。""",
), # いくつかのショットの例 few_shot_prompt, # 新しい質問
(「ユーザー」、「{質問}」)、
]
)
step_back_question_chain = step_back_prompt | ChatOpenAI(temperature=0) | StrOutputParser()
june_print('ステップバック質問:', step_back_question_chain.invoke({"question": question}))
june_print('ステップバック質問の検索されたコンテキスト:', retriever(step_back_question_chain.invoke({"question": question})) )
response_prompt_template = """あなたは世界に関する知識の専門家です。これから質問をさせていただきます。回答は包括的で、後続の文脈と矛盾しないものでなければなりません。関連性がない場合は無視してください。
{通常のコンテキスト}
{ステップバックコンテキスト}
元の質問: {question}
回答:"""response_prompt = ChatPromptTemplate.from_template(response_prompt_template)
ステップバックチェーン = (
{ # 通常の質問を使用してコンテキストを取得します
"normal_context": RunnableLambda(lambda x: x["question"]) | retriever, # ステップバック質問を使用してコンテキストを取得します
"step_back_context": step_back_question_chain | retriever, # 質問をパスする
"質問": lambda x: x["質問"],
}
| 応答プロンプト
| ChatOpenAI(温度=0)
| StrOutputParser()
)
june_print('step_back_chainの結果:', step_back_chain.invoke({"question": question}) ) 結果は次のとおりです。 ----------------------------------------------------------------------------------------------------ステップバックの質問: ChatGPT はいつ利用可能になりましたか?------------------------------------------------------------------------------------------------ステップバック質問の検索コンテキスト: OpenAIは2022年11月30日にChatGPTの初期デモをリリースし、ユーザーがChatGPTの機能例を共有するにつれて、このチャットボットはソーシャルメディアで瞬く間に広まりました。ストーリーとサンプルには以下が含まれていました... 2023年3月14日 - AnthropicがChatGPTの代替となるClaudeをリリースしました。 2023年3月20日 - ChatGPTの大規模な障害が数時間にわたってすべてのユーザーに影響を与えました。 2023年3月21日 - GoogleがBardをリリースしました... ChatGPTが登場する1年近く前から、同じ基本モデルがAPIで利用可能でした。 別の意味では、人間がChatGPTを使ってやりたいことにもっと沿うようにしました。 有料のChatGPT Plusサブスクリプションも利用可能です。 (画像クレジット: OpenAI) ChatGPTはGPT-3.5シリーズの言語モデルに基づいており、OpenAIによると2022年初頭にトレーニングが完了しています。------------------------------------------------------------------------------------------------step_back_chainの結果:いいえ、ChatGPTはトランプ大統領時代には存在していませんでした。 ChatGPTは、トランプ大統領の任期終了後の11月下旬に一般公開されました。提供されている文脈におけるChatGPTへの言及はすべて、2022年11月30日の初期デモのリリースやChatGPT Plusサブスクリプションの開始など、トランプ大統領の任期後に遡るものです。したがって、ChatGPTはトランプ大統領の任期中には存在していなかったと断言できます。 ユーザーの最初のクエリをより抽象的な問題に「一歩後退」し、抽象化されたクエリと抽象化されていないクエリの両方を同時に取得することで、LLM は正しい推論パスに沿って問題を解決する能力を向上させることがわかります。 エドガー・W・ダイクストラは、「抽象化の目的は、物事を曖昧にしたり不確実にしたりすることではなく、問題の本質を抽出し、より高い意味レベルでより正確な理解と表現を達成することです。」と述べています。 04 クエリ2ドキュメント書籍『Query2doc: 大規模言語モデルによるクエリ拡張』[12]では、query2doc法が紹介されています。この手法は、LLMにプロンプト語を用いて擬似文書を生成させ、それらの文書を元のクエリと組み合わせて新しいクエリを作成します(図6参照)。 図6. query2docの少数ショットプロンプト手法の模式図。スペースの制約により、図には完全なコンテキストは示されていません。出典:Query2doc: 大規模言語モデルによるクエリ拡張[12]。 在 Dense Retrieval(译者注:与传统的基于检索的方法相比,这种方法利用预训练的语言模型(如BERT、RoBERTa等)来编码文档和 query,并计算它们之间的相似度。) 这种方法中,新的 query 用 q+ 表示,是最初的 query(q)和 pseudo-documents(d')的简单连接,用 [SEP] 分隔:q+ = concat(q, [SEP], d')。 Query2doc 认为,HyDE 隐含地假设真实文档(groundtruth document)和 pseudo-documents 用不同的词汇表达相同的语义,这对于某些 query 来说可能并不成立。 Query2doc 和 HyDE 之间的另一个区别是, Query2doc 会训练一个supervised dense retriever (译者注:在这种检索器中,通常会使用经过标注的训练数据来训练模型,并学习如何将 query 与相关文档进行匹配。有监督学习的方法可以帮助模型更好地理解 query 和文档之间的语义关系,从而提高检索的准确性和效率。),如这篇论文[12]所述。 目前,在 Langchain 或 LlamaIndex 中尚未发现 query2doc 的类似技术或工具。 05 ITER-RETGENITER-RETGEN 这种方法会使用生成的内容来指导检索过程。它在 “Retrieve-Read-Retrieve-Read” 流程中反复使用 "retrieval-enhanced generation" 技术和 "generation-enhanced retrieval" 技术。 图 7:ITER-RETGEN 迭代地进行检索和生成。在每一次迭代中,ITER-RETGEN 都会利用前一次迭代的模型输出作为上下文,帮助检索更多相关的知识,这种方法有助于改进模型生成的响应(如本图中更正 Hesse Hogan 的身高案例)。为了简洁起见,本图中仅显示了两次迭代过程。实线箭头连接 query 和检索到的知识,虚线箭头表示检索增强生成流程。Source: Enhancing Retrieval-Augmented Large Language Models with Iterative Retrieval-Generation Synergy[13]. 如图 7 所示,对于给定的问题 q 和检索语料库 D = {d}(其中 d 表示文档段落), ITER-RETGEN 会连续执行 T 次检索生成。 在每次迭代 t 中,会首先使用上一次迭代的生成结果 yt-1,将其与 q 结合,并检索出前 k 个段落。然后,引导 LLM 模型 M 生成模型输出 yt,将检索到的文档段落(表示为 Dyt-1||q)和 q 纳入提示词中。因此,每次迭代可以表述如下: 最后的输出 yt 将作为最终的模型响应产生。 与 Query2doc 类似,目前在 Langchain 或 LlamaIndex 中尚未发现 ITER-RETGEN 的类似技术或工具。 05 結論本文介绍了各种 query rewriting 技术,并提供了一些代码演示。 在实践中,这些 query rewriting 方法都可以尝试,至于使用哪种方法或方法组合,则取决于具体效果。 不过,无论采用何种query rewriting 方法,调用 LLM 都会带来一些性能问题(译者注:例如速度较慢、资源消耗较大等。),这需要在实际使用中加以考虑。 此外,还有一些方法,比如 query routing ,将 queries 分解为多个子问题等,它们并不属于 query rewriting 技术,而是属于预检索方法(pre-retrieval methods),有机会会在后续文章中介绍这些方法。 如果您对 RAG 技术感兴趣,欢迎阅读本系列的其他文章!如有任何问题,请在评论区提出。 読んでくれてありがとう! —— フロリアン・ジューン An artificial intelligence researcher, mainly write articles about Large Language Models, data structures and algorithms, and NLP. 終わり 参考文献[1] https://arxiv.org/pdf/2212.10496.pdf [2] https://docs.llamaindex.ai/en/stable/examples/query_transformations/HyDEQueryTransformDemo.html [3] https://github.com/langchain-ai/langchain/blob/master/cookbook/hypothetical_document_embeddings.ipynb [4] https://raw.githubusercontent.com/run-llama/llama_index/main/docs/examples/data/paul_graham/paul_graham_essay.txt [5] https://github.com/run-llama/llama_index/blob/v0.10.12/llama-index-core/llama_index/core/prompts/default_prompts.py#L336 [6] https://github.com/run-llama/llama_index/blob/v0.10.12/llama-index-core/llama_index/core/indices/query/query_transform/base.py#L107 [7] https://docs.llamaindex.ai/en/stable/examples/query_transformations/HyDEQueryTransformDemo.html#failure-case-1-hyde-may-mislead-when-query-can-be-mis-interpreted-without-context [8] https://arxiv.org/pdf/2305.14283.pdf [9] https://github.com/langchain-ai/langchain/blob/master/cookbook/rewrite.ipynb [10] https://arxiv.org/pdf/2310.06117.pdf [11] https://github.com/langchain-ai/langchain/blob/master/cookbook/stepback-qa.ipynb [12] https://arxiv.org/pdf/2303.07678.pdf [13] https://arxiv.org/pdf/2305.15294.pdf この記事は、原著者の許可を得てBaihai IDPによって翻訳されました。翻訳の転載をご希望の場合は、お問い合わせください。 オリジナルリンク: https://medium.com/@florian_algo/advanced-rag-06-exploring-query-rewriting-23997297f2d1 |