3 puntos por GN⁺ 2024-05-04 | 1 comentarios | Compartir por WhatsApp
  • Para reducir las limitaciones de sqlite-vss, sqlite-vec se está desarrollando como una extensión embebida de búsqueda vectorial basada en C puro, orientada a prácticamente cualquier entorno donde se ejecute SQLite
  • El flujo de uso en SQL se simplifica en torno a CREATE VIRTUAL TABLE, INSERT INTO y SELECT, y admite búsquedas estilo KNN junto con entrada de vectores en JSON y en formato binario compacto
  • Al eliminar la dependencia de Faiss, apunta no solo a Linux y macOS, sino también a Windows, WebAssembly, móviles y Raspberry Pi, con un tamaño binario esperado de cientos de KB, menor que los 3 MB a 5 MB anteriores
  • Almacena los vectores por chunks en una shadow table para reducir la necesidad de cargar todo en RAM, y permite acelerar la búsqueda en memoria con PRAGMA mmap_size
  • La versión inicial solo ofrecerá full-scan exhaustivo y no incluirá ANN; el lanzamiento de v0.1.0 está previsto una vez que se completen los 246 TODO de sqlite-vec.c

Cómo sqlite-vec busca cambiar la búsqueda vectorial en SQLite

  • sqlite-vec es una nueva extensión de SQLite escrita en C puro, y es un proyecto que busca reemplazar a sqlite-vss, publicado en febrero de 2023
  • El alcance previsto incluye funciones SQL definidas por el usuario para búsqueda vectorial rápida, tablas virtuales y herramientas y utilidades para operaciones con vectores
    • cuantización
    • conversión JSON/BLOB/numpy
    • aritmética de vectores
  • Los usuarios pueden crear un almacén vectorial y hacer búsquedas usando solo SQL puro
    • crear una tabla virtual para vectores con CREATE VIRTUAL TABLE
    • insertar vectores con INSERT INTO
    • hacer búsquedas estilo KNN con la forma SELECT ... WHERE sample_embedding MATCH ... ORDER BY distance LIMIT ...
  • La entrada de vectores puede usar cadenas JSON o formato binario compacto

Entornos de ejecución que habilitan C puro y cero dependencias

  • sqlite-vec apunta a ser una extensión de C puro sin dependencias, y esa decisión es la condición clave para ampliar las plataformas compatibles
  • El anterior sqlite-vss, debido a su dependencia de C++, solo funcionaba de forma estable en Linux y macOS, y su tamaño binario estaba en el rango de 3 MB a 5 MB
  • La nueva extensión apunta a ejecutarse en los siguientes entornos
    • Linux
    • macOS
    • Windows
    • WebAssembly en el navegador
    • dispositivos móviles
    • dispositivos pequeños como Raspberry Pi
  • El tamaño binario estimado está en el rango de cientos de KB

Control de uso de memoria y velocidad de búsqueda

  • sqlite-vec controla el uso de memoria almacenando los vectores divididos en chunks dentro de una shadow table
    • durante una búsqueda KNN, lee por chunks en lugar de cargar todos los vectores a la vez en RAM
    • no hace falta mantener todos los vectores residentes en memoria
  • Si se necesita velocidad basada en memoria, se puede usar PRAGMA mmap_size de SQLite para acelerar aún más la búsqueda KNN

Nuevas capacidades vectoriales y limitaciones iniciales

  • sqlite-vec busca dar mejor soporte a las siguientes funciones, reflejando las herramientas y líneas de investigación recientes en búsqueda vectorial
  • Estas capacidades sientan la base para ajustar con más precisión la velocidad, la exactitud y el uso de disco de los vectores
  • La versión inicial de sqlite-vec solo soportará búsqueda vectorial exhaustive full-scan
    • al principio no habrá opción de “approximate nearest neighbors”
    • IVF y HNSW son funciones que esperan añadir más adelante

Estructura del demo en navegador

  • El demo usa sqlite-vec ejecutándose en el navegador
  • La configuración visible en las herramientas de desarrollo es la siguiente
    • sqlite3.wasm de 5.9 MB sin optimizar
    • el build oficial de SQLite WASM compilado con sqlite-vec
    • una base de datos SQLite movies.bit.db de 2.6 MB
  • movies.bit.db contiene 4,800 sinopsis de películas en la tabla articles, basadas en TMDB movie metadata
  • Una tabla virtual separada, vec_movies, es el índice vectorial de los embeddings de esas sinopsis

Datos del demo y flujo de búsqueda KNN

  • La tabla articles tiene columnas como title, release_date y overview
  • La columna overview contiene una breve descripción de la trama de cada película y es lo que se embebe en el demo
  • La tabla virtual vec_movies almacena los embeddings de articles.overview en la columna overview_embeddings
    • los vectores son un binary vector de 768 dimensiones
    • el tamaño de almacenamiento es 768 / 8 = 96, es decir, 96 bytes
  • Cuando el usuario selecciona una película con un botón de opción, el ID de la película elegida se coloca en el parámetro :selected_movie de la consulta SQL KNN
  • Los resultados de búsqueda son las 10 películas más cercanas a la película seleccionada
    • como es un binary vector, para calcular la distancia se usa hamming distance
    • el resultado más cercano siempre es la misma película, con distancia 0
  • Los resultados de embeber tramas cortas de una sola frase y un dataset pequeño de películas no son de máxima calidad, y la cuantización binaria sacrifica aún más calidad, pero el enfoque está en mostrar una búsqueda vectorial rápida y “lo suficientemente buena” dentro del navegador
  • Para ver el funcionamiento interno, se puede anteponer EXPLAIN QUERY PLAN a SELECT, y así ver el “index” 0:knn que usa vec_movies

Limitaciones de sqlite-vss y dependencia de Faiss

  • El desarrollo y la adopción de sqlite-vss tuvieron varios obstáculos
    • solo funciona en Linux y macOS, y no soporta Windows, WASM ni dispositivos móviles
    • guarda todos los vectores en memoria
    • tiene bugs y problemas relacionados con transacciones
    • compilarlo es muy difícil y toma mucho tiempo
    • carece de operaciones vectoriales comunes como scalar/binary quantization
  • Casi todos estos problemas provienen de la dependencia de Faiss
  • Algunos podrían resolverse con mucho tiempo y esfuerzo, pero una parte importante puede quedar bloqueada por Faiss
  • A partir de la idea de que una solución de bajo nivel sin dependencias era una opción atractiva, y de que la búsqueda vectorial en sí no es tan compleja, nació sqlite-vec

Estado del lanzamiento y pedido de patrocinio

  • Las funciones principales de sqlite-vec ya funcionan, pero aún faltan mucho manejo de errores y pruebas
  • En el archivo sqlite-vec.c quedan 246 TODO
    • 191 todo_assert()
    • 41 comentarios // TODO
    • 14 todo panic
    • el progreso total aparece como 0/246, 0%, para sqlite-vec v0
  • Cuando se completen los 246 TODO, está previsto el primer lanzamiento v0.1.0
    • documentación
    • demo
    • bindings
    • y otros componentes se ofrecerán junto con él
  • El objetivo es dentro de aproximadamente un mes, pero no hay una fecha confirmada
  • Se buscan patrocinadores entre empresas interesadas en el éxito de sqlite-vec, y se puede consultar por correo electrónico

1 comentarios

 
GN⁺ 2024-05-04
Opiniones de Hacker News
  • Soy el autor; si tienen preguntas, las respondo. Esto es más bien del tipo “estoy trabajando en un proyecto nuevo” que un lanzamiento oficial, y la extensión en sí todavía está en progreso. El enlace del proyecto es https://github.com/asg017/sqlite-vec
    Tengo una idea bastante concreta de cómo se verá la v0.1.0 de esta extensión, pero probablemente falten unas semanas para llegar ahí. El objetivo de este post era sobre todo avisar a los usuarios de sqlite-vss, la extensión de búsqueda vectorial para SQLite que hice antes, qué viene después; cuando esté lista habrá un lanzamiento más grande.
    En general, me entusiasma mucho tener una alternativa de búsqueda vectorial fácil de embeber. Es especialmente bueno que funcione en todos los sistemas operativos, WASM, dispositivos móviles, Raspberry Pi, etc.; personalmente estoy intentando correr una pequeña app de búsqueda semántica en una Beepy, así que está divertido.
    [0] https://beepy.sqfmi.com/

    • Me da curiosidad qué funciones de distancia soporta. Parece que ya soporta vectores binarios, así que me pregunto si también soporta distancia de Hamming.
      También me gustaría conocer el rendimiento comparado con sqlite-vss. Me interesan métricas de profiling tanto de velocidad de consulta como de uso de memoria.
      En general se ve realmente excelente y me gusta esta dirección.
      Me parece 1000% correcto el enfoque de que al principio sqlite-vec solo soporte búsqueda vectorial por escaneo completo exhaustivo y no tenga opciones de vecinos más cercanos aproximados (ANN), pero que más adelante quiera agregar IVF y HNSW. Me gusta que no lo vuelvan excesivamente complejo desde el inicio.
      Ya lancé búsqueda vectorial on-device, y con vectores binarios de 128 bits más distancia de Hamming era lo bastante rápida como para ejecutar una búsqueda de distancia completamente por fuerza bruta en cada frame de cámara, incluso con una base de datos de más de 200 mil elementos. En teléfonos de gama baja daba más de 10 fps, y en teléfonos buenos iba muy fluido. Hay una cantidad sorprendente de casos en los que la fuerza bruta es suficiente.
      Dicho eso, al implementar algoritmos ANN como HNSW, sería genial si pudiera manejarse con el paradigma de índices de tabla. Así, cambiar de búsqueda por fuerza bruta a ANN sería tan simple como crear un índice en la tabla, y experimentar con varios algoritmos ANN y parámetros sería posible ajustando los parámetros de creación del índice. Tal vez ya vaya por ese lado, pero lo menciono por si acaso.
    • Me pregunto si también planean implementar estrategias de indexación como HNSW. El escaneo lineal obviamente es un buen punto de partida y, si los datos están ordenados de forma razonable y, por ejemplo, ocupan menos de 10 MB, puede ser suficientemente rápido, así que no parece motivo para bloquear una beta.
      También me pregunto si lo están compilando junto con sqlite-httpvfs. Parece que encajaría bien con este proyecto: https://github.com/phiresky/sql.js-httpvfs
    • Me encanta que incluya Wasm. Normalmente era difícil usar búsqueda vectorial dentro de SQLite en el navegador.
      Me pregunto si consideraste hacerlo compatible en sintaxis con pgvector para tener un DSL vectorial SQL común. Sospecho que las desventajas no son mucho menores que las ventajas, pero me da curiosidad si es posible.
    • ¿Se podría implementar esto en Rust? También me pregunto si el proyecto sqlite-loadable-rs soporta WASM.
      https://observablehq.com/@asg017/introducing-sqlite-loadable...
    • Antes agregué sqlite-vss a Langchain como almacén vectorial. ¿Crees que este nuevo proyecto ya es lo bastante maduro como para agregarlo también a Langchain, o sería mejor esperar un poco más?
      sqlite-vss ya se usa bien en varios proyectos.
  • Me confunde la parte de “es un vector binario de 768 dimensiones, por lo que ocupa 96 bytes (768 / 8 = 96)”. Creo que ahí está justamente la maldición de la dimensionalidad, un problema que sufren la mayoría de los almacenes vectoriales, y es un problema previo a la indexación.
    Pensé que quizá se refería a 768 dimensiones * 8 bytes (f64), es decir 6144 bytes. Normalmente se reduce a f32, f16 o una representación aún más pequeña, aceptando una pequeña pérdida.
    Si hay una forma de meter 768 dimensiones en 96 bytes mediante compresión, algún tipo de amortización similar a tries, etc., me gustaría leer otro post al respecto. Si se trata de manejar cada dimensión como 1 bit, lo entiendo, pero entonces sigo teniendo dudas sobre la calidad de búsqueda.

    • Soy el autor: aquí vector binario significa cuantizar cada dimensión a 1 bit. Normalmente se necesitan 4 * número de dimensiones bytes por vector. Ese 4 es sizeof(float).
      Algunos modelos de embeddings, como nomic v1.5[0] o el nuevo modelo de mixedbread[1], se entrenan específicamente para mantener la calidad incluso después de la cuantización binaria. No todos los modelos son así, así que los resultados pueden variar. En general, con vectores muy grandes, como el gran modelo de embeddings de OpenAI de 3072 dimensiones, parece funcionar hasta cierto punto aunque no hayan sido entrenados específicamente para eso.
      [0] https://twitter.com/nomic_ai/status/1769837800793243687
      [1] https://www.mixedbread.ai/blog/binary-mrl
    • Binario significa cuantizar cada dimensión a +1 o -1.
      Si usas un índice FAISS sobre los datos y aplicas cuantización de producto (Product Quantization), para características binarias puedes probar vectores binarios con algo como PQ768x1, y también comparar enfoques como cuantizar cada par de vectores a uno de cuatro valores: https://github.com/facebookresearch/faiss/wiki/The-index-fac...
    • Como referencia, la maldición de la dimensionalidad, en sentido estricto, significa que los espacios de alta dimensión se vuelven relativamente dispersos y que, para llenar ese espacio, los datos deben crecer exponencialmente. No tiene relación con la capacidad de almacenamiento.
      Normalmente, en las bases de datos vectoriales, los datos se comprimen o proyectan a un espacio de menor dimensión antes de almacenarlos, así que eso más bien mejora esta situación.
  • Gracias a sqlite-vss pude aprender cómo funciona RAG e implementarlo en un proyecto de juguete. La depuración fue un poco difícil, pero cuando lo ajustabas bien funcionaba sin problemas en Ubuntu, y todavía lo uso.
    Me alegra que estén creando una nueva versión mejor, sin dependencias limitantes.

  • Me pregunto si planean usar solo la API de SQLite pública, o si imaginan algo acoplado a la amalgamación de SQLite.
    Sin duda me interesa una funcionalidad así, pero tendría que pensar cómo distribuirla por separado de SQLite en bindings de Go basados en Wasm. Hasta ahora he empaquetado todo el código C porque es mucho más simple que el “enlace dinámico” en Wasm.
    También mencionaste la E/S incremental de BLOB; seguramente ya lo sabes, pero hay que tener en cuenta que, como los BLOB grandes se almacenan como listas enlazadas de páginas, la E/S de BLOB nunca es de acceso aleatorio.

    • Planeo usar solo la API pública de SQLite. Así que no hace falta acoplarlo a la amalgamación.
      Me gustan mucho los bindings de SQLite de wazero. De hecho planeo ofrecer 1) bindings CGO para sqlite-vec y 2) una compilación WASI personalizada de sqlite-vec que pueda usarse directamente en go-sqlite3. Originalmente pensaba usar los scripts de compilación de ese repositorio para generar el archivo sqlite3.wasm. Si quisieran darle soporte directo en el proyecto, creo que bastaría con poner los archivos sqlite-vec.c/h dentro de go-sqlite3/sqlite3.
      Aprendí por las malas lo de la E/S incremental de BLOB. Definitivamente es un factor limitante en la velocidad de consulta de sqlite-vec. Mantener el tamaño de los chunks relativamente pequeño, en el rango bajo de MB, y aumentar page_size daba un equilibrio razonable, pero tiene efectos secundarios, sobre todo page_size. PRAGMA mmap_size también ayuda mucho, porque parece mantener las páginas en memoria y acelera las búsquedas en overflow, pero obviamente aumenta mucho el uso de memoria. Es un equilibrio difícil.
    • Si esta funcionalidad llega a los bindings de Wasm Go, me interesa mucho.
  • DuckDB anunció hoy la extensión “Vector Similarity Search in DuckDB”.
    https://duckdb.org/2024/05/03/vector-similarity-search-vss.h...

    • Esto promete. Creo que podría simplificar mucho un pequeño proyecto basado en CDN con HNSW que hice antes: https://github.com/jasonjmcghee/portable-hnsw
      Con DuckDB VSS parece que podrías generar embeddings, guardarlos en formato DuckDB y luego ejecutar SQL dentro del CDN.
  • Me gustan los proyectos de este estilo. Me gusta que sean proyectos open source enfocados en un problema muy específico.
    En el ecosistema de TypeScript/Next.js/React sigo pensando si podría crear algo muy útil para algún nicho técnico, pero todavía no se me ocurre la inspiración.

  • En https://github.com/rnadigital/agentcloud, una app de AI RAG, usamos Qdrant vector DB para automatización end-to-end, así que me entusiasma que estén haciendo un sucesor. Me pregunto cuándo estará listo para usarse y si habrá una guía de inicio rápido.
    También podría ayudar con la redacción del blog.

    • Apuntamos a v0.1.0 para dentro de aproximadamente un mes. Planeo incluir bastante documentación y guías de inicio rápido.
      Hay un paquete pip de sqlite-vec sin documentar, así que si quieres llamarlo directamente desde el “Agent Backend” en Python, creo que ya podrías probarlo.
  • Esto se parece bastante a lo que imaginaba que sería el “desarrollo guiado por README”. Me pregunto si el autor empezó por la documentación.

    • Empecé por el código. La extensión en sí ya está escrita en su mayor parte[0].
      Pero está en el estado de “hice el 80% con el 20% del esfuerzo”, así que el 20% restante —manejo de errores, fuzz testing y pruebas de corrección— probablemente se llevará el 80% del tiempo. Aun así, la gente ya estaba preguntando por el estado actual de sqlite-vss, así que pensé que este post de blog de “trabajo en curso” podría responder algunas preguntas.
      También me gusta la idea de empezar por la documentación. Especialmente en extensiones de SQLite, es muy importante cómo se ve la API SQL, es decir, funciones escalares, tablas virtuales, etc. Antes de escribir la mayor parte del código, bosquejé bastante cómo debería verse la parte SQL de sqlite-vec.
      [0] https://github.com/asg017/sqlite-vec/blob/main/sqlite-vec.c
  • Parece casi una respuesta a un issue de GitHub que abrí hace unos meses en SQLite-vss. Técnicamente no es una respuesta a ese issue, pero aun así.
    https://github.com/asg017/sqlite-vss/issues/124

    • Sí. Perdón por no haber respondido ahí.
      De hecho, cuando leí ese ticket por primera vez, caí en la madriguera de “cómo podría mejorar sqlite-vss”, y eso terminó llevándome a “debería crear sqlite-vec”. Gracias por ayudarme a tomar este camino.
      Con la cuantización binaria integrada de sqlite-vec, podrías hacer algo más o menos así:
      CREATE VIRTUAL TABLE vec_files USING vec0 ( contents_embedding bit[1536] );
      INSERT INTO vec_files(rowid,contents_embedding) VALUES ((1, vec_quantize_binary( /* 1536-dimension float vector here*/)))
  • Me pregunto si, al ejecutarse dentro del navegador, sqlite-vec puede persistir datos en el IndexedDB nativo del navegador. O si esto es algo que el usuario debe manejar directamente
    Aunque la respuesta sea que todavía no lo has pensado, agradecería que compartieras tus ideas en esa dirección

    • Podría ser posible. Como se basa en el build oficial de SQLite WASM, se pueden usar las mismas opciones de persistencia[0] que ofrece allí
      No estoy seguro de si IndexedDB está soportado específicamente, pero se pueden usar localStorage/OPFS VFS
      [0] https://sqlite.org/wasm/doc/trunk/persistence.md#kvvfs