LLM의 주요 한계점
LLM은 방대한 양의 텍스트 데이터로 학습하지만, 이 지식은 LLM 모델에 명시적으로 저장되는 것이 아니라, 확률적인 패턴 형태로 저장된다. 이러한 특성 때문에 LLM은 본질적인 한계를 가지고있다.
- 부정확한 정보 생성 (환각 현상): LLM은 확률적으로 가장 그럴듯한 다음 단어를 예측하며 텍스트를 생성한다. 실제 사실인 데이터를 기반으로 학습했다고 했을지라도, 이 데이터들이 모델에 저장될때는 확률적인 패턴으로 저장된다. 이 때문에 응답을 생성할 때 사실이 아닌 내용을 마치 사실인 것처럼 꾸며내는 '환각(Hallucination)' 현상이 발생할 수 있다.
- 최신 정보 부족: LLM은 학습 시점 이후에 발생한 사건이나 새롭게 등장한 정보에 대해서는 알지 못한다. 예를 들어, 만약 "트랄랄레로 트랄랄라"라는 최신 유행어나 밈에 대해 질문한다면, LLM은 해당 용어가 학습 데이터에 없었기 때문에 의미를 이해하지 못하거나 엉뚱한 답변을 하게 된다.
- 소스 불분명: LLM이 학습 데이터를 기반으로 사실인 정보를 제공하더라도, 해당 정보가 어떤 출처에서 비롯되었는지 명확히 제시하지 못하는 경우가 많다. 이는 사용자가 LLM의 응답에 대한 신뢰성을 검증하기 어렵게 만든다.
RAG (검색 증강 생성) 이란
RAG는 Retrieval Augmented Generation의 약자로, 우리말로 '검색 증강 생성'이다. 이름에서 알 수 있듯이, LLM이 답변을 생성하기 전에 외부의 신뢰할 수 있는 지식 소스에서 관련 정보를 검색하고 이 검색된 정보를 바탕으로 답변을 생성하는 접근방식이다.
조금 더 풀어 설명하면, 사용자의 질문이나 요청이 들어오면, RAG 시스템은 먼저 이와 관련된 정보를 사전에 구축된 지식 데이터베이스 나 외부 문서 저장소에서 찾아낸다. 그리고 이렇게 검색된 정보를 LLM에게 함께 전달한다. LLM은 이 자료를 바탕으로 앞서 언급된 환각 현상을 크게 줄이고, 최신 정보에 기반한 답변을 제공할 수 있게 된다.
RAG의 주요 이점
- 정확성 향상: LLM이 내부 지식에만 의존하는 대신, 검증된 외부 정보를 참조하므로 환각을 줄이고 사실에 기반한 답변을 생성할 가능성을 높인다.
- 최신 데이터 확보: 외부 지식 소스를 주기적으로 업데이트함으로써, LLM의 재학습 없이도 최신 정보를 답변할 수 있다.
- 출처 제시: 검색된 문서를 통해 정보의 출처를 함께 제시함으로써 답변의 신뢰성을 높일 수 있다.
RAG의 핵심 구성 요소
RAG은 크게 검색기와 생성기로 구성된다.
- 검색기: 사용자의 질문이 입력되면, 사전에 구축된 지식 소스에서 해당 질문과 가장 관련성이 높은 정보를 찾아내는 역할을 한다. 검색 방식으로는 전통적인 키워드 기반 검색(예: TF-IDF, BM25)부터, 의미 기반의 유사도를 측정하는 벡터 검색까지 다양하게 활용될 수 있다.
- 생성기: 핵심적인 텍스트 생성 역할을 하는 LLM. 검색기를 통해 전달받은 관련 정보와 사용자의 원본 질문을 함께 입력받아서, 이를 종합적으로 이해하고 최종 답변을 생성한다. 즉, LLM은 단순히 모델에 저장된 정보에 의존하는 것이 아니라, 제공된 자료를 참고하여 답변하는 것과 같다.
키워드 검색기를 통한 단순 RAG 구현
아주 간단한 키워드 검색기를 사용한 RAG을 구현해서 앞서 언급된 "트랄랄레로 트랄랄라"라는 질문에 답변할 수 있도록 해보자.
!pip install openai==1.55.3 httpx==0.27.2 --force-reinstall
import os
import openai
from openai import OpenAI
os.environ['OPENAI_API_KEY'] = ""
openai.api_key = os.getenv("OPENAI_API_KEY")
import openai
from openai import OpenAI
client = OpenAI()
gptmodel="gpt-4o"
def sendQuery(query):
# Join all lines to form a single string
prompt = f"다음 질문에 공손하고 친밀하게 응답하세요: {query}"
response = client.chat.completions.create(
model=gptmodel,
messages=[
{"role": "user", "content": prompt}
],
)
return response.choices[0].message.content.strip()
먼저 OpenAI의 gpt-4o 모델에 사용자 쿼리를 보내는 sendQuery메소드를 만들었다. gpt-4o는 2023년까지의 학습 데이터를 사용했기때문에 트랄랄레로 트랄랄라를 모른다.
reference_document_list = [
"트랄랄레로 트랄랄라: 나이키 운동화[4]를 신고 해변 위에 나와 있는 다리가 3개인 상어 캐릭터이다.[5] 이탈리안 브레인롯 밈의 시초인 캐릭터로, 봄바르디로 크로코딜로와 라이벌 관계로 표현되며 유명한 만큼 강자로 추정된다.[6][7] 설명의 일부가 설정으로 편입됐는데, 아들들인 Tralalaleritos과 같이 포트나이트를 즐기는 것이 취미라고 한다. 아들들은 매우 귀여운 아기상어이다. 일부 사람들은 「트랄랄레오(또는 트랄라레로) (또는 트라레뜨로) 트랄랄라」라고 하기도 하지만 이는 잘못된 표기이며, 「트랄랄레로 트랄랄라」가 올바른 표현이다.",
"봄바르디로 크로코딜로: 폭격기와 악어를 합성한 캐릭터이다. 이탈리안 브레인롯에서 트랄랄레로 트랄랄라, 퉁퉁퉁퉁퉁퉁퉁퉁퉁 사후르랑 더불어 유명한 캐릭터. 악어 머리를 제외한 동체 부분은 B-17이나 B-25 등 제2차 세계 대전 당시의 미군 폭격기를 모티브로 한 것으로 추정된다. 알라를 믿지 않기에 팔레스타인과 가자지구에서 어린이를 폭격한다고 한다. Tralalero tralala와 라이벌 관계로 표현된다. 상공에서 폭탄을 떨어뜨리는 식으로 공격하여 강한 모습으로 묘사된다. 하지만 퉁 퉁 퉁 퉁 퉁 퉁 퉁 퉁 퉁 사후르에게 지는 모습을 많이 보이는 것으로 보아 근접전엔 약한 듯하다. 국내에선 봄바르디로가 아닌 봄바르디오 혹은 봄바르딜로로 혼동하는 경우가 잦다.",
"리릴리 라릴라: 샌들을 신고 몸이 선인장인 코끼리 캐릭터. 가지고 있는 시계를 이용해 전투에서 시간을 멈추게 할 수 있는 것으로 묘사되는 경우가 많다. 주로 싸움을 기피하는 모습으로 표현되나, 코끼리 특유의 체격과 긴 코를 이용한 기술적 싸움에서는 강자의 면모를 보여준다. 한 vs 영상에서는 퉁 퉁 퉁 퉁 퉁 퉁 퉁 퉁 퉁 사후르와 대결하는데, 시간을 되감아놓고 그 사이에 봄봄비니 구시니를 불러 퉁 퉁 퉁 퉁 퉁 퉁 퉁 퉁 퉁 사후르를 무력화시킨다. 그 밖에도 근육질의 전투병기로 변신해서 육탄전을 벌이기도 하는 등 자체 체급도 상당하다."
]
def find_best_match_keyword_search(query, reference_document_list):
best_score = 0
best_record = None
query_keywords = set(query.lower().split())
for reference_document in reference_document_list:
reference_document_keywords = set(reference_document.lower().split())
common_keywords = query_keywords.intersection(reference_document_keywords)
current_score = len(common_keywords)
if current_score > best_score:
best_score = current_score
best_record = reference_document
return best_score, best_record
reference_document_list에 gpt-4o가 모르는 최신 밈 정보를 넣었다. find_best_match_keyword_search 메소드는 사용자의 쿼리와 reference_document_list를 파라미터로 받아서, 가장 많이 키워드가 겹치는 요소를 반환하는 메소드이다. 실행 결과를 보면 "트랄랄레로 트랄랄라" 키워드가 있는 요소를 반환하고있다.
import openai
from openai import OpenAI
client = OpenAI()
gptmodel="gpt-4o"
def sendQueryWithKeywordSearch(query):
best_reference_document = find_best_match_keyword_search(query, reference_document_list)
prompt = f"다음 질문에 공손하고 친밀하게 응답하세요: {query}\n다음 내용을 참조하세요: {best_reference_document}"
response = client.chat.completions.create(
model=gptmodel,
messages=[
{"role": "user", "content": prompt}
],
)
return response.choices[0].message.content.strip()
이전의 sendQuery 메소드를 sendQueryWithKeywordSearch로 바꾸었다. 사용자의 요청을 그대로 모델에 보내지 않고, 쿼리를 바탕으로 참조할 정보를 찾아서 이를 함께 모델로 보낸다. 그 결과 gpt-4o가 해당 정보를 사용해서 최신 밈에도 올바른 대답을 하는걸 볼 수 있다.
'STUDY > 실전 RAG 기반 생성형 AI 개발' 카테고리의 다른 글
4. 멀티모달 RAG (1) | 2025.06.17 |
---|---|
3. RAG 검색 성능 개선 (1) | 2025.05.30 |
2. 임베딩과 벡터저장소를 활용한 RAG (2) | 2025.05.27 |