실패 없는 RAG 시스템 구축! 청크부터 리랭커까지 튜닝

궁금하면 500원·2025년 6월 20일
0

AI 미생지능

목록 보기
61/68

1. 청크(Chunk) 튜닝: 데이터를 쪼개는 기술

RAG 시스템의 첫 번째 단계는 청크(Chunk) 튜닝입니다. PDF 같은 문서를 벡터 데이터베이스에 넣으려면 문서를 일정한 크기로 쪼개야 하는데, 이때 어떻게 쪼개느냐에 따라 검색 결과가 완전히 달라집니다.

왜 청크 튜닝이 중요한가요?

  • 글자 수 기반의 문제점: 단순히 글자 수로 문서를 쪼개면 문맥이 끊어지는 문제가 발생합니다. "하늘이 푸르고"라는 문장이 "하늘이"와 "푸르고"로 잘릴 수 있어 의미를 제대로 전달하지 못하게 됩니다.
  • 의미 기반의 필요성: 문맥을 유지한 채 의미적으로 독립적인 덩어리로 쪼개야 임베딩(Embedding)이 정확해지고, 그 결과 벡터 검색의 정확도가 높아집니다.

청크 튜닝 방법

일반적으로는 문장이나 문단 단위로 쪼개는 방법을 사용합니다. 이때 파이썬의 정규표현식이나 nltk, spacy와 같은 라이브러리를 활용해 문맥을 유지하며 쪼개는 것이 중요합니다. LLM의 토크나이저를 이용하면 문장을 더욱 정확하게 분리할 수 있습니다.

from langchain.text_splitter import RecursiveCharacterTextSplitter

# 예제 텍스트
text = "RAG는 검색을 통해 얻은 정보로 LLM의 답변을 보강하는 기술입니다. LLM이 최신 정보나 비공개 데이터를 활용할 수 있게 합니다."

# RecursiveCharacterTextSplitter를 사용하여 문장 단위로 분리
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=50,  # 청크의 최대 크기
    chunk_overlap=10, # 청크 간 겹치는 부분
    separators=["\n\n", "\n", ".", " ", ""] # 분리 기준
)

# 텍스트를 청크로 분할
chunks = text_splitter.split_text(text)

for i, chunk in enumerate(chunks):
    print(f"--- Chunk {i+1} ---")
    print(chunk)

[실행 결과]

--- Chunk 1 ---
RAG는 검색을 통해 얻은 정보로 LLM의 답변을
--- Chunk 2 ---
보강하는 기술입니다. LLM이 최신 정보나 비공개
--- Chunk 3 ---
데이터를 활용할 수 있게 합니다.

위 예제는 LangChainRecursiveCharacterTextSplitter를 사용해 텍스트를 문장과 글자 수를 기준으로 쪼개는 방법을 보여줍니다. RAG 구축 시에는 청크 크기, 겹침(Overlap) 정도, 분리 기준 등을 조절하여 최적의 청크 상태를 찾아야 합니다.


2. 임베딩(Embedding) 모델 튜닝

청크를 만들었다면 이제 그 청크를 벡터로 변환하는 임베딩 모델을 선택하고 튜닝해야 합니다.

왜 임베딩 모델이 중요한가요?

  • 정확한 벡터 변환: 임베딩 모델은 텍스트의 의미를 담은 벡터를 생성합니다. 이 벡터의 품질이 높을수록 검색 시 사용자의 질의와 관련된 청크를 정확하게 찾아낼 수 있습니다.
  • 차원(Dimension)의 영향: 임베딩 벡터의 차원이 높을수록 더 많은 의미적 정보를 담을 수 있습니다. 하지만 벡터 데이터베이스에 따라 지원하는 차원이 다르므로, 이를 확인하고 적절한 모델을 선택해야 합니다.

임베딩 모델 튜닝 방법

  • 더 좋은 모델 선택: 임베딩 모델은 계속해서 새로운 버전이 나오고 있습니다. 최신 모델일수록 성능이 좋을 가능성이 높습니다.
  • 임베딩 키(Key) 변경: 청크 원문 자체를 임베딩하는 것이 일반적이지만, LLM을 활용해 청크 내용과 관련된 질문을 생성하고 그 질문을 임베딩 키로 사용하는 방법도 있습니다. 사용자의 질문이 키워드 검색보다 의미론적 검색에 더 잘 맞게 됩니다.
from transformers import pipeline

# 허깅 페이스의 sentence-transformer 파이프라인 사용
# (실제 환경에서는 별도의 임베딩 모델을 사용합니다)
embedder = pipeline("feature-extraction", model="intfloat/multilingual-e5-large")

# 예제 텍스트
text1 = "고양이는 야행성 동물입니다."
text2 = "고양이는 밤에 활동하는 동물입니다."
text3 = "강아지는 충성심이 강한 동물입니다."

# 텍스트를 벡터로 변환 (임베딩)
vector1 = embedder(text1)
vector2 = embedder(text2)
vector3 = embedder(text3)

# 변환된 벡터 출력
print(f"텍스트 1 임베딩 벡터 차원: {len(vector1[0][0])}")
print(f"텍스트 2 임베딩 벡터 차원: {len(vector2[0][0])}")
print(f"텍스트 3 임베딩 벡터 차원: {len(vector3[0][0])}")

[실행 결과]

텍스트 1 임베딩 벡터 차원: 1024
텍스트 2 임베딩 벡터 차원: 1024
텍스트 3 임베딩 벡터 차원: 1024

위 코드는 huggingface transformers 라이브러리를 사용해 임베딩을 수행하는 예시입니다. 실제 RAG 시스템에서는 LangChain과 같은 프레임워크를 사용해 임베딩 모델을 연동하는 것이 일반적입니다.


3. 검색(Retriever) 튜닝

임베딩된 데이터로 벡터 데이터베이스를 구축했다면, 이제 사용자 질의에 맞춰 관련 청크를 검색하는 검색(Retriever) 튜닝을 할 차례입니다.

왜 검색 튜닝이 중요한가요?

  • 결과 품질 향상: 단순히 임베딩 유사도만으로 검색하면 부족할 수 있습니다. 키워드 검색, 메타데이터 필터링 등을 결합하여 검색 품질을 높여야 합니다.

검색 튜닝 방법

  1. 앙상블 리트리버(Ensemble Retriever) 사용: 벡터 유사도 검색과 키워드 검색(BM25)을 결합하는 방법입니다. 키워드 검색은 Elasticsearch 같은 전문 검색 엔진을 활용해 정확도를 높일 수 있습니다.

  2. 질의 키워드 추출: 사용자의 질문에서 검색에 필요한 핵심 키워드를 LLM으로 추출하여 검색 엔진에 전달하는 방법입니다.


4. 재순위(Reranker) 튜닝

검색 결과가 나왔더라도, 그 순서가 항상 정확한 것은 아닙니다. 이때 재순위(Reranker) 모델을 사용하여 검색 결과를 다시 정렬해 품질을 높일 수 있습니다.

왜 재순위 튜닝이 중요한가요?

  • 정확한 정보 순서: 벡터 유사도가 높더라도 실제 답변에 필요한 정보가 아닐 수 있습니다. 재순위 모델은 질문과 검색된 청크의 연관성을 다시 평가하여 가장 적절한 청크를 상위에 배치합니다.

재순위 튜닝 방법

  • 전용 모델 사용: 크로스 인코더(Cross-encoder) 방식의 재순위 모델은 질문과 청크 쌍을 입력받아 관련성 점수를 계산합니다. 이 점수를 기반으로 결과를 재정렬합니다.
  • LLM 기반 재순위: 프롬프트 엔지니어링을 활용하여 LLM에게 직접 검색된 청크의 순위를 다시 매기도록 지시할 수도 있습니다.

5. 최종 답변 생성(Generator) 튜닝

마지막 단계는 최종 검색 결과를 바탕으로 LLM이 답변을 생성하도록 하는 프롬프트 튜닝입니다.

왜 최종 답변 튜닝이 중요한가요?

  • 일관된 답변: RAG 시스템이 검색한 결과를 제대로 활용하도록 프롬프트를 설계해야 합니다.
  • 편향 및 환각 방지: 검색 결과가 부족하거나 없을 경우 LLM이 엉뚱한 답변을 하거나 "모르겠다"고 답하도록 프롬프트에 명확한 지시를 넣어야 합니다.

최종 답변 튜닝 방법

  • 프롬프트 엔지니어링: '아래 검색 결과를 참고하여 질문에 답변해 줘. 만약 검색 결과에 답이 없다면 '정보를 찾을 수 없습니다'라고 답해줘'와 같은 구체적인 지시문을 프롬프트에 포함합니다.

전체 RAG 시스템 구축 흐름

이상의 단계를 종합하면 RAG 시스템은 다음과 같은 흐름으로 구축할 수 있습니다.

  1. 청킹: 문서를 의미 단위로 쪼개기.
  2. 임베딩: 쪼갠 청크를 벡터로 변환하여 벡터 DB에 저장.
  3. 질의 임베딩: 사용자의 질문을 벡터로 변환.
  4. 검색: 벡터 DB에서 질의 벡터와 유사한 청크 검색.
  5. 재순위: 검색된 청크들을 다시 정렬하여 가장 관련성 높은 정보를 추려냄.
  6. 답변 생성: 재순위된 청크와 사용자의 질문을 LLM에 전달하여 최종 답변 생성.

RAG는 단순히 하나의 기술이 아니라 여러 튜닝 과정을 거쳐야 완성되는 복합적인 시스템입니다. 각 단계에서 지속적인 테스트와 튜닝을 통해 최적의 결과를 얻을 수 있습니다.

profile
꾸준히, 의미있는 사이드 프로젝트 경험과 문제해결 과정을 기록하기 위한 공간입니다.

0개의 댓글