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에서는 Bm25와 Bm25Config 클래스를 사용하고, Rust에서는 qdrant_edge::bm25_embed 모듈의 EdgeBm25와 EdgeBm25Config를 사용해 주세요.
희소 벡터 구성하기 (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는 입력 유형에 따라 다른 용어 가중치를 적용하므로, 잘못된 함수를 사용하면 잘못된 결과가 나와요.