1 pontos por GN⁺ 3 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • Ternlight permite realizar embeddings de texto e busca por similaridade dentro do navegador, sem chamadas ao servidor, possibilitando montar rapidamente uma pequena busca semântica no lado do cliente
  • O pacote básico soma 7 MB entre engine e pesos; a variante mini tem 5 MB e roda na CPU, sem GPU
  • Depois de instalar @ternlight/base, basta importar embed e similar para criar um fluxo de busca semântica em cerca de 3 linhas
  • A chamada de exemplo ordena os 3 principais resultados em uma lista de receitas, com destaque para cerca de 5 ms e nenhuma chamada de rede
  • A demo de busca na documentação do React executa a pesquisa no navegador quando o usuário digita uma pergunta, e é movida pelo tier de 5 MB @ternlight/mini

Embeddings resolvidos dentro do navegador

  • Ternlight se apresenta como um “modelo de embeddings de 7 MB”, incorporando texto em milissegundos sem chamar um servidor
  • Características de execução

    • Sem chamadas de API
    • Engine + pesos: 7 MB
    • Variante mini: 5 MB
    • Embeddings rápidos: cerca de 5 ms
    • Apenas CPU, sem GPU

Instalação e exemplo de uso

  • É oferecido como um único pacote npm e pode ser usado sem etapa separada de download de modelo nem servidor
  • O comando de instalação é o seguinte
npm install @ternlight/base
  • Importe embed e similar de @ternlight/base para executar uma busca semântica
import { embed, similar } from '@ternlight/base';

similar('easy weeknight dinner ideas', recipes, { topK: 3 });
// → ranked matches · ~5 ms · zero network
  • A demo de busca na documentação do React executa a pesquisa no navegador quando o usuário digita uma pergunta, e roda com o tier de 5 MB @ternlight/mini

1 comentários

 
GN⁺ 3 시간 전
Comentários no Hacker News
  • Eu queria rodar um modelo útil no navegador como projeto de hobby, então destilei um pequeno codificador de sentenças a partir do MiniLM e apliquei treinamento com reconhecimento de quantização ternária.
    Também escrevi o mecanismo de inferência por conta própria e o distribuí em Rust → WASM SIMD.
    Não é um LLM, mas um modelo de embeddings: ao inserir um texto, ele gera um vetor de 384 dimensões, e a relevância entre textos é determinada pela similaridade de cosseno entre dois vetores. Por exemplo, "reset my password" e "I forgot my password" saem com algo como 0,88.
    Pode ser usado para busca semântica, correspondência de FAQ/intenção e clustering; por rodar no próprio dispositivo, permite busca semântica rápida assim que a entrada é fornecida, sem depender de APIs.
    A demo pesquisa 2 mil documentos do React inteiramente no dispositivo: https://ternlight-demo.vercel.app
    No npm há dois níveis: @ternlight/base (7 MB, cerca de 5 ms por embedding, embeddings com melhor desempenho) e @ternlight/mini (5 MB de transferência, cerca de 2,5 ms por embedding), empacotados para Node e navegador.
    O repositório inclui detalhes técnicos, licença MIT e pipeline de treinamento: https://github.com/soycaporal/ternlight
    Tenho curiosidade se embeddings no dispositivo são realmente úteis e quais casos de uso existem.

    • Tenho um dicionário que mapeia palavras para tags do OpenStreetMap, como em https://codeberg.org/cartes/web/src/branch/master/components...
      Fico curioso se isso ajudaria a fazer com que, quando o usuário digite "pancake", ele encontre crêpe sem eu precisar escrever explicitamente uma entrada de dicionário "pancake = crêpe".
      Se entendi corretamente, também queria saber se a estrutura é a biblioteca baixar 5 MB uma vez no início e, depois disso, ser usada mais ou menos como hoje uso Fuse.js.
      Também gostaria de saber quão bem ela lida com idiomas além do inglês e se daria para treiná-la com a wiki de tags do OpenStreetMap.
    • Tenho muito interesse em colocar uma busca semântica simples em um app desktop nativo.
      Fico curioso se há alguma comparação com outros modelos de embeddings ultrapequenos. É difícil saber se o MiniLM-L6 foi o ponto de partida por ser um modelo particularmente bom nessa categoria, já que a única métrica fornecida é "Retrieval (SciFact NDCG@10)".
      Ainda assim, há uma diferença considerável em relação ao desempenho alegado: no Firefox em um i5-4570, obtenho apenas 35 embeddings por segundo, não 400. Suspeito que possa estar caindo para um caminho sem SIMD, e pretendo testar também o binário Rust nativo.
    • Acabei de gerar embeddings para toda a documentação do django e para a base de conhecimento interna, e agora consigo pesquisar instantaneamente nas duas fontes.
  • Legal, mas seria bom colocar um botão para iniciar a demo na landing page. Fiquei bem surpreso ao abrir a página e ouvir a ventoinha girando feito louca.

    • Concordo. Ao mesmo tempo, isso me deu uma certa nostalgia de uma época em que era possível saber intimamente o que estava acontecendo só pelo som do computador.
    • Me assustei quando a ventoinha começou a girar. Mas, para ser justo, a torradeira também me assusta de vez em quando.
    • Aproveitamento extremo de ciclos de CPU; quem foi que disse que só GPU era especial?
  • Seria bom transformar isso em um plugin para Astro ou para um metaframework genérico, que analisasse automaticamente todos os arquivos HTML gerados e criasse um pequeno banco de dados de embeddings.
    No front-end, ele poderia ser carregado sob demanda, e talvez o HNSW também pudesse ser armazenado em chunks para carregar só as partes necessárias à consulta de busca.
    Por exemplo, algo parecido com https://pagefind.app/, mas oferecendo busca vetorial totalmente estática.

    • No nosso gerador de sites estáticos, queríamos usar sqlite-vec, mas, da última vez que verifiquei, HNSW não estava implementado ou o suporte a busca vetorial no navegador não era bom. Acho que ainda estava fazendo varredura completa da tabela.
      Como meses, talvez anos, se passaram e continua assim, isso me pareceu um sinal meio decepcionante de falta de capacidade para concluir o projeto direito. Pior: recomendei esse projeto como um bom candidato em uma bolsa à qual me candidatei; eles foram selecionados e eu não.
      Se alguém souber de uma boa solução nessa área, ou se eu estiver errado sobre SQLite-vec, gostaria que me dissesse. No nosso SSG, praticamente decidimos trabalhar alguns meses em outra infraestrutura e, se isso ainda não estiver pronto, vamos fazer nós mesmos.
  • Isso poderia ser uma adição bem interessante a um projeto de busca DuckDB HNSW que vi aqui antes: https://github.com/jasonjmcghee/portable-hnsw
    É realmente interessante que a busca aconteça usando requisições HTTP de intervalo sobre arquivos Parquet hospedados estaticamente.
    Acho que coisas assim podem evoluir para um ecossistema de busca relativamente aberto e distribuído, não controlado por grandes empresas.

    • Em uma ideia parecida, também poderia ser interessante usar um banco SQLite em um host estático com requisições HTTP de intervalo e WASM.
      https://news.ycombinator.com/item?id=27016630
    • Ideia excelente. Gosto muito de requisições de intervalo e de formatos que o cliente consegue percorrer em hospedagem estática.
  • Isso é muito legal e pode ser a peça que faltava para algo que eu queria criar antes.
    Com https://github.com/npiesco/absurder-sql, dá para persistir todo o corpus original dentro do navegador em IndexedDB/SQLite.
    Depois, como em https://weaviate.io/blog/chunking-strategies-for-rag, em vez de indexar tudo antecipadamente, dá para usar o Ternlight para gerar e armazenar embeddings em cache sob demanda.
    Isso também permitiria busca híbrida, combinando o FTS5/BM25 do SQLite nativo com a busca semântica do Ternlight por Reciprocal Rank Fusion.

  • Muito bem feito.
    Ele é promovido como tendo 7 MB, mas também há uma versão mini de 5 MB.
    Pelo visto, a mini usa internamente um vetor de 256 elementos em vez de 384 para reduzir espaço e, no fim, projeta para 384 por compatibilidade.
    O tamanho cai em um terço, mas a perda não é linear; mesmo usando um caminho de dados menor, a perda de informação parece ser menor que um terço.

  • Projeto bacana.
    Já tentei algo parecido antes: http://sol.quipu-strands.com/
    Eu queria carregar um modelo de embeddings no navegador e ordenar textos semanticamente.
    Peguei pesos ONNX (MPNet, MiniLM) do HuggingFace, gerei embeddings com Transformers.js e depois usei clusterizadores do scikit-learn rodando em pyodide dentro da página. Tudo rodava do lado do cliente, e fiquei surpreso por funcionar perfeitamente.

  • A demo se comporta de forma bem estranha. Por exemplo, ao buscar "how to use typescript with createContext", os principais resultados são apenas itens de typescript, então parece que a busca por similaridade falhou.

  • Obrigado. Modelos locais um dia trarão privacidade, e já conheço um ótimo caso de uso que combina muito bem com modelos pequenos de embeddings como este: busca barata e rápida em bancos de dados de produtos.
    No meu caso, depender da CPU também é uma vantagem.

    • Legal. Se houver uma forma de apoiar, ou casos de uso específicos que deveriam ser tratados no roadmap, seria bom saber.
  • É possível gerar previamente os embeddings, que levam 30 segundos, e enviá-los para o navegador?
    Depois disso, a inferência é rápida e boa.

    • Sim. Dá para rodar a indexação uma única vez no lado do servidor e enviar apenas os embeddings para o front-end.