크리스티 버그먼: 제가 Zilliz에 합류한 이유
제 이름은 Christy Bergman이며, 최근 San Francisco에 기반을 둔 Developer Advocate로 Zilliz에 합류했습니다. 저는 이벤트를 조직하고, 참석하고, 발표하며, 블로그와 코드 튜토리얼을 작성하고, 문서를 개선하고, 전반적으로 사람들(바라건대 여러분 같은 분들!)이 세계에서 가장 인기 있는 오픈 소스 벡터 데이터베이스(GitHub stars 기준)인 Milvus를 사용하는 방법을 배울 수 있도록 돕게 될 것입니다.
Zilliz 이전에는 모든 개발자가 분산 컴퓨팅을 쉽게 사용할 수 있도록 하는 오픈 소스 Ray의 창시자인 Anyscale에서 Developer Advocate로 일했습니다. 그 전에는 딥 러닝 예측에 중점을 둔 AWS AI/ML Specialist Solutions Architect였습니다.
그렇다면 왜 Zilliz에 합류하기로 했을까요? 그것은 프롬프트에 컨텍스트를 주입하여 ChatGPT 채팅 출력을 개선하는 것에 대한 호기심에서 시작되었습니다. 그런 다음 임베딩을 tinkering해보는 탐구를 시작했고, 거기서부터 사용하기 쉽고 빠른 벡터 데이터베이스를 찾기 시작했습니다. 이 블로그 글에서는 제가 여기까지 오게 된 단계들을 안내해 드리겠습니다.
의문: 정말 벡터 데이터베이스가 필요할까?
처음에는 벡터 데이터베이스가 과연 필요한지 의문이 들었습니다. 하지만 텍스트를 Python 변수에 임베딩하고, pickle로 저장했다가 다시 불러오는 몇 번의 시도를 해본 후, 임베딩은 pickle보다 더 나은 어딘가에 캐시되거나 영속화되어야 한다고 믿게 되었습니다.
Milvus는 처음부터 벡터를 위해 목적에 맞게 구축된 완전한 데이터베이스입니다. 전 세계 수천 명의 개발자가 Milvus를 사용하고 있으며, 여기에는 다양한 벡터 검색 알고리즘, 벡터 공간 거리 메트릭, Insert/upsert, TopK 및 Range Search, GPU 지원 등과 같은 기타 기능이 포함됩니다. 상용 버전은 Zilliz Cloud라고 하며 99.9% SLA 가용성, 자동 확장 기능을 갖추고 AWS와 GCP에서 실행되며(Azure는 곧 지원 예정), 완전한 SOC2 compliance를 갖춘 상태로 여러분의 클라우드 계정에서 실행할 수 있습니다.
옵션 탐색: 다양한 벡터 데이터베이스 시도하기
임베딩을 벡터 데이터베이스에 넣으려면 다음이 필요합니다: 1) 비정형 데이터, 2) 해당 데이터를 청크로 나누는 방법에 대한 계획, 3) 임베딩 모델, 4) 벡터 데이터베이스.
데이터로는 Stanford AI Lab의 IMDB large movie review dataset을 사용했습니다. 이는 편리하게 처리된 50,000개 데이터셋입니다(긍정/부정 리뷰 50:50 샘플 비율). 이 데이터에는 movie_index, 원본 리뷰 텍스트, 영화 평점 열이 있습니다.
청킹의 경우, 매우 긴 경우가 아니라면 전체 영화 리뷰를 그대로 유지하기로 했습니다. 매우 긴 리뷰의 경우 일반적인 기본값인 512 길이 청크를 사용했습니다. 즉, 긴 리뷰는 여러 개의 512자 길이 조각으로 나뉘었습니다. LLM 용어로는 이것을 “chunk size”라고 합니다.
임베딩 모델의 경우, 널리 쓰이는 OpenAI embeddings를 포함해 다양한 모델을 시도했습니다. 그러다가 가장 쉬운 방법은 MTEB HuggingFace leaderboard의 Retriever 탭에서 가장 작고, 최상위에 랭크되어 있으며, 항상 변하는 대상을 선택하는 것이라는 사실을 알아냈습니다. 당시 저는 “e5-base-v2”로 정했습니다. 임베딩 모델의 선택이 벡터 데이터베이스 자체와 독립적이라는 것은 사실 좋은 점입니다.
데이터베이스로는 FAISS, Qdrant, Chroma, Weaviate, Pinecone, 그리고 물론 Milvus를 시도했습니다. 저는 모두에게 동일한 기준을 적용했습니다: 1) pandas에서 벡터 데이터베이스로 직접 로드하는 것이 얼마나 쉽고 빠른가? 2) 벡터 데이터베이스에서 좋은 쿼리 결과를 얻는 것이 얼마나 쉽고, 빠르고, 조정 가능한가? 3) LangChain을 사용해 RAG를 수행하는 것이 얼마나 쉽고 조정 가능한가(향후 블로그 글!).
Milvus가 돋보이다
제가 살펴본 모든 데이터베이스 중에서 Milvus가 여러 가지 이유로 제 관심을 끌었습니다. 첫째, 특히 메타데이터와 함께 pandas에서 직접 데이터를 삽입할 때 사용자 친화적인 경험을 제공했는데, 이는 데이터 사이언티스트가 처음 시도해 볼 만한 접근 방식이기 때문입니다.
Milvus는 벡터 로딩과 쿼리 속도 때문에도 제 관심을 끌었습니다. Milvus는 다른 접근 방식보다 눈에 띄게 더 빠릿했으며, 일부는 이렇게 작은 규모에서도 느렸습니다. 또한 기능들을 살펴보았고, Milvus가 다른 선택지도 지원하지만 다른 데이터베이스에는 기본 설정(IVF-flat 완전 탐색, L2-metric)을 선택했습니다.
아래는 pandas 데이터 프레임을 Milvus에 삽입하기 위한 gist입니다. 전체 코드는 제 GitHub에 있습니다.
# 전체 코드는 다음에 있습니다: https://github.com/christy/ZillizDemos/blob/main/milvus_onboarding/hello_world_milvus.ipynb
###########
# 1. 로컬에서 사용하기 위해 HuggingFace Hub에서 sentence transformer를 다운로드합니다.
###########
import os
from dotenv import load_dotenv, find_dotenv
from huggingface_hub import login
_ = load_dotenv(find_dotenv())
hub_token = os.getenv("HUGGINGFACEHUB_API_TOKEN")
login(token=hub_token);
from sentence_transformers import SentenceTransformer
model_name = "BAAI/bge-base-en-v1.5"
retriever = SentenceTransformer(model_name, device="cuda")
# 모델 매개변수를 가져와 나중을 위해 저장합니다.
MAX_SEQ_LENGTH = retriever.get_max_seq_length()
HF_EOS_TOKEN_LENGTH = 1
EMBEDDING_LENGTH = retriever.get_sentence_embedding_dimension()
###########
# 2. 편의를 위해 LangChain에서 chunking 함수를 선택합니다.
###########
from langchain.text_splitter import RecursiveCharacterTextSplitter
chunk_size = MAX_SEQ_LENGTH - HF_TOKEN_EOS_LENGTH
chunk_overlap = np.round(chunk_size * 0.10, 0)
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
length_function=len,
)
def chunk_text(text):
chunks = text_splitter.split_text(text)
return [chunk for chunk in chunks if chunk]
###########
# 3. pandas dataframe을 조작합니다.
# 다운로드: https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz
###########
# 1. pandas DataFrame에서 데이터 배치.
batch = df.head(100).copy()
# 2. 기본 키 유형을 문자열로 변경합니다.
batch["movie_index"] = batch["movie_index"].apply(lambda x: str(x))
# 3. 리뷰를 512자로 자릅니다.
batch['chunk'] = batch['text'].apply(chunk_text)
batch = batch.explode('chunk', ignore_index=True)
# 4. df에 새 열로 임베딩을 추가합니다.
review_embeddings = torch.tensor(retriever.encode(batch['chunk']))
# 임베딩을 단위 길이로 정규화합니다.
review_embeddings = F.normalize(review_embeddings, p=2, dim=1)
# 5. 임베딩을 `numpy.ndarray`의 목록으로 변환하며, 각 배열은 `numpy.float32` 숫자를 포함합니다.
converted_values = list(map(np.float32, review_embeddings))
batch['embeddings'] = converted_values
# 6. 편의를 위해 열을 재정렬하여 index를 먼저, labels를 끝에 둡니다.
new_order = ["movie_index", "text", "chunk", "embeddings", "label_int", "label"]
batch = batch[new_order]
###########
# 4. milvus를 설치하고 가져옵니다.
###########
!pip install milvus pymilvus
import milvus, pymilvus
###########
# 5. Milvus에 데이터 로딩을 위한 schema를 정의합니다.
###########
# Milvus collection 이름을 설정합니다.
COLLECTION_NAME = "movies"
fields = [
FieldSchema(name="pk", dtype=DataType.INT64, is_primary=True, auto_id=True),
FieldSchema(name="movie_index", dtype=DataType.VARCHAR, max_length=8),
FieldSchema(name="chunk", dtype=DataType.VARCHAR, max_length=MAX_SEQ_LENGTH),
FieldSchema(name="embeddings", dtype=DataType.FLOAT_VECTOR, dim=EMBEDDING_LENGTH),
FieldSchema(name="label_int", dtype=DataType.INT64),
FieldSchema(name="label", dtype=DataType.VARCHAR, max_length=8),
]
schema = CollectionSchema(fields, "Search imdb movie reviews")
mc = Collection(COLLECTION_NAME, schema, consistency_level="Eventually")
###########
6. 로컬 Milvus 서버(lite)를 시작합니다.
###########
default_server.start()
connections.connect(host='127.0.0.1',
port=default_server.listen_port,
show_startup_banner=True)
###########
# 7. Milvus에 데이터를 삽입합니다.
###########
# 기본값을 사용하는 대신 인덱스 매개변수를 변경하는 방법을 보여줍니다.
index_params = {
"index_type": "HNSW",
"metric_type": "COSINE",
"params": {'M': 16, # int. 4~64, num_layers
"efConstruction": 32} # int. 8~512, num_nearest_neighbors
}
mc.create_index("embeddings", index_params)
insert_result = mc.insert(batch)
# 마지막 엔티티가 삽입된 후, flush를 호출하여 메모리에 남아 있는 growing segment를 중지합니다.
mc.flush()
###########
# 8. 데이터베이스를 쿼리합니다.
###########
# 검색이나 쿼리를 수행하기 전에, 데이터를 메모리에 로드해야 합니다.
mc.load()
# 데이터에 대한 샘플 질문을 정의합니다.
query = "I'm a medical doctor, what movie should I watch?"
# Milvus 컬렉션을 만드는 데 사용한 것과 동일한 임베딩 모델을 사용해 쿼리를 임베딩합니다.
query_embeddings = torch.tensor(retriever.encode([query]))
# 임베딩을 단위 길이로 정규화합니다.
query_embeddings = torch.nn.functional.normalize(query_embeddings, p=2, dim=1)
# 임베딩을 np.float32의 list of list로 변환합니다.
query_embeddings = list(map(np.float32, query_embeddings))
# HNSW 인덱스로 벡터 검색을 실행합니다.
TOP_K = 3
search_params = {
"M": 16,
"ef": 32,
}
results = mc.search(
data=query_embeddings,
anns_field="embeddings",
param=search_params,
output_fields=["movie_index", "chunk", "label"],
limit=TOP_K,
consistency_level="Eventually"
)
제 (빠르고 비공식적인) 테스트를 마친 후, Zilliz의 Developer Advocate 채용 공고에 지원하기 위해 온라인 양식을 작성했습니다. 마지막 면접을 본 뒤, Zilliz 팀은 빠르게 저에게 오퍼를 제안했습니다. 그래서 망설임 없이 수락했습니다!
저에게는 훌륭한 동료들이 있습니다. 그들이 왜 Zilliz에 합류했는지는 Yujian Tang과 Frank Liu의 블로그를 참고하세요. 제 상사인 Chris Churilo는 우리 모두가 이렇게 효율적으로 함께 일할 수 있게 해 주는 숨은 접착제 같은 존재입니다. 그녀는 자신의 지식과 경험을 활용해 우리 모두가 빛나게 해 줍니다. 제 동료 Filip Haltmayer는 온보딩 기간 동안 Milvus와 Zilliz에 관한 모든 것을 저에게 설명해 주는 데 있어 매우 인내심이 많고 뛰어났습니다. 우리 CEO인 Charles Xie는 2017년에 특히 비정형 데이터를 위한 데이터베이스를 구축하겠다는 최초의 비전을 가지고 있었습니다.
다음은 무엇일까요
Milvus를 위한 evangelism 업무를 시작하면서, AI 개발자 커뮤니티의 여러분 모두를 알아가게 되기를 기대합니다! 저는 샌프란시스코에서 Unstructured Data Meetup을 조직할 예정이며, 항상 훌륭한 연사, 장소, 공동 주최자를 찾고 있습니다(연락 주세요!). 블로그 글을 더 쓰고 발표도 더 많이 할 예정입니다(연락해 주세요!). 비정형 데이터 프로젝트에서 오픈소스 Milvus를 사용해 보고 싶다면, 제가 어떻게 도울 수 있을지 알려 주세요!
저는 DataKind, Women in Data Science, 그리고 Code for America의 San Francisco Civil Hack nights 등 여러 데이터 과학을 통한 선한 영향력 조직에 참여하고 있습니다(이 모임을 통해 제 지역의 Sonoma Safe Agriculture 조직을 알게 되었고, 그곳에서 interactive pesticides map을 만들었습니다). 제가 새롭게 배운 벡터 데이터베이스 지식을 더 넓은 데이터 과학을 통한 선한 영향력 커뮤니티에도 전할 수 있도록 함께해 주세요.
계속 읽기

How to Build an Enterprise-Ready RAG Pipeline on AWS with Bedrock, Zilliz Cloud, and LangChain
Build production-ready enterprise RAG with AWS Bedrock, Nova models, Zilliz Cloud, and LangChain. Complete tutorial with deployable code.

Why Not All VectorDBs Are Agent-Ready
Explore why choosing the right vector database is critical for scaling AI agents, and why traditional solutions fall short in production.

AI Agents Are Quietly Transforming E-Commerce — Here’s How
Discover how AI agents transform e-commerce with autonomous decision-making, enhanced product discovery, and vector search capabilities for today's retailers.



