하이브리드 검색
하이브리드 검색 (Hybrid Search)
벡터 검색과 키워드 검색은 서로 다른 강점을 갖고 있어요. 하이브리드 검색은 이 둘을 한 번에 실행하고, 그 결과를 퓨전(fusion) 방식으로 합쳐 하나의 순위를 만들어 줍니다. 검색어 하나를 던지면 키워드(BM25F)로 매칭된 결과와 벡터 유사도로 매칭된 결과가 함께 들어오는 거죠.
여기서 중요한 건 퓨전 방식과 두 검색의 상대 가중치를 직접 조절할 수 있다는 점이에요. 상대적으로 단어 일치를 더 믿고 싶으면 키워드 쪽 비중을, 의미 중심으로 찾고 싶으면 벡터 쪽 비중을 키우면 됩니다.
출처: 공식문서
기본 하이브리드 검색
단일 검색 문자열 하나로 벡터 검색과 키워드 검색을 동시에 수행해요. 파이썬 클라이언트 기준 예시를 볼게요.
jeopardy = client.collections.use("JeopardyQuestion")
response = jeopardy.query.hybrid(query="food", limit=3)
for o in response.objects:
print(o.properties)
Named vectors가 있는 컬렉션
컬렉션에 named vectors가 설정돼 있다면, 어떤 벡터 공간을 검색할지 target_vector를 반드시 지정해야 해요. Weaviate는 쿼리 벡터로 해당 target 벡터 공간을 탐색합니다.
reviews = client.collections.use("WineReviewNV")
response = reviews.query.hybrid(
query="A French Riesling",
target_vector="title_country",
limit=3
)
검색 결과 설명(explain score) 보기
객체가 어떻게 순위가 매겨졌는지 보고 싶다면 쿼리에 explain score를 요청하면 돼요. 순위 점수는 객체 메타데이터의 일부로 함께 내려옵니다.
from weaviate.classes.query import MetadataQuery
response = jeopardy.query.hybrid(
query="food",
alpha=0.5,
return_metadata=MetadataQuery(score=True, explain_score=True),
limit=3,
)
for o in response.objects:
print(o.properties)
print(o.metadata.score, o.metadata.explain_score)
키워드와 벡터의 균형: alpha
결과가 키워드 쪽을 더 따르게 할지, 벡터 쪽을 더 따르게 할지 alpha 값으로 정합니다.
alpha = 1이면 순수 벡터 검색alpha = 0이면 순수 키워드 검색alpha를 지정하지 않으면 유효 가중치는 사용 중인 클라이언트에 따라 달라집니다.
response = jeopardy.query.hybrid(query="food", alpha=0.25, limit=3)
퓨전 방식 바꾸기
v1.24부터 기본 퓨전 방식은 Relative Score Fusion이에요. 키워드·벡터 검색의 검색 순위 대신 상대 점수를 사용하려면 이 방식을 쓰면 되고, hybrid 연산자에 autocut을 함께 쓰려면 역시 Relative Score Fusion이 필요합니다.
from weaviate.classes.query import HybridFusion
response = jeopardy.query.hybrid(
query="food",
fusion_type=HybridFusion.RELATIVE_SCORE,
limit=3,
)
키워드(BM25) 검색 연산자
하이브리드 쿼리의 키워드 부분은 단독 키워드 검색과 같은 연산자를 받아요. 검색 문자열의 토큰 중 몇 개가 매칭돼야 하는지, 그리고 그 토큰들이 한 프로퍼티 안에 전부 있어야 하는지를 정합니다. 옵션은 or(기본값), and, 그리고 v1.38.8부터 추가된 and_cross예요.
or: 최소minimum match개수 이상 토큰이 매칭되면 반환.and: 모든 토큰이 하나의 프로퍼티 안에 함께 존재해야 반환.
from weaviate.classes.query import BM25Operator
# or 연산자 + 최소 2개 토큰 매칭
response = jeopardy.query.hybrid(
query="Australian mammal cute",
bm25_operator=BM25Operator.or_(minimum_match=2),
limit=3,
)
# and 연산자 (모든 토큰 필수)
response = jeopardy.query.hybrid(
query="Australian mammal cute",
bm25_operator=BM25Operator.and_(),
limit=3,
)
GraphQL에서는 이렇게 씁니다.
{
Get {
JeopardyQuestion(
limit: 3
hybrid: {
query: "Australian mammal cute"
bm25SearchOperator: {
operator: Or,
minimumOrTokensMatch: 2
}
}
) {
question
answer
}
}
}
검색할 프로퍼티 지정하기
하이브리드 검색의 키워드 부분을 특정 프로퍼티로 한정할 수 있어요. 벡터 검색 부분에는 영향을 주지 않습니다.
response = jeopardy.query.hybrid(
query="food",
query_properties=["question"],
alpha=0.25,
limit=3,
)
프로퍼티 가중치 설정
키워드 검색에서 객체 properties의 상대적 가치를 지정해요. 값이 클수록 그 프로퍼티가 점수에 더 많이 기여합니다. 아래 예시는 question에 2배 가중치를 줍니다.
response = jeopardy.query.hybrid(
query="food",
query_properties=["question^2", "answer"],
alpha=0.25,
limit=3,
)
검색 벡터 직접 지정
하이브리드의 벡터 부분은 검색 문자열 대신 쿼리 벡터를 직접 줄 수도 있어요. 벡터 검색용 벡터와 키워드 검색용 문자열을 함께 넘기면 됩니다.
query_vector = [-0.02] * 1536 # 객체 벡터와 호환되는 벡터
response = jeopardy.query.hybrid(
query="food",
vector=query_vector,
alpha=0.25,
limit=3,
)
벡터 검색 파라미터
near text나 near vector 검색처럼 group by, move to/move away 같은 벡터 검색 파라미터를 쓸 수 있어요. 벡터 검색의 유사도 임계값은 max vector distance 파라미터로 설정합니다.
from weaviate.classes.query import HybridVector, Move
response = jeopardy.query.hybrid(
query="California",
max_vector_distance=0.4, # 벡터 검색 부분의 최대 허용 거리
vector=HybridVector.near_text(
query="large animal",
move_away=Move(force=0.5, concepts=["mammal", "terrestrial"]),
),
alpha=0.75,
limit=5,
)
그룹핑(Group by)
결과를 특정 기준으로 묶어서 볼 수 있어요.
from weaviate.classes.query import GroupBy
group_by = GroupBy(
prop="round", # 이 프로퍼티 기준으로 그룹핑
objects_per_group=3, # 그룹당 최대 객체 수
number_of_groups=2, # 최대 그룹 수
)
response = jeopardy.query.hybrid(
alpha=0.75,
query="California",
group_by=group_by
)
limit & offset
limit으로 반환할 최대 객체 수를 정하고, 선택적으로 offset으로 페이지네이션을 합니다.
response = jeopardy.query.hybrid(
query="food",
limit=3,
offset=1
)
결과 그룹 제한하기 (autocut)
쿼리와 거리가 비슷한 그룹만 잘라내려면 autocut 필터를 쓰세요. 단, 하이브리드에서 autocut을 쓸 때는 **반드시 Relative Score Fusion**을 지정해야 해요. autocut은 실제 유사도 점수로 절단 지점을 찾기 때문이에요. 순위 위치만 쓰는 Ranked Fusion과는 함께 쓰면 안 됩니다.
from weaviate.classes.query import HybridFusion
response = jeopardy.query.hybrid(
query="food",
fusion_type=HybridFusion.RELATIVE_SCORE,
auto_limit=1
)
필터 적용
검색 결과를 좁히고 싶다면 filter를 사용합니다.
from weaviate.classes.query import Filter
response = jeopardy.query.hybrid(
query="food",
filters=Filter.by_property("round").equal("Double Jeopardy!"),
limit=3,
)
Boost로 부드럽게 순위 조정
v1.38부터 하이브리드 쿼리는 선택적으로 boost 인자를 받아요. 매칭되는 문서를 제거하지 않고 위로 끌어올리거나 아래로 내립니다. 최신성, 인기도, 소프트 필터 같은 신호로 결과를 치우치게 할 때 유용합니다.
부스트는 퓨전된 하이브리드 결과에 한 번 적용돼요. BM25와 벡터 서브 검색은 부스트를 보지 못합니다. 하이브리드의 alpha 블렌드가 먼저 실행된 뒤, 그 결과 풀을 부스트가 재점수화하는 구조예요. 지원 조건 타입(filter, 프로퍼티 값, 시간 감쇠, 숫자 감쇠)과 곡선·블렌딩·깊이 조절은 Boost를 참고하세요.
다양성 선택 (MMR)
:::info v1.38.6에서 추가 :::
하이브리드 검색은 키워드 결과 집합과 벡터 결과 집합을 퓨전하는데, 흔히 퓨전 리스트 맨 위가 거의 중복인 클러스터가 되기 쉽습니다. **MMR(Maximal Marginal Relevance)**은 관련성과 다양성의 균형을 맞춰 그 리스트를 재순위화해요. 각각의 선택된 객체가 결과 집합에 새로운 것을 더하도록 하는 거죠.
다양성 선택은 퓨전 이후에 실행됩니다. 두 검색 다리가 먼저 돌고, 그 결과를 설정된 alpha와 퓨전 방식으로 합친 뒤, 다양성 패스가 퓨전 후보 중에서 다양한 부분집합을 고릅니다.
중요한 노트:
- 최상위 레벨에서만: 다양성 선택은 하이브리드 쿼리 자체에 설정하세요. 서브 검색에 설정하면 오류로 거부됩니다.
- 두 개의 limit: 쿼리 최상위
limit는 다양화될 후보 윈도우이고, 다양성limit는 반환되는 결과 수예요. 다양성 limit은 최소1이상, 쿼리 limit 이하여야 합니다. - 정렬: 결과는 퓨전 점수 순서가 아니라 MMR 순서로 돌아옵니다.
- 페이지네이션:
offset은 후보 윈도우를 옮기므로, 반환된 객체 수가 아니라 쿼리 limit만큼 전진해야 해요. Weaviate는 이를 검증하지 않아, 틀리면 페이지 사이에서 객체가 조용히 반복·누락됩니다. - 미지원: 멀티벡터 컬렉션. 해당 쿼리는 오류로 거부됩니다.