Qdrant에서 멀티벡터 표현으로 재랭킹하기

Qdrant에서 멀티벡터 표현으로 재랭킹하기 (using-multivector-representations)

시간: 30분 난이도: 중급

멀티벡터 표현(Multivector Representations) 은 Qdrant의 가장 강력한 기능 중 하나예요. 하지만 대부분의 사람들이 이를 효과적으로 사용하지 못해서, 엄청난 RAM 오버헤드, 느린 삽입, 낭비되는 계산을 겪곤 하죠.

> 출처: [Qdrant 공식 문서 — using-multivector-representations](https://qdrant.tech/documentation/tutorials-search-engineering/using-multivector-representations)

이 튜토리얼을 끝내면 Qdrant에서 멀티벡터 표현을 효과적으로 사용하는 법을 알게 됩니다. 토큰 단위 벡터가 왜 유용한지, 재랭킹이 왜 필요한지, 그리고 FastEmbed로 ColBERT 스타일 재랭킹 파이프라인을 어떻게 구성하는지 살펴볼게요.

멀티벡터 표현이란? (What are Multivector Representations?)

대부분의 벡터 엔진에서 각 문서는 단일 벡터로 표현돼요. 짧은 텍스트에는 잘 동작하지만 긴 문서에서는 종종 어려움을 겪는 방식이죠. 단일 벡터 표현은 토큰 단위 임베딩을 풀링(pooling)하므로 분명히 일부 정보를 잃게 됩니다.

멀티벡터 표현은 더 세분화된 대안을 제공해요. 단일 문서를 여러 벡터(보통 토큰 또는 구(phrase) 수준)로 표현하죠. 이를 통해 특정 질의 용어와 문서의 관련 부분 사이의 더 정밀한 매칭이 가능해져요. 이 매칭은 ColBERT 같은 Late Interaction 모델에서 특히 효과적인데, 이 모델은 토큰 단위 임베딩을 유지하고 질의 시간에 interaction을 수행해 관련성 점수를 매겨요.

튜토리얼에서 나중에 보겠지만 Qdrant는 멀티벡터, 나아가 late interaction 모델을 네이티브로 지원합니다.

토큰 단위 벡터가 왜 유용한가요? (Why Token-level Vectors are Useful)

토큰 단위 벡터를 사용하면 ColBERT 같은 모델이 특정 질의 토큰을 문서에서 가장 관련 높은 부분과 매칭시킬 수 있어요. 이를 통해 Late Interaction으로 높은 정확도의 검색이 가능해집니다.

Late interaction에서는 각 문서가 단일 벡터 대신 여러 토큰 단위 벡터로 변환돼요. 질의도 토큰화되어 여러 벡터로 임베딩됩니다. 그다음 질의·문서 벡터를 MaxSim이라는 유사도 함수로 매칭해요. 계산 방법은 여기에서 확인할 수 있어요.

전통적인 검색에서는 질의와 문서가 단일 임베딩으로 변환된 뒤 유사도가 계산돼요. 정보가 검색 전에 압축되므로 이것이 early interaction이에요.

재스코어링(Rescoring)이란 무엇이고 왜 쓰나요? (What is Rescoring, and Why is it Used?)

재스코어링은 두 가지로 이뤄져요.

  1. 빠른 모델로 관련 문서를 검색(retrieve)한다.
  2. ColBERT처럼 더 정확하지만 더 느린 모델로 그것들을 재랭킹(rerank)한다.

기본적으로 모든 벡터를 인덱싱하는 것이 왜 문제인가요? (Why Indexing Every Vector by Default is a Problem)

ColBERT 같은 Late Interaction 모델이 쓰는 멀티벡터 표현에서, 단일 논리 문서는 수백 개의 토큰 단위 벡터로 이어져요. Qdrant에서 이 각각의 벡터를 HNSW로 개별 인덱싱하면 다음이 발생할 수 있습니다.

  • 높은 RAM 사용량
  • HNSW 그래프 유지의 복잡성으로 인한 느린 삽입 시간

하지만 멀티벡터는 보통 (밀집 벡터로 1차 검색한 뒤의) 재랭킹 단계에서 쓰이므로, 이 토큰 단위 벡터들을 HNSW로 인덱싱할 필요가 없는 경우가 많아요.

대신 이들을 (HNSW 인덱싱 없는) 멀티벡터 필드로 저장하고 질의 시간에 재랭킹에 사용하면, 리소스 오버헤드를 줄이고 성능을 개선할 수 있습니다.

자세한 내용은 Qdrant의 'Scaling PDF Retrieval with Qdrant' 튜토리얼의 상세 분석을 확인하세요.

Qdrant에서는 인덱싱이 어떻게 동작하는지 완전히 제어할 수 있어요. HNSW m 파라미터를 0으로 설정하면 인덱싱을 비활성화할 수 있습니다.

from qdrant_client import QdrantClient, models

client = QdrantClient("http://localhost:6333")

collection_name = "dense_multivector_demo"

client.create_collection(
    collection_name=collection_name,
    vectors_config={
        "dense": models.VectorParams(
            size=384,
            distance=models.Distance.COSINE
            # 밀집 벡터는 HNSW 인덱싱을 켠 채로 둔다.
        ),
        "colbert": models.VectorParams(
            size=128,
            distance=models.Distance.COSINE,
            multivector_config=models.MultiVectorConfig(
                comparator=models.MultiVectorComparator.MAX_SIM
            ),
            hnsw_config=models.HnswConfigDiff(m=0)  # 재랭킹용 HNSW 비활성화
        )
    }
)

멀티벡터에서 HNSW를 비활성화하면 다음을 얻을 수 있어요.

  • 계산 절약
  • 메모리 사용량 감소
  • 벡터 업로드 속도 향상

FastEmbed로 멀티벡터를 생성하는 법 (How to Generate Multivectors Using FastEmbed)

ColBERT를 간단한 API로 감싼 FastEmbed를 사용해 멀티벡터를 효과적으로 사용하는 법을 시연해 볼게요.

FastEmbed와 Qdrant 설치:

pip install qdrant-client[fastembed]>=1.14.2

단계별: ColBERT + Qdrant 설정 (Step-by-Step: ColBERT + Qdrant Setup)

Qdrant가 실행 중인지 확인하고 클라이언트를 만듭니다.

from qdrant_client import QdrantClient, models

# 1. Qdrant 서버에 연결
client = QdrantClient("http://localhost:6333")

1. 문서 인코딩 (Encode Documents)

다음으로 문서를 인코딩하세요.

from fastembed import TextEmbedding, LateInteractionTextEmbedding

# 예시 문서와 질의
documents = [
    "Artificial intelligence is used in hospitals for cancer diagnosis and treatment.",
    "Self-driving cars use AI to detect obstacles and make driving decisions.",
    "AI is transforming customer service through chatbots and automation.",
    # ...
]

query_text = "How does AI help in medicine?"

dense_documents = [
    models.Document(text=doc, model="BAAI/bge-small-en")
    for doc in documents
]
dense_query = models.Document(text=query_text, model="BAAI/bge-small-en")

colbert_documents = [
    models.Document(text=doc, model="colbert-ir/colbertv2.0")
    for doc in documents
]
colbert_query = models.Document(text=query_text, model="colbert-ir/colbertv2.0")

2. Qdrant 컬렉션 만들기 (Create a Qdrant collection)

두 벡터 타입 모두를 가진 Qdrant 컬렉션을 만듭니다. 밀집 벡터에는 인덱싱을 켜 두지만, 재랭킹에 쓸 colbert 벡터에는 끄는 것에 주목하세요.

collection_name = "dense_multivector_demo"

client.create_collection(
    collection_name=collection_name,
    vectors_config={
        "dense": models.VectorParams(
            size=384,
            distance=models.Distance.COSINE
            # 밀집 벡터는 HNSW 인덱싱을 켠 채로 둔다.
        ),
        "colbert": models.VectorParams(
            size=128,
            distance=models.Distance.COSINE,
            multivector_config=models.MultiVectorConfig(
                comparator=models.MultiVectorComparator.MAX_SIM
            ),
            hnsw_config=models.HnswConfigDiff(m=0)  # 재랭킹용 HNSW 비활성화
        )
    }
)

3. 문서 업로드 (Dense + Multivector) (Upload Documents)

이제 batch_size=8로 벡터를 업로드해요. 문서가 많지 않지만 배칭은 언제나 권장됩니다.

points = [
    models.PointStruct(
        id=i,
        vector={
            "dense": dense_documents[i],
            "colbert": colbert_documents[i]
        },
        payload={"text": documents[i]}
    ) for i in range(len(documents))
]

client.upload_points(
    collection_name="dense_multivector_demo",
    points=points,
    batch_size=8
)

검색 + 재랭킹을 한 번의 호출로 (Query with Retrieval + Reranking in One Call)

이제 검색을 실행해 봅시다.

results = client.query_points(
    collection_name="dense_multivector_demo",
    prefetch=models.Prefetch(
        query=dense_query,
        using="dense",
    ),
    query=colbert_query,
    using="colbert",
    limit=3,
    with_payload=True
)
  • 밀집 벡터가 후보들을 빠르게 검색한다.
  • Colbert 멀티벡터가 토큰 단위 MaxSim으로 세밀한 정밀도의 재랭킹을 수행한다.
  • 상위 3개 결과를 반환한다.

결론 (Conclusion)

멀티벡터 검색은 올바르게 사용될 때 벡터 데이터베이스의 가장 강력한 기능 중 하나예요. Qdrant의 이 기능을 통해 다음을 할 수 있습니다.

  • 토큰 단위 임베딩을 네이티브로 저장한다.
  • 오버헤드를 줄이기 위해 인덱싱을 비활성화한다.
  • 한 번의 API 호출로 빠른 검색과 정확한 재랭킹을 수행한다.
  • late interaction을 효율적으로 확장한다.

FastEmbed와 Qdrant의 결합은 리소스를 낭비하지 않는 ColBERT 스타일 재랭킹을 위한 프로덕션 준비 완료 파이프라인을 만들어 줘요. 로컬에서 하거나 Qdrant Cloud를 쓸 수 있어요. Qdrant는 검색 엔진을 시작하기 쉬운 API를 제공하니, 바로 시작하고 싶다면 Qdrant Cloud에 무료로 가입해 구축을 시작해 보세요.

더 알아보기 (Learn more)