Relative Score Fusion and Distribution-Based Score Fusion

Relative Score Fusion and Distribution-Based Score Fusion

Reciprocal Rank Fusion을 개선하기 위한 두 가지 퓨전 방법, Relative Score Fusion과 Distribution-Based Score Fusion을 QueryFusionRetriever로 사용하는 예시예요. 두 방법의 차이를 코드로 직접 비교해 볼게요.

출처: 문서

본문

이 예시에서는 Reciprocal Rank Fusion을 개선하려는 두 가지 방법으로 QueryFusionRetriever를 사용하는 방법을 보여줍니다:

  1. Relative Score Fusion (Weaviate)
  2. Distribution-Based Score Fusion (Mazzeschi: blog post)
%pip install llama-index-llms-openai
%pip install llama-index-retrievers-bm25
import os
import openai


os.environ["OPENAI_API_KEY"] = "sk-..."
openai.api_key = os.environ["OPENAI_API_KEY"]

Setup

이 노트북을 colab에서 여는 경우 LlamaIndex 🦙를 설치해야 할 수 있습니다.

데이터 다운로드

!mkdir -p 'data/paul_graham/'
!wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/examples/data/paul_graham/paul_graham_essay.txt' -O 'data/paul_graham/paul_graham_essay.txt'
from llama_index.core import SimpleDirectoryReader


documents = SimpleDirectoryReader("./data/paul_graham/").load_data()

다음으로 문서 위에 벡터 인덱스를 구성합니다.

from llama_index.core import VectorStoreIndex
from llama_index.core.node_parser import SentenceSplitter


splitter = SentenceSplitter(chunk_size=256)


index = VectorStoreIndex.from_documents(
    documents, transformations=[splitter], show_progress=True
)

Relative Score Fusion을 사용한 Hybrid Fusion Retriever 생성

이 단계에서는 인덱스를 BM25 기반 리트리버와 융합합니다. 이렇게 하면 입력 쿼리에서 의미적 관계와 키워드를 모두 포착할 수 있습니다.

두 리트리버 모두 점수를 계산하므로, 추가 모델이나 과도한 계산 없이 QueryFusionRetriever를 사용해 노드를 재정렬할 수 있습니다.

다음 예시는 Weaviate의 Relative Score Fusion 알고리즘을 사용하는데, 각 결과 집합에 MinMax 스케일러를 적용한 뒤 가중 합을 만듭니다. 여기서는 벡터 리트리버에 BM25보다 약간 더 큰 가중치를 줍니다(0.6 대 0.4).

먼저 리트리버를 생성합니다. 각 리트리버는 가장 유사한 상위 10개 노드를 가져옵니다.

from llama_index.retrievers.bm25 import BM25Retriever


vector_retriever = index.as_retriever(similarity_top_k=5)


bm25_retriever = BM25Retriever.from_defaults(
    docstore=index.docstore, similarity_top_k=10
)

다음으로 퓨전 리트리버를 만들 수 있는데, 이 리트리버는 리트리버들이 반환한 20개 노드 중 상위 10개를 반환합니다.

벡터와 BM25 리트리버가 동일한 노드를 서로 다른 순서로만 반환했을 수도 있으므로, 이 경우에는 단순히 재정렬기(re-ranker) 역할을 한다는 점에 주의하세요.

from llama_index.core.retrievers import QueryFusionRetriever


retriever = QueryFusionRetriever(
    [vector_retriever, bm25_retriever],
    retriever_weights=[0.6, 0.4],
    similarity_top_k=10,
    num_queries=1,  # set this to 1 to disable query generation
    mode="relative_score",
    use_async=True,
    verbose=True,
)
# apply nested async to run in a notebook
import nest_asyncio


nest_asyncio.apply()
nodes_with_scores = retriever.retrieve(
    "What happened at Interleafe and Viaweb?"
)
for node in nodes_with_scores:
    print(f"Score: {node.score:.2f} - {node.text[:100]}...\n-----")

Distribution-Based Score Fusion

Relative Score Fusion의 변형으로, Distribution-Based Score Fusion은 각 결과 집합의 점수 평균과 표준편차에 기반해 점수를 조금 다르게 스케일링합니다.

from llama_index.core.retrievers import QueryFusionRetriever


retriever = QueryFusionRetriever(
    [vector_retriever, bm25_retriever],
    retriever_weights=[0.6, 0.4],
    similarity_top_k=10,
    num_queries=1,  # set this to 1 to disable query generation
    mode="dist_based_score",
    use_async=True,
    verbose=True,
)


nodes_with_scores = retriever.retrieve(
    "What happened at Interleafe and Viaweb?"
)


for node in nodes_with_scores:
    print(f"Score: {node.score:.2f} - {node.text[:100]}...\n-----")

Query Engine에서 사용하기!

이제 리트리버를 쿼리 엔진에 연결해 자연어 응답을 합성할 수 있습니다.

from llama_index.core.query_engine import RetrieverQueryEngine


query_engine = RetrieverQueryEngine.from_args(retriever)
response = query_engine.query("What happened at Interleafe and Viaweb?")
from llama_index.core.response.notebook_utils import display_response


display_response(response)

더 알아보기 (Learn more)