이 글은 RAG - Say what? 를 읽고 정리한 글이빈다.
이 글에서는 RAG 의 여러 요소 중에서 사용자의 질문을 변형해서 좀 더 관려성 높은 문서를 찾기 위한 방법인 Query Translation 에 대해서 설명한다.
매커니즘은 쿼리를 단순하게 LLM 에게 전달하지 말고, 이것들을 잘 reconstruct 해서 연관된 문서를 찾는 방법임.
- 이 기법은 사용자로부터 쿼리가 주어졌을 때 해당 쿼리를 재작성하고, 검색 엔진을 통해서 검색해보고, 문맥을 가져와서 답변을 작성하는 방법임.
- Vector Store 에 검색을 하는 방법은 아니긴 하나, 아이디어는 차용할 수 있다.
Follow-up question to condensed/standalone one
- 사용자의 질문과, 사용자의 대화 내역을 포함시켜서 질문을 다시 만드는 방법임.
- LangChain 에서는 이를 아래와 같은 프롬포트로 사용하고 있다.
Given the following conversation and a follow up question, rephrase the follow up \
question to be a standalone question.
Chat History:
{chat_history}
Follow Up Input: {question}
Standalone Question:
- 사용자의 질문을 다양한 관점에서 다시 작성해서 여러 쿼리를 만들고 이를 이용해서 문서를 검색한다. 그리고 총 조회된 문서들을 reciprocal rank fusion (RRF) 에 따라서 스코어링을 해서 랭킹을 매긴다.
- 그러니까 1등 문서는 1/1 점, 2등 문서는 1/2 점, 3등 문서는 1/3 점 이런식으로 점수를 매겨서 총 랭킹을 매기는거임.
LangChain Cook Book 의 RAG Fusion 의 예시는 다음과 같다:
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant that generates multiple search queries based on a single input query."),
("user", "Generate multiple search queries related to: {original_query}"),
("user", "OUTPUT (4 queries):")
])
generate_queries = (
prompt | ChatOpenAI(temperature=0) | StrOutputParser() | (lambda x: x.split("\n"))
)
original_query = "impact of climate change"
vectorstore = PineconeVectorStore.from_existing_index("rag-fusion", OpenAIEmbeddings())
retriever = vectorstore.as_retriever()
from langchain.load import dumps, loads
def reciprocal_rank_fusion(results: list[list], k=60):
fused_scores = {}
for docs in results:
# Assumes the docs are returned in sorted order of relevance
for rank, doc in enumerate(docs):
doc_str = dumps(doc)
if doc_str not in fused_scores:
fused_scores[doc_str] = 0
previous_score = fused_scores[doc_str]
fused_scores[doc_str] += 1 / (rank + k)
reranked_results = [
(loads(doc), score)
for doc, score in sorted(fused_scores.items(), key=lambda x: x[1], reverse=True)
]
return reranked_results
chain = generate_queries | retriever.map() | reciprocal_rank_fusion
chain.invoke({"original_query": original_query})
- 주어진 질문을 한단계 추상화해서 그 질문으로 답변을 작성하는 방법임.
- 이것도 Vector Store 를 이용하는 건 아니지만 질문을 reconstruct 하는 매커니즘은 알아두면 좋을듯.
- 질문이 주어졌을 때 새로운 단어들을 생성해서 이를 기반으로 질문을 확장시켜서 검색하는 방법 이거나, 질문이 주어졌을 때 그 질문에 대한 답변을 적어서, 이걸 오리지날 질문과 합쳐서 문서를 검색하는 방법이다.
Hypothetical Document Embeddings (HyDE):
- 질문을 할 경우 가상 답변을 만들어서 이를 기반으로 문서를 검색하는 방법. 이렇게 할 경우 Vector Store 에서 연관된 문서를 더 잘 조회할 것이기 떄문.
LangChain 에서 HyDE 를 이용하는 예시는 다음과 같다:
loader = DirectoryLoader('data/',glob="*.pdf",loader_cls=PyPDFLoader)
documents = loader.load()
# Split text into chunks
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500,chunk_overlap=20)
text_chunks = text_splitter.split_documents(documents)
vectorstore = Chroma.from_documents(documents=text_chunks,
embedding=OpenAIEmbeddings(),
persist_directory="data/vectorstore")
vectorstore.persist()
retriever = vectorstore.as_retriever(search_kwargs={'k':5})
from langchain.prompts import ChatPromptTemplate
hyde_prompt = ChatPromptTemplate.from_template(
"""
Please write a scientific passage of a paper to answer the following question:\n
Question: {question}\n
Passage:
"""
)
generate_doc_chain = (
{'question': RunnablePassthrough()}
| hyde_prompt
| ChatOpenAI(model='gpt-4',temperature=0)
| StrOutputParser()
)
question = "How Low Rank Adapters work in LLMs?"
generate_doc_chain.invoke(question)
retrieval_chain = generate_doc_chain | retriever
retireved_docs = retrieval_chain.invoke({"question":question})
retireved_docs
Conclusion
- 공통적인 Query Translation 매커니즘은 사용자의 질문을 그대로 쓰는 것보다 쿼리를 다양한 관점에서 여러개 생성하고, 그걸 통해서 문서를 검색하고, 랭킹을 매겨서 가장 좋은 문서를 찾는 방법임.
- 중요한 건 LLM 이 확실하게 어떤 식으로 동작하는지 잘 알지 못하기 때문에 실험적인 접근 방법을 취할 수 밖에 없다. 아렇게도 해보고, 저렇게도 해보고 하면서 단순히 가장 성능이 좋게 나오는 방법을 선택하거나, 여러 방법을 섞거나, 유연하게 동적으로 대응하거나 이런 식으로 해야함.
- 실험을 적극적으로 할 수 밖에 없으니 자동화된 테스트와 Evaluation 프로세스를 잘 구축하는 게 중요할 것.
- 질문을 재생성하는 과정에서 이상하게 재생성 될 수 있으니, 이상한 문서가 검색될 수 있으니 할루시네이션에 대한 주의가 필요하다.
Additional Refererences:
'Generative AI > RAG' 카테고리의 다른 글
BM42: New Baseline for Hybrid Search (0) | 2024.07.06 |
---|---|
Advanced RAG Series: Generation and Evaluation (0) | 2024.06.19 |
Advanced RAG Series: Retrieval (0) | 2024.06.18 |
Advanced RAG Series: Routing and Query Construction (0) | 2024.06.14 |
Advanced RAG series: Indexing (0) | 2024.06.05 |