TeddyLee님의 langchain-kr 깃허브: https://github.com/teddylee777/langchain-kr
목차
- 지난 1년간 (2023) 6개 기업의 10여개 프로젝트에서 얻은 RAG 경험담
- 마주한 도전들
- 실패 사례, 극복 시도
- Vector Store, Prompt Engineering, LLM Fine-tuninggi
용어정리
RAG: Retrieval Augmented Generation
RAG 프로세스
자체 정의한 RAG 프로세스 과정
1. 전처리 작업
- Load Document: 처리할 문서를 시스템에 불러옵
- Chunk(Split): 문서를 더 작고 관리하기 쉬운 조각으로 나눈다(llm은 한번에 읽을 수 있는 단어는 few thousands 이기 때문)
- Embedding: 이 조각들을 내용의 본질을 포착하는 숫자 표현으로 변환
- Store: 임베딩들을 데이터베이스에 저장
2. 서비스 단계에서 이뤄지는 작업
- llm 서비스 구동중인 상태에서 이뤄지는 작업
- 유저 질문을 받아 Embedding 처리
- Retrieve(발췌)
- 실시간으로 이루어짐
- 프롬프트 엔지니어링후 LLM에 전달
- output
문서 전처리
모듈
- Document Loader: 문서 로드
- Langchain에서 150가지 loader 지원
- Text Splitter: 분할 전략
- 10 가지 알고리즘 지원
- Embedding: 임베딩
- Langchain + HuggingFace
- Vector Store: Vector DB
- Semantic Search를 위해 RAG에서는 주로 VectorDB사용
- LangChain에서 80가지 지원
- Retrievers: Vector DB 검색기
- 50가지 지원
강의 목적: 위 내용을 조합하여 3억개 이상의 조합 발생 → 모든 상황 테스트 불가능 → pipeline 구성시 참고용 경험 공유
Document Loader(PDF)
- Excel/SCV, PDF을 주로 취급
- 보안에 유리한 PDF 형식의 문서가 많았음
LangChain의 강점: 통합된 Interface를 제공해 여러 모듈들을 바꿔가며 테스트해보기 용이함
Document Loader 선택시 고려사항
- 텍스트를 원형 그대로 가져오는지
- 한글, 특수문자
- 메타데이터(metadata)의 종류
- page_content
- 페이지번호(page)
- 표, 차트, 문서의 coordinates(좌표), 속성(Title, Table, Image, Text)
- 속도
속도 vs 다양한 메타데이터 제공여부가 관건이 될것 같다
Retriever
Multi-Query Retriever
LLM 사용해 사용자 입력 쿼리 -> 여러 관점에서의 여러 쿼리 생성(Prompt Tuning 프로세스 자동화 효과)
각 쿼리에 대해 관련 문서 집합을 검색하고 모든 쿼리에서 고유한 유니온을 사용하여 잠재적으로 관련성이 높은 문서 집합을 가져옴
키워드 기반 검색 시 의미를 잘 포착하지 못하는 문제를 해결함
ex) 질문: “Page Rank에 대해 알려줘” Multi-Query Retriever 결과:
- Page Rank 알고리즘의 작동 원리는 무엇인가요?
- Page Rank를 사용하는 이유와 그 효과에 대해 설명해주세요
- 구글의 Page Rank 기술에 대한 상세한 정보를 제공해줄 수 있나요?
Ensemble Retriever
배경
Semantic Search
의 문제점- 유사 의미를 가지는 문서의 검색에서는 유리
- BUT : 특정 키워드가 반드시 포함되어야 하는 검색시 유사 단어가 포함된 문서가 검색될 수 있음 ex) “비타민A 영양제 추천해줘” -> “비타민C가 풍부한 영양제는 00 영양제”, “비타민D가 부족하다면 이 영양제를 추천합니다”
‘비타민’ 때문에 유사성 발생 -> Semantic Search
위와 같은 경우 키워드 기반 검색을 사용하는 Sparse Retriever이 유리함 -> 여러 Retriever을 ensemble하자
장점:
- 여러 Retriever을 ensemble하여 사용(
Sparse Retriever
+Dense Retriever
의 ensemble) - 서로 다른 알고리즘의 강점 활용 -> 단일 Retriever 대비 성능 향상
- ensemble하는 retriever간 weight 조절도 가능
Sparse Retriever: BM25Retriever
, Dense Retriever: faiss_vectorstore.as_retriver()
사용한 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
#initialize the bm25 retriever and faiss retriever
bm25_retriever = BM25Retriever from_texts(doc_list)
bm25_retriever.k = 2
embedding = OpenAIEmbeddings()
faiss_vectorstore = FAISS.from_texts(doc_list, embedding)
faiss_retriever = faiss_vectorstorem.as_retriver(search_kwargs={"k"::2})
# initialize the ensemble retriever
ensemble_retriever = EnsembleRetriever(
retrievers = [bm25_retriver, faiss_retriever], weights=[0.5, 0.5]
)
Long Context Reorder
- https://arxiv.org/abs/2307.03172
- 모델 아키텍처와 관계없이 검색된 문서를 10개 이상 포함시 상당한 성능 저하 발생
- 긴 context 중간 정보 access해야 하는 경우, 제공된 문서를 무시하는 경향이 발생
- 정렬 방식 : 중요도 기준 내림차순 정렬 -> 매우중요 - 덜 중요 - 중요
- 중요한 문서 검색 결과를 상위로 재조종하여 중요한 정보를 답변에 포함하도록 유도
1
2
3
4
5
6
# 문서 재정렬:
# 관련성이 낮은 문서는 목록의 가운데에 배치
# 시작/끝에 관련성 높은 요소 배치
reordering = LongContextReorder()
reordered_docs = reordering.transform_documents(docs)
- Multi-Vector Retriever: 여러 VectorStore의 Retriever ensemble
- ContextualCOmpressor: 긴 길이의 문서 검색 결과 압축
- LLMChainFilter: 문서의 검색 결과 필터링
Prompt Engineering
- 양식에 맞춘 출력 형식이 중요한 경우(사업기획서, 보고서)
- FewShotPromptTemplate 활용
- LangSmith Hub에 업로드 되어 있는 완성형 Prompt을 Pull하여 사용하기
- 별도의 .yaml 파일로 프롬프트 version관리
Chain of Density(문서요약 프롬프트)
배경
- 요약에 포함할 정보의 “적절한” 양을 선택하는 것은 어려운 작업
- 좋은 요약은 정보의 밀도가 높고, Entity 중심이어야 함
해결 방안
Chain of Density
프롬프트를 사용하여 점점 더 밀도가 높은 요약을 요청- 이전 Missing Entities를 추가 및 갱신하여 요약할때마다 Denser Summary 출력
방식
- Sparse한 요약 생성
- 이전의 Sparse 요약에 Missing Entity 발견
- Missing Entity를 추가한 Denser Summary 생성
- 1~3번을 5회 반복