한글 단어 리트리버 튜닝

검색기

문서 검색을 위한 다양한 Retriever 패턴과 최적화 기법을 다룬다.

AI
RAG
LangChain
저자

Kwangmin Kim

공개

2024년 05월 11일

한글 형태소 분석기 라이브러리인 kiwipiepy 를 설치합니다. - kiwipiepy 프로젝트 링크

# API 키를 환경변수로 관리하기 위한 설정 파일
from dotenv import load_dotenv

# API 키 정보 로드
load_dotenv()
# LangSmith 추적을 설정합니다. https://smith.langchain.com
# !pip install langchain-teddynote
from langchain_teddynote import logging

# 프로젝트 이름을 입력합니다.
logging.langsmith("CH10-Retriever")
# !pip install kiwipiepy
from kiwipiepy import Kiwi

kiwi = Kiwi()

토큰화를 진행합니다.

kiwi.tokenize("안녕하세요? 형태소 분석기 키위입니다")

1 다양한 문장으로 테스트

from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever
from langchain_core.documents import Document
from langchain.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings

docs = [
    Document(
        page_content="금융보험은 장기적인 자산 관리와 위험 대비를 목적으로 고안된 금융 상품입니다."
    ),
    Document(
        page_content="금융저축보험은 규칙적인 저축을 통해 목돈을 마련할 수 있으며, 생명보험 기능도 겸비하고 있습니다."
    ),
    Document(
        page_content="저축금융보험은 저축과 금융을 통해 목돈 마련에 도움을 주는 보험입니다. 또한, 사망 보장 기능도 제공합니다."
    ),
    Document(
        page_content="금융저축산물보험은 장기적인 저축 목적과 더불어, 축산물 제공 기능을 갖추고 있는 특별 금융 상품입니다."
    ),
    Document(
        page_content="금융단폭격보험은 저축은 커녕 위험 대비에 초점을 맞춘 상품입니다. 높은 위험을 감수하고자 하는 고객에게 적합합니다."
    ),
    Document(
        page_content="금보험은 저축성과를 극대화합니다. 특히 노후 대비 저축에 유리하게 구성되어 있습니다."
    ),
    Document(
        page_content="금융보씨 험한말 좀 하지마시고, 저축이나 좀 하시던가요. 뭐가 그리 급하신지 모르겠네요."
    ),
]
for doc in docs:
    print(" ".join([token.form for token in kiwi.tokenize(doc.page_content)]))
# 토큰화 함수를 생성
def kiwi_tokenize(text):
    return [token.form for token in kiwi.tokenize(text)]

1.1 실험: 다양한 종류의 검색기를 사용하여 검색 결과를 비교

bm25 = BM25Retriever.from_documents(docs)

kiwi_bm25 = BM25Retriever.from_documents(docs, preprocess_func=kiwi_tokenize)

faiss = FAISS.from_documents(docs, OpenAIEmbeddings()).as_retriever()

bm25_faiss_73 = EnsembleRetriever(
    retrievers=[bm25, faiss],  # 사용할 검색 모델의 리스트
    weights=[0.7, 0.3],  # 각 검색 모델의 결과에 적용할 가중치
    search_type="mmr",  # 검색 결과의 다양성을 증진시키는 MMR 방식을 사용
)
bm25_faiss_37 = EnsembleRetriever(
    retrievers=[bm25, faiss],  # 사용할 검색 모델의 리스트
    weights=[0.3, 0.7],  # 각 검색 모델의 결과에 적용할 가중치
    search_type="mmr",  # 검색 결과의 다양성을 증진시키는 MMR 방식을 사용
)
kiwibm25_faiss_73 = EnsembleRetriever(
    retrievers=[kiwi_bm25, faiss],  # 사용할 검색 모델의 리스트
    weights=[0.7, 0.3],  # 각 검색 모델의 결과에 적용할 가중치
    search_type="mmr",  # 검색 결과의 다양성을 증진시키는 MMR 방식을 사용
)
kiwibm25_faiss_37 = EnsembleRetriever(
    retrievers=[kiwi_bm25, faiss],  # 사용할 검색 모델의 리스트
    weights=[0.3, 0.7],  # 각 검색 모델의 결과에 적용할 가중치
    search_type="mmr",  # 검색 결과의 다양성을 증진시키는 MMR 방식을 사용
)

retrievers = {
    "bm25": bm25,
    "kiwi_bm25": kiwi_bm25,
    "faiss": faiss,
    "bm25_faiss_73": bm25_faiss_73,
    "bm25_faiss_37": bm25_faiss_37,
    "kiwi_bm25_faiss_73": kiwibm25_faiss_73,
    "kiwi_bm25_faiss_37": kiwibm25_faiss_37,
}
def print_search_results(retrievers, query):
    print(f"Query: {query}")
    for name, retriever in retrievers.items():
        print(f"{name}    \t: {retriever.invoke(query)[0].page_content}")
    print("===" * 20)

검색 결과를 출력합니다.

print_search_results(retrievers, "금융보험")
print_search_results(retrievers, "금융 보험")
print_search_results(retrievers, "금융저축보험")
print_search_results(retrievers, "축산물 보험")
print_search_results(retrievers, "저축금융보험")
print_search_results(retrievers, "금융보씨 개인정보 조회")

2 Konlpy

# !pip install konlpy
from konlpy.tag import Kkma, Okt, Komoran, Hannanum
from kiwipiepy import Kiwi

kkma = Kkma()
okt = Okt()
komoran = Komoran()
hannanum = Hannanum()
kiwi = Kiwi()
text = "안녕하세요? 형태소 분석기 테스트베드입니다."
print("kkma    : \t", end="")
print(" ".join(kkma.morphs(text)))
print("okt     : \t", end="")
print(" ".join(okt.morphs(text)))
print("komoran : \t", end="")
print(" ".join(komoran.morphs(text)))
print("hannanum: \t", end="")
print(" ".join(hannanum.morphs(text)))
print("kiwi    : \t", end="")
print(" ".join([tok.form for tok in kiwi.tokenize(text)]))
kiwi.add_user_word("안녕하세요 반가워요", "NNP", 0)
print("kiwi    : \t", end="")
print(" ".join([tok.form for tok in kiwi.tokenize(text)]))
def kkma_tokenize(text):
    return [token for token in kkma.morphs(text)]
def okt_tokenize(text):
    return [token for token in okt.morphs(text)]
kkma_bm25 = BM25Retriever.from_documents(docs, preprocess_func=kkma_tokenize)
kkma_bm25_faiss_73 = EnsembleRetriever(
    retrievers=[kkma_bm25, faiss],  # 사용할 검색 모델의 리스트
    weights=[0.7, 0.3],  # 각 검색 모델의 결과에 적용할 가중치
    search_type="mmr",  # 검색 결과의 다양성을 증진시키는 MMR 방식을 사용
)
kkma_bm25_faiss_37 = EnsembleRetriever(
    retrievers=[kkma_bm25, faiss],  # 사용할 검색 모델의 리스트
    weights=[0.3, 0.7],  # 각 검색 모델의 결과에 적용할 가중치
    search_type="mmr",  # 검색 결과의 다양성을 증진시키는 MMR 방식을 사용
)

okt_bm25 = BM25Retriever.from_documents(docs, preprocess_func=okt_tokenize)
okt_bm25_faiss_73 = EnsembleRetriever(
    retrievers=[okt_bm25, faiss],  # 사용할 검색 모델의 리스트
    weights=[0.7, 0.3],  # 각 검색 모델의 결과에 적용할 가중치
    search_type="mmr",  # 검색 결과의 다양성을 증진시키는 MMR 방식을 사용
)
okt_bm25_faiss_37 = EnsembleRetriever(
    retrievers=[okt_bm25, faiss],  # 사용할 검색 모델의 리스트
    weights=[0.3, 0.7],  # 각 검색 모델의 결과에 적용할 가중치
    search_type="mmr",  # 검색 결과의 다양성을 증진시키는 MMR 방식을 사용
)

retrievers = {
    "bm25": bm25,
    "kiwi_bm25": kiwi_bm25,
    "faiss": faiss,
    "bm25_faiss_73": bm25_faiss_73,
    "bm25_faiss_37": bm25_faiss_37,
    "kiwi_bm25_faiss_73": kiwibm25_faiss_73,
    "kiwi_bm25_faiss_37": kiwibm25_faiss_37,
    "kkma_bm25": kkma_bm25,
    "kkma_bm25_faiss_73": kkma_bm25_faiss_73,
    "kkma_bm25_faiss_37": kkma_bm25_faiss_37,
    "okt_bm25": okt_bm25,
    "okt_bm25_faiss_73": okt_bm25_faiss_73,
    "okt_bm25_faiss_37": okt_bm25_faiss_37,
}
print_search_results(retrievers, "금융보험")
print_search_results(retrievers, "금융 보험")
print_search_results(retrievers, "금융저축보험")
print_search_results(retrievers, "축산물 보험")
print_search_results(retrievers, "저축금융보험")
print_search_results(retrievers, "금융보씨 개인정보 조회")

Subscribe

Enjoy this blog? Get notified of new posts by email: