En este tutorial, construimos un área de juegos pgvector completa dentro de Google Colab y exploramos cómo PostgreSQL puede funcionar como una poderosa base de datos vectorial para aplicaciones modernas de IA. Comenzamos instalando PostgreSQL, compilando la extensión pgvector, conectándonos a través de Psycopg y registrando tipos de vectores para una integración fluida de Python. Luego, creamos incrustaciones con SentenceTransformers, las almacenamos en PostgreSQL, creamos índices HNSW y ejecutamos búsqueda semántica, búsqueda filtrada, comparaciones de métricas de distancia, almacenamiento de media precisión, cuantificación binaria, búsqueda de vectores dispersos, recuperación híbrida y agregación de vectores. A través de este flujo de trabajo, aprendemos cómo pgvector admite sistemas prácticos de generación de recuperación aumentada, recomendación, búsqueda de similitudes y búsqueda híbrida utilizando solo herramientas de código abierto.
import os import subprocess import sys import time def sh(cmd: str, check: bool = True): “””Ejecutar un comando de shell, transmitiendo un registro compacto.””” print(f” $ {cmd}”) return subprocess.run(cmd, shell=True, check=check, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) print(“[0/10] Instalación de PostgreSQL + compilación de pgvector (≈1–2 min)…”) sh(“apt-get -qq update”) sh(“apt-get -qq install -y postgresql postgresql-contrib ” “postgresql-server-dev-all build-essential git”) if not os.path.exists(“/tmp/pgvector”): sh(“git clone — Depth 1 https://github.com/pgvector/pgvector.git /tmp/pgvector”) sh(“cd /tmp/pgvector && make && make install”) sh(“servicio postgresql inicio”) time.sleep(3) sh(“””sudo -u postgres psql -c “ALTERAR USUARIO postgres CONTRASEÑA ‘postgres’;” “””) print(“[0/10] Instalando paquetes de Python…”) sh(f”{sys.executable} -m pip install -q pgvector psycopg[binary] “f”transformadores de oraciones numpy”)
Configuramos el entorno completo de PostgreSQL y pgvector. Instalamos los paquetes del sistema necesarios, clonamos y compilamos pgvector desde el código fuente, iniciamos el servicio PostgreSQL y configuramos la contraseña de la base de datos. También instalamos las dependencias de Python necesarias para conectarnos a PostgreSQL y trabajar con incrustaciones de vectores.
importar numpy como np importar psycopg desde pgvector importar HalfVector, SparseVector desde pgvector.psycopg importar registrar_vector desde sentencia_transformers importar SentenceTransformer imprimir(“\n[1/10] Conectando y habilitando la extensión ‘vector’…”) conn = psycopg.connect( “host=127.0.0.1 port=5432 dbname=postgres user=postgres contraseña=postgres”, autocommit=True, ) conn.execute(“CREAR EXTENSIÓN SI NO EXISTE vector”) Register_vector(conn) ver = conn.execute(“SELECCIONAR extversion DESDE pg_extension DONDE extname=”vector””).fetchone()[0]
print(f” pgvector version: {ver}”) print(“\n[2/10] Cargando modelo de incrustación + corpus de codificación…”) modelo = SentenceTransformer(“all-MiniLM-L6-v2”) DIM = model.get_sentence_embedding_dimension() corpus = [
(“Octopuses have three hearts and blue blood.”, “animals”),
(“Transformers revolutionized natural language processing.”,”technology”),
(“Quantum computers exploit superposition and entanglement.”,”technology”),
(“GPUs accelerate deep learning by parallelizing matrix math.”,”technology”),
(“Sourdough bread relies on wild yeast and lactobacilli.”, “food”),
(“Dark chocolate contains flavonoid antioxidants.”, “food”),
(“A black hole’s gravity is so strong light cannot escape.”,”space”)
]
contenido = [c for c, _ in corpus]
categorías = [k for _, k in corpus]
incrustaciones = model.encode(contents, normalize_embeddings=True) conn.execute(“DROP TABLE SI EXISTE documentos”) conn.execute(f””” CREAR TABLA documentos (id bigserial CLAVE PRIMARIA, texto de contenido, texto de categoría, vector de incrustación({DIM}) ) “””) con conn.cursor() como cur: cur.executemany( “INSERT INTO documentos (contenido, categoría, incrustación) VALORES (%s, %s, %s)”, lista(zip(contenidos, categorías, [np.asarray(e) for e in embeddings])), ) print(f” Se insertaron {len(corpus)} documentos con incrustaciones {DIM}-d.”)
Nos conectamos a PostgreSQL, habilitamos la extensión pgvector y registramos el soporte vectorial con Psycopg. Cargamos el modelo SentenceTransformers, definimos un pequeño corpus de texto, generamos incrustaciones normalizadas y creamos una tabla PostgreSQL para almacenar documentos. Luego insertamos cada documento con su categoría y representación vectorial para poder realizar una búsqueda semántica más adelante.
imprimir(“\n[3/10] Construyendo un índice HNSW y ejecutando una búsqueda semántica…”) conn.execute( “CREAR ÍNDICE EN documentos USANDO hnsw (incrustando vector_cosine_ops) ” “CON (m = 16, ef_construction = 64)” ) conn.execute(“SET hnsw.ef_search = 100”) def semantic_search(query: str, k: int = 4): q = np.asarray(model.encode(query, normalize_embeddings=True)) return conn.execute( “SELECCIONAR contenido, categoría, incrustación <=> %s AS distancia ” “DE documentos ORDENAR POR LÍMITE de distancia %s”, (q, k), ).fetchall() for content, cat, dist in semantic_search(“animales que son inusualmente rápidos”): print(f” {dist:.3f} [{cat:<10}] {contenido}”) print(“\n[4/10] Búsqueda filtrada (solo categoría = ‘espacio’)…”) q = np.asarray(model.encode(“objetos con gravedad extrema”, normalize_embeddings=True)) filas = conn.execute( “SELECCIONAR contenido, incrustar <=> %s AS distancia ” “DE documentos DONDE categoría = %s ORDENAR POR distancia LÍMITE 3”, (q, “espacio”), ).fetchall() para contenido, dist en filas: print(f” {dist:.3f} {contenido}”) print(“\n[5/10] La misma consulta bajo diferentes métricas de distancia (top hit cada una)…”) q = np.asarray(model.encode(“preparar una bebida caliente con cafeína”, normalize_embeddings=True)) para op, etiqueta en [(“<->”, “L2”), (“<=>”, “cosine”), (“<#>”, “neg-inner”), (“<+>”, “L1″)]: contenido, puntuación = conn.execute( f”SELECCIONAR contenido, incrustar {op} %s COMO s DE documentos ORDENAR POR s LÍMITE 1″, (q,) ).fetchone() print(f” {etiqueta:<10} {puntuación:+.3f} {contenido}")
Creamos un índice HNSW en la columna de incrustación para permitir una búsqueda vectorial más rápida y eficiente. Definimos una función de búsqueda semántica que convierte una consulta en una incrustación y recupera los documentos más similares utilizando similitud de coseno. También realizamos búsquedas filtradas por metadatos y comparamos diferentes operadores de distancia pgvector como L2, coseno, producto interno negativo y L1.
imprimir(“\n[6/10] Almacenamiento de media precisión con halfvec…”) conn.execute(f”ALTERAR TABLA documentos AGREGAR COLUMNA SI NO EXISTE embedding_half halfvec({DIM})”) conn.execute(“ACTUALIZAR documentos SET embedding_half = embedding::halfvec”) conn.execute( “CREAR ÍNDICE EN documentos USANDO hnsw (embedding_half halfvec_cosine_ops)” ) q_half = HalfVector(model.encode(“la galaxia en la que vivimos”, normalize_embeddings=True)) filas = conn.execute( “SELECCIONAR contenido, embedding_half <=> %s AS d FROM documentos ORDENAR POR d LIMIT 2″, (q_half,), ).fetchall() para contenido, d en filas: print(f” {d:.3f} {content}”) print(“\n[7/10] Cuantización binaria (Hamming) + reclasificación exacta…”) conn.execute( f”CREAR ÍNDICE EN documentos ” f”USANDO hnsw ((binary_quantize(embedding)::bit({DIM})) bit_hamming_ops)” ) q = np.asarray(model.encode(“hardware paralelo para entrenamiento de IA”, normalize_embeddings=True)) rerank_sql = f””” SELECCIONAR contenido, candidatos.embedding <=> %(q)s AS exactitud_distancia DESDE ( SELECCIONAR contenido, incrustar DESDE documentos ORDENAR POR binario_quantize(embedding)::bit({DIM}) <~> binario_quantize(%(q)s)::bit({DIM}) LIMIT 8 ) COMO candidatos ORDENAR POR exactitud_distancia LÍMITE 3 “”” para contenido, d en conn.execute(rerank_sql, {“q”: q}).fetchall(): print(f” {d:.3f} {contenido}”) print(“\n[8/10] Vectores dispersos nativos…”) conn.execute(“DROP TABLE IF EXISTS sparse_items”) conn.execute(“CREAR TABLA sparse_items (id bigserial PRIMARY KEY, incrustando sparsevec(10))”) sparse_data = [
SparseVector({0: 1.0, 3: 2.0, 7: 1.5}, 10),
SparseVector({1: 0.5, 3: 1.0, 9: 3.0}, 10),
SparseVector({0: 0.2, 4: 2.5, 7: 0.8}, 10),
]
con conn.cursor() como cur: cur.executemany(“INSERT INTO sparse_items (incrustación) VALUES (%s)”,
[(v,) for v in sparse_data]) query_sparse = SparseVector({0: 1.0, 7: 1.0}, 10) filas = conn.execute( “SELECCIONAR id, incrustar, incrustar <#> %s AS neg_ip ” “DE sparse_items ORDENAR POR neg_ip LIMIT 3″, (query_sparse,), ).fetchall() para _id, vec, neg_ip en filas: print(f” id={_id} internal_product={-neg_ip:.2f} nnz_indices={vec.indices()}”)
Exploramos técnicas avanzadas de almacenamiento y recuperación de pgvector más allá de los vectores densos estándar. Convertimos incrustaciones en vectores de media precisión para reducir el almacenamiento, utilizamos cuantificación binaria con búsqueda de Hamming para una recuperación rápida de candidatos y luego reclasificamos los resultados con vectores de precisión total. También creamos vectores dispersos y los consultamos utilizando la similitud del producto interno, lo cual es útil para la recuperación ponderada por palabras clave o estilo SPLADE.
imprimir(“\n[9/10] Búsqueda híbrida (vector + texto completo) a través de RRF…”) user_query = “fast animal” qvec = np.asarray(model.encode(user_query, normalize_embeddings=True)) hybrid_sql = “”” CON semántico AS ( SELECT id, RANK() OVER (ORDER BY incrustación <=> %(qvec)s) COMO rango DE documentos ORDEN POR incrustación <=> %(qvec)s LIMIT 20 ), palabra clave AS ( SELECT d.id, RANK() OVER (ORDER BY ts_rank_cd(to_tsvector(‘english’, d.content), q) DESC) AS rango FROM documentos d, Plainto_tsquery(‘english’, %(qtext)s) AS q WHERE to_tsvector(‘english’, d.content) @@ q LIMIT 20 ) SELECT d.content, COALESCE(1.0 / (60 + semantic.rank), 0.0) + COALESCE(1.0 / (60 + palabra clave.rank), 0.0) AS rrf_score FROM documentos d LEFT JOIN semantic ON d.id = semantic.id LEFT JOIN palabra clave ON d.id = palabra clave.id DONDE semantic.id NO ES NULO O la palabra clave.id NO ES NULO ORDEN POR rrf_score DESC LIMIT 4 “”” para contenido, puntuación en conn.execute(hybrid_sql, {“qvec”: qvec, “qtext”: user_query}).fetchall(): print(f” {score:.5f} {content}”) print(“\n[10/10] Agregando vectores con AVG (centroide de categoría)…”) centroid = conn.execute( “SELECCIONE AVG(incrustación) DE documentos DONDE categoría = %s”, (“comida”,) ).fetchone()[0]
típico = conn.execute( “SELECCIONAR contenido, incrustar <=> %s AS d FROM documentos ” “DONDE categoría = %s ORDENAR POR d LÍMITE 1”, (np.asarray(centroide), “comida”), ).fetchone() print(f” Centroide dim = {len(centroide)}”) print(f” Documento de ‘comida’ más representativo: {típico[0]}”) imprimir(“\n Hecho. Ahora tiene un área de juegos pgvector funcional dentro de Colab.”) print(” Intente editar `corpus`, las consultas o intercambie su propio modelo de incrustación.”)
Combinamos la búsqueda de vectores semánticos con la búsqueda de texto completo de PostgreSQL utilizando Reciprocal Rank Fusion. Recuperamos resultados de clasificaciones semánticas y de palabras clave, fusionamos sus puntuaciones y producimos un resultado de búsqueda híbrido más sólido. Finalmente, calculamos la incrustación promedio para una categoría y la usamos como centroide para encontrar el documento más representativo de ese grupo.
En conclusión, tenemos un sistema de recuperación basado en pgvector que funciona completamente en Google Colab, sin servicios externos ni claves API. Usamos PostgreSQL no solo como una base de datos relacional tradicional, sino como un motor de búsqueda de vectores flexible que admite vectores densos, vectores de media precisión, recuperación cuantificada binaria, vectores dispersos, búsqueda de texto completo y agregación. También observamos cómo el filtrado de metadatos, la indexación HNSW, la fusión de rangos recíprocos y el análisis basado en centroides hacen que pgvector sea útil para los canales de búsqueda de IA del mundo real.
Consulte los códigos completos con Notebook aquí. Además, no dude en seguirnos en Twitter y no olvide unirse a nuestro SubReddit de más de 150.000 ML y suscribirse a nuestro boletín. ¡Esperar! estas en telegrama? Ahora también puedes unirte a nosotros en Telegram.
¿Necesita asociarse con nosotros para promocionar su repositorio de GitHub O su página principal de Hugging O su lanzamiento de producto O seminario web, etc.? Conéctate con nosotros
La publicación Una guía de codificación para implementar un sistema de búsqueda de vectores semánticos, híbridos, dispersos y cuantificados impulsado por pgvector apareció por primera vez en MarkTechPost.