RAG (Retrieval-Augmented Genration)란?
🐤 1. 비유와 함께 쉽게 이해하는 RAG
1) LLM의 한계와 RAG의 등장
기본적으로 LLM은 특정 시점까지의 지식만 학습되어 있는 상태이다.
따라서 LLM이 알고 있는 지식에 대해서는 잘 답변할 수 있는데, (당연하지만) 최신 정보나 domain-specific한 지식에 대해서는 잘 알지 못한다.
따라서 LLM이 모르는 정보에 대해서 추가적으로 알려주는 작업이 필요한데, 이때 가능한 기법 중 하나가 RAG (Retrieval-Augmented Generation)이다.
RAG를 쉽게 설명하자면, LLM에게 질문하기 전 필요한 추가 정보를 제공해주는 것이다.
2) Augmentation
예를 들어, LLM에게 코드를 짜달라고 하고 싶은 상황이라고 해보자. 이때 내가 지금까지 짜왔던 코딩 스타일을 적용해서 내가 짠 코드처럼 짜게 하고 싶은 상황인 것이다. 물론 LLM은 내가 누군지도 모르고, 내가 어떤 식으로 코드를 짜는 지도 모르니 그냥 시키면 할 수 없을 것이다.
그런데 여기서 내가 지금까지 짠 코드 몇 개들을 LLM한테 같이 제공하면서, “지금까지 내가 짠 코드들 예시야. 이 코드들을 참고해서 비슷한 코딩 스타일을 적용해줘”라고 한다면 어느 정도 요구 사항을 들어줄 수 있을 것이다. 이렇게 추가 정보를 이용해서 더 정확한 답변을 생성하는 과정을 Augmentation (증강)이라 한다.
3) Retrieval & Generation
그러면 다음으로는 이 정보를 어떻게 LLM에 넘겨줄 것인가이다. 항상 LLM에 질문할 때마다 내가 일일이 필요한 정보를 추가해서 넣어줄 수는 없는 노릇이기 때문이다. 따라서 이를 위해 Retrieval (검색)을 하게 된다. 앞선 예시에서 내 코드들이 만약 GitHub에 다 업로드되어 있다면, 굳이 내가 일일이 내 코드를 LLM에게 넘겨줄 필요가 없고, 알아서 GitHub에서 내 코드를 검색해서 가져오면 될 것이다.
4) Vector database
그런데 언제나 모든 GitHub에서 모든 내 코드를 검색하는 일은 비효율적인데, 대표적인 이유는 다음과 같다.
- GitHub의 전체 코드는 매우매우 많다. 이 코드를 다 뒤져보는 것은 너무 오래 걸릴 것이다.
- 코드 (예시는 코드이지만 모든 자연어 데이터가 될 수 있다.)는 자연어 데이터이므로 string으로 표현될 것이다. 이때 string끼리의 동일/유사 여부는 string matching 알고리즘을 사용해야 하는데, 이 또한 너무 오래 걸리는 task이다.
이를 해결할 수 있는 개념이 vector & vector database이다.
NLP 또는 인공지능을 조금이라도 공부했다면 vector에 대한 개념은 이미 알고 있을텐데, 간단하게 설명하자면 “고정된 크기로 데이터를 표현할 수 있고, 두 데이터 간 유사도 비교를 빠르게 수행할 수 있는 자료구조”라고 생각하면 된다.
vector을 사용하면 고정된 크기로 자연어를 표현할 수 있으므로 어느정도 크기 내로 모든 데이터를 표현할 수 있고, 일정한 시간 내에 두 vector 간 유사도 비교가 가능하다. (vector 간 유사도 비교는 cosine similarity, euclidean similarity 등의 알고리즘이 존재한다.)
이러한 특성을 이용해 각 데이터들을 embedding (vector로 만드는 과정)하여 vector database에 넣어 db를 구축하고, retrieval 과정에서 이 vector db에 원하는 정보를 검색하면 될 것이다.
위의 예시에 적용해보자면 먼저 GitHub의 모든 코드를 embedding하여 vector database에 넣어서 db를 구축한다.
이후, 내가 작성한 코드를 찾기 위해 “//@author chominho96” (이해하기 편하게 내 모든 코드에는 이런 주석들이 달려 있다고 가정하자)를 검색하면 내가 작성한 코드들을 찾을 수 있을 것이다.
5) 정리
이를 정리해보자면 다음과 같다.
- LLM은 특정 시점까지만 학습되어 있으므로 학습되어 있지 않은 최신 정보 또는 domain-specific한 정보에 대해 제공할 수 없다.
- 이를 해결하기 위해 필요한 추가 정보를 검색(Retrieval)하여 입력 프롬프트를 증강 (Augmentation) 한 뒤 원하는 응답을 생성 (Generation)할 수 있는데, 이 기법을 RAG (Retrieval-Augmented Generation)이라 한다.
- 이때 검색하고자 하는 외부 정보들은 embedding되어 vector로 표현되고, vector database에 저장되게 된다.
👀 2. RAG 심층 탐구
앞서 RAG를 예시를 통해 추상적으로 이해했다. 이제부터 RAG의 각 단계를 살펴보자.
1) 사전 작업 (Pre-processing)
외부 정보들에 대한 vector database를 만드는 과정에 해당한다.
A. 문서 로드 (Load)
pdf, word, raw data 등 각 데이터들을 가져오는 역할을 담당한다.
B. 분할 (Split)
불러온 문서를 chunk 단위로 분할한다.
이후 각각의 chunk들이 하나의 vector로 표현되게 된다.
split을 하는 이유는, 모든 데이터가 고유한 하나의 방향성을 가지고 있게 하기 위함이다.
이렇게만 들으면 이해하기 어려울 수 있는데 예를 들어 우리가 Apple의 전자 제품들 (아이폰, 아이패드 등)의 정보를 가져오기 위해 Apple 홈페이지를 긁어왔다고 해보자. 이때 이 Apple 홈페이지를 그대로 하나의 vector로 만들게 되면, 아이폰/아이패드/애플워치 등 모든 내용이 다 섞여서 하나의 vector가 될 것이다.
이러면 다음과 같은 문제점이 발생할 수 있다.
- vector은 고정된 크기로 표현되므로, 어느 정도의 정보 손실이 발생하는데, 너무 많은 정보가 들어왔으므로 정보 손실의 양이 커진다.
- 아이폰/아이패드/애플워치 등 모든 정보가 압축된 vector이므로 아이폰, 아이패드, 애플워치 각각에 대해 검색했을 때 이 vector가 검색될 확률이 높다. 이때 이 vector가 선정되어 사용되게 된다면, 아이폰에 대해 질문했는데 애플워치에 관련된 답변을 할 수 있는 것이다.
따라서 vector로 embedding을 할 때 각 데이터는 고유한 하나의 방향성을 지닌 데이터여야 하는 것이다.
하지만 그렇다고 모든 문서를 마구잡이로 split해도 된다는 의미는 아니다.
예를 들어 아이폰을 설명하는 문서가 여러개의 chunk로 분리되어 embedding 되었다고 해보자. 이 경우 아이폰을 검색했을 때 이 여러개의 vector들이 검색되게 될텐데, 이 중 일부만을 사용한다면 우리는 다른 일부의 아이폰 정보를 가져올 수 없게 되는 것이다. 또한, 불필요하게 여러 개의 vector로 나뉘어 저장되므로 공간적으로도 낭비다.
즉, 모든 데이터는 하나의 방향성을 가져야함과 동시에, 각 데이터들의 방향성은 각각 명확하게 구분될 필요가 있는 것이다.
이렇게 되도록 split 전략을 잘 세우는 것이 중요하다.
C. 임베딩 (Embedding)
chunk된 문서를 기준으로 1개의 vector로 변환한다.
이때 원하는 embedding model을 선택하여 embedding을 하게 되는데, 각 task에 가장 좋은 성능을 내는 embedding model을 선정하면 된다.
(embeddng model 성능 leaderboard 예시: NVIDIA Text Embedding Model Tops MTEB Leaderboard)
D. Vector database에 저장
embedding된 vector들을 vector database에 저장한다.
vector database의 경우에도 여러 제품들이 있는데, 각 제품별 지원하는 기능과 성능의 차이가 있으니 이를 비교해서 선정하면 된다.
아래 표는 Searching for Best Practices in Retrieval-Augmented Generation 논문에서 제시된 대표적으로 사용되는 vector database의 기능을 비교한 표이다.
2) RAG 수행
사실 RAG에서 가장 중요하고, 많은 이해를 필요로 하는 부분은 pre-processing이다. pre-processing 과정에서 어떤 과정을 통해 vector database가 구축되는지 이해했다면 실제로 RAG를 수행하는 과정은 쉽게 이해할 수 있다.
A. 검색 (Retrieval)
사용자가 입력한 질문과 가장 유사한 vector을 vector database로부터 찾는다.
이때 cosine similarity 등 vector 유사도를 계산하는 알고리즘을 사용하면 된다.
B. 증강 (Augmentation)
검색된 vector을 바탕으로 프롬프트 증강을 수행한다.
단어가 증강이라 어렵게 느껴질 수 있지만, 검색된 문서와 사용자의 질문을 엮어서 “질문을 할게. 그런데 이 추가 정보를 활용해서 대답해줘: {검색된 정보}”로 프롬프트를 만드는 과정이라고 생각하면 된다.
C. 답변 생성 (Generation)
완성된 프롬프트를 LLM에 질의하고, 생성된 응답을 사용자에게 반환한다.
🤔 3. RAG vs Fine-tuning
Fine-tuning도 RAG와 유사하게 LLM에게 추가적인 정보를 주어 더 특수한 목적으로 사용하는 데에 목적이 있다.
그렇다면 RAG가 Fine-tuning에 비해 가지는 장단점은 무엇일까?
1) 장점
먼저 RAG가 Fine-tuning에 비해 가지는 장점은 다음과 같다.
- Flexibility: RAG는 그때그때 원하는 목적에 맞게 LLM을 사용할 수 있게 한다. 변호사 시스템을 만들고 싶다면 법률 정보에 대한 vector database를 구축하면 되고, 의료 시스템을 만들고 싶다면 의학 정보에 대한 vector database를 구축하면 된다. 이에 비해 fine-tuning된 LLM은 그 목적에 대해서만 사용할 수 있다.
- Forgetting: 앞서 설명한 flexibility에 해당한다고도 볼 수 있는데, RAG를 통해 제공된 정보를 LLM이 기억하지 않기 때문에 불필요한 정보를 계속 가지고 있지 않아도 된다.
- Update: 기본적으로 Fine-tuning을 하는 비용보다 vector database에 정보를 추가적으로 넣어주는 작업의 cost가 더 적다. 따라서 최신 정보를 주기적으로 갱신해야 할 때 RAG가 더 강점을 가질 수 있다.
2) 단점
다음으로 RAG의 단점은 다음과 같다.
- RAG overhead: RAG를 적용하면 LLM에 질의하기 전에 매번 관련된 vector을 찾고, 프롬프트를 증강하는 과정이 필요하므로 이에 대한 overhead가 발생한다. 따라서 정보 갱신이 빈번하게 필요하지 않은 경우에는 Fine-tuning이 성능 상 더 유리할 수 있다.
📖 3. 기술 요약
RAG는 LLM에 학습되어 있지 않은 최신 정보, 또는 domain-specific한 정보를 반영하여 LLM이 답변을 줄 수 있도록 한다.
이를 위해 외부 정보들을 embedding하여 vector database 넣는 pre-processing 과정이 필요하며, embedding 알고리즘과 vector database의 종류는 여러 가지가 존재한다.
⚡개인적으로 공부하면서 포스팅한 글입니다. 오류가 있는 경우 지적해주시면 감사하겠습니다!⚡
댓글남기기