언어모델 LLM/프로젝트 예제

RAG vs Non-RAG: 지하철 정보 검색 실험

General AI 2024. 7. 10. 21:38
728x90
반응형

RAG vs Non-RAG: 지하철 정보 검색 실험

이번 포스트에서는 Retrieval-Augmented Generation (RAG)와 일반 언어 모델의 성능을 비교하는 실험을 진행했습니다. 지하철 역 정보를 기반으로 두 방식의 답변 능력을 테스트했습니다.

RAG vs Non-RAG: 지하철 정보 검색 실험

실험 환경

  • 운영체제: WSL Ubuntu 22.04
  • 데이터: 지하철 역 정보 (약 500줄)
  • 샘플 데이터:
  • 전철역코드 전철역명 전철명명(영문) 호선 외부코드 전철명명(중문) 전철명명(일문) 1904 망월사 Mangwolsa 01호선 112 望月寺 マンウォルサ 1007 신도림 Sindorim 01호선 140 新道林 シンドリム 1708 금정 Geumjeong 01호선 P149 衿井 クムジョン 1914 보산 Bosan 01호선 102 保山 ポサン 1911 덕정 Deokjeong 01호선 105 德亭 トクチョン 1808 동암 Dongam 01호선 154 ?岩 トンアム ...

ex01-rag-subway.txt
0.03MB

실험 결과

  1. 질문: 망월사의 외부코드는?
    • RAG 답변: 112 (정답)
    • Non-RAG 답변: 망월사의 외부 코드는 031-858-0070 입니다. (오답)
  2. 질문: 금정 역의 중문 명칭은?
    • RAG 답변: クムジョン (오답, 중문인데, 일문으로 답함)
    • Non-RAG : 금정역의 중문 명칭은 금정역 입니다. . (오답)
  3. 질문: 세마 역의 일문 명칭은?
    • RAG 답변: セマ (정답)
    • Non-RAG : 세마 역의 일문 명칭은 "世馬駅" 입니다. . (오답)
  4. 질문: 부평 역의 역 코드는?
    • RAG 답변: 1805 (오답 ,1806 임)
    • Non-RAG : 부평 역의 (정답) 역 코드는 A01 입니다. . (오답)
  5. 질문: 덕계 역의 영문명은?
    • RAG 답변: Deokgye (정답)
    • Non-RAG : The English name of Deokgye Station is Deokgye Station. (오답)

실험 결과를 종합적으로 분석해본 결과, Retrieval-Augmented Generation (RAG) 시스템이 전통적인 언어 모델(Non-RAG) 시스템에 비해 현저히 우수한 성능을 보여주었습니다. 구체적으로, RAG 시스템은 총 5개의 질문 중 3개의 질문에 정확한 답변을 제공하여 60%의 정확도를 달성했습니다. 이는 특정 도메인의 정보를 효과적으로 검색하고 활용할 수 있는 RAG 시스템의 강점을 명확히 보여주는 결과입니다. 반면, Non-RAG 시스템은 5개의 질문 모두에 대해 정확한 답변을 제공하지 못해 0%의 정확도를 기록했습니다. 이러한 극명한 대조는 특정 사실에 기반한 질문-답변 태스크에서 RAG 방식의 유효성을 강력하게 입증합니다.

그러나 이 결과를 해석할 때 주의해야 할 점이 있습니다. RAG 시스템이 Non-RAG 시스템보다 월등히 나은 성능을 보였다는 것은 분명하지만, RAG 시스템 역시 완벽한 성능을 보이지는 않았습니다. 60%의 정확도는 상당히 높은 수준이지만, 여전히 개선의 여지가 있음을 시사합니다. 특히, RAG 시스템이 보인 몇 가지 흥미로운 오류 패턴은 주목할 만합니다. 예를 들어, 언어 구분(중국어와 일본어)에 있어서의 혼란이나 유사한 정보(역 코드)에 대한 약간의 오차 등은 RAG 시스템의 한계점을 드러내는 동시에, 향후 개선 방향을 제시해주는 중요한 지표가 됩니다.

이러한 결과는 RAG 시스템이 특정 도메인의 정보를 효과적으로 활용할 수 있는 강력한 도구임을 보여주지만, 동시에 더 정교한 정보 검색 및 처리 메커니즘의 필요성도 제기합니다. 예를 들어, 다국어 처리 능력을 향상시키거나, 유사한 정보를 더 정확하게 구분할 수 있는 알고리즘의 개발이 필요할 것입니다. 또한, Non-RAG 시스템이 보여준 자연스러운 언어 사용 능력과 RAG 시스템의 정보 검색 능력을 결합한 하이브리드 접근 방식도 고려해볼 만한 가치가 있습니다.

전체 코드

from dotenv import load_dotenv
import os
from langchain_community.document_loaders import TextLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate

load_dotenv()
openai_api_key = os.getenv("OPENAI_API_KEY")

def create_rag_chain():
    encodings = ['utf-8', 'euc-kr', 'cp949']
    for encoding in encodings:
        try:
            loader = TextLoader("ex01-rag-subway.txt", encoding=encoding)
            documents = loader.load()
            print(f"성공적으로 로드됨 (인코딩: {encoding})")
            break
        except UnicodeDecodeError:
            print(f"{encoding} 인코딩으로 로드 실패, 다음 인코딩 시도 중...")
    else:
        raise RuntimeError("모든 인코딩 시도 실패")

    text_splitter = CharacterTextSplitter(chunk_size=200, chunk_overlap=20)
    texts = text_splitter.split_documents(documents)

    embeddings = OpenAIEmbeddings(openai_api_key=openai_api_key)
    vectorstore = Chroma.from_documents(texts, embeddings)

    retriever = vectorstore.as_retriever(search_kwargs={"k": 1})

    prompt_template = """주어진 정보를 바탕으로 질문에 답하세요. 정보에 없는 내용이면 "주어진 정보만으로는 답변할 수 없습니다."라고 대답하세요.

{context}

질문: {question}
답변:"""
    PROMPT = PromptTemplate(
        template=prompt_template, input_variables=["context", "question"]
    )

    qa_chain = RetrievalQA.from_chain_type(
        llm=ChatOpenAI(openai_api_key=openai_api_key, max_tokens=150),
        chain_type="stuff",
        retriever=retriever,
        return_source_documents=True,
        chain_type_kwargs={"prompt": PROMPT}
    )

    return qa_chain

def generate_specific_questions(content):
    chat = ChatOpenAI(openai_api_key=openai_api_key)
    prompt = f"""
    지하철 역 정보에 대한 5개의 구체적인 질문을 만드세요. 예:
    1. [역 이름]의 역코드는?
    2. [노선 번호]의 [역 이름] 영문명은?
    3. [역 이름]의 중국어 명칭은?
    4. [역코드]에 해당하는 역 이름은?
    5. [노선 번호]의 [역 이름] 일본어 표기는?

    주어진 내용:
    {content[:1000]}

    질문 목록:
    """
    response = chat.invoke(prompt)
    questions = response.content.split('\n')
    return [q.split('. ')[1] for q in questions if q.strip() and '. ' in q]

def main():
    rag_chain = create_rag_chain()
    non_rag_model = ChatOpenAI(openai_api_key=openai_api_key, max_tokens=150)

    with open("ex01-rag-subway.txt", "r", encoding="utf-8") as file:
        content = file.read()

    questions = generate_specific_questions(content)
    results = []

    for i, question in enumerate(questions, 1):
        print(f"\n질문 {i}: {question}")

        # RAG를 사용한 답변
        rag_response = rag_chain.invoke({"query": question})
        print("RAG 답변:", rag_response['result'])

        # RAG를 사용하지 않은 답변
        non_rag_response = non_rag_model.invoke(question)
        print("Non-RAG 답변:", non_rag_response.content)

        results.append({
            "question": question,
            "rag_response": rag_response['result'],
            "non_rag_response": non_rag_response.content
        })

    with open("rag_vs_non_rag_results.txt", "w", encoding="utf-8") as f:
        for result in results:
            f.write(f"질문: {result['question']}\n")
            f.write(f"RAG 답변: {result['rag_response']}\n")
            f.write(f"Non-RAG 답변: {result['non_rag_response']}\n\n")

    print("\n결과가 rag_vs_non_rag_results.txt 파일에 저장되었습니다.")

if __name__ == "__main__":
    main()

RAG 관련 코드 설명

RAG (Retrieval-Augmented Generation)의 핵심 부분은 create_rag_chain() 함수에 구현되어 있습니다. 주요 단계는 다음과 같습니다:

  1. 문서 로딩: TextLoader를 사용하여 지하철 정보 파일을 로드합니다.
  2. 텍스트 분할: CharacterTextSplitter로 문서를 작은 청크로 나눕니다. 이는 효율적인 검색을 위해 중요합니다.
  3. 임베딩 생성: OpenAIEmbeddings를 사용하여 텍스트 청크를 벡터로 변환합니다.
  4. 벡터 저장소 생성: Chroma를 사용하여 임베딩을 저장하고 검색 가능하게 만듭니다.
  5. 검색기 설정: as_retriever 메서드로 검색기를 생성합니다. k=1은 가장 관련성 높은 1개의 문서만 검색함을 의미합니다.
  6. 프롬프트 템플릿 정의: 검색된 정보를 포함하여 질문에 답하는 방법을 지정합니다.
  7. RAG 체인 생성: RetrievalQA.from_chain_type으로 검색과 생성을 결합한 체인을 만듭니다.

이 RAG 시스템은 주어진 질문에 대해 관련 정보를 검색하고, 이를 바탕으로 답변을 생성합니다. 이는 일반 언어 모델과 달리 특정 데이터셋의 정보를 정확하게 활용할 수 있게 해줍니다.

관련 링크

  1. LangChain 공식 문서: RAG
  2. OpenAI API 문서
  3. Chroma 벡터 데이터베이스

이 실험을 통해 RAG 시스템이 특정 도메인의 질문에 대해 더 정확하고 관련성 높은 답변을 제공할 수 있음을 확인했습니다. 그러나 일반 언어 모델도 때로는 유용한 정보를 제공할 수 있으며, 각 방식의 장단점을 이해하고 적절히 활용하는 것이 중요합니다.

728x90
반응형