見出し画像

高度なRAG検索戦略:文章単位のリトリーバル

基本のRAG検索に比べて、高度なRAGはより詳細な技術的な詳細と複雑な検索戦略を含み、より正確で関連性の高い包括的な情報検索結果を提供します。今日は、高度なRAG検索戦略の中でそのような手法の1つ、文ウィンドウ検索を紹介します。

文ウィンドウ検索の紹介

文ウィンドウ検索に入る前に、基本のRAG検索を簡単に紹介しましょう。以下は基本のRAG検索のフローチャートです。

  • ますます、文書は同じサイズのチャンクにスライスされます

  • スライスされたチャンクは埋め込まれ、ベクトルデータベースに保存されます

  • 質問に基づいて、埋め込みに最も類似した文書ライブラリのK個が取得されます

  • 質問と取得結果はLLMに供給され、回答が生成されます

ベースRAG検索の問題は、文書スライスが比較的大きい場合、取得結果に多くの関連しない情報が含まれ、LLMによって生成される不正確な結果につながることです。さて、文のウィンドウ検索のフローチャートを見てみましょう。

  • 基本のRAG検索に比べて、文章ウィンドウ検索のドキュメントスライシングユニットは通常、文章に基づいて小さくなります。

  • 検索中に、最も一致する文章を見つけるだけでなく、その文章の周囲の文脈も検索結果の一部としてLLMに提出されます。

文章ウィンドウ検索は検索コンテンツをより正確にし、コンテキストウィンドウは検索結果の豊かさを確保します。

原則

文章ウィンドウ検索の原則は非常にシンプルです。まず、スライシングプロセス中にドキュメントが文章に分割され、埋め込まれてデータベースに保存されます。検索中に関連する文章が見つかりますが、検索結果として考慮されるのは取得された文章だけではありません。取得された文章の前後の文章も結果の一部として含まれます。含まれる文章の数はパラメータを通じて調整でき、最終的に検索結果はLLMに一緒に提出されて回答が生成されます。

元イメージ: https://medium.com/@shivansh.kaushik/advanced-text-retrieval-with-elasticsearch-llamaindex-sentence-window-retrieval-cb5ea720aa44

例のコードを通じて文章ウィンドウの取得原則を理解しましょう。RAGフレームワークでは、LlamaIndexが文章ウィンドウの取得機能をうまく実装しています。以下では、LlamaIndexを使用して文章ウィンドウの取得機能をデモンストレーションします。

from llama_index.core.node_parser import SentenceWindowNodeParser
from llama_index.core.schema import Document
node_parser = SentenceWindowNodeParser.from_defaults(
    window_size=3,
    window_metadata_key="window",
    original_text_metadata_key="original_text",
)
text = "hello. how are you? I am fine! Thank you. And you? I am fine too. "
nodes = node_parser.get_nodes_from_documents([Document(text=text)])
  1. SentenceWindowNodeParserというドキュメントパーサーは、window\_sizeを3に設定して作成されます。これは、取得した文の前に3つの文、取得した文自体、および取得した文の後に3つの文を含むことができることを意味します。

  2. ドキュメントは、ドキュメントパーサーを使用して解析され、解析された結果にはwindowとoriginal\_textという2つのメタデータが含まれています。

  3. window\_metadata\_keyは、文のウィンドウに含まれるすべての文を格納するキー値を指し、original\_text\_metadata\_keyは取得した文のキー値を指します。

  4. 最後に、元のドキュメントはドキュメントパーサーを使用して解析されます。

注意: 以前のバージョンでは、取得した文の後に追加される文は2つだけであり、デフォルトのwindow\_size=3設定では、文のウィンドウには合計6つの文しか含まれませんでした。しかし、llama-index-coreにコア機能を抽出した新しいバージョンでは、取得した文の後に3つの文が含まれるようになります。詳細は公式リポジトリコードで確認できます。
解析後のノードの内容を見てみましょう。まず最初に最初のノードが表示されます。

print(nodes[0].metadata)
# Output
{'window': 'hello.  how are you?  I am fine!  Thank you. ', 'original_text': 'hello. '}

最初の文が取得された文の場合、それより前に他の文がないため、文のウィンドウには、取得された文自体とその後の3つの文を含む合計4つの文が含まれています。

print(nodes[3].metadata)
# Output
{'window': 'hello.  how are you?  I am fine!  Thank you.  And you?  I am fine too. ', 'original_text': 'Thank you. '}

4番目の文が取得された文の場合、文のウィンドウには取得された文の前の3つの文、取得された文自体、および取得された文の後の3つの文が含まれます。ただし、後ろには2つの文しかないため、合計は6つの文だけです。

中国語の文の分割

文のウィンドウパーサーは、通常、デフォルトの句読点(.?!など)で英語の文を分割するために使用されます。しかし、この分割方法は中国語には適用できません。この問題に対処するために、ドキュメントパーサーに解析ルールパラメータを追加することができます。

import re
def sentence_splitter(text):
    nodes = re.split("(?<=。)|(?<=?)|(?<=!)", text)
    nodes = [node for node in nodes if node]
    return nodes
node_parser = SentenceWindowNodeParser.from_defaults(
    window_size=3,
    window_metadata_key="window",
    original_text_metadata_key="original_text",
    sentence_splitter=sentence_splitter,
)

sentence\_splitterパラメータを追加し、カスタムのsentence\_splitter関数を渡しました。この関数は、中国の句読点に基づいて文書を分割します。

text = "你好。你好吗?我很好!谢谢。你呢?我也很好。 "
print(nodes[0].metadata)
print(nodes[3].metadata)
# Output
{'window': '你好。 你好吗? 我很好! 谢谢。', 'original_text': '你好。'}
{'window': '你好。 你好吗? 我很好! 谢谢。 你呢? 我也很好。  ', 'original_text': '谢谢。'}

パーサーの解析ルールを置き換えた後、パーサーの解析された文は、英語の解析と同じ効果があります。

文ウィンドウの使用

次に、実際のRAGプロジェクトで文ウィンドウの取得がどのように使用されるかを見てみましょう。ドキュメントデータについては、テストには引き続きアベンジャーズ映画のプロットをWikipediaから使用します。

基本的なRAG検索の例

まず、基本的なRAG検索がドキュメントの分割と検索に与える効果を見てみましょう。

from llama_index.core import SimpleDirectoryReader
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.settings import Settings
from llama_index.core import VectorStoreIndex
from llama_index.embeddings.openai import OpenAIEmbedding
documents = SimpleDirectoryReader("./data").load_data()
text_splitter = SentenceSplitter()
llm = OpenAI(model="gpt-3.5-turbo", temperature=0.1)
embed_model = OpenAIEmbedding()
Settings.llm = llm
Settings.embed_model = embed_model
Settings.node_parser = text_splitter
base_index = VectorStoreIndex.from_documents(
    documents=documents,
)
base_engine = base_index.as_query_engine(
    similarity_top_k=2,
)
  • LlamaIndexを使用してベースのRAG検索を作成し、まずdataディレクトリからドキュメントをロードしました。

  • ドキュメントのパーサーとしてSentenceSplitterを使用しています。デフォルトのTokenTextSplitterとは異なり、SentenceSplitterは通常、分割後に部分的な文ではなく完全な文を含んでいます。

  • ドキュメントの埋め込みと回答生成には、OpenAIの埋め込みとLLMモデルを使用しています。最新バージョンのLlamaIndexでは、以前のServiceContextの代わりにSettingsパラメータが使用されています。

  • 最後に、検索エンジンを作成して、検索結果として最も関連性の高いドキュメントを2つだけ取得します。

さて、テスト結果を見てみましょう:

question = "Which two members of the Avengers created Ultron?"
response = base_engine.query(question)
print(f"response: {response}")
print(f"len: {len(response.source_nodes)}")
text = response.source_nodes[0].node.text
print("------------------")
print(f"Text: {text}")
text = response.source_nodes[1].node.text
print("------------------")
print(f"Text: {text}")
# Output
response: Tony Stark and Bruce Banner
len: 2
------------------
Text: In the Eastern European country of Sokovia, the Avengers—Tony Stark, Thor, Bruce Banner, Steve Rogers, Natasha Romanoff, and Clint Barton—raid a Hydra facility commanded by Baron Wolfgang von Strucker, who has experimented on humans using the scepter previously wielded by Loki. They meet two of Strucker's test subjects—twins Pietro (who has superhuman speed) and Wanda Maximoff (who has telepathic and telekinetic abilities)—and apprehend Strucker, while Stark retrieves Loki's scepter.
Stark and Banner discover an artificial intelligence within the scepter's gem, and secretly decide to use it to complete Stark's "Ultron" global defense program. The unexpectedly sentient Ultron, believing he must eradicate humanity to save Earth, eliminates Stark's A.I. J.A.R.V.I.S. and attacks the Avengers at their headquarters. Escaping with the scepter, Ultron uses the resources in Strucker's Sokovia base to upgrade his rudimentary body and build an army of robot drones. Having killed Strucker, he recruits the Maximoffs, who hold Stark responsible for their parents' deaths by his company's weapons, and goes to the base of arms dealer Ulysses Klaue in Johannesburg to get vibranium. The Avengers attack Ultron and the Maximoffs, but Wanda subdues them with haunting visions, causing Banner to turn into the Hulk and rampage until Stark stops him with his anti-Hulk armor.[a]
A worldwide backlash over the resulting destruction, and the fears Wanda's hallucinations incited, send the team into hiding at Barton's farmhouse. Thor departs to consult with Dr. Erik Selvig on the apocalyptic future he saw in his hallucination, while Nick Fury arrives and encourages the team to form a plan to stop Ultron. In Seoul, Ultron uses Loki's scepter to enslave the team's friend Helen Cho. They use her synthetic-tissue technology, vibranium, and the scepter's gem to craft a new body. As Ultron uploads himself into the body, Wanda is able to read his mind; discovering his plan for human extinction, the Maximoffs turn against Ultron. Rogers, Romanoff, and Barton fight Ultron and retrieve the synthetic body, but Ultron captures Romanoff. The Avengers fight among themselves when Stark and Banner secretly upload J.A.R.V.I.S.—who is still working after hiding from Ultron inside the Internet—into the synthetic body.
Thor returns to help activate the body, based on his vision that the gem on its brow is the Mind Stone, one of the six Infinity Stones, the most powerful objects in existence. This "Vision" earns their trust by being worthy of lifting Thor's hammer, Mjölnir. Vision and the Maximoffs go with the Avengers to Sokovia, where Ultron has used the remaining vibranium to build a machine to lift a large part of the capital city skyward, intending to crash it into the ground to cause global extinction. Banner rescues Romanoff, who awakens the Hulk for the battle. The Avengers fight Ultron's army while Fury arrives in a Helicarrier with Maria Hill, James Rhodes, and S.H.I.E.L.D. agents to evacuate civilians.
Pietro dies when he shields Barton from gunfire, and a vengeful Wanda abandons her post to destroy Ultron's primary body, which allows one of his drones to activate the machine. The city plummets, but Stark and Thor overload the machine and shatter the landmass. In the aftermath, the Hulk, unwilling to endanger Romanoff by being with her, departs in a Quinjet, while Vision confronts and destroys Ultron's last remaining body. Later, with the Avengers having established a new base run by Fury, Hill, Cho, and Selvig, Thor returns to Asgard to learn more about the forces he suspects have manipulated recent events. As Stark leaves and Barton retires, Rogers and Romanoff prepare to train new Avengers: Rhodes, Vision, Sam Wilson, and Wanda.
In a mid-credits scene, Thanos dons a gauntlet[b] and vows to retrieve the Infinity Stones himself.
------------------
Text: In 2018, twenty-three days after Thanos erased half of all life in the universe,[a] Carol Danvers rescues Tony Stark and Nebula from deep space and they reunite with the remaining Avengers—Bruce Banner, Steve Rogers, Thor, Natasha Romanoff, and James Rhodes—and Rocket on Earth. Locating Thanos on an uninhabited planet, they plan to use the Infinity Stones to reverse his actions, only to find that Thanos has already destroyed them, thus preventing any further use. Enraged, Thor decapitates Thanos.
Five years later, Scott Lang escapes from the Quantum Realm.[b] Reaching the Avengers Compound, he explains that he experienced only five hours while trapped. Theorizing that the Quantum Realm allows time travel, they ask a reluctant Stark to help them retrieve the Stones from the past to reverse the actions of Thanos in the present. Stark, Rocket, and Banner, who has since merged his intelligence with the Hulk's strength, build a time machine. Banner notes that altering the past does not affect their present; any changes create alternate realities. Banner and Rocket travel to Norway, where they visit the Asgardian refugees' settlement New Asgard and recruit an overweight and despondent Thor. In Tokyo, Romanoff recruits Clint Barton, who became a vigilante after his family was erased during the execution of Thanos's plan.[a]
Banner, Lang, Rogers, and Stark time-travel to New York City during Loki's attack in 2012.[c] At the Sanctum Sanctorum, Banner convinces the Ancient One to give him the Time Stone after promising to return the various Stones to their proper points in time. At Stark Tower, Rogers retrieves the Mind Stone from Hydra sleeper agents, but Stark and Lang's attempt to steal the Space Stone fails, allowing 2012-Loki to escape with it. Rogers and Stark travel to Camp Lehigh in 1970, where Stark obtains an earlier version of the Space Stone and encounters his father, Howard. Rogers steals Pym Particles from Hank Pym to return to the present and spies his lost love, Peggy Carter.
Meanwhile, Rocket and Thor travel to Asgard in 2013;[d] Rocket extracts the Reality Stone from Jane Foster, while Thor gets encouragement from his mother, Frigga, and retrieves his old hammer, Mjolnir. Barton, Romanoff, Nebula, and Rhodes travel to 2014; Nebula and Rhodes go to Morag and steal the Power Stone before Peter Quill can,[e] while Barton and Romanoff travel to Vormir. The Soul Stone's keeper, Red Skull, reveals it can only be acquired by sacrificing a loved one. Romanoff sacrifices herself, allowing Barton to get the Stone. Rhodes and Nebula attempt to return to their own time, but Nebula is incapacitated when her cybernetic implants link with her past self, allowing 2014-Thanos to learn of his future self's success and the Avengers' attempt to undo it. 2014-Thanos sends 2014-Nebula forward in time to prepare for his arrival.
Reuniting in the present, the Avengers place the Stones into a gauntlet that Stark, Banner, and Rocket have built. Banner, who has the most resistance to their radiation, uses the gauntlet to undo every one of Thanos's disintegrations. Meanwhile, 2014-Nebula, impersonating her future self, uses the time machine to transport 2014-Thanos and his warship to the present, which he then uses to destroy the Avengers Compound. Present-day Nebula convinces 2014-Gamora to betray Thanos, but is unable to convince 2014-Nebula and kills her. Thanos overpowers Stark, Thor and a Mjolnir-wielding Rogers, and summons his army to retrieve the Stones, intent on using them to destroy the universe and create a new one. A restored Stephen Strange arrives with other sorcerers, the restored Avengers and Guardians of the Galaxy, the Ravagers, and the armies of Wakanda and Asgard to fight Thanos's army. Danvers also arrives and destroys Thanos's warship, but Thanos overpowers her and seizes the gauntlet. Stark steals the Stones and uses them to disintegrate Thanos and his army, sacrificing his life in the process.
Following Stark's funeral, Thor appoints Valkyrie as the new king of New Asgard and joins the Guardians. Rogers returns the Stones and Mjolnir to their proper timelines and remains in the past to live with Carter. In the present, an elderly Rogers passes his shield to Sam Wilson.
  • ベースRAG検索からの回答は正しいです。なぜなら、取得された文書には回答に関連するコンテンツが含まれているからです。

  • ベースRAGによって取得された関連文書は2つあり、関連度によって並べ替えられています。

文窓検索の例

from llama_index.core.node_parser import SentenceWindowNodeParser
from llama_index.core.indices.postprocessor import MetadataReplacementPostProcessor
node_parser = SentenceWindowNodeParser.from_defaults(
    window_size=3,
    window_metadata_key="window",
    original_text_metadata_key="original_text",
)
documents = SimpleDirectoryReader("./data").load_data()
llm = OpenAI(model="gpt-3.5-turbo", temperature=0.1)
embed_model = OpenAIEmbedding()
Settings.llm = llm
Settings.embed_model = embed_model
Settings.node_parser = node_parser
sentence_index = VectorStoreIndex.from_documents(
    documents=documents,
)
postproc = MetadataReplacementPostProcessor(target_metadata_key="window")
sentence_window
_engine = sentence_index.as_query_engine(
    similarity_top_k=2, node_postprocessors=[postproc]
)
  • 文のウィンドウ取得のためのコードは、ベースのRAG取得とはいくつかの点で異なります。最初の違いは、私たちがすでに議論したSentenceWindowNodeParserのドキュメントパーサーの使用です。

  • 二番目の違いは、取得結果を後処理するためにMetadataReplacementPostProcessorを使用し、取得結果をwindowメタデータの値で置き換えることです。

テスト結果は以下の通りです:

response = sentence_window_engine.query(question)
print(f"response: {response}")
print(f"len: {len(response.source_nodes)}")
window = response.source_nodes[0].node.metadata["window"]
sentence = response.source_nodes[0].node.metadata["original_text"]
print("------------------")
print(f"Window: {window}")
print("------------------")
print(f"Original Sentence: {sentence}")
window = response.source_nodes[1].node.metadata["window"]
sentence = response.source_nodes[1].node.metadata["original_text"]
print("------------------")
print(f"Window : {window}")
print("------------------")
print(f"Original Sentence: {sentence}")
# Output
response: Tony Stark and Bruce Banner
len: 2
------------------
Window: In the Eastern European country of Sokovia, the Avengers—Tony Stark, Thor, Bruce Banner, Steve Rogers, Natasha Romanoff, and Clint Barton—raid a Hydra facility commanded by Baron Wolfgang von Strucker, who has experimented on humans using the scepter previously wielded by Loki.  They meet two of Strucker's test subjects—twins Pietro (who has superhuman speed) and Wanda Maximoff (who has telepathic and telekinetic abilities)—and apprehend Strucker, while Stark retrieves Loki's scepter.
Stark and Banner discover an artificial intelligence within the scepter's gem, and secretly decide to use it to complete Stark's "Ultron" global defense program.  The unexpectedly sentient Ultron, believing he must eradicate humanity to save Earth, eliminates Stark's A.I.  J.A.R.V.I.S.
------------------
Original Sentence: They meet two of Strucker's test subjects—twins Pietro (who has superhuman speed) and Wanda Maximoff (who has telepathic and telekinetic abilities)—and apprehend Strucker, while Stark retrieves Loki's scepter.
------------------
Window 1: In 2018, twenty-three days after Thanos erased half of all life in the universe,[a] Carol Danvers rescues Tony Stark and Nebula from deep space and they reunite with the remaining Avengers—Bruce Banner, Steve Rogers, Thor, Natasha Romanoff, and James Rhodes—and Rocket on Earth.  Locating Thanos on an uninhabited planet, they plan to use the Infinity Stones to reverse his actions, only to find that Thanos has already destroyed them, thus preventing any further use.  Enraged, Thor decapitates Thanos.
Five years later, Scott Lang escapes from the Quantum Realm.
------------------
Original Sentence: In 2018, twenty-three days after Thanos erased half of all life in the universe,[a] Carol Danvers rescues Tony Stark and Nebula from deep space and they reunite with the remaining Avengers—Bruce Banner, Steve Rogers, Thor, Natasha Romanoff, and James Rhodes—and Rocket on Earth.
  • 文のウィンドウ検索からの回答も正しいですが、取得される文書数はベースのRAG検索よりも少ないです。

  • 文のウィンドウ内の文の数は、以前に紹介したものと一致し、Original Sentenceおよびその前後3つの文を含みます。

検索効果の比較

上記の例コードをテストした後、ベースのRAG検索と文のウィンドウ検索の両方が正しい回答を得ることができることがわかりますが、どちらの検索効果がより良いかは明確ではありません。以前に紹介したLLM評価ツールTrulensを使用して、両者の効果を比較することができます。

from trulens_eval import Tru, Feedback, TruLlama
from trulens_eval.feedback.provider.openai import OpenAI as Trulens_OpenAI
from trulens_eval.feedback import Groundedness
tru = Tru()
openai = Trulens_OpenAI()
def rag_evaluate(query_engine, eval_name):
    grounded = Groundedness(groundedness_provider=openai)
    groundedness = (
        Feedback(grounded.groundedness_measure_with_cot_reasons, name="Groundedness")
        .on(TruLlama.select_source_nodes().node.text)
        .on_output()
        .aggregate(grounded.grounded_statements_aggregator)
    )
    qa_relevance = Feedback(
        openai.relevance_with_cot_reasons, name="Answer Relevance"
    ).on_input_output()
    qs_relevance = (
        Feedback(openai.qs_relevance_with_cot_reasons, name="Context Relevance")
        .on_input()
        .on(TruLlama.select_source_nodes().node.text)
    )
    tru_query_engine_recorder = TruLlama(
        query_engine,
        app_id=eval_name,
        feedbacks=[
            groundedness,
            qa_relevance,
            qs_relevance,
        ],
    )
    with tru_query_engine_recorder as recording:
        query_engine.query(question)
  • query\_engine と eval\_name をパラメータとして使用して評価方法を定義します。

  • Trulens の groundedness、qa\_relevance、および qs\_relevance は RAG 検索結果を評価するために使用されます。

Trulens に関する詳細情報は、以前の記事を参照してください。さて、評価方法を実行しましょう。

tru.reset_database()
rag_evaluate(base_engine, "base_evaluation")
rag_evaluate(sentence_window_engine, "sentence_window_evaluation")
Tru().run_dashboard()

Trulensのウェブページによると、文のウィンドウ取得は常にベースのRAG取得よりも良いとは限らず、時にはそれが悪化することさえあります。これには、window\_sizeやその他のパラメータを調整するなど、文のウィンドウ取得の効果を改善するためのさらなる最適化が必要です。

結論

RAGはLLMアプリケーションのほとんどの問題を解決できますが、それが万能解ではありません。高度なRAG検索はすべてのRAGの問題に対するワンサイズフィットオールの解決策ではありません。特定のプロジェクト要件に基づいてどの検索方法を使用するかを決定し、パラメータの調整、ドキュメントの最適化、その他の方法を通じてRAGアプリケーションを継続的に最適化する必要があります。

データ元:

この記事が気に入ったらサポートをしてみませんか?