ssisOneTeam / Korean-Embedding-Model-Performance-Benchmark-for-Retriever

Korean Sentence Embedding Model Performance Benchmark for RAG
40 stars 4 forks source link

TokenTextSplitter를 이용할 때의 문서 변환, 호환성 문제 #2

Closed PangPangGod closed 9 months ago

PangPangGod commented 10 months ago

Known Issue

HuggingFace Embedding model을 불러오기 한 뒤, 이용하는 과정에서 기존 document loader와 호환이 되지 않는 Issue가 발생하여 작성함.

일단 예제 스크립트를 통해 현상을 진단하고, 해결 방법까지 작성한다.

예제 스크립트는 embeddingtest/test_splitter(tokenizer).ipynb이다. (추후에 embeddingtest/testing으로 옮길 예정)

from langchain.text_splitter import SentenceTransformersTokenTextSplitter
from langchain.document_loaders import UnstructuredMarkdownLoader
from embedding import EmbeddingLoader

steloader = EmbeddingLoader.SentenceTransformerEmbedding(model_name="model/BM-K/KoSimCSE-roberta-multitask/", multi_process=True, encode_kwargs={'normalize_embeddings':True})
ste_embedding = steloader.load()

docloader = UnstructuredMarkdownLoader(file_path="data/teamA/01_생계_지원/01_국민임대주택_공급.md")
doc = docloader.load()
print(doc) ### 제목 미리 저장해놔야 할듯, BaseDBLoader 수정해라.......

splitter = SentenceTransformersTokenTextSplitter(chunk_overlap=10, model_name="model/BM-K/KoSimCSE-roberta-multitask/", tokens_per_chunk=512)
print(splitter.split_documents(doc))

간단하게 예제 document 1개를 불러온 object와, max_seq_length(KoSimCSE-roberta-multitask model의 경우에는 512)에 따라 TokenTextSplit한 예제를 통해 기존 loader 어디에서 Issue가 발생할지를 진단한다.

실행 결과는 다음과 같다.

embedding model in path <model/BM-K/KoSimCSE-roberta-multitask/> has been loaded successfully.
Function call load took 3.466754s to run.

[Document(page_content='국민임대주택 공급\n\n국민임대주택 공급의 대상은 입주자 모집공고일 현재 무주택 세대구성원으로서 소득 및 자산보유기준을 충족하는 사람입니다.\n' ... )]
[Document(page_content='국민임대주택 공급 국민임대주택 공급의 대상은 입주자 모집공고일 현재 무주택 세대구성원으로서 소득 및 자산보유기준을 충족하는 사람입니다.' ... })]

첫 번째 결과가 document를 MarkdownLoader로 불러왔을 때, 두 번째 결과가 첫 번째 결과에 TokenTextSplitter를 붙인 경우이다.

결과적으로 문서 길이 자체가 짧기 때문에 chunk 개수가 여러 개로 나뉘지는 않았지만, 개행문자('\n' 등)가 전부 삭제됨을 알 수 있다.

현행 mdLoader.BaseDBLoader를 이용한 loading은 문서 split까지 전부 진행한 다음, 가장 첫 번째 문서에서 개행 문자를 이용해서 첫 번째 내용을 가져와 title로 사용한다.

이는 TokenTextSplitter에서 첫 번째 chunk의 전체 내용을 가져오는 결과를 가져온다.

{
    "name": "KeyError",
    "message": "'국민임대주택공급국민임대주택공급의대상은입주자모집공고일현재무주택세대구성원으로서소득및자산보유기준을충족하는사람입니다소득기준은도시근로자월평균소득701인가구902인가구80이하입니다또한자산보유기준은총자산이3억6100만원이하자동차3683만원이하입니다국민임대주택공급의내용은전용면적60m2이하주택을시중전세시세의60이상80이하수준으로저렴하게임대'",
    "stack": "---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[6], line 22
     19 a_loader.text_splitter = text_splitter
     20 b_loader.text_splitter = text_splitter
---> 22 a_raw_docs = a_loader.load(is_split=True, is_regex=False)
     23 b_raw_docs = b_loader.load(is_split=True, is_regex=True)
     25 print(f\"total document length : {len(a_raw_docs)}, {len(b_raw_docs)}\")

File d:\\git\\additional-work\\embeddingtest\\document\\mdLoader.py:64, in BaseDBLoader.load(self, is_split, is_regex, show_progress, use_multithreading)
     62         doc_list = self.text_splitter.split_documents(doc_list)
     63     self.storage.extend(doc_list)
---> 64 self.storage = self._process_document_metadata(self.storage)
     66 #timecheck
     67 end_time = datetime.now()

File d:\\git\\additional-work\\embeddingtest\\document\\mdLoader.py:130, in BaseDBLoader._process_document_metadata(self, documents)
    128 title = document.page_content.split(\"\
\")[0]
    129 title_parsed = self._strip_replace_text(title)
--> 130 document.metadata[\"tag\"] = metadata_json[title_parsed]
    132 #### url
    133 result = url_table.loc[url_table[\"source\"] == meta_source_parsed_file_name][\"url\"].values[0]

KeyError: '국민임대주택공급국민임대주택공급의대상은입주자모집공고일현재무주택세대구성원으로서소득및자산보유기준을충족하는사람입니다소득기준은도시근로자월평균소득701인가구902인가구80이하입니다또한자산보유기준은총자산이3억6100만원이하자동차3683만원이하입니다국민임대주택공급의내용은전용면적60m2이하주택을시중전세시세의60이상80이하수준으로저렴하게임대'"
}

따라서 다음과 같은 오류가 발생하는 것이다(title을 이용해서 매칭되는 metadata를 찾는데, 개행문자가 존재하지 않아 전체 chunk로 검색을 한다).


해결 방법

결론적으로 BaseDBLoader의 수정이 필요하다. loader에서 title을 찾는 순서를 이전의 document를 불러오고, split 한 이후에 개행문자를 통해 찾는 방식에서

(document -> split -> extract title)

document를 불러온 이후, title을 가져오고 그 이후에 split하도록 하면 문제가 해결될 것이라 기대한다.

(document -> title -> split)

PangPangGod commented 9 months ago

mdloader 순서 바꿔서 해결함 (embeddingtest/document/mdLoader.py) -> TokenDBLoader class 참조