해당 블로그 포스트는 RAG From Scratch : Coursework
강의 파트 15 - 18 내용을 다루고 있습니다.
별도의 강좌 내용이 보이지 않아서 실습 코드를 바탕으로 Reverse Enginneering
해서 자료를 정리한 내용입니다. 참고 부탁드립니다!
Re-ranking은 검색 시스템에서 초기 검색 결과의 순위를 다시 매기는 과정입니다. 검색된 결과가 사용자의 기대에 부합하지 않거나, 관련성이 충분하지 않을 때 주로 사용됩니다. 기본적으로 검색 알고리즘이 먼저 수행되고, 그 결과를 재정렬하여 사용자에게 더 적합한 항목을 상위에 노출합니다. Re-ranking 과정에서는 사용자의 과거 행동 데이터나 문맥적 요소, 최신성 등 여러 가지 요인을 반영하여 순위를 조정하게 됩니다.
Reciprocal Rank Fusion (RRF) 알고리즘은 여러 검색 결과를 종합하여 재정렬하는 방법 중 하나로, 여러 검색 시스템에서 나온 결과의 순위를 고려하여 검색 결과를 결합합니다. RRF는 검색 결과의 상호 순위에 기반하여 가중치를 부여하는 방식입니다. 각 결과에 대해 순위 역수를 가산하며, 이를 통해 상위에 노출된 결과가 우선시됩니다.
주요 특징:
Re-ranking 코드 예시 (RRF)
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_core.output_parsers import StrOutputParser
from langchain.prompts import ChatPromptTemplate
# 벡터 스토어 생성 및 문서 로드
documents = ["This is document 1 about LLM.", "This is document 2 about AI agents."]
vectorstore = Chroma.from_documents(
documents=documents,
embedding=OpenAIEmbeddings(),
)
retriever = vectorstore.as_retriever()
# 쿼리 생성 및 검색
query = "What is LLM?"
results = retriever.get_relevant_documents(query)
# RRF 재정렬 함수
def reciprocal_rank_fusion(results, k=60):
fused_scores = {}
for rank, doc in enumerate(results):
if doc not in fused_scores:
fused_scores[doc] = 0
fused_scores[doc] += 1 / (rank + k)
reranked_results = sorted(fused_scores.items(), key=lambda x: x[1], reverse=True)
return reranked_results
# RRF를 적용한 재정렬
reranked_results = reciprocal_rank_fusion(results)
print("Reranked results:", reranked_results)
코드 부연 설명:
reciprocal_rank_fusion
함수는 각 검색 결과의 순위를 받아 해당 순위에 따라 점수를 계산합니다. 순위가 높은 결과는 역수 방식으로 높은 점수를 받습니다. k=60
은 역수 계산에서 추가적인 가중치를 부여하는 역할을 하며, 이를 통해 순위가 너무 낮은 결과에 대한 가중치가 지나치게 작아지지 않도록 조정합니다.CRAG는 Retrieval-Augmented Generation(RAG) 기반 시스템의 개선된 형태로, 검색된 문서의 관련성을 평가하고 필요시 질문을 다시 작성하여 검색 과정을 반복하는 방식입니다. 이 시스템은 초기 검색이 충분하지 않거나 부정확할 때 자체적으로 질문을 수정하고 추가 검색을 수행해 최종적으로 더 정확한 답변을 생성합니다.
CRAG의 핵심은 자기 수정 메커니즘으로, 검색된 문서가 부적절하다고 판단되면 시스템이 이를 평가하고 검색 전략을 조정합니다. 이를 통해, 사용자는 더 신뢰할 수 있는 결과를 얻을 수 있습니다.
주요특징
CRAG 코드 예시
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
# 검색기 및 검색 도구
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
web_search_tool = TavilySearchResults(k=3)
# 질문 재작성 LLM
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
rewrite_prompt = ChatPromptTemplate.from_template(
"Here is the initial question: {question}. Rephrase this question for better search."
)
def corrective_rag(question):
# 1단계: 벡터 스토어 검색
results = retriever.get_relevant_documents(question)
# 2단계: 문서 평가
if not results or len(results) < 1:
# 3단계: 질문 재작성 및 웹 검색
rewritten_question = rewrite_prompt.invoke({"question": question})
web_results = web_search_tool.invoke({"query": rewritten_question})
results.extend(web_results)
return results
question = "What are AI agents?"
corrected_docs = corrective_rag(question)
print(corrected_docs)
코드 부연 설명:
corrective_rag
함수는 검색된 결과를 평가하고, 그 결과가 충분하지 않을 경우 질문을 재작성하여 웹 검색을 수행하는 구조입니다. rewrite_prompt
는 초기 질문을 다시 작성하는 LLM 호출을 통해 이루어지며, 이는 질문을 명확하게 하거나 세분화하여 더 나은 검색 결과를 유도합니다.Self-RAG는 언어 모델이 자체적으로 정보를 검색하고, 생성한 결과를 비평하며 필요시 추가 검색을 수행하는 방식입니다. 이는 기존 RAG 시스템에서 발생할 수 있는 제한 사항을 보완하고, 모델이 스스로 생성 결과를 평가하여 더 나은 답변을 만들 수 있는 유연성을 제공합니다. 즉, 모델은 생성된 답변이 충분히 관련성이 없다고 판단되면, 추가 정보를 검색해 이를 보완합니다.
이러한 자가 반영 메커니즘은 특히 정확성이 중요한 분야에서 유용하게 사용될 수 있으며, 모델이 한 번의 검색으로 충분한 정보를 얻지 못할 때 이를 인지하고 추가적으로 검색할 수 있도록 해줍니다.
주요 특징:
Self-RAG 코드 예시
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
# 문서 및 벡터 스토어 설정
documents = ["Document 1 about LLM", "Document 2 about AI agents"]
vectorstore = Chroma.from_documents(
documents=documents,
embedding=OpenAIEmbeddings(),
)
retriever = vectorstore.as_retriever()
# LLM 및 문서 평가
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
def self_rag(question):
# 1단계: 검색
results = retriever.get_relevant_documents(question)
# 2단계: 답변 생성
generated_answer = llm.invoke({
"context": results,
"question": question
})
# 3단계: 생성된 답변 평가
if "insufficient" in generated_answer:
# 문서가 충분하지 않다면 추가 검색
additional_results = retriever.get_relevant_documents(f"{question} more details")
results.extend(additional_results)
generated_answer = llm.invoke({
"context": results,
"question": question
})
return generated_answer
question = "What are the types of AI agents?"
answer = self_rag(question)
print(answer)
코드 부연 설명:
self_rag
함수에서는 먼저 검색을 통해 문서를 가져온 후, 그 문서를 기반으로 LLM이 답변을 생성합니다. 주요 특징:
Active RAG 코드 예시
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.prompts import ChatPromptTemplate
# 질문 분석 및 웹 검색 도구
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
web_search_tool = TavilySearchResults(k=3)
# 질문 라우팅 및 Active RAG
def active_rag(question):
# 질문 분석
initial_results = retriever.get_relevant_documents(question)
if len(initial_results) < 2: # 문서가 부족하면 웹 검색 수행
additional_results = web_search_tool.invoke({"query": question})
initial_results.extend(additional_results)
# 최종 답변 생성
answer = llm.invoke({
"context": initial_results,
"question": question
})
return answer
question = "What is LLM task decomposition?"
final_answer = active_rag(question)
print(final_answer)
코드 부연 설명:
active_rag
함수는 초기 검색을 수행한 후, 검색된 문서가 부족하거나 관련성이 낮으면 웹 검색 도구를 이용해 추가 검색을 실행하는 구조입니다. Adaptive RAG는 질문의 성격에 따라 적절한 검색 방법을 동적으로 선택하는 시스템입니다. 즉, 질문이 단순하면 간단한 벡터 스토어 검색을 사용하고, 질문이 복잡하면 웹 검색이나 데이터베이스 조회를 선택하는 방식으로 질문의 복잡성과 필요에 따라 최적의 검색 전략을 적용합니다. 이를 통해 리소스를 효율적으로 사용하고, 복잡한 질문에 대해 더 정확하고 신속한 답변을 생성할 수 있습니다.
Adaptive RAG는 다양한 검색 방법을 적절히 조합하여 다양한 질문 유형에 대해 높은 성능을 발휘할 수 있도록 설계되었습니다.
주요 특징:
Adaptive RAG 코드 예시
from langchain_openai import OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate
# 벡터 스토어 검색기
vector_retriever = vectorstore.as_retriever()
# LLM 설정
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
# 질문 라우팅 함수
def adaptive_rag(question):
# 질문 분석 및 라우팅
if "LLM" in question:
results = vector_retriever.get_relevant_documents(question)
else:
results = web_search_tool.invoke({"query": question})
# 답변 생성
answer = llm.invoke({
"context": results,
"question": question
})
return answer
question = "What are the types of LLM agents?"
adaptive_answer = adaptive_rag(question)
print(adaptive_answer)
코드 부연 설명:
adaptive_rag
함수는 질문에 따라 벡터 스토어 검색 또는 웹 검색을 선택하며, 그 결과를 기반으로 LLM이 최종 답변을 생성합니다.