RAG 애플리케이션을 구축하는 개발자를 위한 실용적인 팁과 요령
벡터 검색은 effortless하지 않습니다!
벡터 검색은 벡터 유사도 검색 또는 최근접 이웃 검색이라고도 하며, RAG 애플리케이션과 정보 검색 시스템의 데이터 검색에서 주어진 쿼리 벡터와 유사하거나 밀접하게 관련된 항목 또는 데이터 포인트를 찾는 데 사용되는 기술입니다. 대규모 데이터셋을 처리할 때 간단하다고 홍보되는 경우가 많습니다. 일반적인 인식은 데이터를 임베딩 모델에 넣어 벡터 임베딩을 생성한 다음, 이러한 벡터를 벡터 데이터베이스로 전송해 원하는 결과를 검색하면 된다는 것입니다.
벡터 검색을 수행하는 방법
많은 벡터 데이터베이스 제공업체는 "쉬운", "사용자 친화적인", "간단한"과 같은 표현으로 자사의 기능을 홍보합니다. 이들은 머신러닝, AI, ETL 프로세스 또는 세부적인 시스템 튜닝의 복잡성을 피해 단 몇 줄의 코드만으로도 상당한 성과를 얻을 수 있다고 주장합니다.
그리고 그 말은 맞습니다. 벡터 검색은 NumPy와 같은 기본 수치 라이브러리를 사용하는 것만큼 effortless합니다. 이 개념을 보여주기 위해 저는 k-최근접 이웃 알고리즘(KNN)을 사용해 단 10줄의 Python 코드로 짧은 데모를 작성했습니다. 이 간단한 접근 방식은 최대 천 개 또는 만 개 정도의 벡터를 가진 소규모 애플리케이션에서는 효과적이고 정확합니다.
import numpy as np
# Function to calculate Euclidean distance
def euclidean_distance(a, b):
return np.linalg.norm(a - b)
# Function to perform KNN
def knn(data, target, k):
# Calculate distances between target and all points in data
distances = [euclidean_distance(d, target) for d in data]
# Combine distances with data indices
distances = np.array(list(zip(distances, range(len(data)))))
# Sort by distance
sorted_distances = distances[distances[:, 0].argsort()]
# Get the top k closest indices
closest_k_indices = sorted_distances[:k, 1].astype(int)
# Return the top k closest vectors
return data[closest_k_indices]
하지만 데이터셋이 백만 개 또는 천만 개가 넘는 적당한 수준으로 커지면 이 접근 방식은 작동하지 않습니다. 그 이유는 실제 애플리케이션은 사용자와 상호작용해야 하고, 가용성을 갖춰야 하며, 항상 훨씬 더 복잡하기 때문입니다. 확장 가능한 실제 애플리케이션을 구축하려면 코딩을 넘어 검색 품질, 확장성, 가용성, 멀티테넌시, 비용, 보안 등 다양한 요소를 철저히 고려해야 합니다!
그러니 솔직해집시다. "내 컴퓨터에서는 작동하는데?"라는 격언을 기억하시나요? 벡터 검색도 다르지 않습니다. 프로토타입은 항상 작동하지만, 프로덕션의 벡터 검색은 종종 복잡합니다. 그렇다면 프로덕션에서 벡터 검색 기반 애플리케이션을 구축하기 위한 모범 사례는 무엇일까요?
이러한 과제를 헤쳐 나가는 데 도움이 되도록, Milvus를 사용해 RAG 애플리케이션 프로덕션 환경에 벡터 데이터베이스를 효과적으로 배포하기 위한 세 가지 필수 팁을 공유하겠습니다.
효과적인 스키마 설계: 성능과 확장성을 최적화하는 스키마를 만들기 위해 데이터 구조와 쿼리 방식을 신중하게 고려하세요.
확장성 계획: 향후 성장을 예상하고 증가하는 데이터 볼륨과 사용자 트래픽을 수용할 수 있도록 아키텍처를 설계하세요.
최적의 인덱스 선택 및 성능 미세 조정: 사용 사례에 가장 적합한 인덱싱 방법을 선택하고 성능 설정을 지속적으로 모니터링하고 조정하세요.
이러한 모범 사례를 따르면 견고하고 효율적인 벡터 검색 기반 애플리케이션을 구축하는 데 큰 진전을 이룰 수 있습니다. 아래 댓글에서 여러분의 경험과 도움이 되었던 추가 팁을 공유해 주세요!
효과적인 스키마 전략 설계하기
스키마는 테이블, 필드, 관계, 데이터 유형을 포함하여 데이터베이스의 구조를 정의합니다. 이 체계적인 프레임워크는 데이터가 일관되고 예측 가능하게 저장되도록 보장하여 관리, 쿼리, 유지 관리를 단순화합니다. 적절한 스키마를 선택하는 것은 벡터와 메타데이터 및 스칼라 데이터를 포함한 다양한 구조화된 데이터 유형을 처리하는 Milvus와 같은 벡터 데이터베이스에서 특히 중요합니다. 이러한 데이터는 필터링된 검색을 강화하고 전반적인 검색 결과를 개선할 수 있습니다. 이 섹션에서는 가장 효과적인 스키마 전략을 선택할 때 고려해야 할 핵심 요소를 살펴봅니다.
동적 스키마 vs. 고정 스키마
데이터베이스 시스템에서 동적 스키마와 고정 스키마는 데이터를 구조화하는 두 가지 주요 접근 방식을 나타냅니다. 동적 스키마는 유연성을 제공하여 광범위한 데이터 정렬이나 ETL 프로세스 없이도 데이터 삽입 및 검색을 단순화합니다. 이 접근 방식은 데이터 구조를 빠르게 변경해야 하는 애플리케이션에 적합합니다. 반면, 개발자들은 고정 스키마의 컴팩트한 저장 형식 덕분에 성능 효율성과 메모리 절약 측면에서 이를 높이 평가합니다.
하이브리드 스키마 접근 방식은 효율적인 벡터 데이터베이스 애플리케이션을 개발하는 개발자에게 유익할 수 있습니다. 이 방법은 필수 데이터 경로에 대한 고정 스키마의 견고함과 다양한 사용 사례를 수용할 수 있는 동적 스키마의 유연성을 결합합니다. 예를 들어 추천 시스템에서는 제품 이름과 제품 ID 같은 요소의 중요도가 맥락에 따라 달라질 수 있습니다. 하이브리드 스키마를 적용함으로써 개발자는 필요한 곳에서는 최적의 성능을 보장하는 동시에 변화하는 데이터 요구 사항에 적응할 수 있는 능력을 유지할 수 있습니다.
기본 키 및 파티션 키 설정
기본 키와 파티션 키는 벡터 데이터베이스에서 두 가지 중요한 개념입니다. Milvus 벡터 데이터베이스를 예로 들어, 이러한 키가 벡터 데이터베이스 내에서 어떻게 작동하는지 더 깊이 살펴볼 수 있습니다.
Milvus 아키텍처는 데이터를 여러 구성 요소로 분할합니다. 고정 필드와 동적 필드(총칭하여 페이로드라고 함), 필수 벡터 필드, 그리고 기존 관계형 데이터베이스에서 볼 수 있는 것과 유사한 타임스탬프 및 범용 고유 식별자(UUID) 같은 시스템 필드가 있습니다.
기본 키: Milvus에서 기본 키는 종종 고유 식별자 역할을 하며, RAG 사용 사례에서는 청크 ID에 적용될 수 있습니다. 이 키는 자주 액세스되며 자동 생성되도록 구성할 수 있습니다. 데이터베이스 내에서 특정 데이터 항목을 빠르게 찾고 검색하는 데 역할을 합니다.
파티션 키: Milvus에서 컬렉션을 생성할 때 파티션 키를 지정할 수 있습니다. 이 키를 통해 Milvus는 키 값에 따라 데이터 엔터티를 서로 다른 파티션에 저장하여 데이터를 관리 가능한 세그먼트로 효과적으로 구성할 수 있습니다. 파티션 키를 생각하는 간단한 방법은 필터링하려는 데이터 세트가 있는 경우 파티션 키 사용을 고려하는 것입니다. 예를 들어, 멀티테넌트 상황에서는 데이터 격리와 효율적인 분산이 필요하므로 이를 별도의 파티션에 저장하면 이를 달성하는 데 도움이 될 수 있습니다. 파티션 키는 확장성에도 유용한데, 해싱을 통해 데이터를 샤드로 파티셔닝하면 데이터베이스가 대규모 사용자 기반과 멀티테넌시를 더 효율적으로 관리할 수 있기 때문입니다.
기본 키와 파티션 키는 모두 벡터 데이터베이스의 구조적 무결성과 운영 효율성을 유지하는 데 기본적이며, 방대한 데이터 세트를 처리하고 신속한 데이터 액세스 및 검색을 보장하는 데 필수적입니다.
벡터 임베딩 유형 선택
RAG 애플리케이션을 위한 벡터 임베딩을 선택할 때는 벡터 생성을 위한 적절한 ML 모델을 선택하고 또한 사용 가능한 다양한 임베딩 유형, 즉 밀집, 희소, 이진 임베딩을 이해하는 것이 필수입니다.
벡터 임베딩의 세 가지 인기 범주
밀집 임베딩은 의미론적 유사도 검색을 위한 벡터 데이터베이스 애플리케이션에서 가장 일반적으로 사용되는 유형입니다. 다양한 데이터 유형 전반에 걸친 견고성과 일반 적용 가능성으로 잘 알려져 있습니다. 인기 있는 밀집 임베딩 모델에는 OpenAI, BGE, Cohere가 있습니다.
희소 임베딩은 도메인 외 데이터 검색에서의 효율성으로 인해 인기를 얻고 있습니다. Splade 및 BGE M3와 같은 모델의 최근 발전은 이기종 검색에서의 유용성을 향상시켜, 다양한 애플리케이션에 적합한 다재다능한 선택지가 되게 했습니다.
바이너리 임베딩은 바이너리 형식(0과 1)으로 특징지어지며, 메모리 효율적으로 설계되어 단백질 시퀀싱과 같은 특수 사용 사례에 이상적입니다. Meta ESM-2와 같은 모델은 일반적으로 이러한 임베딩을 생성하는 데 사용되어, 특정 검색 요구에 맞춘 솔루션을 제공합니다.
RAG 애플리케이션의 검색 결과 정확성을 보장하려면 밀집 임베딩만 사용하는 것 이상이 필요합니다. 따라서 다양한 벡터 임베딩 유형을 효율적이고 효과적으로 검색하기 위해 서로 다른 인덱싱 알고리즘을 지원하는 솔루션을 찾아야 합니다. Milvus는 밀집, 희소, 바이너리, 나아가 희소와 밀집의 하이브리드 임베딩을 관리하기 위한 다양한 인덱스를 지원하여, 다양한 데이터 차원 전반에서 효율적인 검색을 가능하게 하고 벡터 데이터베이스 애플리케이션에서 최적의 성능을 보장합니다.
스키마 설계: 실용적인 예시
방금 검토한 이러한 요소들을 함께 적용하여 검색 결과의 정확도를 높일 스키마 아키텍처를 효과적으로 설계하는 데 도움을 받아보겠습니다:
| 필드 이름 | 유형 | 설명 | 예시 값 |
|---|---|---|---|
| chunkID | Int64 | 기본 키, 문서의 서로 다른 부분을 고유하게 식별 | 123456789 |
| userID | Int64 | 파티션 키, 검색이 단일 userID 내에서 이루어지도록 userID를 기준으로 데이터 파티셔닝 | 987654321 |
| docID | Int64 | 문서의 고유 식별자, 동일 문서의 서로 다른 청크를 연결하는 데 사용 | 555666777 |
| chunkData | varchar | 문서의 일부로, 수백 바이트의 텍스트를 포함 | "This is a part of the document..." |
| dynamicParams | JSON | 문서의 동적 매개변수(예: 이름, 소스 URL 등)를 저장 | {"name": "Example Document", "source": "example.com"} |
| sparseVector | 특정 형식 | 희소 벡터를 나타내는 데이터. 특정 형식은 희소성을 표현하기 위해 특정 위치에만 0이 아닌 값을 가집니다. | [0.1, 0, 0, 0.8, 0.4] |
| denseVector | 특정 형식 | 밀집 벡터를 나타내는 데이터. 특정 형식은 각 차원에 값을 가진 고정된 수의 차원을 가집니다. | [0.2, 0.3, 0.4, 0.1] |
일반적인 Retrieval Augmented Generation(RAG) 애플리케이션을 위한 스키마 데모
이 스키마를 살펴보면, 기본 키와 벡터 필드를 넘어서는 추가 필드의 존재에 주목하는 것이 중요합니다. 이러한 추가 필드는 벡터 데이터베이스를 구축하고 활용하는 데 역할을 합니다. 자세히 살펴보겠습니다:
기본 사항: 여기에는 데이터베이스의 모든 항목에 필요한 기본 키(chunkID)와 임베딩(denseVector)이 포함됩니다.
멀티테넌시 지원: userID를 추가하여 솔루션의 테넌트별로 데이터를 파티셔닝하는 필드를 추가했습니다. 이 추가는 사용자별로 데이터 접근을 분리하고 관리하는 데 도움이 되어 보안과 개인화를 강화합니다.
검색 결과 개선: 이러한 개선을 돕기 위해 다음을 포함한 몇 가지 다른 필드를 추가했습니다:
docID: 이 필드는 청크의 출처를 나타내며 Milvus의 그룹화 검색 기능을 활용하는 데 사용할 수 있습니다. 예제에서는 문서를 청크로 분할하고 대표 벡터 임베딩을 denseVector 필드에 저장했으며, 이 필드(docID)에는 관련 문서 정보를 저장합니다. search() 작업에group_by_field인수를 포함하여 문서 ID별로 결과를 그룹화하면 유사한 구절이나 청크 대신 관련 문서를 찾을 수 있습니다. 이를 통해 동일한 문서의 개별 청크가 아니라 관련 문서를 반환하는 데 도움이 됩니다.dynamicParams: 이 필드는 필터링할 수 있어 필요에 맞는 데이터를 관리하고 검색할 수 있습니다. 문서 이름, 원본 URL 등과 같은 메타데이터의 보고입니다. 하나의 필드에 여러 키-값 쌍을 저장하도록 필드 json을 설정했습니다.sparseVector: 이 필드는 청크의 희소 임베딩을 보유하며, 이를 통해 ANN 검색을 수행하고 희소 벡터와 관련된 스칼라 값을 기반으로 결과를 검색할 수 있습니다.
이 다이어그램은 별도의 쿼리에서 결과를 수집한 다음 이를 재순위화하여 검색 결과를 개선할 수 있음을 보여줍니다.
이러한 추가 필드를 포함하는 스키마를 설계하면 RAG 애플리케이션 요구 사항에 맞는 더욱 견고하고 유연한 벡터 데이터베이스를 만들 수 있습니다. 이를 통해 전통적인 데이터 관리 및 검색 기법을 통합하면서 벡터 검색의 강점을 활용할 수 있습니다.
확장성을 위한 계획
MVP RAG 애플리케이션에서 성공을 거두었다면 이제 프로덕션 배포를 준비할 때입니다. 여기에는 향후 성장을 예측하고 증가하는 데이터 볼륨과 사용자 트래픽을 수용할 수 있도록 아키텍처를 설계하는 것이 포함됩니다.
애플리케이션이 효과적으로 확장될 수 있도록 하려면, 벡터 데이터베이스의 확장성이 전통적인 관계형 데이터베이스와 비교해 고유한 과제를 가진다는 점을 인식해야 합니다. 데이터가 크고 중앙 집중화된 인덱스에 저장되기 때문입니다. 이러한 구성은 두 가지 주요 문제로 이어질 수 있습니다. 느린 인덱스 속도와 빈번한 업데이트로 인한 인덱스 품질 저하이며, 이는 결과적으로 검색 품질을 낮출 수 있습니다.
Milvus의 샤딩 전략
Milvus는 전체 데이터셋을 관리 가능한 세그먼트로 나누어 이러한 과제를 처리할 수 있습니다. 세그먼트가 불안정해지면 지연 업데이트를 수행하거나 세그먼트를 압축하여 일관된 검색 품질을 유지할 수 있습니다. 이러한 세분화는 효과적인 로드 밸런싱을 촉진하여 모든 처리 코어에 쿼리를 고르게 분산할 수 있게 합니다.
멀티테넌시에 파티션을 사용하는 것도 검색이 파티션 관련 데이터로 제한되기 때문에 확장성과 성능에 도움이 될 수 있습니다. 이 접근 방식은 데이터를 효과적으로 구성하고 적절한 사용자에게만 가시성을 제한함으로써 보안과 개인정보 보호를 강화합니다. 또한 Milvus는 단일 컬렉션에서 최대 100억 개의 데이터 포인트를 효율적으로 관리할 수 있습니다. 엄청난 양입니다!
테넌트가 10,000명 미만인 멀티테넌트 애플리케이션의 경우, 컬렉션별로 데이터를 관리하면 더 큰 데이터 제어력을 제공합니다. 그러나 파티션 키는 수백만 명의 사용자를 가진 서비스에서 데이터를 동적으로 세분화함으로써 무제한 테넌트를 효과적으로 지원할 수 있습니다.
Milvus는 대규모 쿼리 볼륨을 쉽게 처리하도록 설계된 분산 시스템입니다. 그리고 가장 좋은 점은 무엇일까요? 단순히 노드를 더 추가하는 것만으로도 성능을 크게 향상시켜 애플리케이션에 새로운 가능성의 세계를 열 수 있습니다. 처음에는 광범위한 리소스가 필요하지 않은 소규모 데이터셋의 경우, 메모리 예약을 확장하는 것—일반적으로 현재 할당량의 두세 배—만으로도 초당 쿼리 수(QPS)를 효과적으로 두 배로 늘릴 수 있습니다. 이 확장 가능한 프레임워크는 데이터가 증가함에 따라 데이터베이스 기능도 함께 성장할 수 있도록 보장하여 전반적으로 효율적이고 안정적인 성능을 보장합니다.
인덱스 선택, 평가 및 튜닝
프로토타입 단계에서는 더 빠른 처리와 쉬운 개발을 위해 모든 데이터를 메모리에 로드하는 것이 일반적입니다. 그러나 프로덕션으로 이동하고 데이터가 증가하면 모든 것을 메모리에 저장하는 것이 불가능해집니다. 그 이유는 다음과 같습니다:
메모리는 디스크 스토리지에 비해 제한적이고 비쌉니다.
대규모 데이터셋은 사용 가능한 메모리 용량을 초과할 수 있습니다.
모든 데이터를 메모리에 로드하면 시작 시간과 리소스 소비가 크게 증가할 수 있습니다.
프로덕션에서 더 큰 데이터셋을 효율적으로 처리하려면 적절한 인덱싱 전략을 선택해야 합니다. 올바른 인덱스는 쿼리 속도, 스토리지 요구 사항, 지연 시간 측면에서 RAG 애플리케이션의 성능을 최적화할 수 있습니다.
Milvus가 지원하는 인덱스
이 다이어그램은 세 가지 핵심 지표를 기반으로 다양한 인덱스 간의 차이를 시각화하는 데 도움이 됩니다:
초당 쿼리 수(QPS): 이는 인덱스가 초당 처리할 수 있는 검색 쿼리 수를 측정하며, 처리량과 효율성을 반영합니다.
스토리지: 이는 인덱스를 저장하는 데 필요한 디스크 공간의 양을 나타내며, 인프라 비용과 확장성에 영향을 줄 수 있습니다.
지연 시간은 단일 쿼리를 처리하고 결과를 반환하는 데 걸리는 시간을 의미하며, 애플리케이션의 응답성에 영향을 줍니다.
서로 다른 인덱스에서 이러한 지표를 비교함으로써 특정 사용 사례와 성능 요구 사항에 가장 적합한 인덱스를 결정할 수 있습니다.
Milvus에서는 다양한 스토리지 및 성능 요구에 맞춘 유연한 인덱스 선택 프레임워크를 제공합니다:
GPU Index는 고성능 환경을 위한 최고의 옵션으로, 빠른 데이터 처리와 검색을 지원합니다.
Memory Index는 성능과 용량의 균형을 맞추는 중간 계층 옵션으로, 안정적인 초당 쿼리 수(QPS)와 평균 지연 시간이 약 10밀리초인 상태에서 테라바이트 규모의 스토리지까지 확장할 수 있는 능력을 제공합니다.
Disk Index는 약 100밀리초의 합리적인 지연 시간으로 수십 테라바이트를 관리할 수 있어, 더 크고 시간 민감도가 낮은 데이터셋에 적합합니다. Milvus는 디스크 인덱스를 지원하는 유일한 오픈 소스 벡터 데이터베이스입니다.
Swap Index는 S3 또는 기타 객체 스토리지 솔루션과 메모리 간의 데이터 스와핑을 지원합니다. 이 접근 방식은 지연 시간을 효과적으로 관리하면서 비용을 약 10분의 1로 크게 줄입니다. 일반적인 액세스 시간은 약 100밀리초이지만, 덜 자주 액세스되는("더 차가운") 데이터의 경우 몇 초까지 늘어날 수 있어 오프라인 사용 사례와 비용 민감형 애플리케이션에 적합합니다.
인덱스를 선택한 후에는 빌드 시간, 정확도, 성능, 리소스 소비를 기준으로 성능을 평가할 수 있습니다. 예를 들어 최적화되지 않은 인덱스는 별도의 구축 없이 초당 20개의 쿼리만 지원할 수 있습니다. 인덱스를 최적화하면 QPS가 크게 향상될 수 있으며, 튜닝의 각 반복마다 잠재적으로 10배씩 증가할 수 있지만, 그 대가로 뷰 시간이 증가할 수 있습니다.
인덱스를 효과적으로 선택하고 세부 조정하려면 다음을 수행해야 합니다:
특정 요구 사항에 따라 적절한 인덱스 유형을 선택합니다.
성능을 최적화하기 위해 인덱스 매개변수를 조정합니다.
인덱스가 예상대로 작동하는지 확인하기 위해 사용 사례를 벤치마크합니다.
성능을 더욱 향상시키기 위해 검색 매개변수를 조정합니다.
최적화 과정에 확신이 없다면 VectorDBBench와 같은 벤치마킹 도구의 힘을 활용하세요. Zilliz가 개발하고 오픈 소스로 공개한 이 도구는 모든 주요 벡터 데이터베이스를 평가할 수 있습니다. 이를 통해 포괄적인 실험을 수행하고 최적의 성능을 위해 시스템을 세부 조정할 수 있습니다.
빠른 참조를 위해, GPU 인덱스 카탈로그에 있는 각 인덱스의 성능을 개략적으로 보여주는 유용한 치트 시트를 준비했습니다. 이 자료는 애플리케이션의 요구 사항에 가장 적합한 인덱스를 찾도록 안내하여 성능과 비용 효율성을 최적화하는 데 도움이 될 수 있습니다.
인덱스 치트 시트
요약
이 종합 가이드에서는 벡터 데이터베이스의 다면적인 세계와 그 효율성 및 확장성을 극대화하는 데 필요한 실질적인 접근 방식을 살펴보았습니다. 스키마 설계의 기본부터 대규모 데이터 세트 관리의 복잡성까지, 벡터 데이터베이스를 다룰 때 개발자가 알아야 할 핵심 전략과 모범 사례를 다루었습니다.
벡터 데이터베이스가 발전함에 따라, 이러한 측면에 대한 정보를 지속적으로 파악하면 개발자는 더 견고하고 효율적이며 확장 가능한 애플리케이션을 구축할 수 있습니다. 숙련된 데이터베이스 전문가이든 이 분야의 초보자이든, 여기에서 제공한 인사이트는 벡터 데이터베이스의 복잡성을 더 큰 자신감과 역량을 가지고 헤쳐 나가는 데 도움이 될 것입니다.
계속 읽기

How Zilliz Saw the Future of Vector Databases—and Built for Production
An inside look at how Zilliz built vector databases for real-world use, focusing on scalability, stability, and running them reliably at scale.

Balancing Precision and Performance: How Zilliz Cloud's New Parameters Help You Optimize Vector Search
Optimize vector search with Zilliz Cloud’s level and recall features to tune accuracy, balance performance, and power AI applications.

OpenAI o1: What Developers Need to Know
In this article, we will talk about the o1 series from a developer's perspective, exploring how these models can be implemented for sophisticated use cases.



