본문 바로가기
데이터&AI/langchain

[langchain공부]Retriever의 고급기법 (feat. Ensemble, bm5,Sparse, dense)

by 일등박사 2024. 2. 10.

1. Retriever 종류: Sparse Retriever vs Dense Retriever

1. 개념

  Retriever는 대규모 텍스트 데이터에서 질의와 관련된 정보를 검색하는 데 사용되는 모델

  2가지로 구분

 

2. Sparse Retriever : 텍스트 데이터를 벡터로 변환하여 저장, 질의 벡터와의 유사성을 기반으로 문서 검색

  >> TF-IDF와 같은 기법으로 텍스트 데이터를 벡터화 진행

 

장점:

  • 효율적인 메모리 사용
  • 빠른 검색 속도
  • 높은 확장성
  • 질문과 같은 단어만 선택!!!!

단점:

  • 낮은 정확도 (but 같은 직접적 단어만 고를떄는 좋다)
  • 의미론적 정보 손실

3. Dense Retriever : 텍스트 데이터를 임베딩 벡터로 변환하여 저장, 질의 벡터와의 유사성을 기반으로 관련 문서 검색

  >>  BERT와 같은 신경망 모델을 사용하여 텍스트 데이터를 임베딩  진행

 

장점:

  • 높은 정확도
  • 의미론적 정보 유지 (단어가 다른표현으로 진행해도 가능!!)

단점:

  • 높은 메모리 사용
  • 느린 검색 속도
  • 낮은 확장성

4. Sparse vs Dense 비교

  Sparse Retriever Dense Retriever
정확도 낮음 높음
의미론적 정보 손실 유지
메모리 사용 효율적 높음
검색 속도 빠름 느림
확장성 높음 낮음

둘다.. 장단점이 있는데,

무엇을 골라야할까요!

그래서 나타난!!


앙상블!! ENsemble Retriever + reciroprocal rank fusion 의 방식이 개발되었습니다!!!

1. 두가지 방법을 결합해서 검색한 뒤
2 문서의 순위를 재정렬해서!! 품잘을 좋게한다

 

이제 코드를 통해 함꼐 알아봅시다!!

 

1. 패키지 설치 & 함수 임포트

!pip install rank_bm25
# 새로운 패키지를 설치합니다

 

import tiktoken
tokenizer = tiktoken.get_encoding('cl100k_base')
def tiktoken_len(text):
    tokens = tokenizer.encode(text)
    return len(tokens)
import openai    
from langchain.embeddings.sentence_transformer import SentenceTransformerEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import Chroma
from langchain.document_loaders import TextLoader
from langchain.document_loaders import PyPDFLoader
from langchain.document_loaders import PyPDFLoader
from langchain.document_loaders import WebBaseLoader

from langchain.embeddings import HuggingFaceEmbeddings
model_huggingface = HuggingFaceEmbeddings(model_name = 'jhgan/ko-sroberta-multitask'
                                          , model_kwargs = {'device':'cpu'}
                                         , encode_kwargs = {'normalize_embeddings' : True})
                                         
 from langchain.retrievers  import BM25Retriever
from langchain.retrievers import EnsembleRetriever
from langchain_community.vectorstores import FAISS

'

2. 텍스트 가져오기!!  바드에 대한 리뷰 url 두개를 가져와서 로드합니다!!

## pdf 파일로드 하고 쪼개기
loaders = [
    WebBaseLoader('https://www.itworld.co.kr/review/317755')
    , WebBaseLoader('https://textcortex.com/ko/post/google-bard-review')
         ]
docs = []
for loader in loaders:
    docs.extend(loader.load_and_split())
    
## chunk로 쪼개기
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=0,length_function=tiktoken_len)
texts = text_splitter.split_documents(docs)

 

3.!! retriever 선언!! 앙상블과  dense retriever(기존에 사용하던 chroma 기반)

bm25_retriever = BM25Retriever.from_documents(texts)
bm25_retriever.k = 2

chroma_vector = Chroma.from_documents(texts, model_huggingface  )
chroma_retriever = chroma_vector.as_retriever(search_kwargs={'k':2})

 

 

4. 각각의 검색 결과를 비교해보면!?

 >> 앙상블의 결과보기!

ensemble_retriever = EnsembleRetriever(
                    retrievers = [bm25_retriever,chroma_retriever]
                    , weight = {0.5,0.5})
                    
docs = ensemble_retriever.invoke("구글 바드는?")
for i in docs:
    print(i.metadata)
    print(":")
    print(i.page_content)
    print("*"*30)

 

4가지 검색 결과를 찾아줍니다!

 

>> 기존 dense retriever  결과보기!

chroma_vector = Chroma.from_documents(texts, model_huggingface  )
chroma_retriever = chroma_vector.as_retriever(search_kwargs={'k':2})

docs = chroma_retriever.invoke("구글 바드는?")
for i in docs:
    print(i.metadata)
    print(":")
    print(i.page_content)
    print("*"*30)

 

산출된 결과물이 더 적네요!!

 

 

이제!! llm 을 활용하여 최종 답변을 생성해볼까요!?

from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA
llm_model = ChatOpenAI(model_name = 'gpt-3.5-turbo'
                    ,api_key =  "{내key넣기}"  
                    , temperature = 0 )
                    
question = "구글 바드는?"

 

 

1. BM25. 앙상블 Retriever 결과!!

qa = RetrievalQA.from_chain_type(
    llm = llm_model
    , chain_type='stuff'
    , retriever = ensemble_retriever)
res = qa(question)
res['result']

 

 

2. Dense retriever 결과!!

qa = RetrievalQA.from_chain_type(
    llm = llm_model
    , chain_type='stuff'
    , retriever = chroma_retriever)
res = qa(question)
res['result']

 

딱 봐도!! 앙상블의 결과가 훨씬 좋네요~!!

 

 

이런 기술을 활용해서 지속적으로 RAG의 성능향상을 이뤄갈수 있습니다!!


이글은 모두의 AI 유튜브를 보며 정리한 노트입니다~!

https://youtu.be/ehP4vphl_Us?si=TIGZzNBxHMljts0k

 

 

 

댓글