2024년 6월 2일 일요일

대형언어모델 검색증강생성의 핵심기술, 벡터 데이터베이스 Chroma 분석하기

이 글은 대형언어모델(LLM. Large Language Model)의 검색증강생성(RAG. Retrieval-Augmented Generation) 구현 시 핵심기술인 임베딩 벡터 데이터베이스(Embedding vector database)로 유명한 Chroma 핵심 구조를 간략히 분석한다. RAG는 생성AI의 환각현상을 줄여주어, 전문가적인 정보를 생성하는 데 도움을 준다. 

Chroma의 동작 방식을 이해하여, LLM 기술 개발 시 이해도와 응용력를 높일 수 있다. 

이 글에 관련된 용어와 상세 개념은 다음 링크를 참고한다. 
개요
Chroma(크로마)는 AI 지원 오픈소스 벡터 베이터베이스이다. RAG 처리할 때 필수적으로 사용되는 데이터베이스 중 하나이다. 크로마를 이용해 LLM 기반 다양한 앱(지식 서비스 등)을 개발할 수 있다. 
크로마는 임베딩 벡터를 메타데이터와 함께 저장하고, 질의를 통해 해당 임베딩 도큐먼트를 검색할 수 있다. 크로마는 서버로써 동작될 수 있다(데모). 

설치 및 사용
크로마 설치는 다음과 같다. 
pip install chromadb

벡터 데이터베이스에 저장되는 단위는 다음과 같다. 
collection = client.create_collectoin(name='test', embedding_function=emb_fn)

collection.add(
    embeddings=[
        [1.1, 2.3, 3.2],
        [4.5, 6.9, 4.4],
        [1.1, 2.3, 3.2]
    ],
    metadatas=[
        {"uri": "img1.png", "style": "style1"},
        {"uri": "img2.png", "style": "style2"},
        {"uri": "img3.png", "style": "style1"}
    ],
    documents=["doc1", "doc2", "doc3"],
    ids=["id1", "id2", "id3"],
)

보는 것 같이, 벡터 좌표계에 위치할 임베딩 벡터, 벡터에 매달아 놓을 메타데이터와 도큐먼트, ID를 하나의 컬랙션 단위로 저장한다. 이를 통해, 벡터 간 유사도, 거리 등을 계산해, 원하는 도큐먼트, 메타데이터 등을 얻을 수 있다. 이때 임베딩 벡터는 미리 학습된 임베딩 모델을 사용할 수 있다. 

질의해 원하는 벡터를 얻으려면, 벡터 공간에서 거리계산이 필수적이다. 이때 사용하는 함수는 다음과 같다. 

컬렉션에 벡터 추가와 질의는 다음과 같다. 
collection.add(
    documents=["doc1", "doc2", "doc3", ...],
    embeddings=[[1.1, 2.3, 3.2], [4.5, 6.9, 4.4], [1.1, 2.3, 3.2], ...],
    metadatas=[{"chapter": "3", "verse": "16"}, {"chapter": "3", "verse": "5"}, {"chapter": "29", "verse": "11"}, ...],
    ids=["id1", "id2", "id3", ...]
)

collection.query(
    query_texts=["doc10", "thus spake zarathustra", ...],
    n_results=10,
    where={"metadata_field": "is_equal_to_this"},
    where_document={"$contains":"search_string"}
)

여기서, where의 metadata_field를 이용해 다음과 같은 조건 비교 연산이 가능하다.
  $eq, $ne, $gt, $gte, $lt, $lte
 
그리고, 논리 연산자인 $and, $or를 지원한다.

크로마는 향후, 워크플로우, 가시화, 질의 계획, 분석 기능을 준비하고 있다. 

구조 분석
크로마 구조에서 핵심은 임베딩 벡터 모델과 RAG에서 사용되는 query이다. 벡터 데이터를 저장하고 관리하는 방법은 일반적으로 많이 알려져 있는 방식이나, query는 RAG에 특화되도록 개발된 방법을 사용한다.  
Chroma 구조

위에서 SegmentAPI의 구조를 살펴보면, 베이스 클래스가 Collection인 것을 알 수 있다. 이 클래스의 query가 호출되면, 입력된 데이터와 가장 유사한 집합이 리턴된다. 구현은 VectorReader를 통해 이뤄진다. 

다음은 쿼리의 핵심 코드이다. 
coll = _get_collection(collection_id)                   // 컬랙션 획득
query = t.VectorQuery(vectors, k, allowed_ids)  
results = vector_reader.query_vectors(query)       // 저장된 데이터셋에 쿼리 질의
return QueryResult(ids, distance, metadatas, embeddings, documents)   // 결과 리턴

두 벡터 간 유사도 거리 계산을 통해 가까운 벡터 데이터를 리턴하는 알고리즘이 query_vectors 에 구현되어 있다. 

예를 들어 LocalHnswSegment를 확인해보면, 다음 코드를 발견할 수 있다. 
    def query_vectors(
        self, query: VectorQuery
    ) 
        query_vectors = query["vectors"]
        with ReadRWLock(self._lock):
            result_labels, distances = self._index.knn_query(
                query_vectors, k=k, filter=filter_function if ids else None
            )

            all_results: List[List[VectorQueryResult]] = []
            for result_i in range(len(result_labels)):
                results: List[VectorQueryResult] = []
                for label, distance in zip(
                    result_labels[result_i], distances[result_i]
      
이 부분은 query에 담긴 vectors 값을 얻어 KNN 기반 거리 비교로 벡터를 계산하는 방법을 보여준다. 이는 이미 임베딩 벡터 처리할 때, 개념적으로 가까운 데이터는 벡터 값이 가까워지도록 학습된 임베딩 모델을 사용했기 때문에 가능한 것이다. 

다음은 Documents에 포함된 데이터의 임베딩 공간에서 거리를 보여준다. 
마무리
앞서 분석한 바와 같이, 크로마의 핵심은 동일한 임베딩 모델로 계산된 임베딩 벡터들을 저장하고, 텍스트(혹은 이미지) 질의 시 유사도 거리함수를 통해 관련된 임베딩 벡터를 빠르게 검색하는 기능에 있다. 검색된 임베딩 벡터 데이터는 LLM 모델에 사전 프롬프트로 주입됨으로써 환각현상을 막고, 전문적인 답변을 생성한다.

크로마는 해커 기질과 철학이 섞여 있는 Jeff HuberAnton Troynikov가 공동 개발하였다. 이들은 이전에 3D computer vision, 로보틱스 분야에 일했던 경험이 있다. 현재, 크로마는 1,800만달러를 펀딩받았고, 다음 라운드를 준비하고 있다. 
공동 개발자 Jeff Huber, Anton Troynikov

부록: 벡터 데이터베이스 종류 및 장단점 참고
솔루션 (Solution)유형핵심 알고리즘 / 구조장점 (Pros)단점 (Cons)가성비 (Cost-Effectiveness)주요 사용 사례
Cassandra분산 NoSQL (Key-Value)LSM-Tree, 컨시스턴트 해싱압도적 쓰기 성능, 수평 확장, 고가용성 (무중단)복잡한 쿼리/Join 불가, 자체 벡터 검색 기능 없음오픈소스. 대규모 운영 시 전문 인력 및 인프라 비용 고려 필요대규모 데이터의 원본 저장소 (Source of Truth)
Elasticsearch분산 검색 엔진역인덱스 (BM25), 벡터 인덱스 (HNSW)강력한 하이브리드 검색 (키워드+벡터), 성숙한 생태계리소스 사용량 많음, 클러스터 관리가 다소 복잡오픈소스. 관리형 서비스는 편리하나 비용 발생. TCO(총소유비용)는 보통로그 분석, 통합 검색, 텍스트 중심의 RAG
Pinecone 벡터 전문 DB (SaaS)HNSW 등 자체 최적화 ANN압도적인 사용 편의성, 인프라 관리 불필요, 빠른 시작벤더 종속성, 대규모 사용 시 비용 증가, 비오픈소스운영 인력 비용 절감 효과가 커 초기/중규모 팀에게 가성비 높음빠른 프로토타이핑, 인프라 관리 부담을 줄이고 싶은 팀
Milvus 벡터 전문 DB (오픈소스)HNSW, IVF 등 다양한 ANN대규모 확장성, 성숙한 오픈소스(CNCF), 유연성직접 설치/운영 복잡성 높음오픈소스. 대규모 운영 시 전문 인력 및 인프라 비용이 핵심수십억 단위 벡터 검색, 프로덕션급 대규모 AI 시스템
Qdrant / Weaviate벡터 전문 DB (오픈소스)HNSWQdrant(빠른 필터링), Weaviate(GraphQL API)Milvus 대비 생태계 규모 작음Milvus와 유사. 전문 인력 운영 비용 고려특정 기능(필터링, GraphQL)이 중요할 때
PostgreSQL 관계형 DB + 확장 기능pgvector (IVF, HNSW)기존 DB/인력 활용, 관계형 데이터와 벡터 동시 관리전문 벡터 DB 대비 대규모 성능 한계최고의 가성비. 기존 인프라 활용으로 추가 비용 최소화프로토타이핑, 기존 시스템에 벡터 검색 기능 '추가'
Redis 인메모리 DB + 확장 기능RediSearch (HNSW)매우 빠른 속도 (인메모리)데이터 용량이 메모리에 제약, 높은 메모리 비용메모리 비용이 비싸 대규모 데이터에는 비효율적초저지연 실시간 추천, 세션 기반 RAG
ChromaDB 벡터 전문 DB (오픈소스)HNSW매우 쉬운 사용법, LangChain/LlamaIndex와 완벽 통합대규모 확장성 한계 (프로덕션급은 Milvus 등 고려)오픈소스. 개발/프로토타이핑 시간 비용 절감 효과가 매우 큼RAG 애플리케이션 개발 및 프로토타이핑의 사실상 표준
FAISS 알고리즘 라이브러리HNSW, IVF 등 알고리즘 집합체최고 성능과 유연성, GPU 지원DB가 아님, 모든 주변 시스템을 직접 개발해야 함라이브러리는 무료지만, 이를 활용한 시스템 개발 비용은 가장 높음다른 DB의 검색 엔진 개발, 자체 검색 시스템 구축

레퍼런스

댓글 없음:

댓글 쓰기