본문 바로가기

[AI]

왜 내 챗봇은 헛소리를 할까? RAG와 Reranker로 파헤친 정확도의 비밀 (문서 순서의 중요성)

728x90

👋 소개

안녕하세요! 대학생 개발자 주이어입니다!

저는 최근에 저희 학교를 대상으로한 전용 AI 챗봇을 제작하고 있었는데요.

그러다보니 자연스럽게 RAG와 파인튜닝과 같은 AI 관련 기술들을 많이 익히고 있었습니다.

 

특히 프로젝트에서는 RAG를 사용하여 AI챗봇을 구현했는데... 예상과 다르게 자꾸 엉뚱한 답변을 하더라구요...

그래서 찾아봤더니 RAG에서도 정확도를 향상시키기 위한 다양한 방법들이 있었습니다.

기존에 저는 단순히 필요한 데이터를 모으고, 전처리 과정만 거치면 RAG의 끝인줄 알았는데

생각보다 훨씬 더 복잡하고 어려운 내용들이 기다리고 있었습니다.

 

그래서 오늘은 제가 직접 문제를 해결하며 배운 내용을 바탕으로 RAG의 작동 방식, FAISS, Reranker, 임베딩 모델의 선택 등을 정리해보려고 합니다.


❓ RAG 방식이란?

먼저 RAG 방식의 기본 작동 원리를 이해해야 합니다.

저도 AI를 전문적으로 배운게 아니다 보니 정확한 내부적인 원리까지는 모르지만 최대한 열심히 설명해보도록 하겠습니다.

documents -> embedding 
query -> embedding
embedded_documents + embedded_query -> 유사도 문서 검색 -> 답변 생성

제가 이해한 RAG 방식은 대략 위와 같습니다.

먼저 documents 즉, 프로젝트에 필요한 문서들을 embedding 해줍니다.

여기서 "embedding 한다" 라는건 벡터화를 한다고 이해하시면 됩니다.

그 후 사용자의 질문이 담긴 query도 embedding을 해줍니다.

 

그럼 결과적으로 벡터화된 문서들과 벡터화된 질문 2가지가 생성됩니다.

[0.023, -0.874, 1.230, ..., 0.015]

벡터화된 문서 및 질문은 위와 같이 수치화된 정보로 저장됩니다.

이를 통해 질문과 유사한 데이터인지, 전혀 상관 없는 데이터인지를 구분할 수 있게 되는거죠.

 

예를 들어 데이터 중에 "졸업요건"과 관련된 데이터가 있고, 사용자가 "졸업요건"과 관련된 질문을 한다면

위와 같이 벡터화를 통해 유사한 문서들을 선별하게 됩니다.

 

그럼 최종적으로 AI는 유사한 문서를 이용하여 사용자가 원하는 답변을 생성하게 됩니다.

 

여기까지가 RAG의 기본중의 기본이었습니다.


❓ FAISS 란?

FAISS(Facebook AI similarity Search)는 고차원 벡터들 간의 유사한 항목을 빠르게 검색하기 위한 라이브러리입니다.

FAISS는 빠른 속도, GPU 지원, 다양한 인덱싱 방식 제공 등의 다양한 특징을 가지고 있습니다.

 

그럼 FAISS는 왜 필요할까요?

 

벡터 데이터가 많아질수록 단순 루프나 선형 탐색은 속도가 급격히 느려지는데,

FAISS는 이를 해결하기 위해 효율적인 인덱싱 방식과 최적화된 유사도 검색 기능을 제공합니다.

따라서 FAISS를 사용하여 유사한 문서를 빠르게 찾아낼 수 있죠.

query_vec = embed_text([query])
index = faiss.IndexFlatL2(768)
print("Vector shape:", np.array(vectors).shape)
print("Query shape:", np.array(query_vec).shape)

index.add(np.array(vectors))
D, I = index.search(query_vec, k * 5)

위 코드는 FAISS의 예시 입니다.

먼저 faiss.IndexFlatL2(차원수)로 faiss 배열을 생성해줍니다.

그 후 index에 벡터화된 문서들을 넣어주고,

index.search를 통해 벡터화된 문서들에서 벡터화된 질문과 유사한 문서를 찾아냅니다.

 

이때 빠르게 유사한 문서를 찾아주는 것이 FAISS로 이해하시면 됩니다.

 

추가로 FAISS에는 IndexFlatL2 인덱싱 외에도 IndexIVFFlat, IndexHNSW, IndexPQ의 여러가지 방식이 존재합니다. 인덱싱 방법은 상황에 맞춰 사용하시면 됩니다.


❓ Reranker 란?

FAISS로 유사한 문서도 찾았고 AI가 답변도 잘 생성해주는데 Reranker라는건 뭐고 왜 사용하는걸까요?

 

먼저 Reranker는 뜻 그대로 문서에 다시 순위를 매기는 겁니다.

FAISS로 이미 유사한 문서를 뽑았는데 왜 다시 순위를 매길까요?

 

이에 대해서는 aws에서 올라온 밑에 글을 보면 정확하게 이해하실 수 있습니다.

https://share.google/ssjHr2hzccW0XW72m

 

한국어 Reranker를 활용한 검색 증강 생성(RAG) 성능 올리기 | Amazon Web Services

검색 증강 생성 (Retrieval-Augmented Generation, RAG)은 효율적인 데이터 검색과 대규모 언어 모델 (Large Language Model, LLM) 을 결합하여 정확하고 관련성 높은 응답을 생성하는 AI 기술로 부상했습니다. 특히

aws.amazon.com

 

하지만 그래도 제가 간단하게 정리를 해보도록 하겠습니다.

 

Reranker를 사용하는 이유는 제가 생각했을 때 크게 2가지 정도가 있는 것 같습니다.

1. FAISS의 한계

먼저 FAISS는 빠르게 유사 문서를 찾아내지만 그 만큼 정확도의 문제가 있습니다.

FAISS는 위에서 설명했듯이 벡터 값을 비교하여 유사한 문서를 찾아냅니다.

그러다보니 단순 벡터 거리만으로 유사한 문서를 찾게되고, 이는 표면적 의미 유사성만 검증할 수 있습니다.

 

따라서 실제로는 문맥상 유사하지 않은 문서까지 결과에 반영되는 일이 생깁니다.

하지만 Reranker는 LLM모델을 사용하여 문맥 기반 의미까지 이해하여 유사도를 판단하기 때문에 훨씬 더 정확한 유사도 검증을 할 수 있게 됩니다.

2. 문서 순서의 중요성

이 부분은 저도 읽고 나서 엥? 왜지? 라는 생각을 했던 부분입니다.

바로 유사한 문서가 상위에 있을 경우에 더 정확한 답변을 했다 라는 논문이 나온건데요.

얘기하는 바로는 유사한 문서가 상위에 있을수록 정보가 희석되지 않고 AI에게 잘 반영이 된다는 내용이었습니다.

 

Reranker는 유사도가 높은 순으로 정렬하기 때문에 여기서 말하는 문서 순서의 중요성을 만족시킬 수 있습니다.

 

위와 같은 2가지의 이유가 바로 Reranker를 사용하는 가장 큰 이유인 것 같습니다.

 

그럼 처음부터 Reranker만 사용하면 되지 않을까? 라는 생각을 할 수도 있습니다.

하지만 Reranker는 FAISS에 비해 속도가 훨씬 느리기 때문에 전체 문서를 검증하기에는 한계가 있습니다.

따라서 FAISS로 대량의 데이터를 빠르게 유사도 검증 -> Reranker로 정밀하게 유사도 검증후 정렬 이런식으로 같이 사용됩니다.

query_embedding = reranker_model.encode(query, convert_to_tensor=True)
doc_embeddings = reranker_model.encode(doc_texts,convert_to_tensor=True)

similarities = util.cos_sim(query_embedding, doc_embeddings)[0]
reranked_indices = similarities.argsort(descending=True)[:k]

위 코드는 Reranker의 예시입니다.

FAISS에서도 문서와 질문을 벡터화했던 것 처럼, Reranker에서도 문서와 질문을 각각 벡터화를 진행해줍니다.
그 후 유사한 문서를 고르고, 유사도를 기준으로 정렬까지 해주는 코드입니다.

사용된 Reranker 모델은 "KoSimCES" 모델이고, 이에 대해서는 바로 밑에서 자세하게 설명할 예정입니다.


❓ 모델의 차이

위에서 말하는 것들을 다 적용해도 제 챗봇은 답변이 만족스럽지 않았습니다.

기존 FAISS만 사용했을 때보다는 유사한 문서를 잘 찾는 것 같긴 했지만, 그럼에도 전혀 다른 문맥의 문서를 가져오는 일이 빈번했습니다.

그래서 열심히 찾아보니 모델의 차이도 결과에 큰 영향을 미치는 것 같았습니다.

 

처음에는 단순히 GPT 모델을 사용하면 되겠지 라고 생각했지만, 한국어 처리에는 크게 좋은 성능을 내지 못한다는 글을 발견했습니다.

그래서 저는 한국어 전용 모델을 찾아보게 되었고 그 중 "KoSimCES SKT" 라는 SKT에서 만든 한국어 전용 모델을 알게되었습니다. SKT라기에 믿음이 가기도 했고 다른 개발자들의 평가도 괜찮아보여 사용하게 되었습니다.

from sentence_transformers import SentenceTransformer, util

client = OpenAI(api_key="API_KEY")

reranker_model = SentenceTransformer("BM-K/KoSimCSE-roberta")

 

KoSimCES 모델을 사용하기 위해서 위와같이 설정을 해주었습니다.

그리고 저는 간단한 설정을 마치고 바로 테스트를 진행했습니다.

 

그런데 전혀 알 수 없는 오류가 나오기 시작했습니다.
AI도 처음 배우고 있던 상황이라 왜 문제가 생기는지 알 수 없었습니다.

GPT에도 물어봤지만 아직 AI에 대한 정보가 적어서 그런지 원하는 답변은 나오지 않았습니다.

 

그래서 구글링을 엄청하여 차원수 문제라는 것을 알게 되었습니다.

기존에 저는 KoSimCES 모델로 문서를 벡터화하고, 사용자 질문은 gpt-3-small 모델로 벡터화를 했었습니다.

하지만 KoSimCES 모델은 768차원을 반환하고, gpt-3-small은 1536차원을 반환하는 모델이었습니다.

따라서 다른 차원의 벡터를 가지고 유사도를 검증하려고 하니 발생하는 오류였습니다.

 

사용자 질문도 KoSimCES 모델로 벡터화를 진행하고 FAISS 차원수도 768로 설정해주었더니 오류 없이 잘 실행되었습니다.

 

그러고나니 의문점이 하나 생겼습니다. "차원수가 더 높으면 성능이 더 좋은거 아닌가?"

차원수와 성능의 연광성

단순히 같은 모델이라면 차원수가 높은 모델이 성능이 더 좋은 모델이긴 합니다.

하지만 한국어를 사용하여 벡터화를 진행하거나, 특정 상황에서는 더 낮은 차원의 모델이더라도 더 좋은 성능을 보여줄 수 있습니다.

저의 경우에도 1536차원의 gpt-3-small 모델보다 한국어 전용 모델인 768차원의 KoSimCES가 원하는 답변을 더 잘 생성해주었습니다.

즉, 차원수가 높으면 일반적으로 성능이 더 높을 수 있지만 절대적인 상관관계는 아닙니다.

따라서 상황에 맞는 적절한 모델을 찾는 것도 RAG 성능을 향상시키는데 중요한 역할을 하는 것 같습니다.


😊  마무리

지금까지 이렇게 RAG, FAISS, Reranker 등의 대해서 정리해보았습니다.

처음엔 단순한 검색 기반 AI 챗봇 정도로 생각했지만,

직접 구현하면서 검색 정확도를 높이기 위한 수많은 고민들을 하게 되었고,

덕분에 RAG의 구조와 작동 방식에 대해 훨씬 깊이 이해하게 되었습니다.

 

AI 분야를 공부 중인 분들 모두 대단한 것 같고, 다들 화이팅입니다!

 

저와 같이 이런 분야를 같이 공부하고 프로젝트를 제작하고 싶다면 아래 링크로 들어와주세요!

 

https://discord.gg/8Hh8WgM4zp

 

KYT CODING COMMUNITY Discord 서버에 가입하세요!

Discord에서 KYT CODING COMMUNITY 커뮤니티를 확인하세요. 23명과 어울리며 무료 음성 및 텍스트 채팅을 즐기세요.

discord.com

KYT CODING COMMUNITY 가입하기!

728x90