2 puntos por GN⁺ 2024-05-10 | 2 comentarios | Compartir por WhatsApp
  • Proyecto de búsqueda, mapeo y análisis que recopiló más de 40 millones de ítems desde la API pública de Hacker News y añadió embeddings, metadatos y contenido a más de 30 millones de comentarios y 4 millones de publicaciones
  • El enfoque inicial, que solo generaba embeddings del título, tenía límites por los títulos ambiguos y el sesgo de Ask HN/Show HN, así que cambió a un método que también usa el contenido de la página web enlazada, los comentarios principales y el contexto ancestral de los comentarios
  • A medida que creció la escala del procesamiento, se combinó alrededor de 150 GPU de RunPod, un servicio de colas en Rust, un proxy de BD sobre HTTP/2, UMAP, renderizado en Canvas y servidores edge; tras cambiar de modelo, el tiempo de embedding por entrada bajó de unos 600 ms a 6 ms
  • El ranking de búsqueda no usa solo coincidencia simple de cadenas, sino también similitud coseno, puntuación de HN y ponderación temporal para reflejar relevancia semántica, señales sociales y actualidad
  • La demo pública solo incluye datos hasta alrededor del 10 de abril de 2024, y los datos completos y el código están publicados en GitHub para usarse en experimentos de búsqueda, recomendación, análisis de usuarios y actualización en tiempo real

Alcance del proyecto y datos públicos

  • Se creó un mapa que ubica todas las publicaciones de Hacker News en un espacio semántico, junto con herramientas de búsqueda, análisis y visualización
  • El punto de partida fueron experimentos con embeddings de texto, y HN se eligió como dataset adecuado porque tiene mucho contenido curado y ofrece todo su contenido de forma programática
  • Los embeddings representan texto como puntos en un espacio de alta dimensión, y más que la posición absoluta, lo útil es la distancia relativa entre los puntos
  • Había tres usos esperados
    • Búsqueda semántica sobre el contenido acumulado de HN
    • Recomendaciones personalizadas según áreas de interés
    • Análisis por tema de sentimiento, popularidad y puntos de vista opuestos dentro de la comunidad
  • Se publicaron más de 30 millones de comentarios y 4 millones de publicaciones en el dataset release
    • Incluye metadatos como ID, puntuación y autor
    • Incluye embeddings
    • Incluye texto de comentarios y texto de páginas web rastreadas
  • El código está publicado en el repositorio de GitHub de hackerverse

Recolección de datos de Hacker News

  • HN ofrece una sencilla API pública, y todos los objetos se consultan como item
  • maxitem.json entrega el ID más alto, y al momento de escribir esto el ID máximo supera los 40 millones
  • Incluso con un tiempo de respuesta promedio de 10 ms, rastrear 40 millones de ítems de forma secuencial tomaría más de 4 días, así que fue necesario el procesamiento en paralelo
  • Se creó un servicio rápido de recolección en Node.js, pero el enfoque con semáforos y colas de Promise era lento porque la mayor parte del tiempo de CPU se iba en código JS de espacio de usuario
  • Después se distribuyeron las tareas de fetch entre todos los CPU con la API worker_threads, mejorando el rendimiento al saturar todos los núcleos
  • Como la recolección en paralelo desordena la secuencia, se registraron marcadores de completado en orden de ID para evitar huecos si el proceso se interrumpía
  • También hubo características observadas en la API de HN
    • La puntuación parece no bajar de -1
    • No se pueden obtener los downvotes de publicaciones ni el número de votos de los comentarios
    • Algunas publicaciones y comentarios tienen título, texto y URL vacíos aun cuando no están eliminados ni marcados
    • Un ID de comentario puede ser menor que el de su ancestro, posiblemente por movimientos dentro del árbol de comentarios
  • El crawler de HN también se separó como un proyecto independiente en TypeScript: crawler-toolkit-hn

Primer embedding e infraestructura

  • Al principio se generaron embeddings suponiendo que solo con el título de la publicación bastaría para representar su significado
  • Para comparar modelos se tomó como referencia el Massive Text Embedding Benchmark, y el primer modelo elegido fue BGE-M3
  • BGE-M3 puede generar lexical weights además de embeddings densos convencionales, por lo que sirve para búsqueda híbrida combinada con métodos como BM25
  • La infraestructura para generar embeddings no era sencilla
    • Los buenos modelos pueden tener desde millones hasta miles de millones de parámetros
    • Son mucho más eficientes en GPU, pero los clústeres de GPU son caros
    • Si la inferencia toma cientos de ms por entrada, procesar 40 millones de entradas con una sola GPU sería una tarea de casi un año
    • Como los datos, los servidores y las GPU están separados, había que mantener el pipeline para que las GPU no quedaran ociosas
  • Se usó RunPod para desplegar máquinas con GPU en contenedores dentro de centros de datos, aprovechando GPU relativamente económicas como la RTX 4090
  • Como las GPU estaban repartidas por todo el mundo y la latencia de conexión a la BD y el overhead de conexión eran un problema, se creó db-rpc
    • Hace proxy de consultas SQL a una BD local por HTTP/2
    • Usa un gran pool de conexiones compartidas
    • Procesa múltiples consultas sobre una sola conexión mediante multiplexación HTTP/2
  • Como AWS SQS imponía límites bajos de velocidad y costo por mensaje al manejar millones de mensajes de trabajo pequeños, se creó el servicio de colas en Rust basado en RocksDB queued
    • Maneja 100K+ op/s en un solo nodo
    • Reduce la carga de batching, tamaño de mensajes, límites de velocidad y costos
  • Al escalar hasta unas 150 GPU, se lograron generar embeddings para 40 millones de publicaciones y comentarios en pocas horas
  • En ese momento, el costo de embedding por entrada era de alrededor de 600 ms, y la utilización de las GPU se mantuvo alta durante todo el proceso

Refuerzo de contexto mediante rastreo de páginas web

  • El enfoque de generar embeddings solo del título no fue suficiente
    • Muchas publicaciones tienen títulos extraños, creativos o ambiguos
    • Las frases Ask HN y Show HN ocupaban una gran parte del título completo y tendían a agruparse juntas sin relación con el tema
  • En publicaciones de texto y comentarios se podía usar su propio texto, pero para la mayoría de las publicaciones con enlace había que rastrear la página web vinculada
  • Un servicio en Rust obtenía la URL y analizaba metadatos como título, imagen, autor y texto principal desde el HTML
  • La versión inicial en Node.js era 10 veces más lenta que la versión en Rust en tareas intensivas de CPU, y la reescritura en Rust mejoró el rendimiento
  • La extracción de texto se hacía analizando el HTML con scraper, eliminando elementos de HTML5 que no correspondían semánticamente al contenido principal y luego recorriendo el árbol restante
  • La putrefacción de enlaces también fue considerable
    • Aproximadamente 200 mil URL fallaron por 404, error de resolución DNS o timeout de conexión
    • Eso representa menos del 5% de 4 millones de páginas
  • Para reducir faltantes, se recuperó parte de artículos antiguos mediante la Wayback API de Internet Archive
    • El límite de velocidad de Internet Archive era muy bajo, de alrededor de 5 por minuto

Segunda estrategia de embedding

  • Las páginas web son largas, pero BGE-M3 admite una ventana de contexto de 8192 tokens
  • Sin embargo, BGE-M3 era lento, así que se reemplazó por jina-embeddings-v2-small-en
    • Tiene muchos menos parámetros
    • También muestra buen desempeño según MTEB
    • El tiempo de inferencia bajó a unos 6 ms, 100 veces más rápido
  • Debido a la longitud de las entradas, aumentar el tamaño del batch provocaba OOM, así que no se podía saturar por completo la GPU
  • Para complementar páginas con poco texto o que no se pudieron obtener, se añadieron comentarios principales de HN al final del contenido de la publicación
    • Se asumió que los comentarios de nivel superior ya estaban rankeados en item.kids
    • Se excluyeron comentarios eliminados, dead y con puntuación negativa
    • La entrada se limitó a un máximo de 64 KiB
  • Como los comentarios por sí solos pueden tener poco significado, se retrocedió hasta comentarios ancestros e incluso el título de la publicación para construir un contexto más amplio
  • Los valores grandes como embeddings y texto se almacenaron en una tabla kv separada
    • Guardarlos dentro de la misma fila la hacía demasiado pesada
    • Incluso actualizar columnas pequeñas se volvía costoso
    • Los cambios de esquema también se volvían costosos

Crear un mapa semántico 2D con UMAP

  • UMAP es una técnica de reducción de dimensionalidad que busca preservar las relaciones semánticas al reducir embeddings de alta dimensión a dimensiones más bajas
  • Se redujeron vectores de embedding de 1024 dimensiones a puntos 2D para usarlos en visualizaciones de dispersión y mapas
  • UMAP recibió como entrada el grafo de PyNNDescent y los embeddings originales, usando metric="cosine" y n_components=2
  • Entrenar con millones de entradas de alta dimensión tomaba mucho tiempo, así que se usó una VM c7i.metal-48xl de EC2
    • Se aprovechó al máximo un procesador de 96 núcleos
    • La matriz 2D se generó tras aproximadamente 1 hora y 30 minutos
  • Se guardaron los embeddings 2D generados y el modelo UMAP entrenado para poder transformar nuevos embeddings después sin reentrenar
  • El primer gráfico de dispersión tenía demasiados puntos, así que se redujo la densidad seleccionando solo la publicación con mayor puntaje por celda de la cuadrícula para mostrar títulos
  • En el segundo embedding, con más contexto, publicaciones que eran difíciles de ubicar solo por el título quedaron colocadas con mayor precisión cerca de contenido relacionado

Similitud coseno y ranking de búsqueda

  • La clave del uso de embeddings es encontrar la similitud entre dos embeddings
  • En embeddings de texto, la distancia o similitud coseno se usa con más frecuencia que la distancia euclidiana tradicional
  • La distancia coseno es útil cuando importa más la dirección que la magnitud
    • Una discusión larga sobre X debería parecerse más a otra discusión larga sobre X que a una discusión larga sobre Y
    • Si se refleja la magnitud, este tipo de relación puede distorsionarse
  • El cálculo central que se usó es el dot product entre la matriz de embeddings y el embedding de la consulta
  • Si los vectores son unitarios, no hace falta dividir por la magnitud por separado
  • Los resultados de búsqueda no se ordenan solo por similitud pura
    • La similitud coseno es un factor grande, pero no el único
    • El puntaje de HN se usa como prueba social
    • La ponderación temporal se incorpora como un factor negativo proporcional a log(age) para que, en consultas donde importa la actualidad, el contenido viejo pierda posiciones rápidamente

App de mapa en el navegador

  • El objetivo era crear un mapa interactivo para explorar el espacio de embeddings de HN como si fuera Google Maps
  • El comportamiento requerido estaba claro
    • Al hacer zoom con pellizco o con la rueda del mouse, debían mostrarse más puntos
    • La distancia entre los puntos también debía ampliarse
    • Algunos puntos debían llevar etiqueta, pero no todos
    • Al hacer clic en un punto, debían mostrarse los detalles de la publicación
    • Debía soportar tanto toque como mouse en móvil y escritorio
  • No era adecuado enviar todos los puntos al cliente de una sola vez, porque eran millones
  • Se usó una estructura que divide el espacio del mapa en tiles de cuadrícula y hace que el cliente solo traiga los tiles necesarios
    • Los tiles pueden identificarse con coordenadas (x, y)
    • Pueden almacenarse en un almacén KV como S3
    • Es fácil de desplegar sin lógica del lado del servidor
  • El nivel de zoom se manejó con un enfoque LOD
    • En cada nivel se divide en el doble de celdas de cuadrícula por eje
    • Los puntos seleccionados en el nivel anterior se copian al siguiente para que no desaparezcan al hacer zoom
  • El tamaño de cada tile apuntó a menos de 20 KiB comprimidos
    • Se limitó a unas 1,500 puntos
    • Por punto se usaron 8 bytes para (x, y), 4 bytes para el ID y 2 bytes para el puntaje
  • En el renderizado de la web app, usar miles de elementos del DOM destruía mucho el rendimiento
  • La implementación final usó Canvas y redibuja cada vez que se actualiza el viewport
    • Incluso redibujando miles de puntos por frame, funciona de forma fluida y simple
  • El etiquetado selecciona repetidamente las publicaciones con mayor puntaje, pero excluye las que se superponen con etiquetas existentes
    • La detección de colisiones usa la implementación de R-tree RBush
    • La longitud de las etiquetas se aproxima con un arreglo de bytes del largo del título y una fórmula ajustada, en vez de usar measureText() del navegador
  • Como los cálculos iniciales de cajas de etiquetas y colisiones eran intensivos en CPU, se movieron a Web Workers
  • También se probó OffscreenCanvas, pero como la lógica de renderizado ya era eficiente, no tuvo mucho efecto

Terreno, fronteras y etiquetas de ciudades

  • Un mapa con solo puntos carecía de orientación e interés visual, así que se agregaron los conceptos de terreno y ciudades
  • Como no existen geografía real ni fronteras políticas, el terreno se representó como una metáfora de la densidad de puntos
  • La densidad de puntos se usa como señal para mostrar rápidamente áreas con mucha actividad, interés, contenido, participación, popularidad y discusión
  • Al principio se consideró usar Kernel Density Estimation, pero los intentos con bibliotecas estándar tardaban demasiado
  • En su lugar, se mapeó cada punto a celdas grandes de una cuadrícula, se generaron conteos por celda y luego se aplicó un Gaussian blur
  • Los valores lineales de densidad hacían que casi todo se viera cerca de 0, y el mapa no quedaba bien
  • Al aplicar log(density + 1), se generó una estratificación mucho más natural, y donde se encuentran distintos niveles de densidad aparecen fronteras implícitas
  • En vez de una imagen gigante, se generaron rutas SVG para que el cliente las dibujara como polígonos
    • Como solo hay 4 colores, resulta eficiente
    • Las fronteras se mantienen nítidas incluso al hacer zoom
    • Se calcularon curvas cerradas por nivel con la función contour de OpenCV
  • Las etiquetas de ciudades representan temas comunes dentro de un radio determinado
  • Los nombres de las ciudades se embeddearon y luego se obtuvo su posición (x, y) con el modelo UMAP guardado
  • También se intentó generarlas automáticamente
    • Se intentó crear nombres de ciudades con un LLM, pero fue difícil obtener de forma estable la salida deseada solo con prompts
    • K-means no logró encontrar muchos clústeres significativos que una persona agruparía de forma natural
  • Al final, se exploró el mapa manualmente y se escribieron algunas ciudades a mano; tomó alrededor de 1 hora

Despliegue en el edge y capacidad de respuesta

  • Como la exploración del mapa debía ser rápida y responsiva, era importante reducir la latencia al hacer fetch de datos
  • Al principio, todos los datos del mapa estaban en la región ENAM de Cloudflare R2, pero la latencia iba de 600 ms a varios segundos
  • Aunque la latencia física fuera de unos 200 ms, seguía sin ser lo bastante buena considerando que 100 ms es el umbral a partir del cual algo se siente inmediato
  • Para reducir la latencia, se movieron los datos cerca de los usuarios
  • Se colocaron pequeños servidores en Virginia, San Jose, Londres y Sídney, y un servidor en Rust entregaba los datos
  • El cliente llama varias veces a /healthz en distintos edges y elige el servidor que responde primero
  • También podrían haberse usado enfoques como Anycast o un CDN, pero no se hicieron por costo y complejidad excesiva
  • El uso de memoria del proceso en los servidores edge era entre 2 y 4 veces mayor que los datos originales, y eso dejó dudas
    • Posible uso de tipos incorrectos
    • struct padding
    • sobreasignación en Vec y HashMap
    • posible fragmentación o ineficiencia del asignador de memoria

Resultados de búsqueda semántica y limitaciones

  • En consultas simples como “entering the tech industry”, aparecieron juntos resultados muy votados y otros menos visibles, y parecían tener buena relevancia y utilidad
  • Comparado con el servicio de búsqueda existente de HN, la búsqueda semántica con embeddings encuentra resultados aunque no haya coincidencia literal
  • Consultas en forma de pregunta como “what happened to wework” también muestran resultados de varios años sobre despidos, caída de la acción y bancarrota de WeWork
  • Se encuentran coincidencias aunque los resultados no contengan realmente las palabras “what happened” ni estén formulados como pregunta
  • Un problema es que no se filtran resultados que no sean lo bastante parecidos, así que aparecen resultados inferiores totalmente irrelevantes
    • Esto se trata como un problema fácil de corregir
  • En la búsqueda de “career growth” aparecen ensayos creativos y diversos que no incluyen esas palabras de forma literal
  • La calidad de curaduría de HN y sus puntajes ayudan a la calidad de la búsqueda
  • La app tiene consultas sugeridas hardcodeadas como “linus rants”, “self bootstrapping” y “cool things with css”

Comunidades virtuales y análisis de comentarios

  • Con embeddings se puede crear una función de subcomunidades virtuales
  • Si el usuario ingresa el nombre o la descripción de una comunidad, las publicaciones que superen un cierto umbral de similitud se agrupan al instante
  • Como HN no tiene una función para subdividir más las publicaciones, esto permite crear al momento una curaduría centrada en intereses específicos
  • Los fragmentos y las imágenes de las tarjetas de resultados se toman de los metadatos de la página guardados por el rastreador web
  • Los íconos del sitio se simplifican obteniendo /favicon.ico del dominio en el cliente, en lugar de hacer un análisis complejo de metadatos
  • Los hilos de comentarios también pueden mostrarse de la misma manera
  • Como la API de HN no ofrece puntajes de comentarios, solo es posible ordenarlos cronológicamente
  • Como forma de rankear sin puntajes de comentarios, se considera posible usar el historial de comentarios del usuario, la participación alrededor de ese comentario, la publicación, el tema y el contenido
  • También es posible encontrar usuarios influyentes o muy activos en un tema específico
    • En la consulta “cloudflare”, jgrahamc y eastdakota aparecen en los primeros lugares
    • Ambos usuarios son respectivamente el CTO y el CEO de Cloudflare
  • Este trabajo se procesa con operaciones matriciales, sin clasificar los comentarios por separado ni usar búsqueda por palabras clave
  • El filtrado posterior suele ser más rápido y suficiente que el filtrado previo
    • El filtrado previo puede requerir eliminar filas correspondientes de la matriz de embeddings, lo que puede implicar enormes copias de memoria o cálculos lentos de vectorización parcial
    • Parece mejor encontrar primero las filas similares y luego filtrar los resultados
  • El umbral mínimo es importante
    • Incluso elementos no relacionados pueden tener una similitud de alrededor de 0.6
    • Sin un umbral, los usuarios con muchísimos comentarios podrían dominar el ranking simplemente por volumen

Análisis de sentimiento y popularidad de todos los comentarios

  • El análisis a gran escala que se quería probar con 30 millones de comentarios era popularidad y sentimiento
  • El objetivo era ver cómo se siente HN respecto a ciertos temas con el paso del tiempo, qué impacto tienen los eventos importantes en el sentimiento y cómo crecen o decaen los temas de interés
  • Como no había datos de sentimiento, se usó un modelo open source de clasificación de sentimiento de Hugging Face
  • El modelo elegido fue TweetEval, entrenado con contenido de redes sociales
  • Como TweetEval es un modelo para tuits cortos, igual que con los embeddings se usó solo el comentario en sí como entrada, sin añadir contexto ancestro
  • Los comentarios se pusieron en cola, se procesaron en un clúster de GPU y luego se guardaron los resultados
  • Como el modelo es pequeño, se aumentó el tamaño del batch para mejorar la eficiencia de la GPU
  • Aumentar el tamaño del batch usa más VRAM, pero puede reducir las transferencias de memoria entre el host y la GPU y aumentar el paralelismo
  • En los modelos Transformer, la entrada por batch debe ser rectangular, así que se rellena hasta la longitud de la entrada más larga
    • Si unas cuantas entradas cortas se mezclan con una larga, el tamaño total de la entrada y los estados internos pueden aumentar mucho
    • Esto puede causar picos de memoria y errores OOM
  • En el ejemplo de análisis de sentimiento sobre Rust, en general hubo bastante sentimiento positivo hacia Rust
    • Hubo un pico positivo cerca del anuncio de Rust 1.0
    • Las publicaciones más negativas se correlacionaban, según el modelo, con muchos comentarios negativos
  • La popularidad por lenguaje también se estimó ponderando puntajes y similitud
    • Como HN no ofrece puntajes de comentarios, no se pudieron usar puntajes de comentarios
    • Rust va bien, pero no parecía ser tan popular como otros lenguajes
    • Puede que haya que ajustar el umbral de similitud, así que los resultados podrían ser incorrectos

Acelerar cálculos numéricos a gran escala con GPU

  • Las consultas de análisis tardaban entre 10 y 30 segundos incluso en una máquina de 32 núcleos, lo que era lento para experimentación interactiva
  • Después de considerar índices o preprocesamiento, se cambió a usar GPU para los cálculos numéricos vectorizados
  • CuPy y cuDF ofrecen APIs similares a NumPy y pandas, respectivamente, pero ejecutadas en GPU
  • El port fue relativamente sencillo y el tiempo de consulta se redujo a unos cientos de ms
  • La velocidad fue suficiente como para no usar tampoco grafos ANN
  • La parte difícil fue subir la gran matriz de embeddings a la GPU
    • La matriz de embeddings de comentarios tiene tamaño 30M x 512
    • Era difícil alojar más de una copia de la matriz en memoria del sistema o memoria de video
  • Un método de carga simple puede crear varias copias
    • Leer bytes desde disco
    • Cargarlos en un arreglo de NumPy
    • Convertirlos en un arreglo de CuPy
    • Copiarlos a la GPU
    • Este proceso puede crear 4 copias en total, de las cuales 3 quedan en memoria
  • El método final consistió en mapear en memoria la matriz del disco, preasignar en la GPU una matriz no inicializada del mismo tamaño y copiar por chunks
  • Este método evita leer primero en memoria de Python y usa exactamente una copia en RAM del sistema y una en VRAM

Demo y siguientes pasos

  • La app de demo está disponible en hn.wilsonl.in
  • La página principal muestra el mapa y la búsqueda, y se puede acceder a las comunidades y herramientas de análisis con el botón de la esquina superior derecha
  • Las URLs de comunidades o resultados de análisis guardan la consulta en la URL, por lo que pueden compartirse con otras personas
  • El dataset de demo se corta alrededor del 10 de abril de 2024, y no incluye publicaciones ni comentarios en vivo más recientes
  • Hay varias ideas que se quieren explorar a futuro
    • Datos en vivo que se mantengan actualizados continuamente
    • Un sistema de recomendación basado en deep learning que funcione sobre la web curada de HN
    • Mejorar los resultados de búsqueda entrenando un reranker
    • Rutas y recorridos interesantes sobre el mapa
    • Análisis de relaciones de similitud y oposición entre usuarios
    • Análisis de los usuarios más expertos dentro de nichos específicos
  • Tanto los datos como el código completo pueden consultarse en GitHub

2 comentarios

 
GN⁺ 2024-05-10
Opiniones de Hacker News
  • Es un trabajo especialmente impresionante para ser un proyecto de una sola persona.
    Me llamó la atención el gráfico de análisis de sentimiento a lo largo del tiempo, y me pareció interesante porque es la primera vez que veo algo así sobre Rust. Me da curiosidad saber cuáles fueron los temas más positivos con el paso del tiempo, y si hubo alguno que cayera fuerte de repente.
    La frase “parece haber mucho sentimiento negativo en HN en general” también suena intuitivamente cierta para las redes sociales. También sería interesante ver una comparación de sentimiento por plataforma de redes sociales y por periodo.

    • Me gustaría profundizar más en la parte de sentimiento. Como dices, también sería interesante ver un panorama general, no solo consultas específicas.
      Lo que hizo que destacara el sentimiento negativo fue que originalmente esperaba un gráfico de sentimiento más claro. Esperaba algo en general entre neutral y positivo, que se inclinara hacia lo positivo alrededor de publicaciones positivas y hacia lo negativo alrededor de publicaciones negativas, pero en casi todas las consultas el sentimiento era casi siempre negativo. Incluso las publicaciones positivas parecían atraer mucha negatividad según el modelo y el enfoque, y como ambas cosas podrían estar equivocadas, me gustaría explorarlo más en una futura entrada de blog.
    • Además del sentimiento por plataforma de redes sociales y por periodo, estaría bueno ver también la hora del día y si es día laboral o fin de semana.
    • Hace unos meses escribí una entrada de blog en la que analicé el sentimiento de los comentarios de HN sobre IA, blockchain, trabajo remoto y Rust. El gráfico final, al final del artículo, está relacionado con este tema.
      https://openpipe.ai/blog/hn-ai-crypto
    • Es una verdadera lástima que la API de HN no proporcione el número de votos de los comentarios. Me pregunto cómo cambiaría el análisis de sentimiento si se ponderara con votos positivos/negativos.
      No tengo pruebas, pero siento que los ingenieros en general son críticos, y que cuando tienen feedback positivo tienden a dar +1 en vez de escribirlo repetidamente. Las críticas sí las escriben de forma más directa :)
    • Crypto también parece entrar en esa categoría.
  • Es un buen ejemplo para alguien que no está familiarizado con ingeniería de datos/MLOps.
    Sugiero crear clústeres jerárquicos sobre los puntos con HDBSCAN y usar un modelo para generar nombres para los clústeres internos. Así sería más fácil explorar temas hasta llegar a las hojas, mostrando subelementos según su conectividad con el nodo actual.
    Los colores de los grupos deberían ser más distinguibles, y tener clústeres ayudaría. El tamaño del texto de cada publicación debería variar según su importancia o relevancia, ya sea respecto del conjunto completo o de la búsqueda actual. Con más resúmenes de clústeres internos, se podrían reemplazar varias publicaciones por resúmenes de grupo hasta hacer zoom, reduciendo también la congestión de texto.

  • El alcance del proyecto es sorprendentemente grande.
    Dicho eso, no sé si es jina o bge-3/flag, pero los embeddings y el tokenizador no parecen encajar bien con temas técnicos. Las palabras de lenguaje natural están bien, pero si buscas conceptos técnicos como “xaml” o “simd”, después de tokenizar la entrada tiende a intentar encontrar palabras que suenen parecido.
    Como feedback constructivo, sería bueno tener una forma de no mostrar repetidamente los mismos resultados tipo “ranking de HN” cuando el tema es demasiado de nicho y no hay resultados. Cuando buscas una palabra que el embedding no conoce, suele aparecer “Stephen Hawking has died”.
    Tampoco estoy seguro de qué tan bien funciona el análisis de sentimiento. Parecía haber demasiado sentimiento negativo, de una forma que no coincidía con la realidad, e incluso al buscar algo como “Mr Rogers”, que HN vería de forma abrumadoramente positiva, aparece un pico de negatividad fuerte. Si buscas “Carter”, hay un enorme pico negativo relacionado con la muerte de Rosalynn Carter, pero la publicación real era un envío que hablaba de las grandes cosas que hizo la pareja Carter.
    “Popularidad a lo largo del tiempo” probablemente debería ajustarse por la mediana de votos de las publicaciones de ese mes/año. Si simplemente graficas el número de publicaciones, la línea de tendencia sigue subiendo. Al ver la popularidad de “diesel” se entiende a qué me refiero: ese término alcanzó su pico hace 10 años. O quizá debería basarse en la frecuencia de aparición de la palabra clave, o en la cantidad de elementos cuyo índice de similitud coseno con la consulta sea menor que x, en vez de en la puntuación de las publicaciones.
    La función dinámica de hacer clic en una publicación para eliminarla y recalcular el umbral de similitud es excelente.

    • ¿Cómo se podría determinar programáticamente que un modelo de embeddings no reconoce cierto término o palabra?
  • Hay una herramienta excelente que hace casi lo mismo con cualquier dataset: https://github.com/enjalot/latent-scope
    Claro que la escala del proyecto original agrega muchas complejidades interesantes, y esta herramienta no puede manejar una escala tan grande, pero es buena para datasets de tamaño mediano.

  • Quiero analizar si la autopromoción ha aumentado en HN.
    Aquí defino autopromoción no como publicaciones del tipo “Show HN: Something ...”, sino como las de formato “Show HN: I ...”.
    Por ejemplo, entre las 100 principales actuales, “Show HN: Exploring HN by mapping and analyzing 40M posts and comments for fun” y “Show HN: Browser-based knitting (pattern) software” no son títulos de autopromoción. En cada caso, el sujeto es la exploración y el software.
    En cambio, “Show HN: I built a non-linear UI for ChatGPT” y “Show HN: I created 3,800+ Open Source React Icons” sí son títulos de autopromoción. En cada caso, el sujeto es “I”.
    Revisé de forma simple en los resultados de búsqueda de Algolia los títulos que empiezan con “Show HN: I” para cada año a partir del 1 de abril, los dividí entre el total de resultados de ese año y lo grafiqué así:
    2023 ****************************************
    2022 ***********************************
    2021 ***************************
    2020 **************************************
    2019 *************************
    2018 *************
    2017 *******
    2016 **********
    2015 ********
    2014 ************
    2013 *********************
    2012 *****************
    2011 *********
    2010 ***
    Cuando yo crecí, creo que en general la autopromoción se consideraba un rasgo negativo de personalidad. Me enseñaron que tus acciones debían promocionarte, no que tú llamaras la atención sobre ellas, pero siento que esa cultura está cambiando.
    Si la autopromoción realmente aumentó, me pregunto si es por la influencia de las redes sociales y cosas por el estilo. También siento un aumento parecido en YouTube, pero solo tengo la impresión de que hay muchos videos recomendados que empiezan con “I.....”, no datos.

    • Tu definición de autopromoción es un poco distinta de lo que yo suelo entender. Normalmente, si una persona promociona algo que hizo, eso se considera autopromoción. Así que los dos ejemplos que diste como no autopromoción también lo son según mi definición.
      Es decir, lo que tú separaste entre autopromoción y no autopromoción, para mí son casos donde el título deja extremadamente claro que es autopromoción frente a casos donde lo deja menos claro. Dicho eso, la propia frase “Show HN” parece usarse solo para autopromoción, así que quien conoce la convención entiende que lo es aunque no aparezca “I”.
    • Todos los Show HN deberían ser algo creado por quien publica, así que no veo bien qué lo hace más autopromocional por explicitar algo implícito.
      Todos significan “mira, hice algo genial, ¿qué opinas?”.
    • Es algo que también se trata bastante en la biografía de Einstein de Walter Isaacson, así que la gente viene observando esta tendencia desde hace mucho.
      Por ejemplo, se cuenta que los alemanes criticaban a Einstein por autopromocionarse y, en contraste, que en Estados Unidos había una cultura de celebridades. Podría ser un fenómeno cíclico.
  • Creo que es, por mucho, el post más genial que he visto este año en HN.

  • No era evidente al principio, pero la app real está aquí: https://hn.wilsonl.in/

    • Me pregunto si pusiste el enlace de la landing page casi al final a propósito. En la práctica, la estructura hace que solo quienes realmente leyeron el post lleguen al sitio.
      No lo digo con sarcasmo; me parece una buena idea.
    • Parece que los enlaces de búsqueda no se pueden compartir, o que no incluyen el término de búsqueda.
      También me pregunto si embebes la frase de búsqueda palabra por palabra, y si usas el mismo modelo que para los documentos. Busqué “lead generation” y me salieron resultados de intoxicación por plomo, aunque una embedding multipalabra decente debería entender esa expresión.
    • ¡Me encontré a mí y a mis posts ahí! Genial.
  • Una recomendación moderna para UMAP es Parametric UMAP: https://umap-learn.readthedocs.io/en/latest/parametric_umap....
    Entrena un pequeño MLP en Keras para minimizar la pérdida de UMAP y realizar la reducción de dimensionalidad a 2D. La ventaja es que este modelo es pequeño, por lo que se puede guardar y reutilizar para predecir datos nuevos desconocidos. Tradicionalmente, un modelo UMAP entrenado es grande. Además, como usa GPU, en teoría el entrenamiento es mucho más rápido.
    La desventaja es que la implementación del paquete UMAP de Python no es muy buena, y construye y mete en la GPU todo el dataset expandido de nodos/aristas. Por eso solo se puede entrenar hasta unas 100 mil embeddings antes de quedarse sin memoria.
    Como un pipeline completamente no supervisado de UMAP → HDBSCAN → etiquetado de clústeres con IA es tan útil, me dan ganas de crear una implementación de Parametric UMAP más escalable.

    • Hay una implementación rápida en GPU en cuML. No sé bien por qué cuML es tan poco conocido.
    • A primera vista, parece que la implementación mete todo el grafo, es decir, todas las aristas, en la GPU. Se podría mitigar haciendo muestreo de aristas durante el entrenamiento.
  • Para algo que parece un proyecto exploratorio de hobby, es un trabajo sorprendentemente grande. No quiero restarle mérito; es realmente genial, pero me sorprendió la cantidad de recursos invertidos
    Usaron 150 GPU solo para calcular embeddings, y desarrollaron dos sistemas personalizados, db-rpc y queued, para la comunicación entre servidores. También hubo mucho trabajo periférico y cómputo
    Me da curiosidad el contexto del proyecto. También me pregunto cómo consiguieron el financiamiento y el tiempo necesarios para una investigación así
    Habiendo hecho muchas cosas similares profesionalmente, mapeando artículos académicos y el panorama de patentes, no estoy seguro de que realmente hicieran falta 150 GPU. Si al final se trata de proyección 2D y clustering, una bolsa de palabras tradicional o modelado de temas sería mucho más fácil y barato, y creo que casi no se notaría diferencia de calidad. Usando el grafo de autores y hilos de comentarios también se podrían obtener resultados similares

    • No mencioné el costo en el artículo, pero fue del orden de unos cientos de dólares, así que era bastante accesible incluso como proyecto de hobby
      Las GPU resultaron sorprendentemente baratas, y en gran parte escalé solo porque no tengo paciencia :) Todo el clúster corrió apenas unas horas
      Si tienes un enlace al trabajo que hiciste, me gustaría verlo. Suena interesante y quisiera leer más
    • El autor claramente es muy capaz. También es interesante que publique en HN, pero no haya comentado desde 2018, y que después haya empezado este proyecto
      En cuanto a financiamiento y tiempo, puede que esté entre proyectos o empleos, y que haya tenido éxito financiero en una carrera o negocio anterior, por lo que lo hizo con fondos propios. El uso de GPU también parece muy eficiente, así que probablemente el costo no fue tan alto
    • Incluso con embeddings más baratos se pueden obtener resultados bastante buenos
      Aplicando aprendizaje automático clásico como SVM con calibración de probabilidades a estos embeddings, se logran buenos resultados en clasificación y clustering, y la velocidad es más de 100 veces mayor que ajustar finamente un LLM
  • Normalmente no se hace como en esta demo, sino que se normalizan los vectores
    Cuando se usan vectores normalizados, la distancia euclidiana mide la distancia entre los extremos de dos vectores. En cambio, la distancia coseno mide la longitud de la proyección de un vector sobre el otro

    • El problema de la normalización es que se pierde un grado de libertad. En visualización, eso equivale en la práctica a perder una dimensión
      Un vector 2D normalizado en realidad no es más que un vector 1D. Si quieres mostrar relaciones 2D, tienes que usar vectores 3D para volver a tener 2 grados de libertad
 
ggg213 2024-05-10

Falta el título.