일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- dfs
- get_dummies()
- CROSS JOIN
- 백준
- 인접리스트
- 1x1 Convolution
- SQLD 후기
- Two Pointer
- 데이터모델링
- dp
- Inductive Bias
- resnet
- mobilenet
- outer join
- numpy
- skip connection
- Depthwise Separable Convolution
- 인접행렬
- SQL
- SQLD
- feature map
- 연산량 감소
- 엔터티
- pytorch
- 정규화
- bottleneck
- BFS
- 그래프
- depthwise convolution
- 식별자
- Today
- Total
SJ_Koding
[LLM] ANTHROPIC에서 발표한 RAG성능 향상 꿀팁 정리(Contextual Retrieval) 본문
2024년 9월 20일, Claude 제작사의 Anthropic사에서 RAG성능을 향상시키기 위한 기법을 공개했습니다. Claude에 사용된 프롬프트들을 공개하는 등, 본인들이 가지고있는 기술들을 공개하는 데에 꺼리지 않는 모습을 보이는 것 같습니다. (OpenAI는 이런 적이 있었나..)
( 해당 포스팅은 통번역이 아닙니다. 제 경험과 주관이 내포되어있기 때문에 원문과 함께 구독하시면 효과적입니다. )
RAG (Retrieval-Augmented Generation)기술의 필요성
이제는 너무 유명한 기술이라고 생각이 듭니다. 왜 필요한지, 어떤 효과가 있는지 LLM에 조금이라도 관심이 있다면 알고 있을 것이며 적용도 해본 사람이 많을 것입니다. 대부분 기업들의 제한적인 자원 환경에서의 RAG기술은 가볍지만 아주 강한 기술입니다. 예를 들어 뉴스 기사를 찾아준다거나, 유사논문을 찾는다거나 많은 도메인에서 RAG기술이 사용됩니다. 저 또한 여러 프로젝트에서 RAG를 파인튜닝과 함께 필수적으로 사용하고있습니다.
Anthropic은 기존 RAG기술에 대해 정보를 encoding하는 과정에서 context를 제거하기 때문에 관련 정보를 검색하지 못하는 경우가 많다는 것을 문제점으로 삼았습니다. 따라서 이를 해결하기 위한 "Contextual Retrieval"에 대해 소개합니다. 해당 기술은 Contextual Embedding와 BM25 하위 두 가지 기술로 나뉘게 되고 이를 모두 적용하면 검색 실패횟수를 49%까지 줄일 수 있고 rerank기술과 결합하면 최대 67%까지 줄일 수 있다고 설명합니다.
(물론 200K가 넘지 않은 knowledge bases에서는 Retreival를 사용하지 않고 그냥 프롬프트에 포함하는 것이 가장 간단하고 베스트일 수 있다는 antropic의 이야기와 RAG의 기본적인 개념설명을 넘어...)
Embedding models & BM25 (Best matching 25)
Embedding 모델은 자연어를 하나의 벡터로 표시하고 문맥을 파악할 수 있으므로 의미론적 관계를 포착하는데는 탁월합니다. 기존 룰 베이스에서 사과와 배는 유명한 조합의 과일입니다. 여기서 단순히 룰베이스 기반으로는 '사과'와 '배'는 다른 글자를 가지므로 완전히 다른 의미로 해석하지만 embedding모델에서는 사과와 배를 유사한 의미론적 관계를 갖는다는 것을 알 수 있습니다.
하지만 의미가 아닌 토큰(단어) 그 자체가 중요할 때가 있습니다. 대표적으로 사람 이름, 특정 코드와 같은 고유 식별자를 찾아야할 경우입니다. 이때 BM25가 효과적인데 이는 토큰 매칭을 사용해서 정확한 단어 및 구문 일치를 찾아내어 순위를 매깁니다. 수식은 아래와 같습니다.
$\text{BM25}(D, Q) = \sum_{i=1}^{n} IDF(q_i) \cdot \frac{f(q_i, D) \cdot (k_1 + 1)}{f(q_i, D) + k_1 \cdot \left(1 - b + b \cdot \frac{|D|}{\text{avgdl}}\right)}$
BM25는 TF-IDF개념을 기반으로 작동하며 문서 길이와 용어 빈도에 포화함수를 적용하면서 일반적인 단어가 추출해야할 결과를 묻어버리는 것을 방지합니다. 따라서 다음 단계에 따라 embedding과 BM25기술을 결합하여 사용할 수 있다고 합니다.
1. Document를 수백 개의 토큰을 넘지 않는 작은 텍스트로 청크를 나눈다.
2. 해당 청크에 대한 TF-IDF encoding 및 semantic embeddings를 생성한다.
3. BM25를 사용하여 정확히 일치하는 chunks를 찾는다.
4. embedding을 사용하여 의미적 유사성을 기반으로 chunks를 찾는다.
5. rank fusion 기술을 이용하여 결과를 결합하고, 중복을 제거한다.
6. prompt에 상위-K개의 청크를 추가하여 응답을 생성한다.
6단계나 이루어져있지만, 결국엔 BM25로 chunk를 뽑아내고, embedding으로 chunk를 뽑아내어 이를 특정한 rank fusion기술을 활용하여 재정렬 한 후, 이를 프롬프트로 넘기는 방식입니다. 이로써 정확한 용어 매칭과 의미론적 이해의 균형을 맞추며 보다 정확한 RAG시스템이 완성됩니다.
But!!
일반적인 작은 청크들은 각각 충분한 문맥을 갖추지 못하면 문제가 발생할 수 있습니다. 만약에 knowledge bases에 국세청 자료를 가지고 있고, 다음과 같은 질문이 주어졌습니다(원문 예시 인용):
User: "삼성전자의 2023년 2분기 매출 성장률은 어떻게 돼?"
related chunk: "회사의 매출이 전 분기 대비 3% 성장했다."
해당 chunk를 보면 어느 회사의 매출인지, 그리고 전 분기가 언제인지 context를 파악할 수 없기 때문에 올바른 정보를 검색하거나, 그 정보를 효과적으로 사용하는 것이 어려울 수 있습니다.
결국 이 이야기를 하고자 서론이 길었던 것 같습니다. 이를 해결하기 위한 방법으로 Contextual Retrieval기법을 소개하고있습니다.
Introducing Contextual Retrieval★
knowledge bases에 있는 수천 수백만 개의 chunk에 수동으로 문맥을 기입하는것은 현실적으로 어려우므로 Cluade를 활용했다고 합니다. 전체 Document의 context를 사용하여 chunk를 설명하는 간결한 context를 제공하도록 프롬프트를 작성하였고 해당 프롬프트는 다음과 같습니다:
<document>
{{WHOLE_DOCUMENT}}
</document>
Here is the chunk we want to situate within the whole document
<chunk>
{{CHUNK_CONTENT}}
</chunk>
Please give a short succinct context to situate this chunk within the overall document for the purposes of improving search retrieval of the chunk. Answer only with the succinct context and nothing else.
"""
<document>
{{WHOLE_DOCUMENT}}
</document>
여기 전체 문서에서 우리가 위치시키고 싶은 부분입니다.
<chunk>
{{CHUNK_CONTENT}}
</chunk>
이 chunk를 문서 전체 흐름에서 더 잘 검색할 수 있도록 하기 위한 짧고 간결한 문맥을 제공해주세요.
"""
위 프롬프트를 활용해서 일반적으로 chunk당 50~100개의 token수를 가진 데이터가 완성되었다고 합니다. 그 후 flow는 하단 이미지와 같습니다.
Anthropic은 독립적으로 코드베이스, 소설, ArXiv 논문, 과학 논문 등의 지식 영역, embedding 모델, 검색전략, 평가 등의 다양한 실험을 진행했습니다. 이때 몇 가지 질문/답변 쌍의 예시는 해당 링크로 공유했습니다.
그리고 앞서 설명한 기술들을 조합했을 때의 성능 지표를 다음과 같이 나타냈습니다.
확실히 Contextual Retrieval가 Standard Retrieval보다, embedding과 BM25의 결합이, embedding모델보다 우수한 성능을 보였습니다. 모든 기술을 적용한 스코어가 기존 임베딩만 사용했을 때보다 2배가량 우수해보입니다. (하지만 성능 향상 기술이 하나 더 남아있습니다. Rerank! 이후 나옵니다.)
Implementation considerations
Antropic은 구현시 고려할 사항에 대해 팁아닌 팁을 공유했습니다.
1. chunk: 당연한 말이지만 청크 길이, 분류 방법을 반드시 고려해야합니다.
2. embedding model: 결국 근본이 되는 임베딩모델이 좋아야하는 법. Antropic은 Gemini와 Voyage의 embedding API사용을 추천하고 있습니다.
3. 문맥 분석 프롬프트 사용자화: 제공하는 일반적인 프롬프트보다 특정 도메인이나 사용 사례에 맞는 프롬프트를 사용하면 더 나은 결과를 얻을 수 있습니다.
4. 추출할 chunk 수: 추출된 chunk를 만약 상위 1개만 준다면, 많은 정보를 얻지 못해 엉뚱한 RAG를 수행할 수 있고 만약 상위 100개를 준다면 이 중 어느 것을 보고 답변해야할지 헷갈려하며 모델이 멀미할 수도 있습니다. Antropic은 5개, 10개, 20개를 제공해본 결과 20개의 chunk를 주는 것이 가장 성능이 좋았다고 합니다. 당연히 데이터에 따라 상이하므로 실험을 통해 결정해야 할 것입니다.
Further boosting performance with Reranking
rerank기법은 저희 팀에서도 시도해봤던 방법인데, 탐색 시간이 너무 오래걸려서 철회했었던 기술입니다. 다시 한 번 시도해보고 싶네요.
여튼, Rerank기법을 적용하면 성능이 향상된다고 합니다. 해당 기술은 예를 들어 Retrieval로 뽑힌 상위 150개의 chunk가 있고, 이를 사용자의 query와 함께 상위 N개의 chunk를 다시 rerank 모델에 전달하여 최종적으로 상위 K(=20)개의 chunk를 선택합니다. 다시 말해 1차적으로 뽑고, 그 중에서 유사한 chunk를 재선별하는 기술입니다.
Antropic은 다양한 Rerank model중 Cohere reranker를 사용하여 테스트를 진행했다고 합니다. Voyage도 있지만 시간이 없었다고 하네요. 실험 결과 다양한 도메인에서 rerank를 수행하면 검색이 더욱 최적화 되는 것으로 나타났습니다.
해당 섹션의 첫 마디에 시간이 오래걸려서 철회했었다고 말씀드렸습니다. 이에 관련한 내용을 Antropic에서도 언급했습니다. rerank기법의 지연시간에 대한 문제를 강조하면서 이는 chunk수에 따라 점진적으로 증가합니다. 병렬적으로 처리해도 어쨋든 지연시간이 추가될 수 밖에 없다는 것을 강조하면서, 트레이드오프를 적절히 설정하는 것이 좋다고 하네요.
Conclusiuon
요약하겠습니다.
1. embedding과 BM25를 결합한 방법이 embedding 단독 사용보다 더 효과적이다.
2. Voyage와 Gemini가 테스트한 embedding모델 중에서 가장 우수하다.
3. 상위 20개의 chunk를 모델에 전달하는 것이 10개 혹은 5개만 전달하는 것 보다 더욱 효과적이다.
4. chunk에 문맥을 추가하면 검색 정확도가 크게 향상된다.
5. rerank를 사용하는 것이 더욱 효과적이다. 단, 시간소요는 감안해야한다.
6. 위 내용들은 모두 결합할 수 있어 최종적으로 67%의 검색 성능 향상을 보였다.
마치며
RAG에 대해 글을 다루어보고 싶었는데, 리뷰느낌으로 Antropic의 게시글을 정리하였습니다. 주관이 담긴 글이어서 틀린 내용이 있을 수 있습니다. 자세한건 원문을 참고해주시고 언제든 지적해주세요.
감사합니다 :)