Christy Bergman: Por qué me uní a Zilliz
Mi nombre es Christy Bergman, y recientemente me uní a Zilliz como Developer Advocate con sede en San Francisco. Organizaré, asistiré y hablaré en eventos, escribiré blogs y tutoriales de código, mejoraré la documentación y, en general, ayudaré a las personas (¡como tú, espero!) a aprender cómo usar Milvus, la base de datos vectorial open-source más popular del mundo (medida por estrellas en GitHub).
Antes de Zilliz, fui Developer Advocate en Anyscale, el creador open-source de Ray, que facilita la computación distribuida para todos los desarrolladores. Antes de eso, fui AWS AI/ML Specialist Solutions Architect enfocada en pronósticos con deep learning.
Entonces, ¿por qué elegí unirme a Zilliz? Comenzó con mi curiosidad por mejorar las salidas de chat de ChatGPT inyectando contexto en el prompt. Luego inicié una búsqueda para experimentar con embeddings y, a partir de ahí, empecé a buscar una base de datos vectorial rápida y fácil de usar. En esta publicación de blog, te llevaré a través de los pasos que me trajeron hasta aquí.
La duda: ¿realmente necesito una base de datos vectorial?
Al principio, me pregunté si una base de datos vectorial era siquiera necesaria. Sin embargo, algunos intentos de incrustar texto en una variable de Python, hacer pickle y unpickle, me hicieron creer que los embeddings deben almacenarse en caché o persistirse en algún lugar mejor que pickle.
Milvus es una base de datos completa diseñada desde cero específicamente para vectores. Miles de desarrolladores en todo el mundo usan Milvus, que incluye soporte para muchos algoritmos diferentes de búsqueda vectorial, métricas de distancia en espacios vectoriales y otras funciones como Insert/upsert, TopK y Range Search, soporte para GPU y más. La versión comercial se llama Zilliz Cloud y tiene una disponibilidad SLA del 99,9%, autoescalado, se ejecuta en AWS y GCP (Azure próximamente), y puedes ejecutarla en tu cuenta de cloud con pleno cumplimiento SOC2.
Explorando las opciones: probando diferentes bases de datos vectoriales
Para llevar embeddings a una base de datos vectorial, necesitas: 1) datos no estructurados, 2) un plan sobre cómo dividir esos datos en chunks, 3) un modelo de embeddings y 4) una base de datos vectorial.
Para los datos, utilicé el IMDB large movie review dataset del Stanford AI Lab. Es un dataset de 50.000 elementos convenientemente procesado (proporción de muestreo 50:50 de reseñas positivas/negativas). Estos datos tienen columnas: movie_index, texto sin procesar de la reseña y calificación de la película.
Para el chunking, elegí mantener intactas las reseñas completas de películas, a menos que fueran muy largas. Para reseñas muy largas, utilicé los típicos chunks predeterminados de longitud 512. Eso significa que las reseñas largas se dividieron en varias piezas de 512 caracteres de longitud. En la jerga de los LLM, esto se llama “chunk size”.
Para el modelo de embeddings, probé varios modelos, incluidos los omnipresentes embeddings de OpenAI. Hasta que descubrí que lo más fácil es simplemente seleccionar el objetivo siempre cambiante del más pequeño, mejor clasificado en la pestaña Retriever del MTEB HuggingFace leaderboard. En ese momento, me decidí por “e5-base-v2”. En realidad, es algo bueno que la elección del modelo de embeddings sea independiente de la propia base de datos vectorial.
Para las bases de datos, probé FAISS, Qdrant, Chroma, Weaviate, Pinecone y, por supuesto, Milvus. Las sometí a todas a las mismas pruebas: 1) ¿Qué tan fácil y rápido es cargar directamente desde pandas a la base de datos vectorial? 2) ¿Qué tan fácil, rápido y configurable es obtener buenos resultados de consulta desde la base de datos vectorial? 3) ¿Qué tan fácil y configurable es realizar RAG usando LangChain (¡futura publicación de blog!).
Milvus destaca
Entre todas las bases de datos que exploré, Milvus me llamó la atención por varias razones. En primer lugar, ofrecía una experiencia fácil de usar, especialmente al insertar datos directamente desde pandas con metadatos, ya que este es el primer enfoque que un Científico de Datos podría probar.
Milvus también me llamó la atención por su rapidez al cargar vectores y realizar consultas. Milvus era notablemente más ágil que otros enfoques, algunos de los cuales eran lentos, incluso a esta pequeña escala. También observé las características y elegí la configuración predeterminada para otras bases de datos (búsqueda exhaustiva IVF-flat con métrica L2), aunque Milvus admite otras opciones.
A continuación se muestra un gist para insertar un data frame de pandas en Milvus. El código completo está en mi GitHub.
# El código completo está en: https://github.com/christy/ZillizDemos/blob/main/milvus_onboarding/hello_world_milvus.ipynb
###########
# 1. Descargar para usar localmente, un transformador de oraciones desde HuggingFace Hub.
###########
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")
# Obtener los parámetros del modelo y guardar para más adelante.
MAX_SEQ_LENGTH = retriever.get_max_seq_length()
HF_EOS_TOKEN_LENGTH = 1
EMBEDDING_LENGTH = retriever.get_sentence_embedding_dimension()
###########
# 2. Elegir una función de fragmentación de LangChain por conveniencia.
###########
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. Manipular un dataframe de pandas.
# Descargar: https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz
###########
# 1. Lote de datos desde pandas DataFrame.
batch = df.head(100).copy()
# 2. Cambiar el tipo de clave primaria a cadena.
batch["movie_index"] = batch["movie_index"].apply(lambda x: str(x))
# 3. Truncar reseñas a 512 caracteres.
batch['chunk'] = batch['text'].apply(chunk_text)
batch = batch.explode('chunk', ignore_index=True)
# 4. Agregar embeddings como nueva columna en df.
review_embeddings = torch.tensor(retriever.encode(batch['chunk']))
# Normalizar embeddings a longitud unitaria.
review_embeddings = F.normalize(review_embeddings, p=2, dim=1)
# 5. Convertir embeddings a lista de `numpy.ndarray`, cada uno con números `numpy.float32`.
converted_values = list(map(np.float32, review_embeddings))
batch['embeddings'] = converted_values
# 6. Reordenar columnas por conveniencia, de modo que el índice vaya primero y las etiquetas al final.
new_order = ["movie_index", "text", "chunk", "embeddings", "label_int", "label"]
batch = batch[new_order]
###########
# 4. Instalar e importar milvus.
###########
!pip install milvus pymilvus
import milvus, pymilvus
###########
# 5. Definir esquema para cargar datos en Milvus.
###########
# Establecer el nombre de la colección de Milvus.
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, "Buscar reseñas de películas de imdb")
mc = Collection(COLLECTION_NAME, schema, consistency_level="Eventually")
###########
6. Start a local Milvus server (lite).
###########
default_server.start()
connections.connect(host='127.0.0.1',
port=default_server.listen_port,
show_startup_banner=True)
###########
# 7. Insert data into Milvus.
###########
# Showing how to change the index parameters, instead of using defaults.
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)
# After final entity is inserted, call flush to stop growing segments left in memory.
mc.flush()
###########
# 8. Query the database.
###########
# Before conducting a search or a query, you need to load the data into memory.
mc.load()
# Define a sample question about your data.
query = "I'm a medical doctor, what movie should I watch?"
# Embed the query using same embedding model used to create the Milvus collection.
query_embeddings = torch.tensor(retriever.encode([query]))
# Normalize embeddings to unit length.
query_embeddings = torch.nn.functional.normalize(query_embeddings, p=2, dim=1)
# Convert the embeddings to list of list of np.float32.
query_embeddings = list(map(np.float32, query_embeddings))
# Execute a vector search with HNSW index.
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"
)
Después de mis pruebas (rápidas y no oficiales), rellené un formulario en línea para postularme a una vacante de Developer Advocate en Zilliz. Después de mi última entrevista, el equipo de Zilliz no tardó en hacerme una oferta. Así que, sin dudarlo, ¡acepté!
Tengo compañeros de trabajo fabulosos. Consulta los blogs de Yujian Tang y Frank Liu sobre por qué se unieron a Zilliz. Mi jefa, Chris Churilo, es el pegamento secreto por el que todos trabajamos juntos de forma tan eficiente. Usa su conocimiento y experiencia para hacer que todos brillemos. Mi colega Filip Haltmayer es excepcionalmente paciente y bueno explicándome todo lo relacionado con Milvus y Zilliz durante mi incorporación. Nuestro CEO, Charles Xie, tuvo la visión original en 2017 de crear una base de datos, especialmente para datos no estructurados.
Qué sigue
Mientras empiezo a hacer trabajo de evangelización para Milvus, ¡espero conocerlos a todos ustedes en la comunidad de desarrolladores de IA! Organizaré el Meetup de Unstructured Data en San Francisco, donde siempre busco grandes ponentes, espacios y coanfitriones (¡por favor, contáctenme!). Escribiré más en el blog y daré más charlas (¡por favor, comuníquense conmigo!). Si quieres probar Milvus de código abierto en tu proyecto de datos no estructurados, ¡por favor dime cómo puedo ayudar!
Participo en varias organizaciones de ciencia de datos para el bien común, incluidas DataKind, Women in Data Science y las San Francisco Civil Hack nights de Code for America (que me presentaron a mi organización local Sonoma Safe Agriculture, donde creé un mapa interactivo de pesticidas). Acompáñenme mientras llevo también mis nuevos aprendizajes sobre bases de datos vectoriales a la comunidad más amplia de ciencia de datos para el bien común.
Sigue leyendo

Build Multimodal Search for 3D Assets with Tripo and Zilliz Cloud
Generate 3D assets with Tripo, then search them by text, image, and metadata with multimodal embeddings and Zilliz Cloud.

Build for the Boom: Why AI Agent Startups Should Build Scalable Infrastructure Early
Explore strategies for developing AI agents that can handle rapid growth. Don't let inadequate systems undermine your success during critical breakthrough moments.

Vector Databases vs. Key-Value Databases
Use a vector database for AI-powered similarity search; use a key-value database for high-throughput, low-latency simple data lookups.



