Pymilvus란 무엇인가요?
소개
Pymilvus는 Milvus와 Zilliz Cloud를 위해 구축된 Python SDK입니다. 모든 SDK에서 공유되는 공통 Milvus protobuf를 사용하는 gRPC 기반 클라이언트입니다. 신규 사용자와 고급 사용자 모두에게 Milvus가 제공하는 모든 기능에 대한 접근을 제공하며, 가장 인기 있는 SDK 중 하나입니다.
우리가 발견한 문제들
Milvus 벡터 데이터베이스의 핵심 아이디어는 사용자가 자신의 특정 사용 사례에 맞게 시스템을 세밀하게 조정할 수 있도록 가능한 한 많은 조절 수단을 제공하는 것입니다. 그러나 근사 검색은 본질적으로 근사적이며, 모든 상황에 맞는 단일 알고리즘은 없습니다. 각 알고리즘은 연산량 vs. 속도 vs. 재현율/정확도 측면에서 장점을 보입니다. 더 나아가, 각 알고리즘은 앞선 범주들 간의 서로 다른 균형을 달성하도록 구성할 수 있습니다. 예를 들어 HNSW와 IVF-PQ를 비교해 보겠습니다. IVF-PQ는 연산량과 속도에 최적화되어 있으며, 재현율이 크게 감소하는 대가로 메모리 부담과 검색 시간을 크게 줄입니다. HNSW는 그 반대입니다. HNSW는 속도와 재현율을 위해 연산 자원을 희생합니다. 흥미로운 점은 이들의 강점을 설정을 통해 서로 바꿀 수 있다는 것입니다. 이 모든 설정은 인덱스 수준에서만 이루어집니다. 클러스터 수준에서는 비용 vs 속도 측면에서 균형을 맞춰야 하며, 이는 사용자마다 고유합니다. 어떤 사용자는 고가용성에 관심이 없고 복제를 원하지 않을 수 있습니다. 어떤 사용자는 일관성에 관심이 없고 속도의 이점을 위해 일관성을 낮추는 것을 선호할 수 있습니다. 어떤 사용자는 스토리지 비용을 줄이기 위해 데이터에 TTL을 붙이고 싶어 할 수 있는 반면, 다른 사용자는 모든 작업이 백업되기를 원할 수 있습니다. 이러한 사용자 정의 가능성은 수십억 규모의 데이터셋에서 균형을 최대한 끌어내는 것이 유익한 대규모 사용자에게는 훌륭하지만, 소규모 사용자에게는 이 모든 조절 수단이 너무 많은 혼란을 더할 뿐입니다.
이러한 조절 수단이 문제가 되는 것 외에도, 현재 Zilliz Cloud와 Milvus는 인덱스와 연결 파라미터 때문에 서로 호환 가능하게 사용할 수 없습니다.
MilvusClient란 무엇인가
MilvusClient는 대부분의 사용자를 위해 API를 단순화하려는 시도입니다. 많은 사용자는 연결, 스키마, 인덱싱, 로딩, 파라미터 등을 다루고 싶어 하지 않습니다. MilvusClient는 Milvus와 Zilliz 모두에서 동일한 간단한 API로 Pymilvus SDK를 래핑하여 이러한 모든 측면을 숨깁니다. 현재 제공되는 기능은 다음과 같습니다:
- insert_data()
- upsert_data()
- search_data()
- query_data()
- get_vectors_by_pk()
- delete_by_pk()
- add_partition()
- remove_partition()
이 함수들은 기본 사용자라면 누구나 필요로 할 핵심 기능으로 결정되었습니다. 현재 시점에서 이러한 작업들은 Pymilvus가 제공하지만, 올바르게 작동하도록 보장하기 위해 수행해야 할 추가 작업이 많이 있습니다.
insert_data:
MilvusClient를 사용하면 컬렉션을 위한 스키마를 만들 필요가 없습니다. 대신 삽입된 데이터에서 스키마가 자동 생성됩니다. 여기에는 필요한 FieldSchema를 결정하고 이를 어떻게 구성할지 정하는 작업이 포함됩니다. 많은 사용자들이 이 스키마를 만드는 것을 불편한 지점으로 느꼈기 때문에, 우리는 이를 내부적으로 처리하고 있습니다. 동적 스키마가 지원되면 사용자 변경 없이 구현을 변경할 수 있습니다.
def _infer_fields(self, data):
"""Infer all the fields based on the input data."""
# TODO: Assuming ordered dict for 3.7
fields = {}
# Figure out each datatype of the input.
for key, value in data.items():
# Infer the corresponding datatype of the metadata
dtype = infer_dtype_bydata(value)
# Datatype isnt compatible
if dtype in (DataType.UNKNOWN, DataType.NONE):
logger.error(
"Failed to parse schema for collection %s, unrecognized dtype for key: %s",
self.collection_name,
key,
)
raise ValueError(f"Unrecognized datatype for {key}.")
# Create an entry under the field name
fields[key] = {}
fields[key]["name"] = key
fields[key]["dtype"] = dtype
# Area for attaching kwargs for certain datatypes
if dtype == DataType.VARCHAR:
fields[key]["max_length"] = 65_535
return fields
MilvusClient의 경우, 우리는 이 분야의 다른 프로젝트들과 유사한 데이터 형식, 즉 딕셔너리의 리스트를 유지하고자 했습니다. 이 형식은 이해하고 사용하기 쉬우며 LlamaIndex와 LangChain에서 볼 수 있는 Documents와 동등합니다. 그러나 이 형식은 pymilvus와 호환되지 않습니다. pymilvus의 insert는 리스트의 리스트 형태인 컬럼형 데이터를 받기 때문입니다. 이 컬럼형 데이터 형식은 스키마가 작성된 정확한 순서에 맞춰 리스트를 정렬해야 하며 유연성을 제공하지 않았기 때문에 사용하기 쉽지 않습니다. 또한, 잘못된 형식의 데이터에 대해 받은 오류 메시지도 개선의 여지가 있었습니다.
for k in data:
for key, value in k.items():
if key in self.fields:
insert_dict.setdefault(key, []).append(value)
for i in self.tqdm(range(0, len(data), batch_size), disable=not progress_bar):
# Convert dict to list of lists batch for insertion
try:
insert_batch = [
insert_dict[key][i : i + batch_size]
for key in self.fields
if key != ignore_pk
]
향후 schema, JSON 등과 관련된 모든 변경 사항을 고려할 때, 단순한 insert를 감싸는 래퍼를 두면 복잡한 작업은 내부에서 처리하고 사용자에게는 간단한 API를 제공할 수 있습니다.
upsert_data:
버전 2.2에서는 Milvus에 upsert가 존재하지 않습니다. upsert를 수행하려면 delete -> insert를 수행해야 합니다. 많은 사용자가 이 기능을 찾고 있기 때문에, 우리는 이를 클라이언트에 포함하기로 결정했습니다. 해당 기능이 pymilvus에 추가되면, 사용자 코드의 변경을 요구하지 않고도 쉽게 변경할 수 있습니다.
pks = [x[self.pk_field] for x in data]
self.delete_by_pk(pks, timeout)
ret = self.insert_data(
data=data,
timeout=timeout,
batch_size=batch_size,
partition=partition,
progress_bar=progress_bar,
)
search_data:
search 명령의 주요 수정 사항은 기본 search params와 출력을 딕셔너리 리스트 출력으로 변환하는 것이었습니다.
ret = []
for hits in res:
query_result = []
for hit in hits:
ret_dict = {x: hit.entity.get(x) for x in return_fields}
query_result.append({"score": hit.score, "data": ret_dict})
ret.append(query_result)
query_data:
query 명령의 주요 수정 사항은 출력을 딕셔너리 리스트로 변환하는 것이었습니다.
get_vectors_by_pk:
pymilvus에서 벡터를 추출하는 작업은 querying을 통해 수행됩니다. 많은 사용자가 모르는 점은 query가 primary key filter를 기반으로 수행되어야 한다는 것입니다. 여기에 더해, varchar primary key를 사용하는 경우 query의 expression에 이스케이프된 따옴표가 필요하며, 이는 사용자에게 번거롭고 잘 알려져 있지 않은 부분입니다.
# Varchar pks need double quotes around the values
if self.fields[self.pk_field] == DataType.VARCHAR:
ids = ['"' + str(entry) + '"' for entry in pks]
expr = f"""{self.pk_field} in [{','.join(ids)}]"""
else:
ids = [str(entry) for entry in pks]
expr = f"{self.pk_field} in [{','.join(ids)}]"
delete_by_pk:
get_vectors_by_pk와 유사합니다.
add_partition and delete_partition:
partition 로직의 경우, 사용자는 partition을 변경하려면 collection을 unload하고 load해야 한다는 점을 알아야 합니다. 이제 이는 내부에서 처리됩니다.
Conclusion:
전반적으로, 이 클라이언트의 주요 목표는 pymilvus 측에 존재하지 않거나 최적화되지 않은 사용하기 쉬운 작업을 추가하는 것입니다. pymilvus가 개선됨에 따라, 우리는 내부적으로 이러한 작업을 최적화하면서도 사용하기 쉬운 간단한 API를 유지할 수 있을 것입니다.
계속 읽기

What Is a Vector Lakebase?
A Vector Lakebase is a unified, lake-native data architecture for AI that combines vector-database-grade serving with open lake storage, reusable lake-level indexes, and a shared semantic layer.

Why We Built Vector Lakebase: Rethinking Unstructured Data Architecture for AI
Vector Lakebase: a unified, lake-native data foundation for AI workloads — and an answer to what happens after vector databases succeed.

AI Integration in Video Surveillance Tools: Transforming the Industry with Vector Databases
Discover how AI and vector databases are revolutionizing video surveillance with real-time analysis, faster threat detection, and intelligent search capabilities for enhanced security.



