MinHash 함수 (MinHash Function) — Milvus 3.0.x 호환
MinHash 함수는 원시 텍스트를 문서 간 Jaccard 유사도를 근사하는 **이진 벡터(binary vector)**로 변환해요. 텍스트 샤잉(shingling)과 여러 해시 함수를 적용해 고정 길이의 서명(signature) 벡터를 만들어, 대규모에서 빠른 near-duplicate 탐지와 문서 중복 제거(deduplication)를 가능하게 해요.
내장 함수로서 MinHash는 Milvus 안에서 동작하며 외부 모델 추론이나 전처리가 필요 없어요. 원시 텍스트만 넣으면 Milvus가 MinHash 서명 벡터를 자동으로 생성해요.
출처: Milvus 문서
본문
제한 사항 (Limits)
- 출력 필드는
BINARY_VECTOR여야 하고, 각 MinHash 서명이 32비트 해시 값이므로 차원이dim % 32 == 0을 만족해야 해요. - 이진 벡터 필드의
dim은32 * num_hashes와 같아야 해요. 일치하지 않으면 오류가 발생해요. - MinHash 함수 출력에
MINHASH_LSH인덱스를 사용할 때는mh_element_bit_width를32로 설정해야 해요.
MinHash는 어떻게 동작하나요? (How MinHash works)
MinHash는 집합 간 Jaccard 유사도를 추정하는 locality-sensitive hashing 기법이에요. Milvus에서 MinHash 함수는 다음 파이프라인을 따라요. 입력으로 원시 텍스트를 주면 Milvus가 출력으로 이진 벡터를 생성해요 — 중간 단계는 모두 내부에서 처리해요.
전체 워크플로는 문서 수집과 쿼리 처리 양쪽이 공유하는 공통 텍스트 처리 파이프라인과, 저장·검색을 위한 단계별 작업으로 구성돼요.
공통 텍스트 처리 파이프라인 (Shared text processing pipeline)
문서 수집과 쿼리 처리는 모두 원시 텍스트를 같은 네 단계 변환에 통과시켜요:
- 텍스트 분석 (Text analysis): 텍스트는 analyzer로 처리되거나(
token_level이"word"일 때), 직접 사용돼요(token_level이"char"일 때). 단어 수준 토큰화는 입력 필드에 구성된 analyzer를 적용해 텍스트를 용어로 나눠요. 예를 들어"milvus is vector db"는["milvus", "is", "vector", "db"]가 돼요. - 샤잉 (Shingling): 토큰을
shingle_size크기의 겹치는 n-gram(샤잉)으로 나눠요. 예를 들어 단어 수준 3-gram에서 토큰["information", "retrieval", "is", "a", "field"]는["information retrieval is", "retrieval is a", "is a field"]같은 샤잉이 돼요. - MinHash 서명 생성 (MinHash signature generation): 여러 해시 함수(H1, H2, …, Hn, 여기서 n =
num_hashes)를 샤잉 집합에 적용해요. 각 해시 함수에 대해 모든 샤잉 중 최솟값이 선택돼요. 이 최솟값들의 모음이 MinHash 서명을 이뤄요 — 원본 문서의 Jaccard 유사도를 근사하는 고정 길이 표현이에요. - 이진 벡터 인코딩 (Binary vector encoding): 각 서명 값은 32비트 해시이고, 전체 서명은 차원
32 * num_hashes의BINARY_VECTOR로 패킹돼요.
문서 수집 (Document ingestion)
삽입 중에 공통 파이프라인이 만든 이진 벡터는 MINHASH_LSH 인덱스에 저장돼요. 이 인덱스는 비슷한 서명을 같은 버킷으로 묶는 LSH(Locality-Sensitive Hashing) 테이블을 유지해, 쿼리 시점에 빠른 후보 검색을 가능하게 해요.
쿼리 처리 (Query processing)
검색 중에 쿼리 텍스트는 같은 공통 파이프라인을 거쳐 이진 벡터를 만들어요. 이 벡터로 MINHASH_LSH 인덱스에서 LSH 조회를 수행해 유사할 가능성이 높은 후보 쌍을 빠르게 식별해요. Jaccard 정제(refinement) 없이는 Milvus가 추정 Jaccard 유사도로 순위를 매기지 않은 LSH 후보를 반환해요. Jaccard 정제를 켜면 Milvus가 저장된 원시 MinHash 서명을 사용해 후보를 추정 Jaccard 유사도 순으로 정렬하고 상위 K개 결과를 반환해요.
두 경로가 같은 변환 로직을 공유하므로 내용이 크게 겹치는 두 문서는 비슷한 MinHash 서명을 만들어요. 덕분에 단어 순서, 포맷, 사소한 표현 차이가 있더라도 near-duplicate를 찾는 데 효과적이에요.
시작하기 전에 (Before you start)
MinHash 함수를 사용하기 전에 컬렉션 스키마에 다음을 포함하도록 계획하세요:
-
원시 내용용 텍스트 필드 컬렉션에는 원시 텍스트를 저장할
VARCHAR필드가 있어야 해요. 이 필드가 MinHash 함수의 입력이 돼요. -
텍스트 필드용 analyzer (단어 수준 토큰화 사용 시)
token_level을"word"(기본값)로 설정하면 텍스트 필드에 analyzer가 활성화되어 있어야 해요. analyzer는 샤잉 전에 텍스트가 어떻게 토큰화되는지 정의해요. 기본적으로 Milvus는standardanalyzer를 사용해요. 다른 analyzer를 구성하려면 Choose the Right Analyzer for Your Use Case를 참고하세요. -
MinHash 출력용 이진 벡터 필드 컬렉션에는 MinHash 함수가 생성한 이진 벡터를 저장할
BINARY_VECTOR필드가 있어야 해요. 차원은32 * num_hashes와 같아야 해요.
1단계: MinHash 함수로 컬렉션 만들기 (Step 1: Create a collection with a MinHash function)
MinHash 함수를 사용하려면 컬렉션을 만들 때 정의해요. 함수는 컬렉션 스키마의 일부가 되며 데이터 삽입과 검색 중에 자동으로 적용돼요.
스키마 필드 정의 (Define schema fields)
컬렉션 스키마에는 최소 세 개의 필드가 있어야 해요:
- 기본 필드 (Primary field): 컬렉션의 각 엔터티를 고유하게 식별해요.
- 텍스트 필드 (
VARCHAR): 원시 텍스트 문서를 저장해요. Milvus가 MinHash 서명 생성용 텍스트를 처리할 수 있도록enable_analyzer=True로 설정해요. 기본적으로 Milvus는 텍스트 분석에standardanalyzer를 사용해요. 다른 analyzer를 구성하려면 Choose the Right Analyzer for Your Use Case를 참고하세요. - 이진 벡터 필드 (
BINARY_VECTOR): MinHash 함수가 자동 생성한 이진 벡터를 저장해요. 차원은32 * num_hashes와 같아야 해요.
Python
from pymilvus import MilvusClient, DataType, Function, FunctionType
client = MilvusClient(uri="http://localhost:19530", token="root:Milvus")
schema = client.create_schema()
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True, auto_id=True)
schema.add_field(field_name="document_content", datatype=DataType.VARCHAR, max_length=9000, enable_analyzer=True)
schema.add_field(field_name="binary_vector", datatype=DataType.BINARY_VECTOR, dim=8192)
Java
// java
NodeJS
// nodejs
Go
// go
cURL
# restful
MinHash 함수 정의 (Define the MinHash function)
MinHash 함수는 분석된 텍스트를 문서 간 Jaccard 유사도를 근사하는 이진 벡터로 변환해요.
함수를 정의하고 스키마에 추가해요:
Python
minhash_function = Function(
name="minhash_function",
input_field_names=["document_content"], # Name of the VARCHAR field containing raw text
output_field_names=["binary_vector"], # Name of the BINARY_VECTOR field for generated signatures
function_type=FunctionType.MINHASH,
params={
"num_hashes": 256, # Number of hash functions; produces dim = 32 * 256 = 8192
"shingle_size": 3, # N-gram size for shingling
}
)
schema.add_function(minhash_function)
Java
// java
NodeJS
// nodejs
Go
// go
cURL
# restful
구성 옵션
MinHash 함수의 params 딕셔너리는 다음 파라미터를 받아요. 모든 파라미터 이름은 대소문자를 구분하지 않아요.
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
num_hashes |
int | dim / 32에서 유도 |
서명 생성을 위한 해시 함수 수예요. 출력 이진 벡터 차원은 32 * num_hashes와 같아요. 값이 클수록 유사도 추정의 분산이 줄지만 계산량이 늘어나요. 권장: 256 (dim = 8192). |
shingle_size |
int | 3 |
샤잉을 위한 n-gram 크기예요. 단어 수준: 1–3이 일반적이에요. 문자 수준: 2–6이 일반적이에요. |
hash_function |
str | "xxhash" |
사용할 해시 함수예요. 옵션: - "xxhash" (빠름)- "sha1" (더 느리지만 충돌 저항이 더 높음). |
token_level |
str | "word" |
토큰화 수준이에요. 옵션: - "word": 필드의 analyzer로 토큰화한 뒤 n-gram 샤잉을 적용해요.- "char" / "character": analyzer 없이 원시 문자에 n-gram 샤잉을 직접 적용해요.단어 수준은 더 강한 의미론과 높은 효율을 주지만 언어별 토큰화에 의존해요. 문자 수준은 언어와 무관하지만 차원이 더 높은 샤잉과 약한 의미론을 만들어요. |
seed |
int | 1234 |
MinHash 함수 초기화용 난수 시드예요. |
인덱스 구성 (Configure the index)
MinHash 이진 벡터에 권장되는 인덱스 타입은 MINHASH_LSH이고, 메트릭 타입은 MHJACCARD예요.
Python
index_params = client.prepare_index_params()
index_params.add_index(
field_name="binary_vector",
index_type="MINHASH_LSH",
metric_type="MHJACCARD",
params={
"mh_lsh_band": 128,
"mh_element_bit_width": 32,
"with_raw_data": True,
},
)
Java
// java
NodeJS
// nodejs
Go
// go
cURL
# restful
검색이 Jaccard 정제를 사용할 거라면 with_raw_data를 True로 설정해요. LSH 조회가 반환한 후보의 추정 Jaccard 유사도를 계산하려면 원시 MinHash 서명이 필요해요.
컬렉션 만들기 (Create the collection)
위에서 정의한 스키마와 인덱스 파라미터로 컬렉션을 만들어요:
Python
client.create_collection(
collection_name="dedup_collection",
schema=schema,
index_params=index_params,
)
Java
// java
NodeJS
// nodejs
Go
// go
cURL
# restful
2단계: 문서 삽입 (Step 2: Insert documents)
컬렉션을 설정한 뒤 텍스트 데이터를 삽입해요. 원시 텍스트만 제공하면 돼요 — MinHash 함수가 각 문서의 이진 벡터를 자동으로 생성해요.
Python
client.insert(
"dedup_collection",
[
{"document_content": "information retrieval is a field of study that helps users find relevant information in large datasets"},
{"document_content": "information retrieval is a research field focused on helping users find relevant data in large collections"},
{"document_content": "information retrieval is a field of research helping users search for relevant information in large datasets"},
],
)
Java
// java
NodeJS
// nodejs
Go
// go
cURL
# restful
3단계: MinHash로 검색하기 (Step 3: Search with MinHash)
데이터를 삽입했다면 원시 텍스트 쿼리를 제공해 near-duplicate 문서를 검색해요. Milvus가 각 쿼리를 MinHash 이진 벡터로 자동 변환해요. Jaccard 정제를 켜면 LSH 후보를 추정 Jaccard 유사도 순으로 정렬해 줘요.
Python
search_params = {
"metric_type": "MHJACCARD",
"params": {
"mh_search_with_jaccard": True,
"refine_k": 3,
},
}
results = client.search(
collection_name="dedup_collection",
data=["information retrieval is a research field focused on helping users find relevant data in large collections"],
anns_field="binary_vector",
limit=3,
output_fields=["document_content"],
search_params=search_params,
)
for hits in results:
for hit in hits:
print(f"ID: {hit['id']}, Distance: {hit['distance']}")
print(f"Document: {hit['entity']['document_content']}")
Java
// java
NodeJS
// nodejs
Go
// go
cURL
# restful
Jaccard 정제를 켜려면 mh_search_with_jaccard를 True로 설정해요. refine_k는 정제에 사용할 후보 풀 용량을 제어해요. Milvus는 max(refine_k, limit)을 용량으로 사용하지만, LSH 조회가 더 적은 매치를 반환하면 더 적은 후보만 정제할 수 있어요. refine_k를 키우면 추가 계산 비용으로 결과 품질을 높일 수 있어요.
더 알아보기 (Learn more)
- Full Text Search: near-duplicate 탐지 대신 BM25로 어휘 관련성 순위를 매기려면.
- Analyzer Overview: 텍스트 토큰화용 사용자 지정 analyzer 구성하기.
- MINHASH_LSH Index: recall과 성능을 위한 LSH 파라미터 튜닝 배우기.