콘텐츠로 건너뛰기

RAG를 통한 LLM 정확도 향상(2)

지난번 글에서 RAG의 의미와 간단한 예시에 대해서 알아보았습니다. RAG은 LLM 모델이 관련된 정보를 활용해 추론하게 해 정확도를 높이는 역할을 합니다.

이전 글 보기: RAG를 통한 LLM 정확도 향상(1)

이번 글에서는 간단한 예시에서 좀 더 나아가 벡터 데이터베이스를 활용해 데이터를 임베딩 벡터로 저장하고 활용하는 파이프 라인 구성 중 벡터 데이터베이스 대해서 알아보겠습니다.

RAG 파이프라인

지난번 글에서 RAG은 일반적으로 아래와 같은 구조를 가진다고 설명했습니다.

RAG 구조

사용자의 입력이 들어왔을 때 RAG 서버는 관련된 정보를 찾고 정보를 바탕으로 LLM 모델을 통해 데이터를 생성 후 사용자에게 반환하는 과정을 거칩니다.

지난번 글에서는 관련된 정보를 메모리 상의 배열로 단순히 저장했지만, 이번에는 벡터 데이터 베이스를 활용해 임베딩 벡터의 형태로 데이터를 저장하고 사용하는 RAG 파이프 라인을 구성해 보겠습니다.

Vector Database

벡터 데이터베이스는 정보를 벡터의 형태로 저장하는 데이터베이스 입니다. 기존의 관계형 데이터 베이스와 달리 벡터 데이터베이스는 데이터를 고정된 차원을 가지는 수치의 형태로 저장합니다.

벡터 데이터베이스의 장점

  • 이미지, 비디오와 같은 비정형/반정형 데이터의 저장에 유리
  • 연속된 공간에 수치 형태로 저장되어 효육적인 유사성 검색이 능

벡터 데이터는 데이터 사이의 연관성을 추론하는데 높은 강점을 가집니다.

Chroma DB

벡터 데이터베이스에서 사용할 수 있는 옵션은 몇 가지가 있으나 이번 글에서는 SQLite3 기반의 ChromaDB를 사용해 벡터 데이터베이스를 구현하겠습니다.

# docker-compose
version: "3.8"
services:
  chromadb:
    image: ghcr.io/chroma-core/chroma:0.5.20
    container_name: chromadb
    ports:
      - "8000:8000"
    volumes:
      - C:/chromaDb:/chroma/chroma
    environment:
      IS_PERSISTENT: "TRUE"
      ANONYMIZED_TELEMETRY: "TRUE"
    restart: always


# python package
pip install chroma
  • 도커 이미지가 정상적으로 실행되면 “C:/chromaDb” 폴더에 chroma.splite3 파일이 생성되었을 것 입니다.

이어서 파이썬 코드를 작성해 보겠습니다.

import uuid
import chromadb
from chromadb.utils import embedding_functions
import os
from typing import Dict

class ChromaDbImpl:
	def __init__(self, collection_name: str):
		self._config = {
			"host": os.getenv("CHROMA_HOST"),
			"port": int(os.getenv("CHROMA_PORT"))
		}
		self.client = chromadb.HttpClient(**self._config)
		self.collection = self.client.get_or_create_collection(collection_name)
		self.embedding_function = embedding_functions.DefaultEmbeddingFunction()
  • ChromaDB는 host와 port를 이용해 데이터베이스에 연결합니다.
  • Http 요청을 사용하는 클라이언트를 사용합니다.
  • 임베딩 벡터를 생성하기 위한 방법에는 ChromaDB의 기본 임베딩 함수를 사용하겠습니다.
    DefaultEmbeddingFunction은 실제로 임베딩 값을 생성하지는 않습니다. 벡터 임베딩으로 저장하기 위해서는 임베딩 모델을 사용해야 합니다.

문서 추가

	def add_document(self, documents: list[str]) -> Dict[str, str]:
		embeddings = self.embedding_function(documents)
		ids = [str(uuid.uuid4()) for _ in range(len(documents))]
		metadatas = [{"index": i, "version": 1.0} for i in range(len(documents))]
		self.collection.add(
			documents=documents,
			embeddings=embeddings,
			metadatas=metadatas,
			ids=ids
		)
		
		return {ids[i]: documents[i] for i in range(len(ids))}
  • documents를 임베딩하고 랜덤하게 생성된 uuid를 부여해 collection에 임베딩을 저장합니다.
  • MySQL Workbench에서 db 연결 후 쿼리를 실행했을 때 데이터가 저장된 것을 확인할 수 있습니다.

문서 검색

	def search_docuemnt(self, query: str) -> list[str]:
		# testing
		result = self.collection.query(query_embeddings=self.embedding_function(query), n_results=1)
		return result['ids']
  • 사용자의 입력(query)를 동일하게 임베딩 벡터로 변환 후 유사한 문서를 검색합니다.
  • 벡터 공간 상의 거리에 따라서 쿼리와 유사하다고 판단되는 문서들이 반환됩니다.

ChromaDB와 python을 활용해 벡터 데이터 베이스를 구성해 보았습니다.
벡터 데이터베이스를 활용하면 데이터 간 유사도를 바탕으로 검색이 가능하기 때문에 RAG를 쉽게 구현할 수 있는 장점이 있습니다.

다음 글에서는 코드 통합을 위한 Spring AI를 활용한 RAG 구현에 대해서 알아보겠습니다.

[References]
– https://www.elastic.co/kr/what-is/vector-database
– https://www.ibm.com/kr-ko/topics/vector-database

“RAG를 통한 LLM 정확도 향상(2)”의 1개의 댓글

  1. 핑백: RAG를 통한 LLM정확도 향상(3) – BizSpring BLOG

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다