2 pontos por GN⁺ 2024-05-10 | 2 comentários | Compartilhar no WhatsApp
  • É um projeto de busca, mapeamento e análise que coletou mais de 40 milhões de itens da API pública do Hacker News e anexou embeddings, metadados e texto completo a mais de 30 milhões de comentários e 4 milhões de posts
  • A abordagem inicial, que gerava embeddings só dos títulos, tinha limitações por causa de títulos ambíguos e do viés de Ask HN/Show HN, então foi substituída por um método que também usa o corpo das páginas linkadas, os comentários principais e o contexto ancestral dos comentários
  • À medida que a escala do processamento cresceu, foram combinados cerca de 150 GPUs da RunPod, um serviço de fila em Rust, um proxy de banco via HTTP/2, UMAP, renderização em Canvas e servidores de borda; após a troca de modelo, o tempo de embedding por entrada caiu de cerca de 600 ms para 6 ms
  • O ranking de busca não usa apenas correspondência simples de strings, mas combina similaridade de cosseno, pontuação do HN e peso temporal para refletir relevância semântica, sinais sociais e atualidade
  • A demo pública inclui apenas dados até por volta de 10 de abril de 2024, e os dados completos e o código estão disponíveis no GitHub para uso em experimentos de busca, recomendação, análise de usuários e atualização em tempo real

Escopo do projeto e dados públicos

  • Foi criado um mapa que posiciona todos os posts do Hacker News em um espaço semântico, junto com ferramentas de busca, análise e visualização
  • O ponto de partida foi um experimento com embeddings de texto, e o HN foi escolhido como dataset adequado por ter muito conteúdo selecionado e disponibilizar tudo de forma programática
  • Embeddings representam texto como pontos em um espaço de alta dimensão, e o mais útil não é a posição absoluta, mas sim a distância relativa entre os pontos
  • Havia três usos esperados
    • busca semântica no conteúdo acumulado do HN
    • recomendações personalizadas de acordo com áreas de interesse
    • análise de sentimento, popularidade e pontos de vista contrários por tema dentro da comunidade
  • Mais de 30 milhões de comentários e 4 milhões de posts foram publicados no lançamento do dataset
    • inclui metadados como ID, pontuação e autor
    • inclui embeddings
    • inclui o texto dos comentários e o texto das páginas web rastreadas
  • O código está disponível no repositório GitHub do hackerverse

Coleta de dados do Hacker News

  • O HN oferece uma API pública simples, e todos os objetos são consultados como item
  • maxitem.json fornece o maior ID, e no momento da escrita o ID máximo já passava de 40 milhões
  • Mesmo com tempo médio de resposta de 10 ms, rastrear 40 milhões de itens em sequência levaria mais de 4 dias, então foi necessário processamento paralelo
  • Foi criado um serviço rápido de coleta em Node.js, mas a abordagem com semáforo e fila de Promises era lenta porque a maior parte do tempo de CPU era gasta em código JS em espaço de usuário
  • Depois, o trabalho de fetch foi distribuído por todas as CPUs com a API worker_threads, melhorando o desempenho ao saturar todos os núcleos
  • Como a coleta paralela embaralha a ordem, marcadores de conclusão foram gravados em ordem de ID para evitar lacunas em caso de interrupção
  • Também foram observadas algumas características da API do HN
    • a pontuação aparentemente não cai abaixo de -1
    • não é possível obter downvotes de posts nem contagem de votos de comentários
    • alguns posts e comentários têm título, texto e URL vazios mesmo sem estarem deletados ou marcados
    • IDs de comentários podem ser menores que os de seus ancestrais, possivelmente por movimentação na árvore de comentários
  • O crawler do HN também foi separado em um projeto TypeScript à parte, crawler-toolkit-hn

Primeiros embeddings e infraestrutura

  • No começo, foram gerados embeddings partindo da ideia de que apenas os títulos dos posts já seriam suficientes para representar o significado
  • Para comparar modelos, foi usada como referência a Massive Text Embedding Benchmark, e o primeiro modelo escolhido foi o BGE-M3
  • Além de embeddings densos convencionais, o BGE-M3 também pode gerar lexical weights, que podem ser usados em busca híbrida combinada com métodos como BM25
  • A infraestrutura de geração de embeddings não era simples
    • bons modelos podem ter de milhões a bilhões de parâmetros
    • eles são muito mais eficientes em GPU, mas clusters de GPU são caros
    • se a inferência leva centenas de ms por entrada, processar 40 milhões de entradas com uma única GPU vira algo na escala de quase um ano
    • como os dados/servidores e as GPUs estavam separados, era necessário manter o pipeline ativo para que as GPUs não ficassem ociosas
  • Foi usado o RunPod para implantar, em contêineres, máquinas com GPU executadas em datacenters, aproveitando GPUs relativamente baratas como a RTX 4090
  • Como as GPUs estavam espalhadas pelo mundo e a latência de conexão com o banco, além do overhead de conexão, viraram problema, foi criado o db-rpc
    • faz proxy de consultas SQL para o banco local via HTTP/2
    • usa um grande pool de conexões compartilhado
    • processa várias consultas em uma única conexão com multiplexação HTTP/2
  • Como o AWS SQS impunha limites baixos de taxa e custo por mensagem em milhões de pequenas tarefas, foi criado o serviço de fila em Rust baseado em RocksDB, queued
    • processa mais de 100K op/s em um único nó
    • reduz a necessidade de batching, o tamanho das mensagens, limites de taxa e custos
  • Ao escalar para cerca de 150 GPUs, foi possível gerar embeddings de 40 milhões de posts e comentários em poucas horas
  • Na época, o custo de embedding por entrada era de cerca de 600 ms, e a utilização das GPUs permaneceu alta durante todo o tempo

Reforço de contexto com rastreamento de páginas web

  • A abordagem de gerar embeddings apenas dos títulos não foi suficiente
    • muitos posts têm títulos estranhos, criativos ou ambíguos
    • as expressões Ask HN e Show HN ocupavam uma grande parte do título completo, o que fazia esses posts tenderem a se agrupar juntos independentemente do tema
  • Para posts de texto e comentários, dava para usar o próprio texto, mas na maioria dos posts com link era necessário rastrear a página web vinculada
  • Um serviço em Rust buscava as URLs e analisava metadados como título, imagem, autor e texto principal a partir do HTML
  • A versão inicial em Node.js era 10 vezes mais lenta que a versão em Rust em tarefas intensivas de CPU, e a reescrita em Rust melhorou o desempenho
  • A extração de texto era feita analisando o HTML com scraper, removendo elementos HTML5 que semanticamente não eram o conteúdo principal e percorrendo a árvore restante
  • A degradação de links também foi significativa
    • cerca de 200 mil URLs falharam com 404, falha de resolução DNS ou timeout de conexão
    • isso representa menos de 5% dos 4 milhões de páginas
  • Para reduzir faltas, parte dos artigos antigos foi recuperada com a Wayback API do Internet Archive
    • o limite de taxa do Internet Archive era muito baixo, algo em torno de 5 por minuto

Segunda estratégia de embeddings

  • As páginas web são longas, mas o BGE-M3 suporta uma janela de contexto de 8192 tokens
  • Ainda assim, como o BGE-M3 era lento, ele foi trocado por jina-embeddings-v2-small-en
    • tem muito menos parâmetros
    • também apresenta bom desempenho segundo o MTEB
    • o tempo de inferência caiu para cerca de 6 ms, ficando 100 vezes mais rápido
  • Por causa das entradas longas, aumentar o tamanho do batch causava OOM, então não foi possível saturar completamente as GPUs
  • Para complementar páginas com pouco texto ou que não puderam ser obtidas, os principais comentários do HN do post foram adicionados após o corpo
    • foi assumido que os comentários de nível superior em item.kids já vinham ordenados por ranking
    • comentários deletados, dead ou com pontuação negativa foram excluídos
    • a entrada foi limitada a no máximo 64 KiB
  • Como comentários podem ter pouco significado isoladamente, foi montado um contexto maior retrocedendo até comentários ancestrais e até o título do post
  • Valores grandes, como embeddings e texto, foram armazenados em uma tabela kv separada
    • armazená-los na mesma linha deixava as linhas pesadas
    • até atualizações de colunas pequenas ficavam caras
    • mudanças de schema também ficavam caras

Criando um mapa semântico 2D com UMAP

  • UMAP é uma técnica de redução de dimensionalidade que busca preservar relações semânticas ao reduzir embeddings de alta dimensão para dimensões menores
  • Vetores de embedding de 1024 dimensões foram reduzidos a pontos em 2D para uso em visualizações do tipo scatter plot e mapas
  • O UMAP recebeu como entrada o grafo do PyNNDescent e os embeddings originais, usando metric="cosine" e n_components=2
  • O treinamento com milhões de entradas de alta dimensão demorava bastante, então foi usada uma VM c7i.metal-48xl da EC2
    • Aproveitando ao máximo um processador de 96 núcleos
    • Após cerca de 1 hora e 30 minutos, a matriz 2D foi gerada
  • Os embeddings 2D gerados e o modelo UMAP treinado foram salvos, para que novos embeddings possam ser transformados depois sem novo treinamento
  • O scatter plot inicial tinha pontos demais, então, para reduzir a densidade e exibir títulos, foi selecionado apenas o post com maior pontuação por célula da grade
  • No segundo embedding, com mais contexto, posts que eram difíceis de posicionar apenas pelo título passaram a ser colocados com mais precisão perto de conteúdo relacionado

Similaridade de cosseno e ranking de busca

  • O ponto central do uso de embeddings é encontrar a similaridade entre dois embeddings
  • Em embeddings de texto, distância e similaridade de cosseno são usadas com mais frequência do que a distância euclidiana comum
  • A distância de cosseno é útil quando a direção importa mais do que a magnitude
    • Uma discussão longa sobre X deve ser mais parecida com X do que com uma discussão longa sobre Y
    • Se a magnitude entrar na conta, esse tipo de relação pode ser distorcido
  • O cálculo principal usado foi o produto escalar entre a matriz de embeddings e o embedding da consulta
  • Se os vetores forem unitários, não é necessário dividir separadamente pela magnitude
  • Os resultados de busca não são ordenados apenas por similaridade pura
    • A similaridade de cosseno é um fator importante, mas não o único
    • A pontuação do HN é usada como prova social
    • O peso do tempo entra como um fator negativo proporcional a log(age), para que conteúdo antigo caia mais rápido em consultas em que atualidade importa

Aplicativo de mapa no navegador

  • O objetivo era criar um mapa interativo para explorar o espaço de embeddings do HN como se fosse o Google Maps
  • O comportamento desejado era claro
    • Ao dar zoom com pinça ou com a roda do mouse, mais pontos são exibidos
    • O espaçamento entre os pontos também aumenta
    • Alguns pontos recebem rótulos, mas não todos
    • Ao clicar em um ponto, os detalhes do post são exibidos
    • Há suporte a toque e mouse, tanto no mobile quanto no desktop
  • Como havia milhões de pontos, não era adequado enviar tudo de uma vez para o cliente
  • Foi usada uma estrutura que divide o espaço do mapa em tiles de grade, e o cliente busca apenas os tiles necessários
    • Cada tile pode ser identificado por coordenadas (x, y)
    • Eles podem ser armazenados em um repositório KV como o S3
    • Isso facilita a implantação sem lógica no lado do servidor
  • O nível de zoom foi tratado com uma abordagem de LOD
    • Em cada nível, a grade é dividida em 2 vezes mais células por eixo
    • Os pontos selecionados no nível anterior são copiados para o próximo nível, para que não desapareçam ao ampliar
  • O tamanho dos tiles teve como meta ficar abaixo de 20 KiB após compressão
    • Limitados a cerca de 1.500 pontos
    • Usando 8 bytes por ponto para (x, y), 4 bytes para ID e 2 bytes para pontuação
  • Na renderização do app web, a abordagem com milhares de elementos DOM prejudicava muito o desempenho
  • A implementação final usou Canvas e redesenha a cada atualização da viewport
    • Mesmo redesenhando milhares de pontos por frame, o funcionamento ficou suave e simples
  • A rotulagem seleciona repetidamente os posts com maior pontuação, mas exclui os que sobrepõem rótulos já existentes
    • A verificação de colisão usa a implementação R-tree do RBush
    • O comprimento dos rótulos é aproximado por um array de bytes do tamanho do título e uma fórmula ajustada, em vez de measureText() do navegador
  • Como os cálculos iniciais das caixas de rótulo e de colisão exigiam muita CPU, eles foram movidos para Web Workers
  • OffscreenCanvas também foi testado, mas como a lógica de renderização já era eficiente, não houve grande efeito

Terreno, fronteiras e rótulos de cidades

  • Um mapa só com pontos carecia de senso de orientação e interesse visual, então foi adicionado o conceito de terreno e cidades
  • Como não existem geografia real nem fronteiras políticas, o terreno foi tratado por analogia, representando a densidade dos pontos
  • A densidade dos pontos serve como um sinal rápido para mostrar áreas com muita atividade, interesse, conteúdo, participação, popularidade e discussão
  • No início foi considerada a Kernel Density Estimation, mas tentativas com bibliotecas padrão demoraram demais
  • Em vez disso, cada ponto foi mapeado para uma célula grande da grade, gerando contagens por célula, e depois foi aplicado um Gaussian blur
  • Os valores lineares de densidade faziam o mapa parecer ruim, porque a maioria ficava muito próxima de 0
  • Ao aplicar log(density + 1), surgiu uma estratificação muito mais natural, e os pontos de encontro entre níveis de densidade diferentes passaram a parecer fronteiras implícitas
  • Em vez de uma imagem gigante, foram gerados caminhos SVG para que o cliente os desenhe como polígonos
    • Isso é eficiente, já que há apenas 4 cores
    • As fronteiras permanecem nítidas mesmo com zoom
    • A função de contorno do OpenCV foi usada para calcular curvas fechadas por nível
  • Os rótulos de cidades representam temas comuns dentro de um determinado raio
  • Os nomes das cidades foram embeddados e, com o modelo UMAP salvo, tiveram sua posição (x, y) obtida
  • A geração automática também foi tentada
    • Tentou-se criar nomes de cidades com LLMs, mas foi difícil obter com estabilidade a saída desejada via prompt
    • O K-means não conseguiu encontrar muitos clusters semanticamente significativos do jeito que uma pessoa agruparia
  • No fim, o mapa foi explorado manualmente e algumas cidades foram nomeadas à mão, o que levou cerca de 1 hora

Implantação na edge e responsividade

  • Como a exploração do mapa precisa ser rápida e responsiva, reduzir a latência no fetch dos dados era importante
  • No início, todos os dados do mapa estavam na região ENAM do Cloudflare R2, mas a latência ficava entre 600 ms e vários segundos
  • Mesmo que a latência física fosse de cerca de 200 ms, isso ainda estava longe do limiar em que 100 ms parece resposta imediata
  • Para reduzir a latência, os dados foram levados para mais perto dos usuários
  • Pequenos servidores foram colocados em Virginia, San Jose, Londres e Sydney, servindo os dados por meio de um servidor Rust
  • O cliente chama /healthz de várias edges algumas vezes e escolhe o servidor que responder primeiro
  • Abordagens como Anycast ou CDN também seriam possíveis, mas não foram usadas por custo e complexidade excessiva
  • O uso de memória do processo dos servidores edge ficou entre 2 e 4 vezes o tamanho dos dados originais, o que levantou dúvidas
    • Possível uso de tipos incorretos
    • struct padding
    • Superalocação de Vec e HashMap
    • Possível fragmentação ou ineficiência do alocador de memória

Resultados da busca semântica e limitações

  • Em consultas simples como “entering the tech industry”, apareceram juntos resultados com muitos upvotes e outros menos notados, e a relevância e utilidade pareceram boas
  • Em comparação com o serviço de busca existente do HN, a busca semântica por embeddings encontra resultados mesmo sem correspondência literal de texto
  • Consultas em forma de pergunta, como “what happened to wework”, também mostram resultados ao longo de vários anos sobre demissões, queda das ações e falência da WeWork
  • Os resultados são correspondidos mesmo quando não contêm literalmente as palavras “what happened” nem estão em formato de pergunta
  • Um problema é que não havia filtragem de resultados insuficientemente parecidos, então alguns resultados inferiores completamente irrelevantes entravam na lista
    • Isso foi tratado como um problema fácil de corrigir
  • Na busca por “career growth”, apareceram ensaios criativos e diversos que não continham literalmente essas palavras
  • A qualidade de curadoria e as pontuações do HN ajudam na qualidade da busca
  • O app tem sugestões de consulta hardcoded, como “linus rants”, “self bootstrapping” e “cool things with css”

Comunidades virtuais e análise de comentários

  • É possível criar a funcionalidade de subcomunidades virtuais usando embeddings
  • Quando o usuário insere o nome ou a descrição de uma comunidade, posts que ultrapassam um certo limiar de similaridade são agrupados instantaneamente
  • Como o HN não tem um recurso para subdividir posts com mais granularidade, isso funciona como uma forma de criar na hora uma curadoria centrada em interesses específicos
  • Os snippets e imagens dos cartões de resultado vêm dos metadados das páginas armazenados pelo crawler da web
  • O ícone do site foi simplificado usando no cliente o /favicon.ico do domínio, em vez de fazer parsing de metadados complexos
  • Threads de comentários também podem ser exibidas da mesma forma
  • Como a pontuação dos comentários não é fornecida pela API do HN, só é possível ordenar cronologicamente
  • Como forma de ranquear sem a pontuação dos comentários, considera-se possível usar histórico de comentários do usuário, participação ao redor daquele comentário, post, tópico e conteúdo
  • Também é possível encontrar usuários influentes ou ativos em um tema específico
    • Na consulta “cloudflare”, jgrahamc e eastdakota aparecem no topo
    • Os dois usuários são, respectivamente, o CTO e o CEO da Cloudflare
  • Esse trabalho é processado com operações de matriz, sem classificar comentários separadamente nem usar busca por palavras-chave
  • Em geral, filtrar depois costuma ser mais rápido e suficiente do que filtrar antes
    • A filtragem prévia pode exigir remover linhas correspondentes da matriz de embeddings, o que pode demandar cópias enormes de memória ou cálculos lentos de vetorização parcial
    • A ideia é que é melhor primeiro encontrar linhas semelhantes e depois filtrar os resultados
  • Um limiar mínimo é importante
    • Itens não relacionados também podem ter similaridade na faixa de 0,6
    • Sem um limiar, usuários com muitos comentários podem dominar o ranking apenas por escala

Análise de sentimento e popularidade de todos os comentários

  • A análise em grande escala que se queria fazer com 30 milhões de comentários era de popularidade e sentimento
  • O objetivo era ver como o HN sente determinados temas ao longo do tempo, como eventos importantes afetam o sentimento e como tópicos de interesse crescem ou perdem força
  • Como não havia dados de sentimento, foi usado um modelo open source de classificação de sentimento do Hugging Face
  • O modelo escolhido foi o TweetEval, treinado em conteúdo de redes sociais
  • Como o TweetEval é um modelo para tweets curtos, assim como no caso dos embeddings, foi usado apenas o próprio comentário como entrada, sem anexar contexto ancestral
  • Os comentários foram colocados em uma fila, processados em um cluster de GPUs e depois os resultados foram armazenados
  • Como o modelo é pequeno, o tamanho do lote foi aumentado para melhorar a eficiência da GPU
  • Aumentar o tamanho do lote usa mais VRAM, mas pode reduzir a transferência de memória entre host e GPU e aumentar o paralelismo
  • Em modelos Transformer, a entrada em lote precisa ser retangular, então ela é preenchida até o comprimento da entrada mais longa
    • Se vários inputs curtos vierem misturados com um único input longo, o tamanho total da entrada e os estados internos podem crescer bastante
    • Isso pode causar picos de memória e OOM
  • No exemplo de análise de sentimento sobre Rust, havia bastante sentimento positivo em relação a Rust no geral
    • Houve um pico positivo por volta do anúncio do Rust 1.0
    • Posts mais negativos se correlacionavam, segundo o modelo, com muitos comentários negativos
  • A popularidade por linguagem também foi estimada ponderando pontuação e similaridade
    • Como o HN não fornece pontuação de comentários, a pontuação dos comentários não pôde ser usada
    • Rust vai bem, mas aparentemente não é tão popular quanto outras linguagens
    • O limiar de similaridade pode precisar de ajuste, então os resultados podem estar errados

Acelerando grandes cálculos numéricos com GPU

  • Consultas de análise levavam de 10 a 30 segundos mesmo em uma máquina com 32 núcleos, o que era lento para experimentação interativa
  • Depois de considerar índices e pré-processamento, a abordagem mudou para usar GPU nos cálculos numéricos vetorizados
  • CuPy e cuDF oferecem APIs parecidas com NumPy e pandas, respectivamente, mas executadas na GPU
  • O porte foi relativamente simples, e o tempo das consultas caiu para a faixa de algumas centenas de ms
  • A velocidade ficou alta o bastante para nem usar grafos ANN
  • A parte difícil foi colocar a grande matriz de embeddings na GPU
    • A matriz de embeddings de comentários tem tamanho 30M x 512
    • Era difícil acomodar mais de 1x da matriz na memória do sistema ou na memória de vídeo
  • Um método simples de carregamento pode criar várias cópias
    • Lê os bytes do disco
    • Carrega em um array NumPy
    • Converte em um array CuPy
    • Copia para a GPU
    • Esse processo pode gerar 4 cópias no total, das quais 3 ficam na memória
  • A abordagem final foi mapear em memória a matriz no disco, pré-alocar na GPU uma matriz não inicializada do mesmo tamanho e então copiar em chunks
  • Isso evita ler primeiro para a memória do Python e usa exatamente 1x de RAM do sistema e 1x de VRAM

Demo e próximos passos

  • O app de demonstração está disponível em hn.wilsonl.in
  • A página principal traz o mapa e a busca, e os botões no canto superior direito dão acesso às comunidades e às ferramentas de análise
  • As URLs das comunidades e dos resultados de análise armazenam a consulta na URL, então podem ser compartilhadas com outras pessoas
  • O dataset de demonstração foi cortado por volta de 10 de abril de 2024 e não inclui posts e comentários mais recentes ao vivo
  • Há várias ideias que ainda se gostaria de explorar
    • Dados ao vivo mantidos continuamente atualizados
    • Um sistema de recomendação com deep learning operando sobre a web curada do HN
    • Melhorias nos resultados de busca com treino de reranker
    • Caminhos e jornadas interessantes sobre o mapa
    • Análise de relações de similaridade e oposição entre usuários
    • Análise dos usuários mais especializados em determinados nichos
  • Os dados completos e o código podem ser vistos no GitHub

2 comentários

 
GN⁺ 2024-05-10
Opiniões do Hacker News
  • Um trabalho especialmente impressionante para um projeto de uma pessoa só.
    O gráfico de análise de sentimento ao longo do tempo chamou a atenção, e achei interessante porque foi a primeira vez que vi Rust dessa forma. Fiquei curioso para saber quais temas foram os mais positivos ao longo do tempo e se houve algum que despencou de repente.
    A frase “parece haver muito sentimento negativo no HN como um todo” também soa intuitivamente correta em relação às redes sociais. Seria interessante ver uma comparação de sentimentos por plataforma de mídia social e por período.

    • Gostaria de me aprofundar mais na parte de sentimento. Como foi dito, também seria interessante ver uma visão geral, não apenas consultas específicas.
      O sentimento negativo chamou a atenção porque eu esperava originalmente um gráfico de sentimento mais nítido. Imaginava algo em geral neutro a positivo, que puxasse para o positivo perto de textos positivos e para o negativo perto de textos negativos, mas em quase todas as consultas o sentimento era quase sempre negativo. Mesmo textos positivos pareciam atrair muita negatividade segundo o modelo e a abordagem, e ambos podem estar errados, então quero investigar melhor em um post futuro no blog.
    • Além do sentimento por plataforma de mídia social e por período, seria bom também ver horário do dia e dias úteis/fins de semana.
    • Alguns meses atrás escrevi um post no blog analisando o sentimento dos comentários do HN sobre IA, blockchain, trabalho remoto e Rust. O gráfico final, bem no fim do texto, tem relação com este tema.
      https://openpipe.ai/blog/hn-ai-crypto
    • É uma pena mesmo que a API do HN não forneça a contagem de votos dos comentários. Fico curioso para saber como a análise de sentimento mudaria se fosse ponderada por upvotes/downvotes.
      Não tenho base para isso, mas acho que engenheiros em geral são críticos e, em vez de escrever feedback positivo repetidamente, tendem a apertar +1. Já críticas eles escrevem de forma mais direta :)
    • Crypto também parece se encaixar nessa categoria.
  • É um bom exemplo para quem não está familiarizado com engenharia de dados/MLOps.
    Sugiro criar clusters hierárquicos dos pontos com HDBSCAN e gerar nomes para os clusters internos com um modelo. Assim fica mais fácil explorar os temas até as folhas, abrindo subitens com base na conectividade com o nó atual.
    As cores dos grupos deveriam ser mais distintas, e ter clusters provavelmente ajudaria. O tamanho do texto de cada post deveria variar conforme a importância/relevância no conjunto geral ou na busca atual. Com mais resumos dos clusters internos, seria possível substituir vários posts por resumos de grupo até dar zoom, reduzindo a poluição de texto.

  • O escopo do projeto é surpreendentemente grande.
    Dito isso, seja jina ou bge-3/flag, os embeddings e o tokenizer não parecem muito adequados a temas técnicos. Palavras de linguagem natural funcionam bem, mas ao pesquisar conceitos técnicos como “xaml” ou “simd”, o sistema tende a tokenizar a entrada e tentar encontrar palavras que soem parecidas.
    Como feedback construtivo, seria bom haver uma forma de não mostrar repetidamente os mesmos resultados de “ranking do HN” quando o tema é nichado demais e não há resultados. Quando se pesquisa uma palavra com a qual o embedding não está familiarizado, “Stephen Hawking has died” aparece com frequência.
    Também não tenho certeza de quão bem a análise de sentimento funciona. Pareceu haver sentimento negativo demais, de um jeito que não bate com a realidade, e até ao buscar algo como “Mr Rogers”, que o HN veria de forma esmagadoramente positiva, aparece um pico negativo forte. Ao pesquisar “Carter”, há um enorme pico negativo relacionado à morte de Rosalynn Carter, mas o post em si era uma submissão falando das coisas excelentes que o casal Carter fez.
    “Popularidade ao longo do tempo” provavelmente deveria ser normalizada pela mediana de votos dos posts daquele mês/ano. Se você simplesmente plota a quantidade de posts, a linha de tendência só continua subindo. Dá para entender vendo a popularidade de “diesel”, termo que atingiu o pico há 10 anos. Ou talvez devesse usar a frequência de ocorrência da palavra-chave, ou o número de itens cuja similaridade de cosseno com a consulta seja menor que x, em vez da pontuação dos posts.
    A funcionalidade dinâmica de clicar em posts para removê-los e recalcular o limiar de similaridade é excelente.

    • Como seria possível determinar programaticamente que um determinado modelo de embedding não reconhece um termo ou palavra específica?
  • Existe uma ótima ferramenta que faz quase a mesma coisa para qualquer dataset: https://github.com/enjalot/latent-scope
    Claro que a escala do projeto original acrescenta muitas complexidades interessantes, e essa ferramenta não lida com algo desse tamanho, mas é boa para datasets de porte médio.

  • Quero analisar se a autopromoção aumentou no HN
    Aqui, defino autopromoção não como posts no formato “Show HN: Something ...”, mas no formato “Show HN: I ...”
    Entre os 100 principais atuais, por exemplo, “Show HN: Exploring HN by mapping and analyzing 40M posts and comments for fun” e “Show HN: Browser-based knitting (pattern) software” não são títulos de autopromoção. Os sujeitos são, respectivamente, a exploração e o software
    Por outro lado, “Show HN: I built a non-linear UI for ChatGPT” e “Show HN: I created 3,800+ Open Source React Icons” são títulos de autopromoção. O sujeito de cada um é “I”
    Verificando de forma simples, nos resultados de busca da Algolia, os títulos que começam com “Show HN: I” em cada ano a partir de 1º de abril, e dividindo pelo número total de resultados daquele ano para plotar um gráfico, ficou assim
    2023 ****************************************
    2022 ***********************************
    2021 ***************************
    2020 **************************************
    2019 *************************
    2018 *************
    2017 *******
    2016 **********
    2015 ********
    2014 ************
    2013 *********************
    2012 *****************
    2011 *********
    2010 ***
    Na época em que cresci, acho que autopromoção em geral era vista como um traço de caráter ruim. Aprendi que suas ações deveriam promover você, não que você devesse chamar atenção para elas, mas sinto que essa cultura está mudando
    Se a autopromoção realmente aumentou, fico curioso se é influência das redes sociais e coisas do tipo. No YouTube também sinto um aumento parecido, mas só tenho a impressão de ver muitos vídeos recomendados começando com “I.....”; não tenho dados

    • A definição de autopromoção é um pouco diferente da que eu normalmente considero. Em geral, quando uma pessoa promove algo que ela mesma criou, vejo isso como autopromoção. Então os dois exemplos dados como não sendo autopromoção também seriam autopromoção pela minha definição
      Ou seja, o que você separou em autopromoção e não autopromoção, pelos meus critérios, são casos em que o título deixa muito claro que é autopromoção e casos em que deixa isso menos claro. Dito isso, a própria expressão “Show HN” parece ser usada apenas para autopromoção, então quem conhece a convenção sabe que é autopromoção mesmo sem o “I”
    • Todo Show HN deve ser algo feito pelo autor, então não sei bem por que explicitar o que está implícito tornaria algo mais autopromocional
      Todos significam “olha, eu fiz uma coisa legal; o que vocês acham?”
    • Isso também é bastante abordado na biografia de Einstein por Walter Isaacson, então as pessoas observam esse fluxo há muito tempo
      Por exemplo, os alemães acusavam Einstein de fazer autopromoção e, em contraste, os EUA tinham uma cultura de celebridades. Talvez seja um fenômeno cíclico
  • Acho que é, de longe, o post mais legal que vi no HN este ano

  • Não ficou claro quando vi pela primeira vez, mas o app real está aqui: https://hn.wilsonl.in/

    • Fico curioso se o link da landing page foi colocado quase no fim de propósito. Na prática, a estrutura faz com que só quem realmente leu o post vá para o site
      Não estou sendo sarcástico; acho uma boa ideia
    • O link de busca não parece ser compartilhável, ou não parece incluir o termo de busca nele
      Também fico curioso se a frase de busca é embutida palavra por palavra e se usa o mesmo modelo usado para os documentos. Pesquisei “lead generation” e apareceram resultados sobre intoxicação por chumbo, embora uma incorporação decente de expressões não unigramas devesse entender essa expressão
    • Encontrei a mim e meus posts lá! Legal
  • Uma recomendação moderna para UMAP é o Parametric UMAP: https://umap-learn.readthedocs.io/en/latest/parametric_umap....
    Ele treina um pequeno MLP em Keras para realizar redução de dimensionalidade para 2D minimizando a perda do UMAP. A vantagem é que esse modelo é pequeno, então pode ser salvo e reutilizado para prever novos dados desconhecidos. Um modelo UMAP treinado tradicionalmente é grande. Além disso, como usa GPU, em teoria o treinamento é muito mais rápido
    A desvantagem é que a implementação no pacote Python de UMAP não é muito boa, e ela cria e envia para a GPU o dataset inteiro expandido de nós/arestas. Por isso, só consegue treinar em cerca de 100 mil embeddings antes de ficar sem memória
    Como um pipeline totalmente não supervisionado do tipo UMAP → HDBSCAN → rotulagem de clusters por IA é extremamente útil, isso me deu vontade de criar uma implementação de Parametric UMAP mais escalável

    • Há uma implementação rápida em GPU no cuML. Não sei muito bem por que o cuML é tão pouco conhecido
    • À primeira vista, parece que é porque a implementação envia o grafo inteiro, ou seja, todas as arestas, para a GPU. Fazer amostragem de arestas durante o treinamento pode aliviar isso
  • Para algo que parece um projeto exploratório de hobby, é um trabalho surpreendentemente grande. Não quero diminuir o mérito, é realmente muito legal, mas fiquei surpreso com o volume de esforço
    Foram usadas 150 GPUs só para calcular os embeddings, e foram desenvolvidos dois sistemas customizados, db-rpc e queued, para comunicação entre servidores. Também houve muito trabalho periférico e computação envolvida
    Fiquei curioso sobre o contexto do projeto. Também queria saber como conseguiram o financiamento e o tempo necessários para uma pesquisa assim
    Como alguém que já fez bastante coisa parecida profissionalmente, mapeando artigos acadêmicos e o panorama de patentes, não tenho certeza se 150 GPUs eram realmente necessárias. Se no fim é uma projeção em 2D e clusterização, abordagens tradicionais de bag of words ou modelagem de tópicos seriam muito mais fáceis e baratas, e acho que quase não se perceberia diferença de qualidade. Usar grafos de autores e threads de comentários também poderia produzir resultados semelhantes

    • O custo não foi mencionado no texto, mas ficou na casa de algumas centenas de dólares, então era bem acessível até como projeto de hobby
      As GPUs eram surpreendentemente baratas, e na maior parte eu só escalei porque estava impaciente :) O cluster inteiro também rodou por apenas algumas horas
      Se tiver um link para o trabalho que você fez, eu gostaria de ver. Parece interessante e eu queria ler mais sobre isso
    • O autor claramente é muito competente. Também é curioso que ele publique no HN, mas não comente desde 2018; depois disso começou este projeto
      Em termos de dinheiro e tempo, é possível que ele esteja entre um trabalho e outro, ou entre empregos, e que tenha tido sucesso financeiro em uma carreira ou negócio anterior, conduzindo isso com recursos próprios. O uso de GPU também parece ter sido muito eficiente, então o custo provavelmente não foi tão alto
    • Dá para obter resultados bem bons usando embeddings mais baratos
      Aplicar aprendizado de máquina clássico, como SVM com calibração de probabilidade, a esses embeddings produz bons resultados em classificação e clusterização, e é mais de 100 vezes mais rápido do que fazer fine-tuning de LLMs
  • Normalmente não se faz como nesta demo; os vetores são normalizados
    Ao usar vetores normalizados, a distância euclidiana mede a distância entre as extremidades dos dois vetores. Já a distância de cosseno mede o comprimento da projeção de um vetor sobre o outro

    • O problema da normalização é que você perde um grau de liberdade. Em visualização, isso é efetivamente como perder uma dimensão
      Um vetor 2D normalizado, na prática, é apenas um vetor 1D. Se você quer mostrar relações em 2D, precisa usar vetores 3D para recuperar dois graus de liberdade
 
ggg213 2024-05-10

O título está faltando.