
【무료】 로컬 LLM으로 RAG의 Reranking 시도해보기
요약
로컬 환경에서 RAG의 정밀도를 높이기 위해 Cross-Encoder를 활용한 Reranking 과정을 실험합니다. Bi-Encoder 기반의 벡터 검색 한계를 극복하고, 검색된 후보군을 재정렬하여 답변의 정확도를 개선하는 메커니즘을 다룹니다.
핵심 포인트
- Bi-Encoder의 벡터 검색 한계와 Reranking의 필요성 설명
- Cross-Encoder를 활용한 질문-청크 쌍의 정밀한 관련성 평가
- 로컬 LLM 및 Oracle DB를 활용한 RAG 평가 파이프라인 구축
- Reranking을 통한 검색 결과의 순위 재조정 및 정밀도 향상
- Windows 11 Home 25H2
- RAM 16.0GB
- NVIDIA GeForce RTX 3050 Ti Laptop GPU (4 GB)
- Oracle 26ai free
- Python
AI 학습의 시작으로서, 무료이며 로컬에서 부담 없이 시도할 수 있는 환경에서 동작 확인을 진행합니다.
첫 번째 시도에서는 아오조라 분코(青空文庫)의 소설을 요약하려다 할루시네이션 (Hallucination)을 일으키는 부분까지 확인하였고,
지난번에는 정밀도를 향상시키기 위해 RAG의 정밀도 평가 메커니즘을 구현했습니다.
지난번의 흐름은 다음과 같습니다.
-
텍스트를 읽어와 청크 (Chunk)로 분할하고, Oracle에 벡터를 저장
-
청크별로 평가용 데이터(질문과 답변)를 LLM으로 생성
-
생성한 질문으로 본문의 청크를 벡터 검색하여 코사인 유사도 (Cosine Similarity)가 가까운 것을 10행 가져오고, 생성 원본 청크를 가져올 수 있다면 OK (Retrieval 평가)
-
생성한 질문으로 본문의 청크를 벡터 검색하여 코사인 유사도가 가까운 것을 3행 가져오고, 이를 근거 자료로 LLM에 동일한 질문을 하여 답변을 생성
-
4에서 생성한 답변과 2에서 생성한 답변(정답)을 비교 (Generation 평가)
・BERTScore
・Embedding Similarity
・LLM Judge
3과 4에서 벡터 검색을 수행하고 있지만, 벡터 검색은 의미가 가까운 문자열을 가져올 뿐입니다.
이것만으로는 질문에 대한 답변이 되는 문장을 가져올 수 있느냐고 묻는다면, 어느 정도 운 좋게 맞출 때도 있지만 고정밀도라고 할 수는 없습니다.
질문 문장과 유사한 의미를 가진 단어가 많이 출현하는 문장에 답변의 근거가 있을 가능성이 높다는 것은, 우리 인간의 국어 독해 시험 접근 방식에서도 흔히 볼 수 있는 일입니다.
국어 시험에서는 우선 대략적으로 관계가 있어 보이는 문장 후보를 찾고, 그중에서 정말로 질문의 답변이 될 법한 문장을 엄선합니다.
이 엄선하는 과정이 바로 Reranking입니다.
Reranking은 시간이 걸리는 처리이므로, 먼저 벡터 검색으로 후보를 좁힌 다음 그 안에서 Reranking을 하는 것이 일반적입니다. 단순히 정렬(Sort)만 해도 되고, 순위가 낮은 것을 버려서 압축해도 됩니다.
지금까지의 벡터 검색은 Bi-Encoder라고 불리며, 질문과 본문 청크를 각각 별도로 인코딩(Encoding)한 후 유사도를 평가합니다.
그에 반해, Reranking에서는 Cross-Encoder라고 불리는 것을 사용하여 질문과 본문 청크를 조합하여 인코딩하고 관련성을 평가합니다. 조합 방식이기 때문에 청크가 많아지면 계산량이 폭발하므로, 범위를 좁힐 필요가 있는 것입니다.
단편은 청크 수가 너무 적기 때문에 이번에는 중편으로서 아쿠타가와 류노스케의 「지옥변」을 선택했습니다.
- 텍스트의 청크화
- 평가 데이터 생성
까지는 지난번과 동일하므로 생략합니다.
Reranking 전에 SQL로 Retrieval 평가를 하면 다음과 같았습니다.
지난번과 비교하여 낮아졌지만, 단순히 청크 수가 많아지면 적중률(Hit rate)도 낮아질 것으로 보입니다.
"HIT1","HIT5","HIT10","HIT20"
"27.3%","59.5%","72.6%","80.9%"
정보를 저장할 테이블을 다시 만들어 둡니다.
지난번과 비교하면 Bi_Encoder와 Cross_Encoder의 스코어를 나눈 것뿐입니다.
DROP TABLE RAG_EVALUATION_CANDIDATE;
CREATE TABLE RAG_EVALUATION_CANDIDATE(
EVALUATION_ID NUMBER,
...
질문 문장 하나에 대해, 질문과 본문 청크를 쌍(Pair)으로 만들어 인코딩합니다. 전용 Encoder가 필요합니다.
결과는 관련성을 나타내는 수치로 출력되므로, 이를 내림차순으로 정렬하면 Rerank가 됩니다.
import oracledb
from sentence_transformers import SentenceTransformer
from sentence_transformers import CrossEncoder
...
그럼 다시 SQL로 Retrieval 평가를 해보겠습니다.
SELECT
TRUNC(HIT1/TOTAL*100,1) || '%' AS HIT1,
TRUNC(HIT5/TOTAL*100,1) || '%' AS HIT5,
...
출력은 다음과 같았습니다.
"HIT1","HIT5","HIT10","HIT20"
"57.1%","76.1%","77.3%","80.9%"
Reranking 전이
"HIT1","HIT5","HIT10","HIT20"
"27.3%","59.5%","72.6%","80.9%"
이었으므로, HIT1, HIT5, HIT10의 정밀도(Precision)가 향상되었음을 알 수 있습니다.
그럼 다음으로 생성 (Generation) 평가를 해보겠습니다.
Reranking 전에는 다음과 같습니다.
BERT F1 AVG: 0.63920975
EMB AVG: 0.8459409
bert_f1 emb_similarity scores
...
Rerank된 것을 기반으로 답변을 생성하게 합니다.
from ollama import chat
import oracledb
if __name__ == "__main__":
...
점수 취득은 지난번과 동일합니다.
BERT F1 AVG: 0.6406883
EMB AVG: 0.8460993
bert_f1 emb_similarity scores
...
결과는 거의 변하지 않습니다.
검색 (Retrieval) 평가에 비해 생성 (Generation) 평가가 그다지 개선되지 않는 것은 흔히 있는 일인 것 같습니다.
예를 들어
1위: A
2위: B
3위: C
를
1위: C
2위: A
3위: B
로 Reranking 하더라도, LLM에 전달하는 근거 자료 3개는 변하지 않기 때문에 답변은 거의 변하지 않습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기