본문 바로가기
프로젝트 개발 일지/AI

[LLM with RAG] Retrieval 효율 개선

by ballbig 2024. 9. 26.
728x90

 

데이터 전처리

 

 

데이터가 위와 같은 이미지 파일인 경우,

이를 읽어올 수 없기 때문에 데이터를 전달 할 수 없다.

이미지, 표 등과 같은 데이터는 vector DB에 효율적으로 저장할 수 없다.

 

따라서 알맞은 데이터를 전달하기 위해서는 데이터의 전처리가 중요하다!!

다음과 같이 이해할 수 있는 Markdown 포멧으로 데이터를 전처리 해줘야 한다.

| 종합소득 과세표준          | 세율                                         |
|-------------------|--------------------------------------------|
| 1,400만원 이하     | 과세표준의 6퍼센트                             |
| 1,400만원 초과     5,000만원 이하     | 84만원 + (1,400만원을 초과하는 금액의 15퍼센트)  |
| 5,000만원 초과   8,800만원 이하     | 624만원 + (5,000만원을 초과하는 금액의 24퍼센트) |
| 8,800만원 초과 1억5천만원 이하    | 3,706만원 + (8,800만원을 초과하는 금액의 35퍼센트)|
| 1억5천만원 초과 3억원 이하         | 3,706만원 + (1억5천만원을 초과하는 금액의 38퍼센트)|
| 3억원 초과    5억원 이하         | 9,406만원 + (3억원을 초과하는 금액의 38퍼센트)   |
| 5억원 초과      10억원 이하        | 1억 7,406만원 + (5억원을 초과하는 금액의 42퍼센트)|
| 10억원 초과        | 3억 8,406만원 + (10억원을 초과하는 금액의 45퍼센트)|

 

 

 

 

키워드 사전을 활용하는 이유

 

  • 예를 들어, 다음과 같은 질문이 전달되었다고 하자.
query = '연봉 5천만원인 거주자의 종합소득세는?'

 

  • 다음의 코드로 query와 유사한 4개의 데이터를 Vector DB에서 뽑아보면, 원하는 데이터가 잘 추출되지 않는 것을 볼 수 있다.
retriever = database.as_retriever(search_kwargs={'k': 4})
retriever.invoke(query)

 

 

이럴 때에는 질문 query를 바로 전달하지 않고,

다음과 같이 keyword 사전을 활용하여 사용자 사용자 질문을 수정한 뒤 넘겨주는 것도 좋은 방법이다!

 

 

 

LangChain Expression Language (LCEL)

 

https://python.langchain.com/v0.2/docs/concepts/#langchain-expression-language

 

Conceptual guide | 🦜️🔗 LangChain

This section contains introductions to key parts of LangChain.

python.langchain.com

 

  • 내용이 방대하지만, LCEL의 주요 요점은  두 개의 실행 파일을 시퀀스로 "체인"할 수 있다는 것임
  • 이전 실행 가능 항목의 .invoke() 호출 출력은 다음 실행 가능 항목의 입력으로 전달된다.
  • 이는 파이프 연산자(|) 또는 동일한 작업을 수행하는 보다 명시적인 .pipe() 메서드를 사용하여 수행할 수 있다.

 

The pipe operator: |

https://python.langchain.com/v0.2/docs/how_to/sequence/#the-pipe-operator-

 

How to chain runnables | 🦜️🔗 LangChain

This guide assumes familiarity with the following concepts:

python.langchain.com

 

 

 

pipe operator
  • pipe operator를 사용하여 선언한 chain의 경우, chain 사용 시 파라미터를 전달 할 때 invoke()함수를 쓰면 된다!

ex)

dictionary_chain = prompt | llm | StrOutputParser()
question = dictionary_chain.invoke({"question": query, "dictionary" : dictionary})

prompt = ChatPromptTemplate.from_template(f"""
    사용자의 질문을 보고, 우리의 사전을 참고해서 사용자의 질문을 변경해주세요.
    만약 변경할 필요가 없다고 판단된다면, 사용자의 질문을 변경하지 않아도 됩니다.
    그런 경우에는 질문만 리턴해주세요
    사전: {dictionary}

    질문: {{question}}
""")

llm = ChatOpenAI(model='gpt-4o')  # 모델 불러오기

#keyword 사전을 이용해 query 수정하기
dictionary_chain = prompt | llm | StrOutputParser()
question = dictionary_chain.invoke({"question": query, "dictionary" : dictionary})

 

 

[전체 코드]

from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import OpenAIEmbeddings
from langchain_pinecone import PineconeVectorStore
from langchain_community.document_loaders import Docx2txtLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain.chains import RetrievalQA
from langchain_openai import ChatOpenAI
from pinecone import Pinecone
from langchain import hub
import getpass
import os
from dotenv import load_dotenv

# .env 파일에서 환경 변수를 로드
load_dotenv()

# 텍스트 스플리터 설정
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=400,
    chunk_overlap=100,
)

loader = Docx2txtLoader('../trabean.docx')
document_list = loader.load_and_split(text_splitter=text_splitter)

# PINECONE API 키 설정
if not os.getenv("PINECONE_API_KEY"):
    os.environ["PINECONE_API_KEY"] = getpass.getpass("Enter your Pinecone API key: ")
pinecone_api_key = os.environ.get("PINECONE_API_KEY")

# LangSmith API 키 설정
if not os.getenv("LANGSMITH_API_KEY"):
    os.environ["LANGSMITH_API_KEY"] = getpass.getpass("Enter your LangSmith API key: ")
langsmith_api_key = os.environ.get("LANGSMITH_API_KEY")

index_name = "chatbot-index"
pc = Pinecone(api_key=pinecone_api_key)

embedding = OpenAIEmbeddings(model='text-embedding-3-large')
database = PineconeVectorStore.from_existing_index(index_name=index_name, embedding=embedding)
# database = PineconeVectorStore.from_documents(document_list, index_name=index_name, embedding=embedding)

query = 'Trabean 통장의 환전 기능에 대해 알고 싶어'

dictionary = ["사람을 나타내는 표현 -> 거주자", "Trabean -> bean"]

prompt = ChatPromptTemplate.from_template(f"""
    사용자의 질문을 보고, 우리의 사전을 참고해서 사용자의 질문을 변경해주세요.
    만약 변경할 필요가 없다고 판단된다면, 사용자의 질문을 변경하지 않아도 됩니다.
    그런 경우에는 질문만 리턴해주세요
    사전: {dictionary}

    질문: {{question}}
""")

llm = ChatOpenAI(model='gpt-4o')  # 모델 불러오기

#keyword 사전을 이용해 query 수정하기
dictionary_chain = prompt | llm | StrOutputParser()
question = dictionary_chain.invoke({"question": query, "dictionary" : dictionary})

print(question)

# `k` 값을 조절해서 얼마나 많은 데이터를 불러올지 결정
retriever = database.as_retriever(search_kwargs={'k': 4})
relevant_docs = retriever.invoke(question)

# 검색된 문서 출력
for doc in relevant_docs:
    print(doc.page_content)

prompt = hub.pull("rlm/rag-prompt")  # 알맞은 프롬프트 생성 모델 불러오기

qa_chain = RetrievalQA.from_chain_type(  # RetrievalQA 객체 생성
    llm,  # llm 모델
    retriever=retriever,
    chain_type_kwargs={"prompt": prompt}
)

answer = qa_chain.invoke({"query": question})

print(answer)

'프로젝트 개발 일지 > AI' 카테고리의 다른 글

[LLM with RAG] History  (0) 2024.09.26
LangChain 이란?  (0) 2024.09.26
LLM이란?  (3) 2024.09.26