2 điểm bởi GN⁺ 2024-05-10 | 2 bình luận | Chia sẻ qua WhatsApp
  • Đây là một dự án tìm kiếm, bản đồ và phân tích thu thập hơn 40 triệu mục từ API công khai của Hacker News, rồi gắn embedding, metadata và nội dung gốc cho hơn 30 triệu bình luận và 4 triệu bài đăng
  • Cách làm ban đầu chỉ embedding tiêu đề có giới hạn do tiêu đề mơ hồ và thiên lệch Ask HN/Show HN, nên đã chuyển sang dùng kết hợp nội dung trang web được liên kết, các bình luận cấp cao và ngữ cảnh tổ tiên của bình luận
  • Khi quy mô xử lý tăng lên, dự án kết hợp khoảng 150 GPU trên RunPod, dịch vụ hàng đợi viết bằng Rust, HTTP/2 DB proxy, UMAP, kết xuất Canvas và máy chủ edge; sau khi thay mô hình, thời gian embedding cho mỗi đầu vào giảm từ khoảng 600ms xuống 6ms
  • Xếp hạng tìm kiếm không chỉ dựa trên khớp chuỗi đơn giản mà kết hợp độ tương đồng cosine, điểm HN và trọng số thời gian để phản ánh mức liên quan về ngữ nghĩa, tín hiệu xã hội và tính mới
  • Bản demo công khai chỉ bao gồm dữ liệu đến khoảng ngày 10 tháng 4 năm 2024; toàn bộ dữ liệu và mã nguồn được công bố trên GitHub để dùng cho tìm kiếm, gợi ý, phân tích người dùng và thử nghiệm cập nhật thời gian thực

Phạm vi dự án và dữ liệu công khai

  • Dự án tạo một bản đồ đặt toàn bộ bài đăng trên Hacker News vào không gian ngữ nghĩa, đồng thời xây dựng các công cụ tìm kiếm, phân tích và trực quan hóa
  • Điểm khởi đầu là thử nghiệm embedding văn bản; HN được chọn làm bộ dữ liệu phù hợp vì có nhiều nội dung đã được chọn lọc và toàn bộ nội dung đều có thể truy cập theo cách lập trình
  • Embedding biểu diễn văn bản thành các điểm trong không gian nhiều chiều, và khoảng cách tương đối giữa các điểm hữu ích hơn vị trí tuyệt đối
  • Có ba hướng ứng dụng được kỳ vọng
    • Tìm kiếm dựa trên ngữ nghĩa trong kho nội dung tích lũy của HN
    • Gợi ý cá nhân hóa theo vùng quan tâm
    • Phân tích cảm xúc, mức độ phổ biến và góc nhìn đối lập theo chủ đề trong cộng đồng
  • Hơn 30 triệu bình luận và 4 triệu bài đăng được công bố qua bản phát hành bộ dữ liệu
    • Bao gồm metadata như ID, điểm số, tác giả
    • Bao gồm embedding
    • Bao gồm văn bản bình luận và văn bản trang web đã crawl
  • Mã nguồn được công bố trong kho GitHub hackerverse

Thu thập dữ liệu Hacker News

  • HN cung cấp một API công khai đơn giản, trong đó mọi đối tượng đều được truy vấn dưới dạng item
  • maxitem.json trả về ID lớn nhất, và tại thời điểm viết bài, ID tối đa đã vượt 40 triệu
  • Ngay cả khi thời gian phản hồi trung bình là 10ms, việc crawl tuần tự 40 triệu mục vẫn mất hơn 4 ngày, nên cần xử lý song song
  • Ban đầu đã tạo một dịch vụ thu thập nhanh bằng Node.js, nhưng cách dùng semaphore và hàng đợi Promise khiến phần lớn thời gian CPU bị tiêu tốn trong mã JS ở không gian người dùng nên chạy chậm
  • Sau đó dùng API worker_threads để phân tán tác vụ fetch lên toàn bộ CPU, cải thiện hiệu năng bằng cách bão hòa tất cả các lõi
  • Vì thu thập song song làm đảo lộn thứ tự, dự án ghi lại dấu hoàn tất theo thứ tự ID để tránh bị sót khi quá trình bị gián đoạn
  • Cũng có một số đặc điểm được quan sát từ HN API
    • Điểm số dường như không giảm xuống dưới -1
    • Không thể lấy downvote của bài đăng và số phiếu của bình luận
    • Một số bài đăng và bình luận không ở trạng thái deleted hay flagged nhưng tiêu đề, văn bản và URL vẫn trống
    • ID bình luận có thể nhỏ hơn tổ tiên của nó, có thể do việc di chuyển trong cây bình luận
  • Trình crawl HN cũng đã được tách riêng thành dự án TypeScript crawler-toolkit-hn

Embedding đầu tiên và hạ tầng

  • Ban đầu, dự án tạo embedding với giả định chỉ cần tiêu đề bài đăng là đã đủ để biểu diễn ngữ nghĩa
  • Để so sánh mô hình, tác giả tham khảo Massive Text Embedding Benchmark, và mô hình đầu tiên được chọn là BGE-M3
  • Ngoài embedding dense thông thường, BGE-M3 còn có thể tạo lexical weights nên có thể dùng cho tìm kiếm lai kết hợp với các phương pháp như BM25
  • Hạ tầng tạo embedding không hề đơn giản
    • Mô hình tốt có thể có từ hàng triệu đến hàng tỷ tham số
    • Chạy trên GPU hiệu quả hơn nhiều nhưng cụm GPU lại đắt đỏ
    • Nếu suy luận mất vài trăm mili giây cho mỗi đầu vào, thì xử lý 40 triệu đầu vào bằng một GPU sẽ gần tương đương quy mô một năm
    • Dữ liệu, máy chủ và GPU nằm tách rời nên cần duy trì pipeline để GPU không bị nhàn rỗi
  • Dự án dùng RunPod để triển khai các máy GPU chạy trong trung tâm dữ liệu dưới dạng container, tận dụng các GPU tương đối rẻ như RTX 4090
  • Vì GPU phân tán khắp thế giới khiến độ trễ kết nối DB và overhead kết nối trở thành vấn đề, tác giả đã tạo db-rpc
    • Proxy truy vấn SQL tới DB cục bộ qua HTTP/2
    • Sử dụng một connection pool dùng chung lớn
    • Xử lý nhiều truy vấn trên một kết nối duy nhất nhờ HTTP/2 multiplexing
  • Vì AWS SQS có giới hạn tốc độ thấp và chi phí theo từng message trở nên nặng nề với hàng triệu tác vụ nhỏ, tác giả đã tạo dịch vụ hàng đợi Rust dựa trên RocksDB là queued
    • Xử lý 100K+ op/s trên một node đơn
    • Giảm gánh nặng về batching, kích thước message, giới hạn tốc độ và chi phí
  • Sau khi mở rộng lên khoảng 150 GPU, dự án đã embedding 40 triệu bài đăng và bình luận chỉ trong vài giờ
  • Khi đó chi phí embedding cho mỗi đầu vào vào khoảng 600ms, và mức sử dụng GPU luôn được giữ cao trong toàn bộ thời gian

Tăng cường ngữ cảnh bằng cách crawl trang web

  • Cách embedding chỉ bằng tiêu đề là chưa đủ
    • Nhiều bài đăng có tiêu đề lạ, sáng tạo hoặc mơ hồ
    • Cụm từ Ask HNShow HN chiếm tỷ trọng lớn trong toàn bộ tiêu đề nên có xu hướng bị gom cụm với nhau bất kể chủ đề
  • Với bài đăng dạng văn bản và bình luận, có thể dùng chính văn bản của chúng, nhưng phần lớn bài đăng liên kết cần phải crawl trang web được dẫn tới
  • Một dịch vụ Rust lấy URL và phân tích metadata như tiêu đề, ảnh, tác giả và văn bản nội dung từ HTML
  • Phiên bản Node.js ban đầu chậm hơn phiên bản Rust tới 10 lần ở các tác vụ nặng CPU, và việc viết lại bằng Rust đã cải thiện hiệu năng
  • Việc trích xuất văn bản dùng scraper để phân tích HTML, loại bỏ các phần tử HTML5 không phải nội dung chính về mặt ngữ nghĩa rồi duyệt cây còn lại
  • Link rot cũng là vấn đề lớn
    • Khoảng 200 nghìn URL thất bại do 404, lỗi tra cứu DNS hoặc timeout kết nối
    • Con số này vẫn dưới 5% trong tổng số 4 triệu trang
  • Để giảm thiếu hụt, dự án lấy lại một phần bài viết cũ qua Wayback API của Internet Archive
    • Giới hạn tốc độ của Internet Archive rất thấp, chỉ khoảng 5 yêu cầu mỗi phút

Chiến lược embedding thứ hai

  • Các trang web thường dài, nhưng BGE-M3 hỗ trợ cửa sổ ngữ cảnh 8192 token
  • Tuy vậy, BGE-M3 chậm nên đã được thay bằng jina-embeddings-v2-small-en
    • Số tham số ít hơn nhiều
    • Hiệu năng theo MTEB cũng tốt
    • Thời gian suy luận giảm xuống khoảng 6ms, nhanh hơn 100 lần
  • Do đầu vào dài, việc tăng batch size gây OOM nên không thể bão hòa hoàn toàn GPU
  • Để bù cho các trang có ít văn bản hoặc không lấy được nội dung, dự án thêm các bình luận HN cấp cao của bài đăng vào sau phần thân
    • Các bình luận cấp cao được dùng với giả định item.kids đã được xếp hạng sẵn
    • Loại trừ bình luận deleted, dead và có điểm âm
    • Đầu vào bị giới hạn ở tối đa 64KiB
  • Vì một bình luận riêng lẻ có thể thiếu ngữ nghĩa, dự án lần ngược lên các bình luận tổ tiên và cả tiêu đề bài đăng để tạo ngữ cảnh dài hơn
  • Các giá trị lớn như embedding và văn bản được lưu trong bảng kv riêng
    • Nếu lưu cùng trong hàng thì hàng sẽ phình to
    • Việc cập nhật các cột nhỏ cũng trở nên đắt đỏ
    • Thay đổi schema cũng tốn kém

Tạo bản đồ ngữ nghĩa 2D bằng UMAP

  • UMAP là kỹ thuật giảm chiều nhằm giảm embedding nhiều chiều xuống chiều thấp hơn trong khi vẫn cố gắng bảo toàn các quan hệ ngữ nghĩa
  • Vector embedding 1024 chiều được giảm xuống thành các điểm 2D để dùng cho biểu đồ phân tán và trực quan hóa dạng bản đồ
  • UMAP nhận đồ thị PyNNDescent và embedding gốc làm đầu vào, dùng metric="cosine"n_components=2
  • Việc huấn luyện với hàng triệu đầu vào nhiều chiều mất nhiều thời gian nên đã dùng VM c7i.metal-48xl trên EC2
    • Tận dụng tối đa bộ xử lý 96 lõi
    • Sau khoảng 1 giờ 30 phút thì tạo được ma trận 2D
  • Embedding 2D đã tạo và mô hình UMAP đã huấn luyện được lưu lại để sau này có thể biến đổi embedding mới mà không cần huấn luyện lại
  • Biểu đồ phân tán ban đầu có quá nhiều điểm, nên chỉ chọn bài đăng có điểm cao nhất trong mỗi ô lưới để giảm mật độ và hiển thị tiêu đề
  • Ở embedding thứ hai có thêm nhiều ngữ cảnh hơn, những bài đăng khó hiểu nếu chỉ nhìn tiêu đề được đặt chính xác hơn gần các nội dung liên quan

Độ tương đồng cosine và xếp hạng tìm kiếm

  • Cốt lõi của việc dùng embedding là tìm độ tương đồng giữa hai embedding
  • Với embedding văn bản, khoảng cách và độ tương đồng cosine thường được dùng nhiều hơn khoảng cách Euclid thông thường
  • Khoảng cách cosine hữu ích khi hướng quan trọng hơn độ lớn
    • Một cuộc thảo luận dài về X nên giống X hơn là giống một cuộc thảo luận dài về Y
    • Nếu phản ánh độ lớn, mối quan hệ như vậy có thể bị bóp méo
  • Phép tính cốt lõi được dùng là dot product giữa ma trận embedding và embedding truy vấn
  • Nếu các vector là vector đơn vị thì không cần chia thêm cho độ lớn
  • Kết quả tìm kiếm không được sắp xếp chỉ theo độ tương đồng thuần túy
    • Độ tương đồng cosine là yếu tố lớn nhưng không phải yếu tố duy nhất
    • Điểm HN được dùng như bằng chứng xã hội
    • Trọng số thời gian được phản ánh bằng một thành phần âm tỷ lệ với log(age) để nội dung cũ nhanh chóng tụt hạng trong các truy vấn coi trọng tính mới

Ứng dụng bản đồ trên trình duyệt

  • Mục tiêu là tạo một bản đồ tương tác để có thể khám phá không gian embedding của HN giống như Google Maps
  • Hành vi mong muốn được xác định rất rõ
    • Phóng to bằng thao tác pinch hoặc con lăn chuột thì sẽ hiển thị nhiều điểm hơn
    • Khoảng cách giữa các điểm cũng được nới rộng ra
    • Một số điểm có nhãn, nhưng không phải tất cả
    • Nhấp vào điểm sẽ hiển thị chi tiết bài đăng
    • Hỗ trợ cả cảm ứng và chuột trên di động lẫn desktop
  • Việc gửi toàn bộ điểm cho client cùng lúc là không phù hợp vì có tới hàng triệu điểm
  • Cấu trúc được dùng là chia không gian bản đồ thành các tile lưới và client chỉ lấy những tile cần thiết
    • Tile có thể được định danh bằng tọa độ (x, y)
    • Có thể lưu trong kho KV như S3
    • Dễ triển khai mà không cần logic phía server
  • Mức zoom được xử lý theo kiểu LOD
    • Mỗi cấp được chia thành số ô lưới nhiều gấp đôi theo từng trục
    • Các điểm được chọn ở cấp trước được sao chép sang cấp tiếp theo để khi zoom vào, điểm không bị biến mất
  • Kích thước tile được nhắm dưới 20KiB sau khi nén
    • Giới hạn khoảng 1.500 điểm
    • Mỗi điểm dùng 8 byte cho (x, y), 4 byte cho ID, 2 byte cho điểm số
  • Khi render web app, cách dùng hàng nghìn phần tử DOM làm hiệu năng giảm mạnh
  • Bản triển khai cuối cùng dùng Canvas và vẽ lại mỗi khi viewport được cập nhật
    • Dù vẽ lại hàng nghìn điểm mỗi frame, nó vẫn hoạt động mượt và đơn giản
  • Việc gắn nhãn được thực hiện bằng cách lặp lại chọn bài đăng có điểm cao nhất, nhưng loại bỏ nếu chồng lấn với nhãn đã có
    • Kiểm tra va chạm dùng triển khai R-tree RBush
    • Độ dài nhãn được ước lượng bằng mảng byte độ dài tiêu đề và công thức đã hiệu chỉnh thay vì measureText() của trình duyệt
  • Tính toán va chạm và khung nhãn ban đầu tốn CPU nên được chuyển sang Web Workers
  • Cũng đã thử OffscreenCanvas, nhưng do logic render vốn đã hiệu quả nên không mang lại nhiều khác biệt

Địa hình, ranh giới và nhãn thành phố

  • Nếu bản đồ chỉ có điểm thì sẽ thiếu cảm giác định hướng và sự hấp dẫn thị giác, nên đã thêm khái niệm địa hình và thành phố
  • Vì không có địa lý hay ranh giới chính trị thực tế, địa hình được dùng như một phép ẩn dụ để thể hiện mật độ điểm
  • Mật độ điểm được dùng như tín hiệu giúp nhanh chóng cho thấy những vùng có nhiều hoạt động, sự quan tâm, nội dung, tương tác, mức độ phổ biến và thảo luận
  • Ban đầu đã cân nhắc Kernel Density Estimation nhưng thử với thư viện tiêu chuẩn mất quá nhiều thời gian
  • Thay vào đó, mỗi điểm được ánh xạ vào một ô lưới lớn, tạo số đếm cho từng ô rồi áp dụng Gaussian blur
  • Giá trị mật độ tuyến tính khiến phần lớn khu vực trông gần như bằng 0 nên bản đồ không đẹp
  • Khi áp dụng log(density + 1), các lớp trở nên tự nhiên hơn nhiều và nơi các mức mật độ khác nhau gặp nhau trông như ranh giới ngầm
  • Thay vì dùng một ảnh khổng lồ, hệ thống tạo các đường SVG để client vẽ thành đa giác
    • Chỉ có 4 màu nên rất hiệu quả
    • Phóng to vẫn giữ ranh giới sắc nét
    • Hàm contour của OpenCV được dùng để tính các đường khép kín theo từng mức
  • Nhãn thành phố đại diện cho chủ đề chung trong một bán kính nhất định
  • Tên thành phố được embedding rồi dùng mô hình UMAP đã lưu để lấy vị trí (x, y)
  • Cũng đã thử tạo tự động
    • Đã thử dùng LLM để tạo tên thành phố nhưng khó ổn định đầu ra mong muốn chỉ bằng prompt
    • K-means không tìm được nhiều cụm có ý nghĩa mà con người có thể nhóm như vậy
  • Cuối cùng, tác giả tự khám phá bản đồ và ghi nhãn thủ công một vài thành phố, mất khoảng 1 giờ

Triển khai ở edge và độ phản hồi

  • Việc khám phá bản đồ cần nhanh và có độ phản hồi tốt, nên giảm độ trễ khi fetch dữ liệu là rất quan trọng
  • Ban đầu toàn bộ dữ liệu bản đồ được đặt ở vùng ENAM của Cloudflare R2, nhưng độ trễ dao động từ 600ms đến vài giây
  • Dù độ trễ vật lý chỉ khoảng 200ms, điều đó vẫn chưa đủ tốt vì 100ms là ngưỡng khiến phản hồi có cảm giác tức thì
  • Để giảm độ trễ, dữ liệu được đưa lại gần người dùng hơn
  • Các máy chủ nhỏ được đặt ở Virginia, San Jose, London và Sydney, dùng server Rust để phục vụ dữ liệu
  • Client gọi /healthz của nhiều edge vài lần rồi chọn server phản hồi nhanh nhất
  • Cũng có thể dùng Anycast hay CDN, nhưng không áp dụng vì chi phí và độ phức tạp không cần thiết
  • Mức dùng bộ nhớ của tiến trình server edge lớn hơn dữ liệu gốc từ 2 đến 4 lần, và đây vẫn là một dấu hỏi
    • Có thể đã dùng sai kiểu dữ liệu
    • struct padding
    • Cấp phát dư trong Vec, HashMap
    • Khả năng phân mảnh hoặc kém hiệu quả của bộ cấp phát bộ nhớ

Kết quả tìm kiếm ngữ nghĩa và giới hạn

  • Với các truy vấn đơn giản như “entering the tech industry”, kết quả vừa có những bài được upvote nhiều vừa có những bài ít được chú ý hơn, và mức độ liên quan cùng tính hữu ích trông khá tốt
  • So với dịch vụ tìm kiếm hiện có của HN, tìm kiếm ngữ nghĩa bằng embedding có thể tìm ra kết quả ngay cả khi không khớp chữ nghĩa trực tiếp
  • Các truy vấn dạng câu hỏi như “what happened to wework” cũng cho ra kết quả trải dài nhiều năm về sa thải, giá cổ phiếu giảm và phá sản của WeWork
  • Kết quả vẫn khớp ngay cả khi thực tế không chứa cụm từ “what happened” hoặc không ở dạng câu hỏi
  • Một vấn đề là chưa lọc bỏ các kết quả không đủ giống, nên có lẫn các kết quả phía dưới hoàn toàn không liên quan
    • Đây được xem là vấn đề dễ sửa
  • Khi tìm “career growth”, hệ thống hiện ra các bài viết đa dạng và giàu tính sáng tạo dù không chứa nguyên văn cụm từ đó
  • Chất lượng chọn lọc của HN và điểm số của nó giúp cải thiện chất lượng tìm kiếm
  • Ứng dụng có các gợi ý truy vấn được hardcode như “linus rants”, “self bootstrapping”, “cool things with css”

Phân tích cộng đồng ảo và bình luận

  • Có thể tạo tính năng cộng đồng con ảo bằng embedding
  • Khi người dùng nhập tên hoặc mô tả cộng đồng, các bài đăng vượt qua ngưỡng độ tương đồng nhất định sẽ được nhóm lại ngay lập tức
  • Vì HN không có tính năng phân chia bài đăng chi tiết hơn, đây là cách tạo tuyển chọn theo một mối quan tâm cụ thể ngay tức thì
  • Đoạn trích và hình ảnh trong thẻ kết quả được lấy từ metadata trang mà trình thu thập web đã lưu
  • Biểu tượng trang được đơn giản hóa bằng cách để client lấy /favicon.ico của domain thay vì phân tích metadata phức tạp
  • Chuỗi bình luận cũng có thể được hiển thị theo cùng cách
  • Điểm bình luận không được HN API cung cấp nên chỉ có thể sắp xếp theo thời gian
  • Một cách xếp hạng không cần điểm bình luận là tận dụng lịch sử bình luận của người dùng, mức độ tham gia quanh bình luận đó, bài đăng, chủ đề và nội dung
  • Cũng có thể tìm ra những người dùng có ảnh hưởng hoặc hoạt động tích cực trong một chủ đề cụ thể
    • Trong truy vấn “cloudflare”, jgrahamceastdakota xuất hiện ở nhóm đầu
    • Hai người dùng này lần lượt là CTO và CEO của Cloudflare
  • Công việc này được xử lý bằng phép toán ma trận, không cần phân loại riêng bình luận hay dùng tìm kiếm từ khóa
  • Lọc hậu kỳ thường nhanh hơn và đã đủ dùng so với lọc trước
    • Lọc trước đòi hỏi phải loại bỏ các hàng tương ứng khỏi ma trận embedding, có thể cần sao chép bộ nhớ khổng lồ hoặc tính toán vector con chậm
    • Tốt hơn là tìm các hàng tương tự trước rồi mới lọc kết quả
  • Ngưỡng tối thiểu là rất quan trọng
    • Các mục không liên quan cũng có thể có mức tương đồng khoảng 0.6
    • Nếu không có ngưỡng, những người dùng có rất nhiều bình luận có thể thống trị thứ hạng chỉ vì quy mô

Phân tích cảm xúc và độ phổ biến trên toàn bộ dữ liệu bình luận

  • Phân tích quy mô lớn mà tác giả muốn thử với 30 triệu bình luận là độ phổ biến và cảm xúc
  • Mục tiêu là xem HN cảm nhận về một chủ đề cụ thể theo thời gian như thế nào, các sự kiện lớn ảnh hưởng ra sao đến cảm xúc, và các chủ đề quan tâm tăng trưởng hay suy giảm như thế nào
  • Vì không có dữ liệu cảm xúc nên tác giả dùng mô hình phân loại cảm xúc mã nguồn mở của Hugging Face
  • Mô hình được chọn là TweetEval, một mô hình được huấn luyện trên nội dung mạng xã hội
  • Vì TweetEval là mô hình cho tweet ngắn, nên giống như khi tạo embedding, chỉ bản thân bình luận được dùng làm đầu vào mà không gắn thêm ngữ cảnh tổ tiên
  • Các bình luận được đưa vào hàng đợi, xử lý trên cụm GPU rồi lưu kết quả
  • Vì mô hình nhỏ nên tăng kích thước batch để nâng hiệu suất GPU
  • Tăng kích thước batch sẽ dùng nhiều VRAM hơn, nhưng có thể giảm truyền bộ nhớ giữa host và GPU và tăng tính song song
  • Với mô hình Transformer, đầu vào theo batch phải có dạng hình chữ nhật nên sẽ được padding theo độ dài đầu vào dài nhất
    • Nếu một đầu vào rất dài lẫn với vài đầu vào ngắn, kích thước đầu vào tổng thể và trạng thái bên trong có thể tăng mạnh
    • Vì vậy có thể xảy ra spike bộ nhớ và OOM
  • Trong ví dụ phân tích cảm xúc về Rust, nhìn chung cảm xúc tích cực với Rust là khá nhiều
    • Có một đợt tăng mạnh cảm xúc tích cực quanh thời điểm công bố Rust 1.0
    • Các bài đăng tiêu cực hơn cũng tương quan với nhiều bình luận tiêu cực theo tiêu chí của mô hình
  • Độ phổ biến theo từng ngôn ngữ cũng được ước lượng bằng cách gán trọng số theo điểm số và độ tương đồng
    • HN không cung cấp điểm bình luận nên không thể dùng điểm bình luận
    • Rust đang làm khá tốt nhưng có vẻ chưa phổ biến bằng một số ngôn ngữ khác
    • Ngưỡng độ tương đồng có thể cần điều chỉnh nên kết quả có thể chưa chính xác

Tăng tốc tính toán số quy mô lớn bằng GPU

  • Các truy vấn phân tích mất 10~30 giây ngay cả trên máy 32 lõi, nên quá chậm cho thử nghiệm tương tác
  • Sau khi cân nhắc lập chỉ mục hay tiền xử lý, tác giả chuyển sang dùng GPU cho tính toán số đã vector hóa
  • CuPycuDF cung cấp API tương tự NumPy và pandas nhưng chạy trên GPU
  • Việc port tương đối đơn giản, và thời gian truy vấn giảm xuống còn mức vài trăm ms
  • Tốc độ đủ nhanh nên cũng không dùng đồ thị ANN
  • Phần khó là đưa ma trận embedding lớn lên GPU
    • Ma trận embedding bình luận có kích thước 30M x 512
    • Rất khó chứa hơn một bản sao của ma trận trong bộ nhớ hệ thống hoặc bộ nhớ video
  • Cách nạp đơn giản có thể tạo ra nhiều bản sao
    • Đọc byte từ đĩa
    • Nạp vào mảng NumPy
    • Chuyển thành mảng CuPy
    • Sao chép lên GPU
    • Quá trình này có thể tạo tổng cộng 4 bản sao, trong đó 3 bản nằm trong bộ nhớ
  • Cách cuối cùng là memory-map ma trận trên đĩa, cấp phát trước một ma trận chưa khởi tạo có cùng kích thước trên GPU, rồi sao chép theo từng chunk
  • Cách này tránh việc phải đọc trước vào bộ nhớ Python, và dùng đúng 1x RAM hệ thống cùng 1x VRAM

Bản demo và các bước tiếp theo

  • Ứng dụng demo có tại hn.wilsonl.in
  • Trang chính là bản đồ và tìm kiếm, có thể truy cập cộng đồng và công cụ phân tích bằng nút ở góc trên bên phải
  • URL của cộng đồng hoặc kết quả phân tích lưu truy vấn trong URL nên có thể chia sẻ với người khác
  • Bộ dữ liệu demo dừng ở khoảng ngày 10 tháng 4 năm 2024, không bao gồm bài đăng và bình luận trực tiếp mới nhất
  • Còn nhiều ý tưởng mà tác giả muốn tiếp tục khám phá
    • Dữ liệu trực tiếp luôn được cập nhật mới
    • Hệ thống gợi ý dựa trên deep learning hoạt động trên web được HN chọn lọc
    • Cải thiện kết quả tìm kiếm bằng huấn luyện reranker
    • Những tuyến đường và hành trình thú vị trên bản đồ
    • Phân tích quan hệ tương đồng và đối lập giữa người dùng
    • Phân tích những người dùng có chuyên môn sâu nhất trong một ngách cụ thể
  • Toàn bộ dữ liệumã nguồn có thể xem trên GitHub

2 bình luận

 
GN⁺ 2024-05-10
Các ý kiến trên Hacker News
  • Một công việc đặc biệt ấn tượng đối với một dự án một người làm
    Biểu đồ phân tích cảm xúc theo thời gian rất nổi bật, và thật thú vị vì đây là lần đầu tôi thấy Rust được nhìn theo cách này. Tôi cũng tò mò chủ đề nào là tích cực nhất theo thời gian, và liệu có chủ đề nào đột ngột giảm mạnh hay không
    Câu “nhìn chung HN có vẻ có nhiều cảm xúc tiêu cực” cũng nghe khá đúng với cảm nhận về mạng xã hội. Sẽ rất thú vị nếu có thể so sánh cảm xúc theo từng nền tảng mạng xã hội và từng giai đoạn

    • Phần cảm xúc là thứ tôi muốn đào sâu thêm. Như đã nói, xem tổng quan thay vì một truy vấn cụ thể cũng có vẻ thú vị
      Lý do cảm xúc tiêu cực gây chú ý là vì ban đầu tôi kỳ vọng một biểu đồ cảm xúc rõ ràng hơn. Tôi đã nghĩ nhìn chung sẽ từ trung tính đến tích cực, rồi nghiêng tích cực quanh các bài tích cực và nghiêng tiêu cực quanh các bài tiêu cực; nhưng với gần như mọi truy vấn, cảm xúc hầu như lúc nào cũng tiêu cực. Ngay cả các bài tích cực cũng có vẻ kéo theo nhiều tiêu cực theo mô hình và cách tiếp cận này, và cả hai cũng có thể đều sai, nên tôi muốn đào sâu hơn trong một bài blog sau
    • Ngoài cảm xúc theo nền tảng mạng xã hội và khung thời gian, sẽ tốt nếu xem cả thời điểm trong ngày và ngày thường/cuối tuần
    • Vài tháng trước tôi đã viết một bài blog phân tích cảm xúc bình luận trên HN về AI, blockchain, làm việc từ xa và Rust. Biểu đồ cuối cùng ở phần cuối bài có liên quan đến chủ đề này
      https://openpipe.ai/blog/hn-ai-crypto
    • Thật đáng tiếc là HN API không cung cấp số phiếu bầu của bình luận. Tôi tò mò phân tích cảm xúc sẽ thay đổi thế nào nếu gán trọng số theo upvote/downvote
      Không có bằng chứng, nhưng tôi đoán các kỹ sư nhìn chung hay phê bình, còn phản hồi tích cực thì thường bấm +1 hơn là viết lặp lại. Phê bình thì được viết trực tiếp hơn :)
    • Crypto có lẽ cũng thuộc nhóm đó
  • Đây là một ví dụ hay cho những người chưa quen với data engineering/MLOps
    Tôi đề xuất tạo cụm phân cấp cho các điểm bằng HDBSCAN, rồi dùng mô hình để tạo tên cho các cụm bên trong. Khi đó sẽ dễ khám phá chủ đề đến tận các lá bằng cách hiện dần các mục chi tiết dựa trên mức độ liên kết với nút hiện tại
    Màu nhóm cần rõ ràng hơn, và nếu có cụm thì sẽ hữu ích. Kích thước chữ của từng bài nên thay đổi theo tầm quan trọng/mức độ liên quan trên toàn bộ dữ liệu hoặc theo tiêu chí tìm kiếm hiện tại. Nếu có nhiều tóm tắt cụm bên trong hơn, có thể thay nhiều bài viết bằng tóm tắt nhóm cho đến khi phóng to, nhờ đó giảm bớt tình trạng chữ chồng chéo

  • Phạm vi dự án lớn đến mức đáng kinh ngạc
    Tuy nhiên, dù là jina hay bge-3/flag, có vẻ embedding và tokenizer không thật sự phù hợp với các chủ đề kỹ thuật. Từ ngôn ngữ tự nhiên thì ổn, nhưng khi tìm các khái niệm kỹ thuật như “xaml”, “simd”, nó có xu hướng tokenize đầu vào rồi cố bắt các từ nghe tương tự
    Góp ý mang tính xây dựng: sẽ tốt nếu có cách để không lặp đi lặp lại cùng các kết quả kiểu “bảng xếp hạng HN” khi chủ đề quá ngách và không có kết quả. Khi tìm một từ mà embedding không quen thuộc, kết quả “Stephen Hawking has died” thường xuất hiện
    Tôi cũng không chắc phân tích cảm xúc hoạt động tốt đến đâu. Có vẻ có quá nhiều cảm xúc tiêu cực không khớp với thực tế; ngay cả khi tìm thứ mà HN gần như chắc chắn sẽ nhìn nhận cực kỳ tích cực như “Mr Rogers” thì vẫn xuất hiện đỉnh tiêu cực nghiêm trọng. Tìm “Carter” thì có một đỉnh tiêu cực rất lớn liên quan đến việc Rosalynn Carter qua đời, nhưng bài thực tế là một bài gửi lên nói về những việc tuyệt vời mà vợ chồng Carter đã làm
    “Mức độ phổ biến theo thời gian” có lẽ cần được hiệu chỉnh theo số phiếu trung vị của các bài trong tháng/năm đó. Nếu chỉ vẽ số lượng bài, đường xu hướng sẽ liên tục đi lên. Nhìn vào mức độ phổ biến của “diesel” sẽ thấy ý tôi: thuật ngữ này đã đạt đỉnh 10 năm trước. Hoặc có lẽ nên dùng tỷ lệ xuất hiện từ khóa, hay số lượng mục có chỉ số cosine similarity với truy vấn nhỏ hơn x, thay vì điểm bài viết
    Tính năng động cho phép nhấp vào bài để loại bỏ và tính lại ngưỡng tương đồng rất tuyệt

    • Làm thế nào để có thể xác định bằng chương trình rằng một mô hình embedding không nhận ra một thuật ngữ hay từ cụ thể?
  • Có một công cụ tuyệt vời làm gần như cùng việc đó cho bất kỳ dataset nào: https://github.com/enjalot/latent-scope
    Tất nhiên quy mô của dự án gốc bổ sung thêm rất nhiều độ phức tạp thú vị, và công cụ này không xử lý được quy mô như vậy, nhưng nó tốt cho các dataset cỡ trung

  • Muốn phân tích xem tự quảng bá trên HN có tăng lên hay không
    Ở đây, tự quảng bá được định nghĩa không phải là các bài dạng “Show HN: Something ...”, mà là các bài có dạng “Show HN: I ...”
    Trong top 100 hiện tại, chẳng hạn “Show HN: Exploring HN by mapping and analyzing 40M posts and comments for fun”, “Show HN: Browser-based knitting (pattern) software” không phải là tiêu đề tự quảng bá. Chủ ngữ lần lượt là việc khám phá và phần mềm
    Ngược lại, “Show HN: I built a non-linear UI for ChatGPT”, “Show HN: I created 3,800+ Open Source React Icons” là tiêu đề tự quảng bá. Chủ ngữ của từng tiêu đề là “I”
    Từ kết quả tìm kiếm Algolia, chỉ kiểm tra các tiêu đề bắt đầu bằng “Show HN: I” theo từng năm tính từ ngày 1 tháng 4, rồi chia cho tổng số kết quả của cả năm đó và vẽ thành biểu đồ thì được như sau
    2023 ****************************************
    2022 ***********************************
    2021 ***************************
    2020 **************************************
    2019 *************************
    2018 *************
    2017 *******
    2016 **********
    2015 ********
    2014 ************
    2013 *********************
    2012 *****************
    2011 *********
    2010 ***
    Thời tôi lớn lên, nhìn chung tự quảng bá có vẻ bị xem là một nét tính cách xấu. Tôi được dạy rằng hành động mới nên quảng bá cho bản thân, chứ không phải thu hút sự chú ý vào chính mình; nhưng tôi có cảm giác văn hóa đó đang thay đổi
    Nếu tự quảng bá thực sự đã tăng lên, tôi tò mò liệu đó có phải do ảnh hưởng của mạng xã hội hay không. Trên YouTube tôi cũng cảm thấy có mức tăng tương tự, nhưng chỉ là cảm giác rằng có nhiều video được đề xuất bắt đầu bằng “I.....”, chứ không có dữ liệu

    • Định nghĩa về tự quảng bá hơi khác với cách tôi thường nghĩ. Thông thường, nếu một người quảng bá thứ do chính họ làm ra thì đó được xem là tự quảng bá. Vì vậy hai ví dụ bạn nêu là không tự quảng bá, theo định nghĩa của tôi, cũng là tự quảng bá
      Nói cách khác, phần bạn chia thành tự quảng bá và không tự quảng bá, theo tiêu chí của tôi, là các trường hợp tiêu đề thể hiện rất rõ rằng đó là tự quảng bá và các trường hợp thể hiện kém rõ hơn. Dù vậy, vì cụm “Show HN” dường như chỉ được dùng cho tự quảng bá, nên ngay cả khi không có “I”, người biết quy ước này vẫn hiểu đó là tự quảng bá
    • Mọi bài Show HN đều phải là thứ do tác giả tạo ra, nên tôi không rõ việc nói rõ điều vốn ngầm hiểu khiến thứ gì đó trở nên tự quảng bá hơn ở điểm nào
      Tất cả đều có nghĩa là “nhìn này, tôi đã làm ra một thứ hay ho, bạn nghĩ sao?”
    • Đây cũng là nội dung được đề cập nhiều trong tiểu sử Einstein của Walter Isaacson, nên người ta đã quan sát xu hướng này từ lâu rồi
      Ví dụ như người Đức chỉ trích Einstein là tự quảng bá, còn ngược lại ở Mỹ thì có văn hóa người nổi tiếng. Đây cũng có thể là một hiện tượng mang tính chu kỳ
  • Có lẽ đây chắc chắn là bài viết tuyệt nhất tôi thấy trên HN trong năm nay

  • Lúc mới xem thì không rõ, nhưng ứng dụng thật nằm ở đây: https://hn.wilsonl.in/

    • Tôi tự hỏi có phải họ cố ý đặt liên kết landing page gần như ở cuối hay không. Cấu trúc này khiến chỉ những người thực sự đọc bài mới đi tới trang đó
      Tôi không nói mỉa; tôi nghĩ đây là một ý hay
    • Có vẻ liên kết tìm kiếm không chia sẻ được, hoặc không đưa từ khóa tìm kiếm vào trong đó
      Tôi cũng tò mò liệu cụm tìm kiếm được embedding theo từng từ hay có dùng cùng mô hình như đã dùng cho tài liệu không. Tôi tìm “lead generation”, một cụm mà embedding đa từ đúng nghĩa lẽ ra phải hiểu được, nhưng lại ra kết quả về nhiễm độc chì
    • Tôi đã tìm thấy tôi và các bài viết của tôi ở đó! Hay đấy
  • Một khuyến nghị hiện đại cho UMAP là Parametric UMAP: https://umap-learn.readthedocs.io/en/latest/parametric_umap....
    Nó huấn luyện một Keras MLP nhỏ để thực hiện giảm chiều xuống 2D, đồng thời tối thiểu hóa hàm mất mát UMAP. Ưu điểm là mô hình này nhỏ, nên có thể lưu lại và tái sử dụng để dự đoán cả trên dữ liệu mới chưa biết. Các mô hình UMAP được huấn luyện theo cách truyền thống thì lớn. Ngoài ra, vì dùng GPU nên về lý thuyết việc huấn luyện nhanh hơn nhiều
    Nhược điểm là phần triển khai trong gói Python UMAP không được tốt lắm, và nó tạo rồi đẩy toàn bộ tập dữ liệu nút/cạnh đã mở rộng vào GPU. Vì vậy chỉ có thể huấn luyện đến khoảng 100 nghìn embedding trước khi hết bộ nhớ
    Pipeline hoàn toàn không giám sát gồm UMAP → HDBSCAN → gán nhãn cụm bằng AI hữu ích đến mức tôi muốn thử tạo một triển khai Parametric UMAP có khả năng mở rộng tốt hơn

    • Có một triển khai GPU nhanh trong cuML. Tôi không rõ vì sao cuML lại ít được biết đến như vậy
    • Thoạt nhìn thì có vẻ nguyên nhân là phần triển khai đẩy toàn bộ đồ thị, tức tất cả các cạnh, vào GPU. Có thể giảm nhẹ bằng cách lấy mẫu cạnh trong quá trình huấn luyện
  • Đối với thứ trông như một dự án sở thích mang tính khám phá, đây là một khối lượng công việc lớn đến đáng kinh ngạc. Tôi không có ý hạ thấp thành quả; nó thực sự rất tuyệt, nhưng tôi ngạc nhiên vì mức đầu tư quá lớn
    Chỉ riêng để tính embedding đã dùng 150 GPU, và để giao tiếp giữa các máy chủ, họ đã phát triển hai hệ thống tùy chỉnh là db-rpc và queued. Ngoài ra còn có rất nhiều công việc phụ trợ và tính toán khác
    Tôi tò mò về bối cảnh của dự án. Cũng muốn biết họ đã xoay xở kinh phí và thời gian cần thiết cho kiểu nghiên cứu này như thế nào
    Từ góc nhìn của người đã làm khá nhiều việc tương tự một cách chuyên nghiệp, tôi từng lập bản đồ bối cảnh các bài báo học thuật và bằng sáng chế, nhưng không chắc liệu 150 GPU có thật sự cần thiết hay không. Nếu cuối cùng chỉ là chiếu xuống 2 chiều và phân cụm, thì các phương pháp truyền thống như bag-of-words hoặc topic modeling sẽ dễ hơn và rẻ hơn nhiều, mà có lẽ gần như không cảm nhận được khác biệt về chất lượng. Dùng đồ thị tác giả và luồng bình luận cũng có thể cho kết quả tương tự

    • Chi phí không được nhắc trong bài, nhưng chỉ ở mức vài trăm đô la, nên ngay cả với một dự án sở thích cũng khá dễ tiếp cận
      GPU rẻ hơn tôi tưởng, và phần lớn việc mở rộng chỉ là vì tôi thiếu kiên nhẫn thôi :) Toàn bộ cụm cũng chỉ chạy trong vài giờ
      Nếu có liên kết tới công việc bạn đã làm thì tôi muốn xem. Nghe có vẻ thú vị nên tôi muốn đọc thêm
    • Tác giả rõ ràng là rất giỏi. Cũng thú vị là họ đăng bài lên HN nhưng không bình luận kể từ năm 2018, rồi sau đó bắt đầu dự án này
      Về mặt tiền bạc và thời gian, có thể họ đang ở giữa các công việc hoặc giữa các công ty, và đã thành công về tài chính từ sự nghiệp hay dự án kinh doanh trước đó nên tự bỏ tiền làm. Mức sử dụng GPU cũng rất hiệu quả, nên chi phí có lẽ không quá lớn
    • Ngay cả dùng embedding rẻ hơn cũng có thể cho kết quả khá tốt
      Áp dụng học máy cổ điển như SVM có hiệu chỉnh xác suất lên các embedding kiểu này có thể đạt kết quả tốt trong phân loại và phân cụm, đồng thời nhanh hơn tinh chỉnh LLM hơn 100 lần
  • Thông thường người ta không làm như bản demo này mà sẽ chuẩn hóa vector
    Khi dùng vector đã chuẩn hóa, khoảng cách Euclid đo khoảng cách giữa hai điểm đầu mút của vector. Ngược lại, khoảng cách cosine đo độ dài của phép chiếu một vector lên vector kia

    • Vấn đề của chuẩn hóa là mất đi một bậc tự do. Trong trực quan hóa, điều đó thực chất tương đương với mất một chiều
      Vector 2 chiều đã chuẩn hóa thực ra chỉ là vector 1 chiều. Nếu muốn thể hiện quan hệ 2 chiều, cần dùng vector 3 chiều để lấy lại 2 bậc tự do
 
ggg213 2024-05-10

Có vẻ như đã thiếu tiêu đề.