[RAG Project] GlobalMacro QA chatbot - Data Preprocessing - GraphState (5)
2024.09.10 - [RAG Project] - [RAG Project] GlobalMacro QA chatbot - Data Preprocessing - excel 파일 (4) [RAG Project] GlobalMacro QA chatbot - Data Preprocessing - excel 파일 (4)2024.09.10 - [RAG Project] - [RAG Project] GlobalMacro QA chatbot - Data
hibyeys.tistory.com
이전 포스팅까지해서 데이터 전처리를 마무리했다. 그 다음 과정은 필요한 데이터를 불러와 Embedding을 수행하고 VectorStore에 저장 하는 것이다. 이번 포스팅에서는 그 과정을 수행해보겠다.
1. Extract GraphState Data
1.1 Retriever 선정
먼저 Embedding을 수행하기 전에 GraphState 형태로 저장한 데이터를 Document 객체로 만들어야 한다.
그런데 어떤 데이터를 뽑아서 Document 화 할지를 결정할 때, 어떤 Retriever를 사용할 것인지를 고려해야한다.
예를 들어 MultiVector Retriever를 사용한다고 가정하면 vectorstore 뿐만 아니라 docstore도 같이 고려해야하고
또한 어떤 방식을 쓸것인지에 따라서 각각의 store에 넣는 데이터도 달라지게 된다. (Retriever 가 궁금하다면 링크)
우선적으로 고려한 Retriever는 바로 BM25 Retriever 이다. 이유는 전문 용어들이 많은 문서에는 유사도 기반의 검색기보다는
키워드 기반의 검색기가 더 성능이 좋다는 실험들을 보았기 때문이다. (링크)
하지만 이 방식은 문서의 키워드를 기반으로 검색하기 때문에 Embedding을 수행하지 않아도 된다.
그 다음 고려한것은 위의 예시를 든 MultiVector Retriever 인데 여러 방식이 있지만 그 중에 요약본을 Vectorstore에 저장 하는 방식을
사용하려한다. docstore에 chunk된 문서를 저장하고 vectorstore에 요약본을 저장해서 질문과 유사도 검색시 요약본이 걸리면 그 요약본의 chunk를 LLM에게 전달하는 것이다. 요약본을 vectorstore에 저장함으로써 Embedding 비용도 절감할 수 있고 문서에 따라 chunk의 내용보다 정확하게 추출할 수 있어서 시도해 볼만하다.
다음은 MultiQuery Retriever 이다. 쿼리를 임베딩해서 유사도 검색시 쿼리의 세부적인 차이로 인해 검색결과가 달라질 수 있다.
MultiQuery Retriever는 이런 문제를 보완하기 위해 사용자의 입력 쿼리를 LLM에게 전달하여 다양한 관점에서 여러 쿼리를 생성한다.
그리고 각각의 쿼리에 대해 관련 문서 집합을 검색하고, 모든 쿼리를 아우르는 문서들의 합집합을 추출해 잠재적으로 관련된 더 큰 문서 집합을 얻을 수 있게 해준다. 사실 쿼리나 프롬프트를 빡세게 작성하면 필요가 없을 수도 있고 LLM이 개입하여 비용을 증가 시키는 단점이 있지만 그만큼 고민할 시간을 줄여준다는 장점이 있어 위의 두 Retriever 와 적절히 섞어서 사용해 볼 생각이다.
결론적으로 내가 생성해야할 Vectorstore는 두 가지 이다.
- VectorStore : 요약본 & DocStore : Chunk
- VectorStore : Chunk & DocStore : Chunk
1.2 Extract
첫번째는 page_number 별로 Docstore에 summary 들을 저장하고 Vectorstore에 기존의 chunk들을 저장한다.
그리고 image_summary가 검색되었을 때, 나중에 LLM에게 원본 이미지를 넣어줄 수 있게 metadata에 images를 넣어준다.
두번째도 첫번째와 마찬가지로 데이터를 추출한다. (요약본만 chunk로 변경해서)
2. Embedding
임베딩 모델은 딱히 고민하지 않고 upstage 것을 사용하였다. 이유는 계속 언급한대로 한국어 성능이 다른것들 보다 뛰어나고 가격도 나쁘지 않기 때문이다. 다만 아래와 같이 Context Length가 제한되어 있다.
그래서 문서들중 token 수가 3000이 넘는 chunk들은 Semantic Chunker를 통해 한번더 분할해 주었다.
3. VectorStore
VectorStore는 Main으로 Pinecone을 선택했다. Chroma, faiss , neo4j , mongoDB 등 여러 선택지가 있지만 pinecone을 선택한 이유는
- 뛰어난 분산 아키텍처와 효율적인 인덱싱 기술 덕분에 확장성이 좋기 때문이다.
- 물론 다른 클라우드 기반 DB들도 좋은 성능을 낼 수도 있지만, 사람들이 많이 사용하는 VectorDB라는 것이 신뢰성을 더 해준다.
- 게다가 무료 plan이 개인이 사용하기 충분하다.
3.1 Chroma DB를 통한 Test
Chroma DB를 이용해서 간단하게 Embedding과 아까 선택한 Retriever를 Test 해 보았다.
3.2 Pinecone
MultiVector Retriever 를 사용한다고 했지만 아무래도 BM25 Retriever가 주가 될 것 같아 두번째 방법인 singlevector 방식으로 데이터를 추출 했다. (중간에 간단히 AutoRAG를 돌린 결과도 BM25를 꼽기도해서..)
이번에는 Chroma DB 때와는 다르게 BM25를 쓰기 위해 좀 더 디테일하게 데이터를 처리하였다.
Pinecone docs를 보면 굉장히 불친절하게 만들어져 있는데, 마침 테디노트님이 RAG 강의를 하면서 만드신 langchain_teddynote 라는 패키지(링크)로 쉽게 구현할 수 있었다. (데이터 형식이 좀 달라서 preprocessing 하는데 좀 고생 좀 한거 말고는..)
이번에는 Sparse Encoder를 통해 values를 생성하기 위해 Kiwi Tokenizer(링크)와 한글 불용어 사전을 이용하였다.
pincone 에서 Sparse Encoder로 BM25 Encoder를 제공하는데 이는 한글을 처리하는데 적합하지 않다.
그래서 지능형 한국어 형태소 분석기인 kiwi와 결합해 Sparse Encoder를 학습하였다.
데이터가 잘 업로드 되었다.