Milvus
Zilliz
  • Home
  • Blog
  • 진정한 엔티티 레벨 검색을 실현합니다: Milvus의 새로운 구조체 배열 및 MAX_SIM 기능

진정한 엔티티 레벨 검색을 실현합니다: Milvus의 새로운 구조체 배열 및 MAX_SIM 기능

  • Engineering
December 05, 2025
Jeremy Zhu, Min Tian

벡터 데이터베이스를 기반으로 AI 애플리케이션을 구축했다면, 데이터베이스는 개별 청크의 임베딩을 검색하지만 애플리케이션은 엔티티에 관심을 갖는다는 동일한 문제점에 직면했을 것입니다. 이러한 불일치로 인해 전체 검색 워크플로가 복잡해집니다.

이런 일이 몇 번이고 반복되는 것을 보셨을 것입니다:

  • RAG 지식창고: 문서가 단락 임베딩으로 덩어리로 묶여 있어 검색 엔진이 전체 문서 대신 흩어져 있는 조각을 반환합니다.

  • 이커머스 추천: 한 제품에 여러 개의 이미지 임베딩이 있는 경우, 시스템에서 고유한 제품 5개가 아닌 동일한 품목의 5개 각도를 반환합니다.

  • 비디오 플랫폼: 동영상이 클립 임베딩으로 분할되어 있지만 검색 결과에 하나의 통합된 항목이 아닌 동일한 동영상의 조각이 표시됩니다.

  • 콜버트/콜팔리 스타일 검색: 문서가 수백 개의 토큰 또는 패치 수준의 임베딩으로 확장되어 병합이 필요한 작은 조각으로 결과가 표시됩니다.

대부분의 벡터 데이터베이스는 각 임베딩을 고립된 행으로 취급하는 반면, 실제 애플리케이션은 문서, 제품, 동영상, 항목, 장면 등 상위 수준의 엔터티에서 작동한다는 점에서 이러한 모든 문제는 동일한 아키텍처적 격차에서 비롯됩니다. 따라서 엔지니어링 팀은 중복 제거, 그룹화, 버킷화, 순위 재지정 로직을 사용해 엔티티를 수동으로 재구성해야 합니다. 이 방법은 작동하지만 취약하고 느리며 애초에 존재해서는 안 되는 로직으로 애플리케이션 계층을 부풀립니다.

Milvus 2.6.4는 새로운 기능으로 이러한 격차를 해소합니다: MAX_SIM 메트릭 유형의 구조체 배열입니다. 이 기능을 사용하면 단일 엔티티에 대한 모든 임베딩을 단일 레코드에 저장할 수 있으며, Milvus가 엔티티를 전체적으로 점수화하여 반환할 수 있습니다. 더 이상 중복으로 가득 찬 결과 세트가 없습니다. 순위 재조정 및 병합과 같은 복잡한 사후 처리가 필요 없습니다.

이 문서에서는 구조 배열과 MAX_SIM의 작동 원리를 살펴보고 두 가지 실제 예제를 통해 이를 시연해 보겠습니다: Wikipedia 문서 검색과 ColPali 이미지 기반 문서 검색입니다.

구조체 배열이란 무엇인가요?

Milvus에서 구조체 배열 필드는 단일 레코드에 각각 동일한 사전 정의된 스키마를 따르는 정렬된 구조체 요소의 목록을 포함할 수 있게 해줍니다. 구조체는 스칼라 필드, 문자열 또는 기타 지원되는 모든 유형뿐만 아니라 여러 벡터를 포함할 수 있습니다. 즉, 단락 임베딩, 이미지 보기, 토큰 벡터, 메타데이터 등 하나의 엔티티에 속하는 모든 조각을 한 행 안에 직접 묶을 수 있습니다.

다음은 구조체 배열 필드를 포함하는 컬렉션의 엔티티 예시입니다.

{
    'id': 0,
    'title': 'Walden',
    'title_vector': [0.1, 0.2, 0.3, 0.4, 0.5],
    'author': 'Henry David Thoreau',
    'year_of_publication': 1845,
    // highlight-start
    'chunks': [
        {
            'text': 'When I wrote the following pages, or rather the bulk of them...',
            'text_vector': [0.3, 0.2, 0.3, 0.2, 0.5],
            'chapter': 'Economy',
        },
        {
            'text': 'I would fain say something, not so much concerning the Chinese and...',
            'text_vector': [0.7, 0.4, 0.2, 0.7, 0.8],
            'chapter': 'Economy'
        }
    ]
    // hightlight-end
}

위의 예에서 chunks 필드는 구조체 배열 필드이며 각 구조체 요소에는 text, text_vector, chapter 과 같은 고유한 필드가 포함되어 있습니다.

이 접근 방식은 벡터 데이터베이스의 오랜 모델링 문제를 해결합니다. 기존에는 모든 임베딩 또는 속성이 자체 행이 되어야 했기 때문에 다중 벡터 엔티티(문서, 제품, 동영상)를 수십, 수백, 심지어 수천 개의 레코드로 분할해야 했습니다. 구조 배열을 사용하면 전체 멀티 벡터 엔티티를 단일 필드에 저장할 수 있으므로 단락 목록, 토큰 임베딩, 클립 시퀀스, 멀티뷰 이미지 또는 하나의 논리적 항목이 여러 벡터로 구성된 모든 시나리오에 자연스럽게 적합합니다.

구조체 배열은 MAX_SIM에서 어떻게 작동하나요?

이 새로운 구조 배열 구조 위에 시맨틱 검색 엔티티를 인식하는 새로운 채점 전략인 MAX_SIM이 계층화되어 있습니다. 쿼리가 들어오면 Milvus는 각 구조체 배열 내의 모든 벡터와 비교하여 최대 유사도를 엔티티의 최종 점수로 삼습니다. 그런 다음 해당 단일 점수를 기준으로 엔티티의 순위를 매기고 반환합니다. 이렇게 하면 흩어진 조각을 검색하고 그룹화, 중복 제거, 순위 재지정의 부담을 애플리케이션 계층으로 떠넘기는 기존의 벡터 데이터베이스 문제를 피할 수 있습니다. MAX_SIM을 사용하면 엔티티 수준 검색이 내장되어 일관성 있고 효율적으로 이루어집니다.

MAX_SIM이 실제로 어떻게 작동하는지 이해하기 위해 구체적인 예제를 살펴보겠습니다.

참고: 이 예제의 모든 벡터는 동일한 임베딩 모델에 의해 생성되며, 유사도는 [0,1] 범위의 코사인 유사도로 측정됩니다.

사용자가 "머신 러닝 초급 과정"을 검색한다고 가정해 보겠습니다.

이 쿼리는 3개의 토큰으로 토큰화됩니다:

  • 머신 러닝

  • beginner

  • course

그런 다음 이러한 각 토큰은 문서에 사용된 것과 동일한 임베딩 모델에 의해 임베딩 벡터로 변환됩니다.

이제 벡터 데이터베이스에 두 개의 문서가 포함되어 있다고 가정해 보겠습니다:

  • doc_1: 파이썬을 사용한 심층 신경망 입문 가이드

  • doc_2: LLM 논문 읽기에 대한 고급 가이드

두 문서 모두 벡터로 임베드되어 구조체 배열 안에 저장되어 있습니다.

1단계: doc_1에 대한 MAX_SIM 계산하기

각 쿼리 벡터에 대해 Milvus는 doc_1의 모든 벡터에 대해 코사인 유사성을 계산합니다:

소개가이드심층 신경망python
머신 러닝0.00.00.90.3
beginner0.80.10.00.3
코스0.30.70.10.1

각 쿼리 벡터에 대해 MAX_SIM은 해당 행에서 가장 높은 유사도를 선택합니다:

  • 머신 러닝 → 심층 신경망(0.9)

  • 초보자 → 입문(0.8)

  • 코스 → 가이드(0.7)

가장 잘 일치하는 항목을 합산하면 doc_1의 MAX_SIM 점수는 2.4가 됩니다.

2단계: doc_2에 대한 MAX_SIM 계산하기

이제 doc_2에 대해 이 과정을 반복합니다:

고급가이드LLMpaper읽기
머신 러닝0.10.20.90.30.1
beginner0.40.60.00.20.5
코스0.50.80.10.40.7

doc_2에 가장 잘 어울리는 단어는 다음과 같습니다:

  • "머신 러닝" → "LLM"(0.9)

  • "초보자" → "가이드"(0.6)

  • "코스" → "가이드"(0.8)

이를 합산하면 doc_2의 MAX_SIM 점수는 2.3이 됩니다.

3단계: 점수 비교

2.4 > 2.3이므로 doc_1이 doc_2보다 더 높은 순위를 차지하며, 이는 직관적으로도 doc_1이 입문용 머신 러닝 가이드에 더 가깝기 때문입니다.

이 예제에서 MAX_SIM의 세 가지 핵심 특징을 강조할 수 있습니다:

  • 키워드 기반이 아닌 시맨틱 우선: MAX_SIM은 텍스트 리터럴이 아닌 임베딩을 비교합니다. '머신 러닝 '과 '심층 신경망 '은 겹치는 단어가 0개임에도 불구하고 의미적 유사도는 0.9입니다. 따라서 MAX_SIM은 동의어, 의역어, 개념적 중복 및 최신 임베딩이 많은 워크로드에 강력합니다.

  • 길이와 순서에 민감하지 않습니다: MAX_SIM은 쿼리와 문서에 동일한 수의 벡터가 필요하지 않습니다(예: doc_1에는 4개의 벡터가 있고 doc_2에는 5개가 있어도 둘 다 정상적으로 작동). 또한 쿼리의 앞부분에 나타나는 '초보자'와 문서의 뒷부분에 나타나는 '소개'는 점수에 아무런 영향을 미치지 않는 등 벡터 순서도 무시합니다.

  • 모든 쿼리 벡터가 중요합니다: MAX_SIM은 각 쿼리 벡터에 대해 가장 잘 일치하는 것을 취하고 그 중 가장 좋은 점수를 합산합니다. 이렇게 하면 일치하지 않는 벡터로 인해 결과가 왜곡되는 것을 방지하고 모든 중요한 쿼리 토큰이 최종 점수에 기여하도록 보장할 수 있습니다. 예를 들어, doc_2에서 '초보자'에 대한 품질이 낮은 일치 항목은 전체 점수를 직접적으로 낮춥니다.

벡터 데이터베이스에서 MAX_SIM + 구조체 배열이 중요한 이유

Milvus는 오픈 소스 고성능 벡터 데이터베이스로, 이제 MAX_SIM과 구조체 배열을 완벽하게 지원하여 벡터 네이티브, 엔티티 수준의 다중 벡터 검색을 가능하게 합니다:

  • 멀티 벡터 엔티티를 기본적으로 저장하세요: 구조체 배열을 사용하면 관련 벡터 그룹을 별도의 행이나 보조 테이블로 나누지 않고 단일 필드에 저장할 수 있습니다.

  • 효율적인 베스트매치 계산: MAX_SIM은 IVF 및 HNSW와 같은 벡터 인덱스와 결합하여 모든 벡터를 스캔하지 않고도 최적의 일치 항목을 계산할 수 있어 대용량 문서에서도 높은 성능을 유지합니다.

  • 시맨틱이 많은 워크로드를 위해 특별히 설계되었습니다: 이 접근 방식은 긴 텍스트 검색, 다면 시맨틱 매칭, 문서 요약 정렬, 다중 키워드 쿼리 및 유연하고 세밀한 의미론적 추론이 필요한 기타 AI 시나리오에서 탁월한 성능을 발휘합니다.

구조 배열을 사용하는 경우

구조체 배열 의 가치는 이 기능이 무엇을 지원하는지 살펴보면 명확해집니다. 이 기능은 기본적으로 세 가지 기본 기능을 제공합니다:

  • 벡터, 스칼라, 문자열, 메타데이터등 이질적인 데이터를하나의 구조화된 개체로묶어줍니다.

  • 스토리지를 실제 엔터티에 맞춰 정렬하므로 각 데이터베이스 행이 기사, 제품 또는 동영상과 같은 실제 항목에 깔끔하게 매핑됩니다.

  • MAX_SIM과 같은 집계 함수와 결합하면 데이터베이스에서 직접 진정한 엔티티 수준의 멀티벡터 검색이 가능하므로 애플리케이션 계층에서 중복 제거, 그룹화 또는 순위 재지정이 필요하지 않습니다.

이러한 특성으로 인해 구조 배열은 단일 논리적 엔티티가 여러 개의 벡터로 표현될 때마다 자연스럽게 적합합니다. 일반적인 예로는 문단으로 분할된 기사, 토큰 임베딩으로 분해된 문서, 여러 이미지로 표현된 제품 등이 있습니다. 검색 결과에서 중복 히트, 흩어진 조각 또는 동일한 엔티티가 상위 결과에 여러 번 나타나는 경우, 배열 구조는 애플리케이션 코드의 사후 패치가 아닌 저장 및 검색 레이어에서 이러한 문제를 해결합니다.

이 패턴은 다중 벡터 검색에 의존하는 최신 AI 시스템에서 특히 강력합니다. 예를 들어, 콜버트는 하나의 문서를 나타냅니다:

  • ColBERT는 법률 텍스트나 학술 연구와 같은 영역에서 세분화된 의미론적 매칭을 위해 단일 문서를 100-500개의 토큰 임베딩으로 표현합니다.

  • ColPali는 재무제표, 계약서, 송장, 기타 스캔 문서에서 교차 모드 검색을 위해 각 PDF 페이지를 256~1024개의 이미지 패치로변환합니다 .

Milvus는 구조 배열을 통해 이러한 모든 벡터를 단일 엔티티 아래에 저장하고 총 유사도(예: MAX_SIM)를 효율적이고 기본적으로 계산할 수 있습니다. 이를 보다 명확하게 설명하기 위해 두 가지 구체적인 예를 들어보겠습니다.

이전에는 여러 개의 이미지가 있는 제품이 행당 하나의 이미지가 있는 평면 스키마에 저장되었습니다. 정면, 측면, 각도가 있는 제품의 경우 세 개의 행이 생성되었습니다. 검색 결과에 동일한 제품의 이미지가 여러 개 표시되는 경우가 많았기 때문에 수동으로 중복 제거 및 순위 재지정이 필요했습니다.

구조 배열을 사용하면 각 제품이 하나의 행이 됩니다. 모든 이미지 임베딩과 메타데이터(각도, is_primary 등)는 images 필드 안에 구조 배열로 존재합니다. Milvus는 이러한 이미지가 동일한 제품에 속한다는 것을 이해하고 개별 이미지가 아닌 제품 전체를 반환합니다.

이전에는 하나의 Wikipedia 문서가 N개의 단락 행으로 나뉘어 있었습니다. 검색 결과는 흩어져 있는 단락을 반환했기 때문에 시스템에서 이를 그룹화하고 어느 문서에 속하는지 추측해야 했습니다.

구조 배열을 사용하면 전체 문서가 하나의 행이 됩니다. 모든 단락과 그 임베딩은 단락 필드 아래에 그룹화되며 데이터베이스는 단편적인 조각이 아닌 전체 문서를 반환합니다.

실습 튜토리얼: 구조 배열로 문서 수준 검색하기

1. 위키백과 문서 검색

이 튜토리얼에서는 구조체 배열을 사용하여 단락 수준 데이터를 전체 문서 레코드로 변환하는 방법을 살펴봄으로써 Milvus가 고립된 조각을 반환하는 대신 진정한 문서 수준 검색을 수행할 수 있도록 하는 방법을 안내합니다.

많은 지식창고 파이프라인은 Wikipedia 문서를 문단 덩어리로 저장합니다. 이는 임베딩과 인덱싱에는 효과적이지만 검색을 방해합니다. 사용자 쿼리는 일반적으로 흩어져 있는 단락을 반환하므로 문서를 수동으로 그룹화하고 재구성해야 합니다. 구조체 배열과 MAX_SIM을 사용하면 각 문서가 하나의 행이 되도록 저장 스키마를 재설계할 수 있으며, Milvus는 기본적으로 전체 문서의 순위를 매기고 반환할 수 있습니다.

다음 단계에서는 그 방법을 보여드리겠습니다:

  1. Wikipedia 문단 데이터 로드 및 사전 처리하기

  2. 동일한 문서에 속하는 모든 단락을 구조 배열로 묶습니다.

  3. 이 구조화된 문서를 Milvus에 삽입합니다.

  4. MAX_SIM 쿼리를 실행하여 중복 제거나 순위 재조정 없이 전체 문서를 깔끔하게 검색하기

이 튜토리얼이 끝나면 Milvus가 사용자가 기대하는 방식으로 엔티티 수준 검색을 직접 처리하는 작업 파이프라인을 갖추게 됩니다.

데이터 모델:

{
    "wiki_id": int,                  # WIKI ID(primary key) 
    "paragraphs": ARRAY<STRUCT<      # Array of paragraph structs
        text:VARCHAR                 # Paragraph text
        emb: FLOAT_VECTOR(768)       # Embedding for each paragraph
    >>
}

1단계: 데이터 그룹화 및 변환

이 데모에서는 간단한 Wikipedia 임베딩 데이터 세트를 사용합니다.

import pandas as pd
import pyarrow as pa

# Load the dataset and group by wiki_id df = pd.read_parquet(“train-*.parquet”) grouped = df.groupby(‘wiki_id’)

# Build the paragraph array for each article wiki_data = [] for wiki_id, group in grouped: wiki_data.append({ ‘wiki_id’: wiki_id, ‘paragraphs’: [{‘text’: row[‘text’], ‘emb’: row[‘emb’]} for _, row in group.iterrows()] })

2단계: Milvus 컬렉션 만들기

from pymilvus import MilvusClient, DataType

client = MilvusClient(uri=“http://localhost:19530”) schema = client.create_schema() schema.add_field(“wiki_id”, DataType.INT64, is_primary=True)

# Define the Struct schema struct_schema = client.create_struct_field_schema() struct_schema.add_field(“text”, DataType.VARCHAR, max_length=65535) struct_schema.add_field(“emb”, DataType.FLOAT_VECTOR, dim=768)

schema.add_field(“paragraphs”, DataType.ARRAY, element_type=DataType.STRUCT, struct_schema=struct_schema, max_capacity=200)

client.create_collection(“wiki_docs”, schema=schema)

3단계: 데이터 삽입 및 색인 구축

# Batch insert documents
client.insert("wiki_docs", wiki_data)

# Create an HNSW index index_params = client.prepare_index_params() index_params.add_index( field_name="paragraphs[emb]", index_type=“HNSW”, metric_type=“MAX_SIM_COSINE”, params={“M”: 16, “efConstruction”: 200} ) client.create_index(“wiki_docs”, index_params) client.load_collection(“wiki_docs”)

4단계: 문서 검색

# Search query
import cohere
from pymilvus.client.embedding_list import EmbeddingList

# The dataset uses Cohere’s multilingual-22-12 embedding model, so we must embed the query using the same model. co = cohere.Client(f"<>") query = ‘Who founded Youtube’ response = co.embed(texts=[query], model=‘multilingual-22-12’) query_embedding = response.embeddings query_emb_list = EmbeddingList()

for vec in query_embedding[0]: query_emb_list.add(vec)

results = client.search( collection_name=“wiki_docs”, data=[query_emb_list], anns_field="paragraphs[emb]", search_params={ “metric_type”: “MAX_SIM_COSINE”, “params”: {“ef”: 200, “retrieval_ann_ratio”: 3} }, limit=10, output_fields=[“wiki_id”] )

# Results: directly return 10 full articles! for hit in results[0]: print(f"Article {hit[‘entity’][‘wiki_id’]}: Score {hit[‘distance’]:.4f}")

출력 비교: 기존 검색과 구조 배열 비교

구조 배열의 영향은 데이터베이스가 실제로 무엇을 반환하는지 살펴보면 명확해집니다:

차원기존 접근 방식구조체 배열
데이터베이스 출력상위 100개 단락 반환(중복성이 높음)상위 10개의 전체 문서 반환 - 깔끔하고 정확함
애플리케이션 로직그룹화, 중복 제거, 재순위 지정 필요(복잡함)후처리 필요 없음 - Milvus에서 직접 엔티티 수준 결과 제공

Wikipedia 예시에서는 단락 벡터를 통합된 문서 표현으로 결합하는 가장 간단한 사례만 보여드렸습니다. 하지만 구조 배열의 진정한 강점은 기존의 검색 파이프라인과 최신 AI 아키텍처를 포함한 모든 다중 벡터 데이터 모델에 일반화할 수 있다는 점입니다.

기존의 다중 벡터 검색 시나리오

잘 정립된 많은 검색 및 추천 시스템은 자연스럽게 여러 개의 연관 벡터를 가진 엔티티에서 작동합니다. 구조 배열은 이러한 사용 사례에 깔끔하게 매핑됩니다:

시나리오데이터 모델엔티티별 벡터
🛍️ 이커머스 제품하나의 제품 → 여러 이미지5-20
🎬 동영상 검색하나의 동영상 → 여러 클립20-100
📖 문서 검색하나의 논문 → 여러 섹션5-15

AI 모델 워크로드(주요 멀티 벡터 사용 사례)

구조 배열은 세분화된 의미 추론을 위해 엔티티당 대규모 벡터 세트를 의도적으로 생성하는 최신 AI 모델에서 더욱 중요해집니다.

모델데이터 모델엔티티당 벡터애플리케이션
콜버트하나의 문서 → 다수의 토큰 임베딩100-500법률 텍스트, 학술 논문, 세분화된 문서 검색
ColPali하나의 PDF 페이지 → 많은 패치 임베딩256-1024재무 보고서, 계약서, 송장, 멀티모달 문서 검색

이러한 모델에는 다중 벡터 저장 패턴이 필요합니다. 구조 배열 이전에는 개발자가 벡터를 여러 행으로 분할하고 결과를 다시 수작업으로 연결해야 했습니다. 이제 Milvus를 사용하면 이러한 엔티티를 기본적으로 저장하고 검색할 수 있으며 MAX_SIM이 문서 수준 스코어링을 자동으로 처리합니다.

ColPali는 크로스 모달 PDF 검색을 위한 강력한 모델입니다. 텍스트에 의존하는 대신 각 PDF 페이지를 이미지로 처리하고 최대 1024개의 시각적 패치로 분할하여 패치당 하나의 임베딩을 생성합니다. 기존 데이터베이스 스키마에서는 단일 페이지를 수백 또는 수천 개의 개별 행으로 저장해야 하므로 데이터베이스에서 이러한 행이 동일한 페이지에 속한다는 것을 이해할 수 없습니다. 그 결과, 엔티티 수준 검색은 파편화되고 비실용적이 됩니다.

구조 배열은 모든 패치 임베딩을 단일 필드 안에 저장하여 Milvus가 페이지를 하나의 응집력 있는 다중 벡터 엔티티로 취급할 수 있도록 함으로써 이 문제를 깔끔하게 해결합니다.

기존의 PDF 검색은 페이지 이미지를 텍스트로 변환하는 OCR에 의존하는 경우가 많습니다. 이는 일반 텍스트에는 효과적이지만 차트, 표, 레이아웃 및 기타 시각적 단서가 손실됩니다. ColPali는 페이지 이미지에서 직접 작업하여 모든 시각적 및 텍스트 정보를 보존함으로써 이러한 제한을 피합니다. 각 페이지에는 이제 수백 개의 벡터가 포함되므로 많은 임베딩을 하나의 엔티티로 집계할 수 있는 데이터베이스가 필요하며, 바로 Array of Structures + MAX_SIM이 제공하는 기능입니다.

가장 일반적인 사용 사례는 각 PDF 페이지가 다중 벡터 엔티티가 되는 Vision RAG입니다. 일반적인 시나리오는 다음과 같습니다:

  • 재무 보고서: 수천 개의 PDF에서 특정 차트나 표가 포함된 페이지를 검색합니다.

  • 계약서: 스캔하거나 촬영한 법률 문서에서 조항을 검색합니다.

  • 송장: 공급업체, 금액 또는 레이아웃별로 송장을 찾습니다.

  • 프레젠테이션: 특정 그림이나 도표가 포함된 슬라이드를 찾습니다.

데이터 모델:

{
    "page_id": int,                     # Page ID (primary key) 
    "page_number": int,                 # Page number within the document 
    "doc_name": VARCHAR,                # Document name
    "patches": ARRAY<STRUCT<            # Array of patch objects
        patch_embedding: FLOAT_VECTOR(128)  # Embedding for each patch
    >>
}

1단계: 데이터 준비ColPali가 이미지나 텍스트를 다중 벡터 표현으로 변환하는 방법에 대한 자세한 내용은 문서를 참조하세요.

import torch
from PIL import Image

from colpali_engine.models import ColPali, ColPaliProcessor

model_name = “vidore/colpali-v1.3”

model = ColPali.from_pretrained( model_name, torch_dtype=torch.bfloat16, device_map=“cuda:0”, # or “mps” if on Apple Silicon ).eval()

processor = ColPaliProcessor.from_pretrained(model_name) # Example: 2 documents, 5 pages each, total 10 images images = [ Image.open(“path/to/your/image1.png”), Image.open(“path/to/your/image2.png”), … Image.open(“path/to/your/image10.png”) ] # Convert each image into multiple patch embeddings batch_images = processor.process_images(images).to(model.device) with torch.no_grad(): image_embeddings = model(**batch_images)

2단계: 밀버스 컬렉션 만들기

from pymilvus import MilvusClient, DataType

client = MilvusClient(uri=“http://localhost:19530”) schema = client.create_schema() schema.add_field(“page_id”, DataType.INT64, is_primary=True) schema.add_field(“page_number”, DataType.INT64) schema.add_field(“doc_name”, DataType.VARCHAR, max_length=500)

# Struct Array for patches struct_schema = client.create_struct_field_schema() struct_schema.add_field(“patch_embedding”, DataType.FLOAT_VECTOR, dim=128)

schema.add_field(“patches”, DataType.ARRAY, element_type=DataType.STRUCT, struct_schema=struct_schema, max_capacity=2048)

client.create_collection(“doc_pages”, schema=schema)

3단계: 데이터 삽입 및 색인 구축

# Prepare data for insertion
page_data=[
    {
        "page_id": 0,
        "page_number": 0,
        "doc_name": "Q1_Financial_Report.pdf",
        "patches": [
            {"patch_embedding": emb} for emb in image_embeddings[0]
        ],
    },
    ...,
    {
        "page_id": 9,
        "page_number": 4,
        "doc_name": "Product_Manual.pdf",
        "patches": [
            {"patch_embedding": emb} for emb in image_embeddings[9]
        ],
    },
]

client.insert(“doc_pages”, page_data)

# Create index index_params = client.prepare_index_params() index_params.add_index( field_name="patches[patch_embedding]", index_type=“HNSW”, metric_type=“MAX_SIM_IP”, params={“M”: 32, “efConstruction”: 200} ) client.create_index(“doc_pages”, index_params) client.load_collection(“doc_pages”)

4단계: 교차 모달 검색: 텍스트 쿼리 → 이미지 결과

# Run the search
from pymilvus.client.embedding_list import EmbeddingList

queries = [ “quarterly revenue growth chart”
] # Convert the text query into a multi-vector representation batch_queries = processor.process_queries(queries).to(model.device) with torch.no_grad(): query_embeddings = model(**batch_queries)

query_emb_list = EmbeddingList() for vec in query_embeddings[0]: query_emb_list.add(vec) results = client.search( collection_name=“doc_pages”, data=[query_emb_list], anns_field="patches[patch_embedding]", search_params={ “metric_type”: “MAX_SIM_IP”, “params”: {“ef”: 100, “retrieval_ann_ratio”: 3} }, limit=3, output_fields=[“page_id”, “doc_name”, “page_number”] )

print(f"Query: '{queries[0]}'") for i, hit in enumerate(results, 1): entity = hit[‘entity’] print(f"{i}. {entity[‘doc_name’]} - Page {entity[‘page_number’]}") print(f" Score: {hit[‘distance’]:.4f}\n")

샘플 출력:

Query: 'quarterly revenue growth chart'
1. Q1_Financial_Report.pdf - Page 2
   Score: 0.9123

2. Q1_Financial_Report.pdf - Page 1 Score: 0.7654

3. Product_Manual.pdf - Page 1 Score: 0.5231

여기서 결과는 전체 PDF 페이지를 직접 반환합니다. 기본 1024 패치 임베딩에 대해 걱정할 필요가 없습니다. Milvus가 모든 집계를 자동으로 처리하기 때문입니다.

결론

대부분의 벡터 데이터베이스는 각 조각을 독립적인 레코드로 저장하므로 애플리케이션은 전체 문서, 제품 또는 페이지가 필요할 때 해당 조각을 재조립해야 합니다. 구조체 배열은 이를 바꿔줍니다. 스칼라, 벡터, 텍스트 및 기타 필드를 하나의 구조화된 개체로 결합함으로써 하나의 데이터베이스 행이 하나의 완전한 엔티티를 엔드투엔드로 나타낼 수 있습니다.

그 결과, 애플리케이션 계층에서 복잡한 그룹화, 중복 제거, 순위 재지정이 필요했던 작업이 기본 데이터베이스 기능이 되어 간단하지만 강력해집니다. 더 풍부한 구조, 더 스마트한 검색, 더 간단한 파이프라인 등 벡터 데이터베이스의 미래는 바로 이러한 방향으로 나아가고 있습니다.

구조체 배열과 MAX_SIM에 대한 자세한 내용은 아래 설명서를 참조하세요:

최신 Milvus의 기능에 대해 궁금한 점이 있거나 자세히 알아보고 싶으신가요? Discord 채널에 참여하거나 GitHub에 이슈를 제출하세요. 또한 Milvus 오피스 아워를 통해 20분간의 일대일 세션을 예약하여 인사이트, 안내 및 질문에 대한 답변을 얻을 수도 있습니다.

    Try Managed Milvus for Free

    Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.

    Get Started

    Like the article? Spread the word

    계속 읽기