Qdrant Edge로 BM25 사용하기

Qdrant Edge로 BM25 사용하기 (edge-edge-bm25)

BM25(Best Matching 25)는 전체 텍스트 검색을 위한 널리 쓰이는 희소 벡터 랭킹 알고리즘이에요. Qdrant Edge에는 내장 BM25 임베더가 포함되어 있어서, 인터넷 연결이나 외부 임베딩 서비스 없이도 키워드 검색을 실행할 수 있어요.

BM25 임베더는 서버 측 BM25와 호환돼요. Qdrant Edge 임베더가 생성한 벡터는 Qdrant 서버의 텍스트 검색 파이프라인과 동일한 토큰 ID와 점수 공식을 사용해요. 서버 스냅샷에서 Edge 샤드를 초기화하고 로컬에서 생성한 BM25 벡터로 재인덱싱 없이 쿼리할 수 있어요.

Python에서는 Bm25Bm25Config 클래스를 사용하고, Rust에서는 qdrant_edge::bm25_embed 모듈의 EdgeBm25EdgeBm25Config를 사용해 주세요.

희소 벡터 구성하기 (Configure a Sparse Vector)

BM25를 시작하려면 희소 벡터 필드와 Modifier.Idf로 Edge 샤드를 만들어 주세요. IDF 수정자는 BM25 점수 계산에 필요한 역문서 빈도 가중치를 활성화해요:

from qdrant_edge import (
    EdgeConfig,
    EdgeShard,
    EdgeSparseVectorParams,
    Modifier,
)

config = EdgeConfig(
    sparse_vectors={"text": EdgeSparseVectorParams(modifier=Modifier.Idf)},
)

shard = EdgeShard.create(SHARD_DIRECTORY, config)
const SHARD_DIRECTORY: &str = "./qdrant-edge-bm25";

use std::path::*;
use qdrant_edge::*;

let config = EdgeConfigBuilder::new()
    .sparse_vector("text", EdgeSparseVectorParamsBuilder::new()
        .modifier(Modifier::Idf)
        .build())
    .build();

let shard = EdgeShard::new(Path::new(SHARD_DIRECTORY), config)?;

BM25 임베더 만들기 (Create a BM25 Embedder)

언어 설정으로 BM25 임베더를 인스턴스화해 주세요. 임베더는 지정된 언어에 대해 스테밍(stemming)과 불용어(stopword) 필터링을 적용해요:

from qdrant_edge import Bm25, Bm25Config

bm25 = Bm25(Bm25Config(language="english"))
use qdrant_edge::bm25_embed::*;

let bm25 = EdgeBm25::new(EdgeBm25Config {
    language: Some("english".to_string()),
    ..Default::default()
})?;

Bm25Config는 다음 파라미터를 받아요:

파라미터 설명
language 스테밍과 불용어용 언어 (예: "english", "german"). 기본값은 None으로, 영어 스테밍과 불용어로 대체돼요.
k 용어 빈도 포화 파라미터. 기본값: 1.2.
b 문서 길이 정규화 계수. 기본값: 0.75.
avg_len 토큰 단위의 예상 평균 문서 길이. 기본값: 256.
lowercase 임베딩 전에 토큰을 소문자로 변환. 기본값: true.
ascii_folding 악센트 문자를 ASCII 등가물로 정규화. 기본값: false.
stemmer 스테밍 알고리즘 재정의.
stopwords 불용어 목록 재정의.
tokenizer 텍스트를 개별 토큰(단어)으로 분해하는 토크나이저. "prefix", "whitespace", "word", "multilingual" 중 하나. 기본값: "word".
min_token_len 포함할 최소 토큰 길이.
max_token_len 포함할 최대 토큰 길이.

각 파라미터에 대한 전체 설명은 BM25 파라미터 구성을 참고해 주세요.

각 문서에 대한 희소 벡터를 생성하려면 embed_document를 사용한 다음 포인트를 upsert해 주세요. 벌크 삽입 후 희소 인덱스를 구축하려면 optimize를 호출해 주세요:

from qdrant_edge import Point, UpdateOperation

shard.update(UpdateOperation.upsert_points([
    Point(1, {"text": bm25.embed_document("the quick brown fox")}, {"title": "Article 1"}),
    Point(2, {"text": bm25.embed_document("a lazy dog sleeps")},   {"title": "Article 2"}),
    Point(3, {"text": bm25.embed_document("foxes are clever")},    {"title": "Article 3"}),
]))
shard.optimize()
use qdrant_edge::external::serde_json::json;
use qdrant_edge::*;

let docs = [
    (1u64, "the quick brown fox", "Article 1"),
    (2,    "a lazy dog sleeps",   "Article 2"),
    (3,    "foxes are clever",    "Article 3"),
];

let points = docs.iter().map(|(id, text, title)| {
    PointStruct::new(
        *id,
        Vectors::new_named([("text", bm25.embed_document(text))]),
        json!({ "title": title }),
    ).into()
}).collect();

shard.update(UpdateOperation::PointOperation(
    PointOperations::UpsertPoints(PointInsertOperations::PointsList(points)),
))?;
shard.optimize()?;

쿼리하기 (Query)

쿼리 텍스트에 대한 희소 벡터를 생성하려면 embed_query를 사용한 다음 샤드를 쿼리해 주세요:

from qdrant_edge import Query, QueryRequest

query_vector = bm25.embed_query("clever fox")

results = shard.query(QueryRequest(
    query=Query.Nearest(query_vector, using="text"),
    limit=3,
    with_payload=True,
))
use qdrant_edge::*;

let query_vector = bm25.embed_query("clever fox");

let results = shard.query(
    QueryRequestBuilder::new(3)
        .query(ScoringQuery::Vector(QueryEnum::Nearest(NamedQuery {
            query: VectorInternal::from(query_vector),
            using: Some("text".to_string()),
        })))
        .with_payload(WithPayloadInterface::Bool(true))
        .build(),
)?;

쿼리 텍스트에는 항상 embed_query를, 문서 텍스트에는 항상 embed_document를 사용해 주세요. BM25는 입력 유형에 따라 다른 용어 가중치를 적용하므로, 잘못된 함수를 사용하면 잘못된 결과가 나와요.

더 알아보기 (Learn more)

출처: Qdrant 공식문서 - BM25