LLM を利用した QA システム用の新しいドキュメント サマリー インデックス

May 09 2023
このブログ投稿では、まったく新しい LlamaIndex データ構造であるドキュメント サマリー インデックスを紹介します。従来のセマンティック検索と比較して検索パフォーマンスを向上させる方法について説明し、例を紹介します。

このブログ投稿では、まったく新しい LlamaIndex データ構造であるドキュメント サマリー インデックスを紹介します。従来のセマンティック検索と比較して検索パフォーマンスを向上させる方法について説明し、例を紹介します。

バックグラウンド

大規模言語モデル (LLM) の主要な使用例の 1 つは、独自のデータに対する質問応答です。これを行うために、LLM を、知識コーパスに対して情報検索を実行できる「検索」モデルと組み合わせ、LLM を使用して検索されたテキストに対して応答合成を実行します。この全体的なフレームワークは、Retrieval-Augmented Generation と呼ばれます。

現在、LLM を利用した QA システムを構築しているほとんどのユーザーは、次のような形で行う傾向があります。

  1. ソース ドキュメントを取得し、それぞれをテキスト チャンクに分割します
  2. ベクターデータベースにテキストチャンクを保存する
  3. クエリ時に、類似度フィルターやキーワード フィルターを埋め込んでテキスト チャンクを取得します。
  4. 応答合成を実行する

既存のアプローチの限界

テキスト チャンクを使用した埋め込み検索には、いくつかの制限があります。

  • テキスト チャンクにはグローバル コンテキストがありません。多くの場合、質問には、特定のチャンクでインデックス化されているものを超えたコンテキストが必要です。
  • 上位 k / 類似度スコアのしきい値を慎重に調整します。値が小さすぎると、コンテキストが失われます。値を大きくしすぎると、より無関係なコンテキストでコスト/レイテンシが増加する可能性があります。
  • 埋め込みは、質問に対して最も関連性の高いコンテキストを常に選択するとは限りません。埋め込みは、本質的にテキストとコンテキストの間で個別に決定されます。

ドキュメント サマリー インデックス

文書概要索引の図

各ドキュメントの構造化されていないテキストの要約を抽出/インデックス化するLlamaIndexの新しいインデックスを提案します。このインデックスは、既存の検索アプローチを超えて検索パフォーマンスを向上させるのに役立ちます。単一のテキスト チャンクよりも多くの情報をインデックス化するのに役立ち、キーワード タグよりも意味的な意味を持ちます。また、より柔軟な形式の検索も可能になります。LLM 検索と埋め込みベースの検索の両方を行うことができます。

使い方

ビルド時に各ドキュメントを取り込み、LLM を使用して各ドキュメントから要約を抽出します。また、ドキュメントをテキスト チャンク (ノード) に分割します。要約とノードの両方が、ドキュメント ストアの抽象化内に格納されます。要約からソース ドキュメント/ノードへのマッピングを維持します。

クエリ時に、次のアプローチを使用して、サマリーに基づいてクエリに関連するドキュメントを取得します。

  • LLM ベースの検索:ドキュメント サマリーのセットを LLM に提示し、LLM にどのドキュメントが関連性があり、その関連性スコアが高いかを判断するよう依頼します。
  • 埋め込みベースの検索:埋め込み類似度の要約に基づいて、関連するドキュメントを検索します (上位 k カットオフあり)。

ドキュメントの要約を保存すると、LLM ベースの検索も可能になります。最初にドキュメント全体を LLM に渡す代わりに、まず LLM に簡潔なドキュメントの要約を検査させて、それがクエリに関連しているかどうかを確認することができます。これは、埋め込みベースのルックアップよりも高度な LLM の推論機能を活用しますが、ドキュメント全体を LLM にフィードするコスト/レイテンシを回避します。

追加の洞察

要約による文書検索は、すべての文書にわたるセマンティック検索と力ずくの要約の「中間」と考えることができます。指定されたクエリとの関連性の概要に基づいてドキュメントを検索し、取得したドキュメントに対応するすべての *ノード* を返します。

なぜこれを行う必要があるのですか?この検索方法は、ドキュメント レベルでコンテキストを取得することにより、テキスト チャンクの上位 k よりも多くのコンテキストをユーザーに提供します。ただし、トピック モデリングよりも柔軟で自動化されたアプローチでもあります。テキストに適切なキーワード タグが含まれているかどうかを心配する必要はもうありません。

さまざまな都市に関するウィキペディアの記事のドキュメント サマリー インデックスを示す例を見てみましょう。

このガイドの残りの部分では、関連するコード スニペットを紹介します。完全なチュートリアルはここにあります(ノートブックのリンクはここにあります)。

GPTDocumentSummaryIndexドキュメントのセットを構築し、オブジェクトを渡してResponseSynthesizerドキュメントの要約を合成できます。

from llama_index import (
    SimpleDirectoryReader,
    LLMPredictor,
    ServiceContext,
    ResponseSynthesizer
)
from llama_index.indices.document_summary import GPTDocumentSummaryIndex
from langchain.chat_models import ChatOpenAI

# load docs, define service context
...

# build the index
response_synthesizer = ResponseSynthesizer.from_args(response_mode="tree_summarize", use_async=True)
doc_summary_index = GPTDocumentSummaryIndex.from_documents(
    city_docs, 
    service_context=service_context,
    response_synthesizer=response_synthesizer
)

summary = doc_summary_index.get_document_summary("Boston")

from llama_index.indices.document_summary import DocumentSummaryIndexRetriever

retriever = DocumentSummaryIndexRetriever(
    doc_summary_index,
    # choice_select_prompt=choice_select_prompt,
    # choice_batch_size=choice_batch_size,
    # format_node_batch_fn=format_node_batch_fn,
    # parse_choice_select_answer_fn=parse_choice_select_answer_fn,
    # service_context=service_context
)
retrieved_nodes = retriever.retrieve("What are the sports teams in Toronto?")
print(retrieved_nodes[0].score)
print(retrieved_nodes[0].node.get_text())The retriever will retrieve a set of relevant nodes for a given index.

8.0
Toronto ( (listen) tə-RON-toh; locally [təˈɹɒɾ̃ə] or [ˈtɹɒɾ̃ə]) is the capital city of the Canadian province of Ontario. With a recorded population of 2,794,356 in 2021, it is the most populous city in Canada...

高レベル API

query_engine = doc_summary_index.as_query_engine(
  response_mode="tree_summarize", use_async=True
)
response = query_engine.query("What are the sports teams in Toronto?")
print(response)

# use retriever as part of a query engine
from llama_index.query_engine import RetrieverQueryEngine

# configure response synthesizer
response_synthesizer = ResponseSynthesizer.from_args()

# assemble query engine
query_engine = RetrieverQueryEngine(
    retriever=retriever,
    response_synthesizer=response_synthesizer,
)

# query
response = query_engine.query("What are the sports teams in Toronto?")
print(response)

任意のテキストに対する自動要約のアプローチは、非常に興味深いものです。次の 2 つの分野で拡張機能を開発できることを嬉しく思います。

  • さまざまなレイヤーで自動要約の調査を続けます。現在はドキュメント レベルですが、大きなテキスト チャンクを小さなテキスト チャンクに要約するにはどうすればよいでしょうか。(例:ワンライナー)。
  • 要約がロックを解除するのに役立つLLMベースの検索を引き続き調べてください。

ドキュメント概要ガイド

ノートブック リンク