1 puntos por GN⁺ 3 시간 전 | 1 comentarios | Compartir por WhatsApp
  • Ternlight permite realizar embeddings de texto y búsquedas por similitud dentro del navegador, sin llamadas al servidor, lo que facilita armar rápidamente una pequeña búsqueda semántica del lado del cliente
  • El paquete base combina motor y pesos en 7 MB; la variante mini pesa 5 MB y funciona en CPU, sin GPU
  • Tras instalar @ternlight/base, al importar embed y similar se puede crear un flujo de búsqueda semántica en unas 3 líneas
  • La llamada de ejemplo ordena los 3 mejores resultados en una lista de recetas y destaca unos 5 ms y cero llamadas de red
  • La demo de búsqueda en la documentación de React ejecuta la búsqueda en el navegador cuando el usuario ingresa una pregunta, impulsada por @ternlight/mini, el tier de 5 MB

Embeddings que se resuelven dentro del navegador

  • Ternlight se presenta como un “modelo de embeddings de 7 MB” que genera embeddings de texto en milisegundos y no llama al servidor
  • Características de ejecución

    • Sin llamadas a API
    • Motor + pesos: 7 MB
    • Variante mini: 5 MB
    • Embeddings rápidos: alrededor de 5 ms
    • Solo CPU, sin GPU

Instalación y ejemplo de uso

  • Se ofrece como un único paquete npm y puede usarse sin un paso separado de descarga del modelo ni servidor
  • El comando de instalación es el siguiente
npm install @ternlight/base
  • Desde @ternlight/base se importan embed y similar para ejecutar una búsqueda semántica
import { embed, similar } from '@ternlight/base';

similar('easy weeknight dinner ideas', recipes, { topK: 3 });
// → ranked matches · ~5 ms · zero network
  • La demo de búsqueda en la documentación de React realiza la búsqueda en el navegador cuando el usuario ingresa una pregunta, y está impulsada por el tier de 5 MB @ternlight/mini

1 comentarios

 
GN⁺ 3 시간 전
Opiniones de Hacker News
  • Quería probar un modelo útil en el navegador como proyecto personal, así que destilé un codificador de oraciones pequeño a partir de MiniLM y apliqué entrenamiento consciente de cuantización ternaria.
    También escribí el motor de inferencia yo mismo y lo distribuí como Rust → WASM SIMD.
    No es un LLM, sino un modelo de embeddings: si ingresas texto, devuelve un vector de 384 dimensiones, y la relevancia entre textos se determina con la similitud coseno de dos vectores. Por ejemplo, "reset my password" y "I forgot my password" dan algo como 0.88.
    Se puede usar para búsqueda semántica, matching de FAQ/intenciones y clustering; como corre en el dispositivo, permite búsqueda semántica rápida al ingresar datos, sin depender de una API.
    La demo busca entre 2.000 documentos de React completamente en el dispositivo: https://ternlight-demo.vercel.app
    En npm hay dos niveles: @ternlight/base (7 MB, aprox. 5 ms por embedding, embeddings de mejor rendimiento) y @ternlight/mini (5 MB transferidos, aprox. 2,5 ms por embedding), empaquetados para Node y navegador.
    El repositorio incluye detalles técnicos, licencia MIT y el pipeline de entrenamiento: https://github.com/soycaporal/ternlight
    Me da curiosidad si los embeddings en el dispositivo son realmente útiles y qué casos de uso hay.

    • Tengo un diccionario que mapea palabras a etiquetas de OpenStreetMap, como en https://codeberg.org/cartes/web/src/branch/master/components...
      Me pregunto si ayudaría a que, cuando el usuario escriba "pancake", encuentre crêpe sin tener que poner explícitamente una entrada de diccionario "pancake = crêpe".
      Si entendí bien, también me pregunto si la estructura es que la biblioteca descarga 5 MB una vez al inicio y luego se usa como ahora uso Fuse.js.
      También quisiera saber qué tan bien maneja idiomas distintos del inglés y si se podría entrenar con la wiki de etiquetas de OpenStreetMap.
    • Me interesa mucho incorporar una búsqueda semántica simple en una app nativa de escritorio.
      Me pregunto si hay comparaciones con otros modelos de embeddings ultrapequeños. Es difícil saber si empezaron con MiniLM-L6 porque es un modelo particularmente bueno en esa categoría, ya que la única métrica provista es "Retrieval (SciFact NDCG@10)".
      Dicho eso, veo una diferencia considerable respecto del rendimiento declarado: en Firefox sobre un i5-4570 obtengo solo 35 embeddings por segundo, no 400. Sospecho que puede estar cayendo a una ruta sin SIMD, y también voy a probar el binario nativo en Rust.
    • Acabo de embeber toda la documentación de django y la base de conocimiento interna, y ahora puedo buscar instantáneamente en ambas fuentes.
  • Está genial, pero estaría bueno poner un botón en la landing page para iniciar la demo. Me sorprendió bastante que, apenas abrí la página, el ventilador empezara a sonar como loco.

    • De acuerdo. Al mismo tiempo, también me dio gusto recordar la época en la que podías saber íntimamente qué estaba pasando con solo escuchar la computadora.
    • Me asusté cuando empezó a girar el ventilador. Aunque la tostadora también me asusta a veces.
    • Aprovechamiento extremo de ciclos de CPU; ¿quién dijo que solo las GPU eran especiales?
  • Sería bueno convertirlo en un plugin para Astro o para metaframeworks generales, que parseara automáticamente todos los archivos HTML generados y creara una pequeña base de datos de embeddings.
    En el frontend se podría cargar de forma diferida, y tal vez HNSW también podría guardarse por chunks para cargar solo las partes necesarias para la consulta de búsqueda.
    Sería algo parecido a https://pagefind.app/, por ejemplo, pero ofreciendo búsqueda vectorial completamente estática.

    • Para nuestro generador de sitios estáticos quería usar sqlite-vec, pero la última vez que revisé, HNSW no estaba implementado o el soporte para búsqueda vectorial en el navegador no era bueno. Creo que probablemente seguía haciendo un escaneo completo de la tabla.
      Me decepcionó bastante que, si después de meses o años seguía así, pareciera una señal de que al proyecto le falta capacidad para completarlo bien. Incluso recomendé ese proyecto como buen candidato para una beca que apoyé; ellos fueron seleccionados y yo no.
      Si alguien conoce una buena solución en este espacio, o si me equivoco sobre SQLite-vec, me gustaría saberlo. En nuestro SSG casi decidimos trabajar unos meses en otra infraestructura y, si para entonces todavía no está listo, hacerlo nosotros mismos.
  • Podría ser un agregado bastante interesante para aquel proyecto de búsqueda HNSW con DuckDB que vi antes por aquí: https://github.com/jasonjmcghee/portable-hnsw
    Es muy interesante que la búsqueda ocurra sobre archivos Parquet alojados estáticamente usando solicitudes HTTP de rango.
    Creo que cosas como estas podrían crecer hasta convertirse en un ecosistema de búsqueda relativamente abierto y distribuido, no controlado por grandes empresas.

    • Con una idea similar, también podría ser interesante usar una DB SQLite en un host estático con solicitudes HTTP de rango y WASM.
      https://news.ycombinator.com/item?id=27016630
    • Gran idea. Me encantan los range requests y los formatos en hosting estático que el cliente puede explorar.
  • Esto está realmente genial, y podría ser la pieza que faltaba para algo que quise construir antes.
    Con https://github.com/npiesco/absurder-sql se puede persistir todo el corpus original dentro del navegador en IndexedDB/SQLite.
    Luego, como en https://weaviate.io/blog/chunking-strategies-for-rag, se podría usar Ternlight para generar y cachear embeddings cuando haga falta, en vez de indexar todo de antemano.
    Eso también permitiría búsqueda híbrida con Reciprocal Rank Fusion, combinando FTS5/BM25 de SQLite nativo con la búsqueda semántica de Ternlight.

  • Está bien hecho.
    Se promociona como de 7 MB, pero también hay una versión mini de 5 MB.
    Parece que mini usa internamente vectores de 256 elementos en vez de 384 para reducir espacio, y al final los proyecta a 384 por compatibilidad.
    El tamaño baja un tercio, pero la pérdida no es lineal, así que, aunque use una ruta de datos más pequeña, la pérdida de información parece ser menor que un tercio.

  • Gran proyecto.
    Antes intenté algo parecido: http://sol.quipu-strands.com/
    Quería cargar un modelo de embeddings dentro del navegador para ordenar textos semánticamente.
    Traje pesos ONNX (MPNet, MiniLM) desde HuggingFace, generé embeddings con Transformers.js y luego usé un clusterizador de scikit-learn ejecutado con pyodide dentro de la página. Todo corría del lado del cliente, y me sorprendió que funcionara perfectamente.

  • La demo se comporta de forma bastante rara. Por ejemplo, si busco "how to use typescript with createContext", los primeros resultados son solo entradas de typescript, así que parece que la búsqueda por similitud falla.

  • Gracias. Los modelos locales algún día traerán privacidad, y ya conozco un gran caso de uso que encaja perfecto con estos modelos de embeddings pequeños: búsqueda barata y rápida en una base de datos de productos.
    En mi caso, que dependa de la CPU también es una ventaja.

    • Buenísimo. Si hay alguna forma de apoyar o casos de uso concretos que deban cubrirse en el roadmap, avísame.
  • ¿Se puede pregenerar la generación de embeddings que toma 30 segundos y enviarla al navegador?
    Después la inferencia es rápida y buena.

    • Sí. Se puede correr el índice una sola vez del lado del servidor y enviar solo los embeddings al frontend.