สำรวจโพสต์และความคิดเห็น 40 ล้านรายการบน HN ด้วยแผนที่ embedding
(blog.wilsonl.in)- โปรเจกต์ค้นหา แผนที่ และวิเคราะห์ที่รวบรวมไอเท็มมากกว่า 40 ล้านรายการจาก Hacker News public API แล้วแนบ embedding, metadata และเนื้อหา ให้กับความคิดเห็นมากกว่า 30 ล้านรายการและโพสต์ 4 ล้านรายการ
- แนวทางแรกที่ทำ embedding เฉพาะชื่อเรื่องมีข้อจำกัดจากชื่อเรื่องที่กำกวมและอคติจาก Ask HN/Show HN จึงเปลี่ยนมาใช้เนื้อหาของเว็บเพจที่ลิงก์ ความคิดเห็นยอดนิยม และบริบทของความคิดเห็นบรรพบุรุษร่วมกัน
- เมื่อสเกลการประมวลผลใหญ่ขึ้น จึงผสาน GPU ประมาณ 150 ตัวของ RunPod, บริการคิวที่เขียนด้วย Rust, HTTP/2 DB proxy, UMAP, การเรนเดอร์ด้วย Canvas และ edge server และหลังเปลี่ยนโมเดล เวลา embedding ต่ออินพุตลดจากประมาณ 600ms เหลือ 6ms
- การจัดอันดับผลค้นหาไม่ได้ใช้การตรงกันของสตริงแบบง่าย ๆ แต่ใช้ cosine similarity, คะแนน HN และน้ำหนักตามเวลาร่วมกัน เพื่อสะท้อนความเกี่ยวข้องเชิงความหมาย สัญญาณทางสังคม และความใหม่
- เดโมสาธารณะมีเฉพาะข้อมูลถึงราววันที่ 10 เมษายน 2024 ส่วนข้อมูลทั้งหมดและโค้ดเปิดเผยบน GitHub เพื่อใช้กับการทดลองด้านการค้นหา การแนะนำ การวิเคราะห์ผู้ใช้ และการอัปเดตแบบเรียลไทม์
ขอบเขตโปรเจกต์และข้อมูลที่เปิดเผย
- สร้างแผนที่ที่วางโพสต์ทั้งหมดของ Hacker News ไว้ใน พื้นที่เชิงความหมาย พร้อมสร้างเครื่องมือค้นหา วิเคราะห์ และแสดงภาพประกอบกัน
- จุดเริ่มต้นคือการทดลอง text embedding และเลือก HN เป็นชุดข้อมูลที่เหมาะสม เพราะมีคอนเทนต์ที่คัดสรรมาจำนวนมาก และให้เข้าถึงคอนเทนต์ทั้งหมดได้แบบโปรแกรม
- embedding แทนข้อความเป็นจุดในปริภูมิมิติสูง โดย ระยะห่างสัมพัทธ์ระหว่างจุดต่าง ๆ มีประโยชน์มากกว่าตำแหน่งสัมบูรณ์
- การใช้งานที่คาดหวังมี 3 อย่าง
- การค้นหาเชิงความหมายในคอนเทนต์ที่สะสมไว้ของ HN
- คำแนะนำแบบปรับให้เหมาะกับพื้นที่ความสนใจ
- การวิเคราะห์อารมณ์ ความนิยม และมุมมองคัดค้านแยกตามหัวข้อในชุมชน
- ความคิดเห็นมากกว่า 30 ล้านรายการและโพสต์ 4 ล้านรายการถูกเผยแพร่เป็น ชุดข้อมูลรีลีส
- รวม metadata เช่น ID, คะแนน และผู้เขียน
- รวม embedding
- รวมข้อความความคิดเห็นและข้อความเว็บเพจที่ครอลมา
- โค้ดเปิดเผยใน hackerverse GitHub repository
การรวบรวมข้อมูล Hacker News
- HN มี public API แบบเรียบง่าย และอ็อบเจกต์ทั้งหมดถูกดึงในรูป
item maxitem.jsonให้ ID ที่ใหญ่ที่สุด และ ณ เวลาที่เขียน ID สูงสุดเกิน 40 ล้านแล้ว- แม้เวลาตอบสนองเฉลี่ยจะอยู่ที่ 10ms แต่หากครอล 40 ล้านรายการตามลำดับจะใช้เวลามากกว่า 4 วัน จึงจำเป็นต้องมี การประมวลผลแบบขนาน
- สร้างบริการรวบรวมข้อมูลความเร็วสูงด้วย Node.js แต่แนวทาง semaphore และคิว Promise ช้า เพราะเวลาของ CPU ส่วนใหญ่ถูกใช้กับโค้ด JS ใน user space
- ต่อมาใช้ API
worker_threadsกระจายงาน fetch ไปยัง CPU ทั้งหมด และปรับปรุงประสิทธิภาพด้วยการทำให้ทุกคอร์ทำงานเต็มที่ - การรวบรวมแบบขนานทำให้ลำดับสลับกัน ดังนั้นเพื่อไม่ให้มีรายการตกหล่นเมื่อหยุดกลางคัน จึงบันทึก marker ของรายการที่เสร็จแล้วตามลำดับ ID
- มีคุณลักษณะบางอย่างที่สังเกตได้จาก HN API ด้วย
- คะแนนดูเหมือนจะไม่ลดต่ำกว่า -1
- ไม่สามารถดึง downvote ของโพสต์และจำนวนโหวตของความคิดเห็นได้
- โพสต์และความคิดเห็นบางส่วนไม่มีชื่อเรื่อง ข้อความ หรือ URL แม้จะไม่ได้อยู่ในสถานะถูกลบหรือถูก flag
- ID ความคิดเห็นอาจน้อยกว่าบรรพบุรุษ ซึ่งอาจเกิดจากการย้าย comment tree
- HN crawler ยังถูกแยกเป็นโปรเจกต์ TypeScript อีกตัวชื่อ crawler-toolkit-hn
embedding ครั้งแรกและโครงสร้างพื้นฐาน
- ตอนแรกมองว่าการใช้เฉพาะชื่อเรื่องโพสต์ก็น่าจะเพียงพอสำหรับการแทนความหมาย จึงสร้าง embedding จากส่วนนี้
- การเปรียบเทียบโมเดลอ้างอิงจาก Massive Text Embedding Benchmark และโมเดลแรกที่เลือกคือ BGE-M3
- BGE-M3 นอกจากสร้าง dense embedding ทั่วไปแล้ว ยังสร้าง lexical weights ได้ด้วย จึงใช้กับการค้นหาแบบ hybrid ที่ผสมกับแนวทางอย่าง BM25 ได้
- โครงสร้างพื้นฐานสำหรับสร้าง embedding ไม่ได้เรียบง่าย
- โมเดลที่ดีอาจมีพารามิเตอร์ตั้งแต่หลายร้อยล้านถึงหลายพันล้านตัว
- ทำงานบน GPU ได้มีประสิทธิภาพกว่ามาก แต่คลัสเตอร์ GPU มีค่าใช้จ่ายสูง
- หาก inference ใช้เวลาหลายร้อย ms ต่ออินพุต การประมวลผลอินพุต 40 ล้านรายการด้วย GPU เดี่ยวแทบจะอยู่ในระดับ 1 ปี
- ข้อมูล/เซิร์ฟเวอร์อยู่ห่างจาก GPU จึงต้องรักษา pipeline ไว้ไม่ให้ GPU ว่างงาน
- ใช้ RunPod เพื่อ deploy เครื่อง GPU ที่รันใน data center เป็น container และใช้ GPU ที่ค่อนข้างประหยัดอย่าง RTX 4090
- เมื่อ GPU กระจายอยู่ทั่วโลก latency ของการเชื่อมต่อ DB และ overhead ของ connection กลายเป็นปัญหา จึงสร้าง db-rpc
- proxy คำสั่ง SQL query ไปยัง DB ในเครื่องผ่าน HTTP/2
- ใช้ shared connection pool ขนาดใหญ่
- ประมวลผลหลาย query บนการเชื่อมต่อเดียวด้วย HTTP/2 multiplexing
- AWS SQS มีข้อจำกัดด้านอัตราความเร็วต่ำและค่าใช้จ่ายต่อข้อความเป็นภาระสำหรับข้อความงานขนาดเล็กหลายล้านรายการ จึงสร้างบริการคิวด้วย Rust บน RocksDB ชื่อ queued
- ประมวลผลได้ 100K+ op/s บน node เดียว
- ลดภาระจาก batching, ขนาดข้อความ, rate limit และค่าใช้จ่าย
- หลังขยายไปถึง GPU ประมาณ 150 ตัว ก็ทำ embedding ให้โพสต์และความคิดเห็น 40 ล้านรายการได้ภายในไม่กี่ชั่วโมง
- เวลานั้นต้นทุนการทำ embedding ต่ออินพุตอยู่ที่ประมาณ 600ms และอัตราการใช้งาน GPU อยู่ในระดับสูงตลอดช่วงเวลา
เสริมบริบทด้วยการครอลเว็บเพจ
- แนวทางทำ embedding จากชื่อเรื่องอย่างเดียวไม่เพียงพอ
- โพสต์จำนวนมากมีชื่อเรื่องแปลก สร้างสรรค์ หรือกำกวม
- วลี
Ask HNและShow HNกินสัดส่วนมากในชื่อเรื่องทั้งหมด ทำให้มีแนวโน้มถูกจัดคลัสเตอร์ร่วมกันโดยไม่เกี่ยวกับหัวข้อ
- สำหรับโพสต์แบบข้อความและความคิดเห็นสามารถใช้ข้อความของตัวเองได้ แต่โพสต์แบบลิงก์ส่วนใหญ่ต้องครอลเว็บเพจที่เชื่อมโยงอยู่
- ใช้บริการ Rust ดึง URL และ parse metadata เช่น ชื่อเรื่อง รูปภาพ ผู้เขียน และข้อความเนื้อหาจาก HTML
- เวอร์ชัน Node.js แรกช้ากว่าเวอร์ชัน Rust 10 เท่าในงานที่ใช้ CPU หนัก และปรับปรุงประสิทธิภาพด้วยการเขียนใหม่เป็น Rust
- การสกัดข้อความใช้ scraper parse HTML จากนั้นลบองค์ประกอบ HTML5 ที่ไม่ใช่เนื้อหาหลักตามความหมาย แล้ววนผ่าน tree ที่เหลือ
- link rot ก็มีมากเช่นกัน
- URL ประมาณ 200,000 รายการล้มเหลวจาก 404, DNS lookup ล้มเหลว และ connection timeout
- คิดเป็นน้อยกว่า 5% จาก 4 ล้านหน้า
- เพื่อลดข้อมูลที่ขาดหาย จึงดึงบทความเก่าบางส่วนด้วย Wayback API ของ Internet Archive
- rate limit ของ Internet Archive ต่ำมาก อยู่ที่ประมาณ 5 รายการต่อนาที
กลยุทธ์ embedding ครั้งที่สอง
- เว็บเพจมีความยาว แต่ BGE-M3 รองรับ context window 8192 token
- อย่างไรก็ตาม BGE-M3 ช้า จึงเปลี่ยนเป็น jina-embeddings-v2-small-en
- จำนวนพารามิเตอร์น้อยกว่ามาก
- ประสิทธิภาพตาม MTEB ก็อยู่ในเกณฑ์ดี
- เวลา inference ลดเหลือประมาณ 6ms เร็วขึ้น 100 เท่า
- เนื่องจากอินพุตยาว การเพิ่ม batch size ทำให้เกิด OOM จึงไม่สามารถทำให้ GPU ทำงานเต็มที่ได้ทั้งหมด
- เพื่อชดเชยหน้าที่มีข้อความน้อยหรือดึงไม่ได้ จึงเพิ่มความคิดเห็นยอดนิยมของ HN ต่อท้ายเนื้อหาของโพสต์
- ใช้ความคิดเห็นระดับบนโดยถือว่า
item.kidsถูกจัดอันดับไว้แล้ว - ตัดความคิดเห็นที่ถูกลบ dead หรือมีคะแนนติดลบออก
- จำกัดอินพุตสูงสุดไว้ที่ 64KiB
- ใช้ความคิดเห็นระดับบนโดยถือว่า
- ความคิดเห็นอาจมีความหมายไม่เพียงพอเมื่ออยู่เดี่ยว ๆ จึงสร้างบริบทที่ยาวขึ้นโดยย้อนขึ้นไปถึงความคิดเห็นบรรพบุรุษและชื่อเรื่องของโพสต์
- ค่าขนาดใหญ่อย่าง embedding และข้อความถูกเก็บไว้ในตาราง
kvแยกต่างหาก- หากเก็บรวมในแถวเดียวกัน แถวจะอ้วนขึ้น
- แม้การอัปเดตคอลัมน์เล็ก ๆ ก็จะแพงขึ้น
- การเปลี่ยน schema ก็จะแพงขึ้น
สร้างแผนที่ความหมายแบบ 2D ด้วย UMAP
- UMAP เป็นเทคนิคการลดมิติที่พยายามรักษาความสัมพันธ์เชิงความหมายไว้ ขณะลด embedding มิติสูงให้เหลือมิติต่ำ
- ลดเวกเตอร์ embedding ขนาด 1024 มิติให้เป็นจุด 2D เพื่อใช้กับ scatter plot และการแสดงผลเป็นแผนที่
- UMAP รับกราฟ PyNNDescent และ embedding ต้นฉบับเป็นอินพุต โดยใช้
metric="cosine",n_components=2 - การเรียนรู้จากอินพุตมิติสูงจำนวนหลายล้านรายการใช้เวลานาน จึงใช้ VM
c7i.metal-48xlบน EC2- ใช้โปรเซสเซอร์ 96 คอร์อย่างเต็มที่
- หลังจากประมาณ 1 ชั่วโมง 30 นาที ก็ได้เมทริกซ์ 2D ออกมา
- บันทึก embedding 2D ที่สร้างขึ้นและโมเดล UMAP ที่ฝึกแล้วไว้ เพื่อให้สามารถแปลง embedding ใหม่ในภายหลังได้โดยไม่ต้องฝึกใหม่
- scatter plot แรกมีจุดมากเกินไป จึงลดความหนาแน่นด้วยการเลือกเฉพาะโพสต์คะแนนสูงสุดในแต่ละเซลล์กริด แล้วแสดงชื่อเรื่อง
- ใน embedding ชุดที่สองซึ่งใส่บริบทมากขึ้น โพสต์ที่จัดวางได้ยากหากดูจากชื่อเรื่องเพียงอย่างเดียว ถูกวางใกล้คอนเทนต์ที่เกี่ยวข้องได้แม่นยำขึ้น
Cosine similarity และการจัดอันดับค้นหา
- แก่นของการใช้ embedding คือการหา ความคล้ายกัน ระหว่าง embedding สองตัว
- ใน text embedding มักใช้ cosine distance/ similarity มากกว่า Euclidean distance ทั่วไป
- cosine distance มีประโยชน์ในกรณีที่ทิศทางสำคัญกว่าขนาด
- การสนทนายาวเกี่ยวกับ X ควรคล้ายกับ X มากกว่าการสนทนายาวเกี่ยวกับ Y
- หากนำขนาดมาคิดด้วย ความสัมพันธ์เช่นนี้อาจทำให้เข้าใจผิดได้
- การคำนวณหลักที่ใช้คือ dot product ระหว่างเมทริกซ์ embedding กับ embedding ของคิวรี
- หากเวกเตอร์เป็น unit vector ก็ไม่จำเป็นต้องหารด้วยขนาดเพิ่มเติม
- ผลการค้นหาไม่ได้เรียงลำดับด้วยความคล้ายล้วน ๆ
- cosine similarity เป็นปัจจัยใหญ่ แต่ไม่ใช่ปัจจัยเดียว
- คะแนน HN ถูกใช้เป็น social proof
- time weighting ถูกสะท้อนเป็นปัจจัยลบที่แปรผันตาม
log(age)เพื่อให้คอนเทนต์เก่าถูกดันลงอย่างรวดเร็วในคิวรีที่ความสดใหม่สำคัญ
แอปแผนที่บนเบราว์เซอร์
- เป้าหมายคือทำให้พื้นที่ embedding ของ HN เป็น แผนที่แบบโต้ตอบได้ ที่สำรวจได้เหมือน Google Maps
- พฤติกรรมที่ต้องการนั้นชัดเจน
- เมื่อซูมเข้าด้วยการ pinch หรือ scroll wheel ของเมาส์ จะมีจุดแสดงมากขึ้น
- ระยะห่างระหว่างจุดก็ขยายตามไปด้วย
- บางจุดมี label แต่ไม่ได้ติด label ให้ทุกจุด
- เมื่อคลิกจุด จะแสดงรายละเอียดโพสต์
- รองรับทั้ง touch และเมาส์บนมือถือและเดสก์ท็อป
- การส่งทุกจุดไปยัง client ในครั้งเดียวไม่เหมาะสม เพราะมีจำนวนหลายล้านจุด
- ใช้โครงสร้างที่แบ่งพื้นที่แผนที่เป็น tile แบบกริด และให้ client ดึงเฉพาะ tile ที่ต้องใช้
- tile ระบุได้ด้วยพิกัด
(x, y) - สามารถเก็บใน KV storage เช่น S3 ได้
- deploy ได้ง่ายโดยไม่ต้องมี logic ฝั่ง server
- tile ระบุได้ด้วยพิกัด
- ระดับการซูมจัดการด้วยวิธี LOD
- ในแต่ละระดับ จะแบ่งเป็นเซลล์กริดตามแกนมากขึ้น 2 เท่า
- จุดที่ถูกเลือกในระดับก่อนหน้าจะถูกคัดลอกไปยังระดับถัดไปด้วย เพื่อไม่ให้จุดหายไปเมื่อซูมเข้า
- ตั้งเป้าให้ขนาด tile หลังบีบอัดต่ำกว่า 20KiB
- จำกัดไว้ที่ประมาณ 1,500 จุด
- ใช้
(x, y)8 ไบต์ต่อจุด, ID 4 ไบต์, คะแนน 2 ไบต์
- ในการเรนเดอร์เว็บแอป วิธีใช้ DOM element หลายพันรายการทำให้ประสิทธิภาพแย่ลงมาก
- implementation สุดท้ายใช้ Canvas และวาดใหม่ทุกครั้งที่ viewport อัปเดต
- แม้จะวาดจุดหลายพันจุดใหม่ทุกเฟรม ก็ยังทำงานได้ลื่นและเรียบง่าย
- การติด label ทำโดยเลือกโพสต์คะแนนสูงสุดซ้ำ ๆ แต่ตัดออกหากทับกับ label ที่มีอยู่
- การตรวจ collision ใช้ implementation R-tree ของ RBush
- ความยาว label ประมาณด้วยอาร์เรย์ไบต์ของความยาวชื่อเรื่องและสูตรที่ปรับแล้ว แทนการใช้
measureText()ของเบราว์เซอร์
- การคำนวณกล่อง label เริ่มต้นและ collision ใช้ CPU หนัก จึงย้ายไปใช้ Web Workers
- ทดลองใช้ OffscreenCanvas ด้วย แต่ logic การเรนเดอร์มีประสิทธิภาพอยู่แล้ว จึงไม่ได้ผลมากนัก
ภูมิประเทศ ขอบเขต และ label เมือง
- หากแผนที่มีแต่จุดจะขาดความรู้สึกด้านทิศทางและความน่าสนใจทางภาพ จึงเพิ่มแนวคิดเรื่อง ภูมิประเทศและเมือง
- เนื่องจากไม่มีภูมิศาสตร์จริงหรือขอบเขตทางการเมือง จึงใช้ภูมิประเทศเป็นอุปมาสำหรับความหนาแน่นของจุด
- ความหนาแน่นของจุดใช้เป็นสัญญาณที่แสดงพื้นที่ที่มีกิจกรรม ความสนใจ คอนเทนต์ การมีส่วนร่วม ความนิยม และการสนทนามากได้อย่างรวดเร็ว
- ตอนแรกพิจารณา Kernel Density Estimation แต่การลองใช้ไลบรารีมาตรฐานใช้เวลานานเกินไป
- จึงเปลี่ยนเป็น mapping แต่ละจุดไปยังเซลล์กริดขนาดใหญ่ นับจำนวนต่อเซลล์ แล้วใช้ Gaussian blur
- ค่าความหนาแน่นแบบเชิงเส้นส่วนใหญ่ดูใกล้ 0 ทำให้แผนที่ออกมาไม่ดี
- เมื่อใช้
log(density + 1)ก็ได้ลำดับชั้นที่เป็นธรรมชาติมากขึ้น และบริเวณที่ระดับความหนาแน่นต่างกันมาบรรจบกันดูเหมือนขอบเขตโดยนัย - แทนที่จะใช้ภาพขนาดใหญ่ สร้าง SVG path เพื่อให้ client วาดเป็น polygon
- มีเพียง 4 สี จึงมีประสิทธิภาพ
- แม้ซูมเข้า ขอบเขตก็ยังคมชัด
- ใช้ฟังก์ชัน contour ของ OpenCV คำนวณเส้นปิดตามแต่ละระดับ
- label เมืองแสดงหัวข้อร่วมกันภายในรัศมีหนึ่ง
- ชื่อเมืองถูกนำไปทำ embedding แล้วใช้โมเดล UMAP ที่บันทึกไว้เพื่อหาตำแหน่ง
(x, y) - เคยลองสร้างอัตโนมัติด้วย
- พยายามให้ LLM สร้างชื่อเมือง แต่ยากที่จะทำให้ได้ผลลัพธ์ที่ต้องการอย่างเสถียรด้วย prompt
- K-means หา cluster ที่มีความหมายในแบบที่มนุษย์น่าจะจัดกลุ่มได้ไม่มากนัก
- สุดท้ายจึงสำรวจแผนที่เองและจดชื่อเมืองบางส่วนด้วยมือ ซึ่งใช้เวลาประมาณ 1 ชั่วโมง
Edge deployment และการตอบสนอง
- การสำรวจแผนที่ต้องเร็วและตอบสนองดี ดังนั้นการลด latency ในการ fetch ข้อมูลจึงสำคัญ
- ตอนแรกเก็บข้อมูลแผนที่ทั้งหมดไว้ใน region ENAM ของ Cloudflare R2 แต่ latency อยู่ที่ 600ms ไปจนถึงหลายวินาที
- แม้ latency ทางกายภาพจะประมาณ 200ms ก็ยังไม่ดีพอ เพราะ 100ms คือ threshold ที่ให้ความรู้สึกเหมือนตอบสนองทันที
- เพื่อลด latency จึงย้ายข้อมูลไปใกล้ผู้ใช้มากขึ้น
- วาง server ขนาดเล็กไว้ที่ Virginia, San Jose, London, Sydney และให้บริการข้อมูลด้วย server ที่เขียนด้วย Rust
- client เรียก
/healthzของ edge หลายแห่งไม่กี่ครั้ง แล้วเลือก server ที่ตอบกลับก่อน - วิธีอย่าง Anycast หรือ CDN ก็เป็นไปได้ แต่ไม่ใช้เพราะต้นทุนและความซับซ้อนเกินจำเป็น
- การใช้หน่วยความจำของ process บน edge server มากกว่าข้อมูลต้นฉบับ 2–4 เท่า จึงยังเป็นข้อสงสัย
- อาจใช้ type ผิด
- struct padding
- การ over-allocation ของ
Vec,HashMap - ความเป็นไปได้ของ fragmentation หรือความไม่มีประสิทธิภาพของ memory allocator
ผลลัพธ์และข้อจำกัดของ semantic search
- ในคิวรีง่าย ๆ อย่าง “entering the tech industry” พบทั้งผลลัพธ์ที่มี upvote มากและผลลัพธ์ที่ไม่ได้รับความสนใจมากนัก ซึ่งดูมีความเกี่ยวข้องและมีประโยชน์ดี
- เมื่อเทียบกับบริการค้นหาเดิมของ HN การค้นหาด้วย semantic embedding สามารถหาผลลัพธ์ได้แม้ข้อความจะไม่ได้ตรงกันตามตัวอักษร
- คิวรีแบบคำถามอย่าง “what happened to wework” ก็แสดงผลลัพธ์ที่ครอบคลุมหลายปี เช่น การปลดพนักงาน ราคาหุ้นตก และการล้มละลายของ WeWork
- แม้ผลการค้นหาจะไม่ได้มีคำว่า “what happened” จริง ๆ หรือไม่ได้อยู่ในรูปคำถาม ก็ยัง match ได้
- ปัญหาหนึ่งคือไม่ได้กรองผลลัพธ์ที่ไม่คล้ายกันพอ ทำให้มีผลลัพธ์ลำดับล่างที่ไม่เกี่ยวข้องโดยสิ้นเชิงปะปนเข้ามา
- เรื่องนี้ถูกมองว่าเป็นปัญหาที่แก้ได้ง่าย
- ในการค้นหา “career growth” มีเอสเซย์ที่สร้างสรรค์และหลากหลายซึ่งไม่ได้มีคำนั้นตรง ๆ ปรากฏขึ้น
- คุณภาพการคัดสรรและคะแนนของ HN ช่วยยกระดับคุณภาพการค้นหา
- ในแอปมี query suggestion แบบ hardcoded เช่น “linus rants”, “self bootstrapping”, “cool things with css”
ชุมชนเสมือนและการวิเคราะห์ความคิดเห็น
- สามารถใช้ embedding เพื่อสร้างฟีเจอร์ ซับคอมมูนิตี้เสมือน ได้
- เมื่อผู้ใช้ป้อนชื่อหรือคำอธิบายของชุมชน โพสต์ที่มีความคล้ายกันเกินค่า threshold ที่กำหนดจะถูกจัดกลุ่มขึ้นมาทันที
- เนื่องจาก HN ไม่มีฟีเจอร์สำหรับแบ่งย่อยโพสต์ให้ละเอียดกว่านี้ จึงเป็นวิธีสร้างการคัดสรรเนื้อหาตามความสนใจเฉพาะได้ทันที
- snippet และรูปภาพในการ์ดผลลัพธ์นำมาจาก metadata ของหน้าเว็บที่เว็บครอว์เลอร์บันทึกไว้
- ไอคอนของเว็บไซต์ถูกทำให้ง่ายขึ้น โดยให้ฝั่งไคลเอนต์ดึง
/favicon.icoของโดเมน แทนการ parse metadata ที่ซับซ้อน - เธรดความคิดเห็นก็สามารถแสดงด้วยวิธีเดียวกันได้
- HN API ไม่ได้ให้คะแนนความคิดเห็น จึงทำได้เพียงเรียงตามเวลา
- มองว่าวิธีจัดอันดับโดยไม่มีคะแนนความคิดเห็นสามารถใช้ข้อมูลอย่างประวัติการคอมเมนต์ของผู้ใช้ การมีส่วนร่วมรอบความคิดเห็นนั้น โพสต์ หัวข้อ และเนื้อหาได้
- ยังสามารถค้นหาผู้ใช้ที่มีอิทธิพลหรือมีความเคลื่อนไหวสูงในหัวข้อเฉพาะได้ด้วย
- ในคิวรี “cloudflare” มี
jgrahamcและeastdakotaปรากฏอยู่ในอันดับต้น ๆ - ผู้ใช้ทั้งสองเป็น CTO และ CEO ของ Cloudflare ตามลำดับ
- ในคิวรี “cloudflare” มี
- งานนี้ประมวลผลด้วยการคำนวณเมทริกซ์ โดยไม่ต้องแยกประเภทความคิดเห็นต่างหากหรือใช้การค้นหาด้วยคีย์เวิร์ด
- โดยทั่วไปการกรองหลังประมวลผลมักเร็วกว่าและเพียงพอกว่าการกรองก่อนประมวลผล
- การกรองก่อนต้องลบแถวที่สอดคล้องกันออกจากเมทริกซ์ embedding ซึ่งอาจต้องคัดลอกหน่วยความจำขนาดใหญ่มาก หรือคำนวณแบบ vectorization บางส่วนที่ช้า
- มองว่าควรหาแถวที่คล้ายกันก่อน แล้วค่อยกรองผลลัพธ์
- ค่า threshold ขั้นต่ำเป็นสิ่งสำคัญ
- รายการที่ไม่เกี่ยวข้องก็อาจมีค่าความคล้ายกันระดับ 0.6 ได้
- หากไม่มี threshold ผู้ใช้ที่มีจำนวนความคิดเห็นมากมากอาจครองอันดับได้เพียงเพราะปริมาณที่มาก
การวิเคราะห์อารมณ์และความนิยมจากข้อมูลความคิดเห็นทั้งหมด
- การวิเคราะห์ขนาดใหญ่ที่อยากลองทำกับความคิดเห็น 30 ล้านรายการคือ ความนิยมและอารมณ์
- เป้าหมายคือดูว่า HN รู้สึกอย่างไรต่อหัวข้อหนึ่ง ๆ เมื่อเวลาผ่านไป เหตุการณ์สำคัญส่งผลต่ออารมณ์อย่างไร และหัวข้อที่ผู้คนสนใจเติบโตหรือเสื่อมความนิยมอย่างไร
- เนื่องจากไม่มีข้อมูลอารมณ์ จึงใช้โมเดลจำแนกอารมณ์โอเพนซอร์สของ Hugging Face
- โมเดลที่เลือกคือ TweetEval ซึ่งเป็นโมเดลที่ฝึกกับคอนเทนต์บนโซเชียลมีเดีย
- เนื่องจาก TweetEval เป็นโมเดลสำหรับทวีตสั้น ๆ จึงใช้เฉพาะตัวความคิดเห็นเองเป็นอินพุต โดยไม่แนบบริบทของบรรพบุรุษเหมือนตอนทำ embedding
- นำความคิดเห็นเข้า queue แล้วประมวลผลงานบนคลัสเตอร์ GPU จากนั้นบันทึกผลลัพธ์
- เนื่องจากโมเดลมีขนาดเล็ก จึงเพิ่ม batch size เพื่อเพิ่มประสิทธิภาพการใช้ GPU
- การเพิ่ม batch size ใช้ VRAM มากขึ้น แต่ช่วยลดการถ่ายโอนหน่วยความจำระหว่างโฮสต์กับ GPU และเพิ่มความขนานได้
- ในโมเดล Transformer อินพุตแบบ batch ต้องเป็นรูปสี่เหลี่ยมผืนผ้า จึงถูก padding ให้ตรงกับความยาวอินพุตที่ยาวที่สุด
- หากมีอินพุตยาวหนึ่งรายการปะปนกับอินพุตสั้น ๆ ไม่กี่รายการ ขนาดอินพุตทั้งหมดและสถานะภายในอาจเพิ่มขึ้นอย่างมาก
- ด้วยเหตุนี้จึงอาจเกิด memory spike และ OOM ได้
- ในตัวอย่างการวิเคราะห์อารมณ์เกี่ยวกับ Rust พบว่าโดยรวมมีอารมณ์เชิงบวกต่อ Rust ค่อนข้างมาก
- มี spike เชิงบวกในช่วงที่ประกาศ Rust 1.0
- โพสต์ที่เป็นลบมากกว่าสัมพันธ์กับความคิดเห็นเชิงลบจำนวนมากตามเกณฑ์ของโมเดล
- ยังประเมินความนิยมตามภาษาโดยถ่วงน้ำหนักคะแนนกับค่าความคล้ายกันด้วย
- HN ไม่ให้คะแนนความคิดเห็น จึงใช้คะแนนความคิดเห็นไม่ได้
- Rust ทำได้ดี แต่ดูเหมือนจะยังไม่เป็นที่นิยมเท่าภาษาอื่น ๆ
- ค่า threshold ของความคล้ายกันอาจต้องปรับ ดังนั้นผลลัพธ์อาจผิดได้
เร่งความเร็วการคำนวณเชิงตัวเลขขนาดใหญ่ด้วย GPU
- คิวรีวิเคราะห์ใช้เวลา 10–30 วินาทีแม้บนเครื่อง 32 คอร์ จึงช้าเกินไปสำหรับการทดลองแบบโต้ตอบ
- หลังจากพิจารณาเรื่อง index และ preprocessing จึงเปลี่ยนมาใช้ GPU สำหรับการคำนวณเชิงตัวเลขแบบ vectorization
- CuPy และ cuDF ให้ API ที่คล้ายกับ NumPy และ pandas ตามลำดับ แต่รันบน GPU
- การพอร์ตค่อนข้างง่าย และเวลาคิวรีลดลงเหลือระดับไม่กี่ร้อยมิลลิวินาที
- เนื่องจากความเร็วเพียงพอแล้ว จึงไม่ได้ใช้กราฟ ANN
- ส่วนที่ยากคือการนำเมทริกซ์ embedding ขนาดใหญ่ขึ้นไปไว้บน GPU
- เมทริกซ์ embedding ของความคิดเห็นมีขนาด 30M x 512
- เป็นเรื่องยากที่จะเก็บเมทริกซ์มากกว่า 1 เท่าไว้ในหน่วยความจำระบบหรือหน่วยความจำวิดีโอ
- วิธีโหลดแบบง่ายอาจสร้างสำเนาหลายชุด
- อ่านไบต์จากดิสก์
- โหลดเป็นอาร์เรย์ NumPy
- แปลงเป็นอาร์เรย์ CuPy
- คัดลอกไปยัง GPU
- กระบวนการนี้อาจสร้างสำเนารวม 4 ชุด และในนั้น 3 ชุดอยู่ในหน่วยความจำ
- วิธีสุดท้ายคือทำ memory mapping เมทริกซ์บนดิสก์ จัดสรรเมทริกซ์ที่ยังไม่ถูก initialize ขนาดเท่ากันบน GPU ไว้ล่วงหน้า แล้วคัดลอกเป็น chunk
- วิธีนี้หลีกเลี่ยงการอ่านเข้าไปในหน่วยความจำของ Python ก่อน และใช้ system RAM กับ VRAM อย่างละพอดี 1 เท่า
เดโมและขั้นตอนถัดไป
- แอปเดโมให้บริการที่ hn.wilsonl.in
- หน้าหลักคือแผนที่และการค้นหา และสามารถเข้าถึงเครื่องมือชุมชนและวิเคราะห์ได้จากปุ่มมุมขวาบน
- URL ของชุมชนหรือผลการวิเคราะห์สามารถแชร์กับผู้อื่นได้ เพราะเก็บคิวรีไว้ใน URL
- ชุดข้อมูลเดโมสิ้นสุดอยู่ราววันที่ 10 เมษายน 2024 และไม่รวมโพสต์กับความคิดเห็นสดล่าสุด
- มีหลายไอเดียที่อยากสำรวจต่อไป
- ข้อมูลสดที่อัปเดตให้เป็นปัจจุบันอยู่เสมอ
- ระบบแนะนำที่ใช้ deep learning ซึ่งทำงานบนเว็บที่ HN คัดสรรไว้
- การปรับปรุงผลการค้นหาผ่านการฝึก reranker
- เส้นทางและการเดินทางที่น่าสนใจบนแผนที่
- การวิเคราะห์ความสัมพันธ์ที่คล้ายหรือขัดแย้งกันระหว่างผู้ใช้
- การวิเคราะห์ผู้ใช้ที่เชี่ยวชาญที่สุดใน niche เฉพาะ
- สามารถดู ข้อมูล และ โค้ด ทั้งหมดได้บน GitHub
2 ความคิดเห็น
ความเห็นบน Hacker News
ถือว่าน่าประทับใจมาก โดยเฉพาะเมื่อเป็นโปรเจ็กต์ที่ทำคนเดียว
กราฟวิเคราะห์อารมณ์ความรู้สึก ตามช่วงเวลาเด่นสะดุดตา และก็น่าสนใจเพราะนี่เป็นครั้งแรกที่ได้เห็น Rust ในมุมแบบนี้ เลยอยากรู้ว่าหัวข้อไหนเป็นบวกมากที่สุดเมื่อเวลาผ่านไป และมีหัวข้อไหนที่ร่วงลงแรงแบบฉับพลันบ้างไหม
ประโยคที่ว่า “ดูเหมือนว่า HN โดยรวมจะมีอารมณ์เชิงลบเยอะ” ก็ฟังดูตรงกับความรู้สึกที่มีต่อโซเชียลมีเดียเหมือนกัน ถ้าได้เห็นการเปรียบเทียบอารมณ์ความรู้สึกระหว่างแพลตฟอร์มโซเชียลมีเดียต่าง ๆ และแต่ละช่วงเวลาก็น่าจะน่าสนุก
ที่สังเกตเห็นอารมณ์เชิงลบเด่นชัด เป็นเพราะเดิมคาดหวังว่าจะได้เห็น กราฟอารมณ์ความรู้สึกที่ชัดเจนกว่าเดิม โดยทั่วไปคิดว่าน่าจะออกมากลาง ๆ ถึงเชิงบวก และกระโดดไปทางบวกใกล้โพสต์เชิงบวก หรือไปทางลบใกล้โพสต์เชิงลบ แต่กลับพบว่าแทบทุก query ให้อารมณ์ออกมาเป็นลบเกือบตลอดเวลา แม้แต่โพสต์เชิงบวกก็ดูเหมือนจะดึงความเป็นลบออกมาเยอะตามเกณฑ์ของโมเดลและวิธีการที่ใช้ ซึ่งก็อาจเป็นไปได้ว่าทั้งสองอย่างผิด เลยอยากไปขุดต่อในบล็อกโพสต์ถัดไป
https://openpipe.ai/blog/hn-ai-crypto
ไม่มีหลักฐานอะไร แต่รู้สึกว่าวิศวกรมักจะมีมุมวิจารณ์มากกว่า และเวลามีฟีดแบ็กเชิงบวกก็มักกด +1 มากกว่าจะพิมพ์ซ้ำ ๆ ส่วนคำวิจารณ์มักจะเขียนตรง ๆ มากกว่า :)
เป็นตัวอย่างที่ดีสำหรับคนที่ไม่คุ้นกับ data engineering/MLOps
ขอเสนอให้ใช้ HDBSCAN สร้างคลัสเตอร์แบบลำดับชั้นจากจุดต่าง ๆ แล้วใช้โมเดลสร้างชื่อคลัสเตอร์ย่อยภายใน แบบนี้จะช่วยให้สำรวจหัวข้อไปจนถึงระดับใบไม้ได้ง่ายขึ้น โดยค่อย ๆ แสดงหัวข้อย่อยตามความเชื่อมโยงกับโหนดปัจจุบัน
สีของกลุ่มควรชัดกว่านี้ และถ้ามีคลัสเตอร์ก็น่าจะช่วยได้ ขนาดตัวอักษรของแต่ละโพสต์ก็ควรเปลี่ยนตามความสำคัญ/ความเกี่ยวข้องเมื่อเทียบกับทั้งระบบหรือผลค้นหาปัจจุบัน ถ้ามีสรุปคลัสเตอร์ย่อยภายในมากขึ้น ก็อาจแทนหลายโพสต์ด้วยสรุปกลุ่มไปก่อนจนกว่าจะซูมเข้า เพื่อลดความแน่นของข้อความ
https://docs.rapids.ai/api/cuml/stable/api/#clustering / https://developer.nvidia.com/blog/faster-hdbscan-soft-cluste...
ข้อสังเกตเรื่องการทำให้แผนที่ชัดขึ้นก็ดีมาก และคิดว่ายังมีวิธีปรับปรุงง่าย ๆ ได้อีกหลายอย่าง อันนี้ก็เพิ่มเข้าไปในรายการสิ่งที่ต้องทำเหมือนกัน :)
ขอบเขตของโปรเจ็กต์ใหญ่จนน่าทึ่ง
แต่ดูเหมือนว่าไม่ว่าจะเป็น jina หรือ bge-3/flag ทั้ง embedding และ tokenizer จะยังไม่ค่อยเหมาะกับหัวข้อสายเทคนิคนัก คำธรรมดาในภาษาธรรมชาติพอใช้ได้ แต่พอค้นหาแนวคิดทางเทคนิคอย่าง “xaml” หรือ “simd” ระบบจะโน้มเอียงไปทางจับคำที่ฟังคล้ายกันหลังจาก tokenize อินพุตแล้ว
ในเชิงฟีดแบ็กแบบสร้างสรรค์ อยากให้มีวิธีไม่แสดงผล “อันดับ HN” แบบเดิมซ้ำ ๆ ตอนที่หัวข้อเฉพาะทางเกินไปจนไม่มีผลลัพธ์ เวลาค้นหาคำที่ embedding ไม่คุ้นเคยก็มักจะเจอ “Stephen Hawking has died” โผล่มาบ่อยมาก
ส่วนการวิเคราะห์อารมณ์ความรู้สึกก็ยังไม่แน่ใจว่าทำงานได้ดีแค่ไหน เพราะดูมีอารมณ์เชิงลบมากเกินจริง เช่นลองค้นหา “Mr Rogers” ซึ่ง HN น่าจะมองในแง่บวกอย่างท่วมท้น ก็ยังได้พีคเชิงลบรุนแรง หรือค้น “Carter” ก็มีพีคเชิงลบใหญ่มากที่เกี่ยวกับการเสียชีวิตของ Rosalynn Carter ทั้งที่โพสต์จริงเป็นการส่งลิงก์ที่พูดถึงสิ่งดี ๆ มากมายที่คู่สามีภรรยา Carter ทำไว้
ส่วน “ความนิยมตามเวลา” น่าจะต้องปรับด้วยคะแนนโหวตมัธยฐานของโพสต์ในเดือน/ปีนั้น ถ้าแค่พล็อตจำนวนโพสต์ เส้นแนวโน้มก็จะสูงขึ้นเรื่อย ๆ ดูคำว่า “diesel” แล้วจะเห็นว่าหมายถึงอะไร เพราะคำนี้พีคไปตั้งแต่ 10 ปีก่อนแล้ว หรืออีกทางอาจไม่ควรใช้คะแนนโพสต์ แต่ใช้ความถี่การเกิดของคีย์เวิร์ด หรือจำนวนรายการที่มีดัชนี cosine similarity กับ query ต่ำกว่า x แทนก็ได้
ฟีเจอร์ไดนามิกที่คลิกโพสต์เพื่อตัดออกแล้วคำนวณ ค่า threshold ของ similarity ใหม่ถือว่ายอดเยี่ยมมาก
มีเครื่องมือที่ยอดเยี่ยมซึ่งทำสิ่งเกือบเหมือนกันนี้ให้กับ dataset แบบไหนก็ได้: https://github.com/enjalot/latent-scope
แน่นอนว่าเพราะโปรเจ็กต์ต้นฉบับมีขนาดใหญ่มาก จึงมีความซับซ้อนที่น่าสนใจเพิ่มเข้ามาเยอะ และเครื่องมือนี้อาจจัดการกับสเกลขนาดนั้นไม่ได้ แต่สำหรับ dataset ขนาดกลางถือว่าเหมาะมาก
อยากลองวิเคราะห์ว่าบน HN มีการโปรโมตตัวเองเพิ่มขึ้นหรือไม่
ในที่นี้นิยามการโปรโมตตัวเองว่าเป็นโพสต์รูปแบบ “Show HN: I ...” ไม่ใช่ “Show HN: Something ...”
ตอนนี้ใน 100 อันดับบนสุด ตัวอย่างเช่น “Show HN: Exploring HN by mapping and analyzing 40M posts and comments for fun”, “Show HN: Browser-based knitting (pattern) software” ไม่ใช่หัวข้อโปรโมตตัวเอง เพราะประธานของแต่ละประโยคคือการสำรวจและซอฟต์แวร์
ในทางกลับกัน “Show HN: I built a non-linear UI for ChatGPT”, “Show HN: I created 3,800+ Open Source React Icons” เป็นหัวข้อโปรโมตตัวเอง เพราะประธานคือ “I”
ถ้าตรวจอย่างง่ายจากผลค้นหาของ Algolia โดยดูหัวข้อที่ขึ้นต้นด้วย “Show HN: I” แยกตามแต่ละปีที่เริ่มนับตั้งแต่ 1 เมษายน แล้วหารด้วยจำนวนผลลัพธ์ทั้งหมดของปีนั้น จากนั้นพล็อตเป็นกราฟ จะได้ดังนี้
2023 ****************************************
2022 ***********************************
2021 ***************************
2020 **************************************
2019 *************************
2018 *************
2017 *******
2016 **********
2015 ********
2014 ************
2013 *********************
2012 *****************
2011 *********
2010 ***
ตอนที่ฉันโตมา โดยทั่วไปเหมือนการโปรโมตตัวเองจะถูกมองว่าเป็นลักษณะนิสัยที่ไม่ดี เราถูกสอนว่าการกระทำควรเป็นตัวโปรโมตตัวเอง ไม่ใช่การเรียกร้องความสนใจโดยตรง แต่รู้สึกว่าวัฒนธรรมนั้นกำลังเปลี่ยนไป
ถ้าการโปรโมตตัวเองเพิ่มขึ้นจริง ก็สงสัยว่าเป็นผลจากโซเชียลมีเดียหรืออย่างอื่นหรือไม่ บน YouTube ก็รู้สึกว่ามีแนวโน้มคล้ายกัน แต่มีแค่ความรู้สึกว่ามีวิดีโอแนะนำที่ขึ้นต้นด้วย “I.....” เยอะขึ้น ยังไม่มีข้อมูลยืนยัน
พูดอีกอย่างคือ สิ่งที่คุณแบ่งเป็นโปรโมตตัวเองกับไม่โปรโมตตัวเอง สำหรับฉันคือกรณีที่หัวข้อแสดงชัดมากว่าเป็นการโปรโมตตัวเอง กับกรณีที่แสดงชัดน้อยกว่าเท่านั้น แต่เพราะวลี “Show HN” เองดูเหมือนจะใช้เฉพาะกับการโปรโมตตัวเองอยู่แล้ว คนที่รู้ธรรมเนียมนี้ก็น่าจะรู้ได้ว่าเป็นการโปรโมตตัวเองแม้ไม่มี “I”
ทั้งหมดก็มีความหมายประมาณว่า “ดูสิ ฉันสร้างของเจ๋ง ๆ มา คุณคิดว่าไง?”
เช่น มีการพูดว่าชาวเยอรมันตำหนิ Einstein ว่าโปรโมตตัวเอง ขณะที่อเมริกามีวัฒนธรรมคนดังอยู่แล้ว อาจเป็นปรากฏการณ์ที่เกิดเป็นวัฏจักรก็ได้
นี่น่าจะเป็นโพสต์ที่เจ๋งที่สุดที่ฉันเห็นบน HN ในปีนี้แบบทิ้งห่าง
ตอนแรกที่ดูยังไม่ชัด แต่ตัวแอปจริงอยู่ที่นี่: https://hn.wilsonl.in/
ไม่ได้ประชดนะ คิดว่าเป็นไอเดียที่ดี
เลยสงสัยว่าฝังความหมายของข้อความค้นหาแบบแยกเป็นรายคำหรือเปล่า และใช้โมเดลเดียวกับที่ใช้กับเอกสารไหม ฉันลองค้น “lead generation” ซึ่งถ้าเป็น embedding ของหลายคำที่ทำงานถูกต้องก็น่าจะเข้าใจได้ แต่กลับได้ผลลัพธ์เรื่องพิษตะกั่ว
คำแนะนำสมัยใหม่ของ UMAP คือ Parametric UMAP: https://umap-learn.readthedocs.io/en/latest/parametric_umap....
มันฝึก Keras MLP ขนาดเล็กเพื่อย่อมิติลงมาเป็น 2 มิติ โดยทำให้ UMAP loss ต่ำที่สุด ข้อดีคือโมเดลนี้มีขนาดเล็ก จึงบันทึกและนำกลับมาใช้ซ้ำเพื่อทำนายกับข้อมูลใหม่ที่ไม่เคยเห็นมาก่อนได้ ขณะที่โมเดล UMAP แบบดั้งเดิมที่ฝึกแล้วมีขนาดใหญ่ นอกจากนี้ยังใช้ GPU จึงในทางทฤษฎีแล้วฝึกได้เร็วกว่าเยอะ
ข้อเสียคือ implementation ใน Python UMAP package ยังไม่ค่อยดีนัก และมันสร้างชุดข้อมูลโหนด/ขอบแบบขยายทั้งหมดแล้วส่งเข้า GPU เลย ทำให้ฝึกได้เพียงราว 100,000 embedding ก่อนจะเจอปัญหา out-of-memory
pipeline แบบไม่มีผู้สอนทั้งหมดที่ต่อกันเป็น UMAP → HDBSCAN → AI cluster labeling มีประโยชน์มากจนทำให้อยากลองสร้าง Parametric UMAP implementation ที่ขยายได้ดีกว่านี้
สำหรับสิ่งที่ดูเหมือนโปรเจ็กต์งานอดิเรกเชิงสำรวจ นี่เป็นงานที่ใหญ่อย่างน่าประหลาดใจ
ไม่ได้จะลดทอนความสำเร็จนะ มันเจ๋งมากจริง ๆ แต่ก็แปลกใจที่ต้องลงแรงกันขนาดนี้
แค่การคำนวณ embedding ก็ใช้ GPU 150 ตัว แล้ว และยังพัฒนาระบบคัสตอมสองตัวคือ db-rpc และ queued สำหรับการสื่อสารระหว่างเซิร์ฟเวอร์ด้วย
นอกจากนี้ยังมีงานประกอบและการคำนวณอีกมากมาย
เลยสงสัยถึงบริบทของโปรเจ็กต์นี้ รวมถึงอยากรู้ว่าหาเงินทุนและเวลาเพื่อทำงานวิจัยแบบนี้ได้อย่างไร
จากมุมของคนที่เคยทำงานลักษณะคล้าย ๆ กันในเชิงอาชีพมาเยอะ ผมเคยทำแผนที่ภูมิทัศน์ของงานวิชาการและสิทธิบัตรมาก่อน แต่ก็ไม่แน่ใจว่า GPU 150 ตัว จำเป็นจริงไหม
ถ้าสุดท้ายคือการฉายลง 2 มิติและการทำคลัสเตอร์ วิธีแบบดั้งเดิมอย่าง bag-of-words หรือ topic modeling น่าจะง่ายกว่า ถูกกว่า และแทบไม่รู้สึกถึงความต่างด้านคุณภาพ
แม้จะใช้กราฟของผู้เขียนและเธรดความคิดเห็น ก็อาจได้ผลลัพธ์คล้ายกัน
GPU ถูกอย่างน่าประหลาดใจ และส่วนใหญ่ที่ขยายสเกลก็เพราะผมใจร้อนไม่ค่อยอยากรอ :) ทั้งคลัสเตอร์ก็รันอยู่แค่ไม่กี่ชั่วโมง
ถ้ามีลิงก์ไปยังงานที่คุณเคยทำ อยากดูมากเลย น่าสนใจจนอยากอ่านต่อ
ในแง่เงินทุนและเวลา เป็นไปได้ว่าเขาอยู่ในช่วงคั่นระหว่างงานถัดไปหรือระหว่างงานประจำ และประสบความสำเร็จทางการเงินจากอาชีพหรือธุรกิจก่อนหน้านี้ จึง ออกทุนเอง ทำโปรเจ็กต์นี้
การใช้ GPU ก็ดูมีประสิทธิภาพมาก ค่าใช้จ่ายจึงน่าจะไม่ได้สูงมากนัก
สามารถนำ แมชชีนเลิร์นนิงแบบคลาสสิก อย่าง SVM ที่ทำ probability calibration มาใช้กับ embedding ลักษณะนี้ เพื่อให้ได้ผลดีทั้งในการจำแนกและการทำคลัสเตอร์ และยังเร็วกว่า fine-tuning LLM มากกว่า 100 เท่า
ปกติแล้วจะไม่ทำแบบเดโมนี้ แต่จะ normalize เวกเตอร์แทน
เมื่อใช้เวกเตอร์ที่ normalize แล้ว ระยะทางแบบ Euclidean จะวัดระยะระหว่างปลายของเวกเตอร์สองตัว ขณะที่ระยะทางแบบ cosine จะวัดความยาวของการฉายเวกเตอร์หนึ่งลงบนอีกเวกเตอร์หนึ่ง
เวกเตอร์ 2 มิติที่ normalize แล้ว ในความเป็นจริงเทียบได้กับเวกเตอร์ 1 มิติเท่านั้น ถ้าต้องการแสดงความสัมพันธ์แบบ 2 มิติ ก็ต้องใช้เวกเตอร์ 3 มิติแทนเพื่อให้กลับมามีอิสระ 2 องศา
ดูเหมือนว่าชื่อเรื่องจะหายไปนะ