[LLM] RAG와 Vector Database

2025. 5. 26. 01:31·Data/AI
728x90

Retrieval Augmented Generation(RAG)란?

검색 증강 생성으로 대규모 언어 모델의 출력을 최적화하여 응답을 생성하기 전에 학습 데이터 소스 외부의 신뢰할 수 있는 지식 베이스를 참조하도록 하는 프로세스

 

즉, 내가 가져온 데이터를 제공할테니 이 정보를 아는 것처럼 "답변을 생성"하는 것

 

답변은 LLM이 생성하기 때문에 우리는 데이터를 잘 가져와서 LLM에 전달하면 된다. 

 

그럼, 사용자의 질문과 관련있는 데이터인지 어떻게 파악할까?

Vector

판단을 하는 기준은 Vector를 활용한다. (Vector는 Raw 하나 짜리 행렬이라고 보면 된다.)

Vector는 단어 또는 문장의 유사도를 파악해서 관련성을 측정하는 기술.

Vector를 생성하는 방법

Embedding 모델을 활용해서 vector를 생성

이 모델에서는 문장 내의 비슷한 단어가 자주 붙어있는 것을 학습한다.

  • 왕은 왕자의 아버지다.
  • 여왕은 왕자의 어머니다.

이 경우, "왕자의" 앞에 붙어있는 "왕"과 "여왕"은 유사할 가능성이 높다. 

 Vector Database

Embedding 모델을 활용해 생성된 vector를 저장한다. 

단순히 vector만 저장하면 안된다. metadata도 함께 저장해야한다.

할루시네이션을 방지하기 위해 출처를 함께 표기하는 것이 중요하기 때문! (LLM이 생성하는 답변의 퀄리티 및 신뢰도가 상승)

ex) metadata는 문서의 이름, 페이지 번호 등등

Vector를 대상으로 유사도 검색 실시

사용자의 질문과 가장 비슷한 문서를 가져오는 것!

  1. RAG의 knowledge base로 활용할 문서를 정해서
  2. 문서를 chunk, 나눠서 저장한다. (문서 전체를 활용하면 속도도 느리고, 토큰수 초과로 답변 생성이 안될 수도 있음)
  3. 가져온 문서를 prompt 통해 LLM에 제공하고,
  4. LLM은 prompt 통해 답변 생성한다.

OpenAI API키를 통해 받아본 답변

from dotenv import load_dotenv

#.env 파일 읽어오기
load_dotenv()

# langchain에서 openai를 사용하기 위함
from langchain_openai import ChatOpenAI

llm = ChatOpenAI()

ai_message = llm.invoke("인프런에 어떤 강의가 있나요")

ai_message.content
'인프런에는 다양한 주제의 강의들이 있습니다. 프로그래밍, 디자인, 비즈니스, 언어 등 다양한 분야의 강의들이 제공되며, 실무에 바로 적용할 수 있는 실습 강의부터 이론에 대한 깊은 이해를 돕는 강의까지 다양한 수준의 강의들이 있습니다. 또한, 명문 대학교나 전문가들이 진행하는 강의들이 많이 있어서 신뢰할 만한 강의를 수강할 수 있습니다. 자신에게 필요한 분야나 수준에 맞는 강의를 선택하여 공부를 할 수 있습니다.'

그럼 프로젝트에서는 어떻게 활용해야할까?

1. 일단 한국은행의 경제 용어 정리본 pdf를 읽어온다.

2. 그리고 읽어온 pdf를 잘 나눠야 한다.. 조건을 잘 확인해서 나눠보자. (용어/설명/유사용어)

3. 임베딩을 통해 벡터 데이터베이스에 저장한다.

4. 질문이 있을 때, 벡터 데이터베이스에 유사도 검색을 진행한다.

5. 유사도 검색으로 가져온 문서를 LLM에 질문과 같이 전달한다.

 

from langchain.document_loaders import PyPDFLoader
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import SupabaseVectorStore
from supabase import create_client
from dotenv import load_dotenv
from langchain_core.documents import Document
import os
import re


# 1. 환경변수 불러오기
load_dotenv()
supabase_url = os.getenv("SUPABASE_URL")
supabase_key = os.getenv("SUPABASE_SERVICE_KEY")

# 2. Supabase 클라이언트 생성
supabase_client = create_client(supabase_url, supabase_key)

# 3. PDF 전체 텍스트 불러오기
loader = PyPDFLoader("word.pdf")
pages = loader.load()

# 4. 원하는 페이지 범위 선택 (인덱스는 0부터 시작하므로 17~368)
selected_pages = pages[17:369]

# 5. 페이지들을 하나의 문자열로 합치기
full_text = "\n".join([page.page_content for page in selected_pages])

# 6. 중복된 항목 제거 후 용어 기준으로 항목 분리
 # 페이지 번호 제거
full_text = re.sub(r"^\s*\d+\s*$", "", full_text, flags=re.MULTILINE)

full_text = re.sub(r"\n{2,}", "\n", full_text)

 # 문서 제목 반복 제거
full_text = re.sub(r"경제금융용어\s*700선", "", full_text)

 # 한글 자모 구분자 제거 (ㄱ~ㅎ 단독 줄)
full_text = re.sub(r"^\s*[ㄱ-ㅎ]\s*$", "", full_text, flags=re.MULTILINE)

 # ABC 제거
full_text = re.sub(r"ABC", "", full_text, flags=re.MULTILINE)

# 페이지 바뀔 때 단어 • 라고 기입되는 문장 제거
full_text = re.sub(r"\b\w+\s*[∙•·ㆍ]\s*", "", full_text)

 # 불필요한 빈 줄 정리
full_text = re.sub(r"\n{2,}", "\n", full_text)

entries = re.split(r"\n(?=\w{1,50}\s*[\(]?[A-Z]*\)?\s*\n)", full_text)

# 7. 항목을 structured document로 생성
structured_docs = []

for entry in entries:
    lines = entry.strip().split("\n")
    if not lines:
        continue

    term = lines[0].strip()
    body = "\n".join(lines[1:])
    
    match = re.search(r"연관검색어\s*:\s*(.*)", body)
    if match:
        explanation = body[:match.start()].strip()
        related = match.group(1).strip()
    else:
        explanation = body.strip()
        related = ""

    # 설명/유사용어 텍스트 내의 개행 전처리 → 이전에 처리하게 되면 구조가 망가지게 됨
    def clean_text(text):
        # 단어 중간 개행 제거: 원리금상\n환비율 → 원리금상환비율
        text = re.sub(r"(?<=\w)-?\n(?=\w)", "", text)
        # 문장 끝 개행은 공백으로: ~다.\n → ~다. 
        text = re.sub(r"([.!?])\n", r"\1 ", text)
        # 남은 개행은 공백으로
        text = text.replace("\n", " ")
        return re.sub(r"\s{2,}", " ", text).strip()

    term = clean_text(term)
    explanation = clean_text(explanation)
    related = clean_text(related)

    content = f"용어: {term}\n설명: {explanation}\n유사용어: {related}"
    
    structured_docs.append(
        Document(page_content=content, metadata={"term": term})
    )

structured_docs
Document(metadata={'term': '가계부실위험지수(HDRI)'}, page_content='용어: 가계부실위험지수(HDRI)\n설명: 가구의 소득 흐름은 물론 금융 및 실물 자산까지 종합적으로 고려하여 가계부채의 부실위험을 평가하는 지표로, 가계의 채무상환능력을 소득 측면에서 평가하는 원리금상환비율(DSR; Debt Service Ratio)과 자산 측면에서 평가하는 부채/자산비율(DTA; Debt To Asset Ratio)을 결합하여 산출한 지수이다. 가계부실위험지수는 가구의 DSR과 DTA가 각각 40%, 100%일 때 100의 값을 갖도록 설정되어 있으며, 동 지수가 100을 초과하는 가구를 ‘위험가구’로 분류한다. 위험가구는 소득 및 자산 측면에서 모두 취약한 ‘고위험가구’, 자산 측면에서 취약한 ‘고DTA가구’, 소득 측면에서 취약한 ‘고DSR가구’로 구분할 수 있다. 다만 위험 및 고위험 가구는 가구의 채무상환능력 취약성 정도를 평가하기 위한 것이며 이들 가구가 당장 채무상환 불이행, 즉 임계상황에 직면한 것을 의미하지 않는다 .\n유사용어: 총부채원리금상환비율(DSR)'),
Document(metadata={'term': '가계수지'}, page_content="용어: 가계수지\n설명: 가정에서 일정 기간의 수입(명목소득)과 지출을 비교해서 남았는지 모자랐는지를 표시한 것을 가계수지(household's total income and expenditure)라 한다. 가계수지가 흑자를 냈다면 그 가정은 벌어들인 수입 일부만을 사용했다는 것을 의미하며, 적자를 냈다면 수입 외에 빚을 추가로 얻어 사용한 것이라고 보아야 한다. 우리나라는 통계청에서 가계의 수입과 지출을 조사하여 국민의 소득수준 및 생활실태를 파악하기 위해 표본으로 선정된 가계에 가계부를 나누어 주고 한 달간의 소득과 지출을 기록하도록 한 다음 이를 토대로 가계수지 통계를 작성하여 발표하고 있다. 가계부의 소득항목에는 근로소득・사업소득・재산소득・이전소득 항목이 있고, 비용항목에는 식료품비・주거비・ 수도광열비・보건의료비・교육비 항목이 있다 .\n유사용어: 경상수지, 재정수지"),

이런식으로 전처리를 진행하면 벡터 DB에 위와 같은 형식으로 저장될 수 있다!!!!!!!!!!

728x90

'Data > AI' 카테고리의 다른 글

완벽한 스미싱 분류 모델을 향해서 (2)  (1) 2025.05.28
완벽한 스미싱 분류 모델을 향해서 (1)  (4) 2025.05.27
[NLP] 1. 텍스트 마이닝 기초  (0) 2025.05.01
[LLM] Streamlit + Ollama를 통해 LLM 기반 챗봇 웹 앱 구현  (0) 2025.04.14
[LLM] Ollama + gemma2로 간단한 챗봇 만들기  (0) 2025.04.14
'Data/AI' 카테고리의 다른 글
  • 완벽한 스미싱 분류 모델을 향해서 (2)
  • 완벽한 스미싱 분류 모델을 향해서 (1)
  • [NLP] 1. 텍스트 마이닝 기초
  • [LLM] Streamlit + Ollama를 통해 LLM 기반 챗봇 웹 앱 구현
DROPDEW
DROPDEW
💻 Developer | 기록하지 않으면 존재하지 않는다
  • DROPDEW
    제 2장 1막
    DROPDEW
  • 전체
    오늘
    어제
    • Dev (434) N
      • App·Android (1)
      • BE (49) N
        • HTTP 웹 기본 지식 (8)
        • 스프링 입문 - 코드로 배우는 스프링 부트, 웹 .. (12)
        • 스프링부트와 JPA 활용 (11)
        • 스프링부트 시큐리티 & JWT (0)
        • PHP (11) N
      • FE·Client (23)
        • HTML (1)
        • React (19)
        • Unity (1)
      • Data (27) N
        • AI (7)
        • Bigdata (6)
        • Database (1)
        • 빅데이터분석기사 (12) N
      • Infra (0)
      • Activity (2)
        • Education (0)
        • Intern (0)
        • 리모트 인턴십 6기 (2)
        • 구름톤 유니브 4기 (0)
        • SW교육기부단 15기 (0)
        • SK AI Dream Camp (0)
      • CS (8)
      • 취준 (13)
        • 자격증 (4)
        • 인적성·NCS (6)
        • 코테·필기·면접 후기 (3)
      • 코테 (270)
        • Algorithm (222)
        • SQL (35)
        • 정리 (13)
      • 인사이트 (27)
        • 회고 (0)
        • 금융경제뉴스 (7)
        • 금융용어·지식 (2)
        • 북마크 (7)
  • 블로그 메뉴

    • 홈
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    최단경로
    이분탐색
    너비우선탐색
    매개변수탐색
    다이나믹프로그래밍
    티스토리챌린지
    그래프탐색
    누적합
    문자열
    자료구조
    수학
    구현
    그래프이론
    오블완
    정렬
    브루트포스 알고리즘
    그리디알고리즘
    투포인터
    백준
    시뮬레이션
  • 최근 댓글

  • 최근 글

  • 250x250
  • hELLO· Designed By정상우.v4.10.3
DROPDEW
[LLM] RAG와 Vector Database
상단으로

티스토리툴바