23 คะแนน โดย GN⁺ 4 일 전 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • ทำ text embedding และ similarity search ภายในเบราว์เซอร์โดยไม่ต้องเรียกเซิร์ฟเวอร์ ช่วยสร้างการค้นหาเชิงความหมาย (Semantic search) ได้อย่างรวดเร็ว
  • ทำงานด้วย CPU เท่านั้น โดยไม่ต้องใช้ GPU; แพ็กเกจพื้นฐานรวมเอนจินและ weights แล้วมีขนาด 7MB ส่วน mini มีขนาด 5MB
  • รวมเอนจิน·โมเดล·BERT tokenizer ไว้ในไฟล์ .wasm เดียว จึงไม่ต้องมี postinstall หรือ runtime fetch
  • ทำ distillation จากโมเดล all-MiniLM-L6 โดยใช้ quantization-aware training (QAT) แบบ BitNet b1.58
  • หลังติดตั้ง @ternlight/base แล้ว import embed, similar ก็สร้าง flow สำหรับ semantic search ได้ด้วยโค้ดเพียง 3 บรรทัด
  • บีบอัดได้ 30× โดยเสียความแม่นยำเพียงเล็กน้อย ทำให้ทำ การค้นหาแบบ privacy-preserving·ออฟไลน์·edge ได้โดยไม่ต้องรับส่งข้อมูลผ่านเครือข่าย

ternlight - embedding ที่จบในเบราว์เซอร์

  • เครื่องมือ semantic embedding ที่มาเป็น WebAssembly bundle ขนาด 5–7MB โดยบรรจุ เอนจิน + โมเดล + tokenizer ไว้ในไฟล์เดียว และทำ embedding search บน CPU
  • embed ข้อความได้ในระดับมิลลิวินาทีและไม่เรียกเซิร์ฟเวอร์
  • มีสอง tier ด้วย API เดียวกัน เลือกได้ตาม trade-off ระหว่างขนาด/คุณภาพ
    • @ternlight/base — tier คุณภาพ (7MB wire, ประมาณ 5ms ต่อ embed)
    • @ternlight/mini — tier ขนาดเล็ก·ความเร็วสูง (5MB wire, ประมาณ 2.5ms ต่อ embed)

API หลัก

  • primitive เดียว: string → คืนค่า Float32Array ขนาด 384 มิติที่ทำ L2 normalization แล้ว
  • มีสามฟังก์ชัน embed, cosineSim, similar
    • cosineSim(embed('reset my password'), embed('I forgot my password')) → 0.88
    • similar รองรับการค้นหา nearest neighbor บน corpus และกำหนด topK ได้
      • ตัวอย่าง: เมื่อค้นหา 'I want my money back' จะคืน 'Refunds: how to get your money back' (sim 0.70), 'Update your billing address' (sim 0.24)
  • ทำงานบน Node ≥ 18, เบราว์เซอร์ (ผ่าน bundler), Cloudflare Workers, Vercel Edge, Deno, Bun และ route อัตโนมัติไปยัง loader ที่เหมาะกับแต่ละ environment

หลักการออกแบบ

  • distill จาก all-MiniLM-L6 และใช้ quantization-aware training สไตล์ BitNet b1.58 พร้อมตัวเลือกการออกแบบ 3 อย่างเพื่อให้ได้ขนาดระดับไม่กี่ MB
  • Ternary weights

    • weights ทั้งหมดเป็นค่าใดค่าหนึ่งใน -1, 0, +1 ทำให้ inference ประมวลผลด้วยการบวกและลบ
    • โมเดลถูกเทรนเป็น ternary model ตั้งแต่ต้นเพื่อรักษาคุณภาพ
  • One bundle

    • รวมโมเดล + BERT tokenizer + เอนจินไว้ใน .wasm ไฟล์เดียว
    • ไม่มีขั้นตอน postinstall หรือ runtime fetch
  • SIMD inference engine

    • Rust ที่เขียนด้วยมือถูกคอมไพล์เป็น WASM SIMD
    • การบวก/ลบใช้ประโยชน์จาก คำสั่ง vector ของ CPU

ตัวชี้วัดประสิทธิภาพ

  • ตัวเลขทั้งหมดวัดจาก int4 build ที่ปล่อยออกมาแล้ว (Mac ซีรีส์ M, Node/V8)
  • @ternlight/mini

    • Wire size (gzip wasm): 5.0MB, Latency (p50): 2.5ms
    • Throughput (single thread): ประมาณ 400 emb/s
    • Spearman (เทียบกับ teacher): 0.820, Retrieval (SciFact NDCG@10): 0.439
    • สถาปัตยกรรม: 2-layer · d_model=256 · 4 heads, พารามิเตอร์ประมาณ 9.5M
  • @ternlight/base

    • Wire size (gzip wasm): 7.2MB, Latency (p50): 5.1ms
    • Throughput (single thread): ประมาณ 195 emb/s
    • Spearman (เทียบกับ teacher): 0.844, Retrieval (SciFact NDCG@10): 0.465
    • สถาปัตยกรรม: 2-layer · d_model=384 · 6 heads, พารามิเตอร์ประมาณ 15.4M
  • สเปกร่วม

    • เอาต์พุต: 384 มิติ ทำ L2 normalization แล้ว
    • อินพุตสูงสุด: 128 tokens (ประมาณ 95 คำ)
    • Quantization: ternary weights · int4 embeddings

การใช้งาน embedding บนอุปกรณ์

  • Search-as-you-type

    • แสดงผลลัพธ์ก่อนที่ผู้ใช้จะพิมพ์เสร็จ เร็วกว่าการรับส่งข้อมูลผ่านเครือข่ายทุกแบบ
  • แอปที่ไวต่อความเป็นส่วนตัว

    • query และเอกสารไม่ออกจากอุปกรณ์ จึงไม่มีสัญญาการประมวลผลข้อมูลหรือความเสี่ยงข้อมูลรั่วไหล
  • แอปแบบ offline-first

    • ส่วนขยายเบราว์เซอร์, Obsidian plugin, แอปเดสก์ท็อป
  • แอปบน edge runtime

    • บน Cloudflare Workers, Deno Deploy, Vercel Edge การทำ embedding ถูกวางไว้ตำแหน่งเดียวกับ request handler จึงไม่ต้องเรียก inference service แยก
  • อุปกรณ์ edge และฮาร์ดแวร์ IoT

    • Raspberry Pi, คอมพิวเตอร์บอร์ดเดี่ยว, industrial gateways, kiosks
    • การบวก/ลบทำงานได้อย่างมีประสิทธิภาพบน ARM cores จึงไม่ต้องใช้ GPU หรือ NPU
  • Static sites

    • บน Jekyll, Hugo, Astro สามารถ deploy โมเดลไปพร้อมกับ bundle และให้ semantic search ทำงานได้โดยไม่ต้องมี backend

การติดตั้งและตัวอย่างการใช้งาน

  • มาในรูป npm package เดียว ใช้งานได้โดยไม่ต้องมีขั้นตอนดาวน์โหลดโมเดลแยกหรือเซิร์ฟเวอร์
  • คำสั่งติดตั้งมีดังนี้
    npm install @ternlight/base  
    
  • import embed, similar จาก @ternlight/base เพื่อรันการค้นหาแบบอิงความหมาย
    import { embed, similar } from '@ternlight/base';  
    
    similar('easy weeknight dinner ideas', recipes, { topK: 3 });  
    // → ranked matches · ~5 ms · zero network  
    

โอเพนซอร์สพื้นฐานและไลเซนส์

  • BitNet b1.58 (Ma et al., Microsoft Research, 2024) — งานวิจัยสถาปัตยกรรมสำหรับการเทรน ternary weights
  • bitlinear — implementation อ้างอิงของ BitLinear บน PyTorch ใช้โดยตรงระหว่างการเทรน (bitlinear==2.4.6) และ Rust inference engine สะท้อน forward propagation operation ตามนั้น
  • sentence-transformers/all-MiniLM-L6-v2 — teacher model ที่ student model ถูก distill มา
  • ไลเซนส์: MIT

1 ความคิดเห็น

 
GN⁺ 4 일 전
ความคิดเห็นบน Hacker News
  • อยากลองรัน โมเดลที่ใช้งานได้จริงในเบราว์เซอร์ เป็นโปรเจกต์งานอดิเรก เลย distill ตัวเข้ารหัสประโยคขนาดเล็กจาก MiniLM และใช้การฝึกแบบรับรู้การ quantization แบบ ternary
    เขียน inference engine เองด้วย แล้วปล่อยเป็น Rust → WASM SIMD
    มันไม่ใช่ LLM แต่เป็นโมเดล embedding ดังนั้นเมื่อใส่ข้อความเข้าไปจะได้เวกเตอร์ 384 มิติ และใช้ cosine similarity ของเวกเตอร์สองตัวเพื่อตัดสินความเกี่ยวข้องของข้อความ ตัวอย่างเช่น "reset my password" กับ "I forgot my password" จะได้ประมาณ 0.88
    ใช้ได้กับการค้นหาเชิงความหมาย การจับคู่ FAQ/intent และการจัดกลุ่ม และเพราะรันในอุปกรณ์ จึงค้นหาเชิงความหมายได้เร็วทันทีหลังป้อนข้อมูลโดยไม่ต้องพึ่ง API
    เดโมค้นหาเอกสาร React 2,000 รายการทั้งหมดในอุปกรณ์: https://ternlight-demo.vercel.app
    บน npm มี 2 ระดับคือ @ternlight/base (7MB, ประมาณ 5ms ต่อ embedding, embedding คุณภาพดีกว่า) และ @ternlight/mini (ขนาดส่งผ่าน 5MB, ประมาณ 2.5ms ต่อ embedding) และ bundle ไว้สำหรับ Node กับเบราว์เซอร์
    repo มีรายละเอียดทางเทคนิค ไลเซนส์ MIT และ pipeline การฝึก: https://github.com/soycaporal/ternlight
    อยากรู้ว่า embedding ในอุปกรณ์มีประโยชน์จริงไหม และมี use case แบบไหนบ้าง

    • มีพจนานุกรมที่แมปคำกับแท็ก OpenStreetMap อยู่ หน้าตาประมาณ https://codeberg.org/cartes/web/src/branch/master/components...
      อยากรู้ว่าจะช่วยให้ผู้ใช้พิมพ์ "pancake" แล้วหา crêpe เจอได้ไหม โดยไม่ต้องเขียนรายการพจนานุกรมแบบชัดเจนว่า "pancake = crêpe"
      ถ้าเข้าใจถูก โครงสร้างคือไลบรารีจะดาวน์โหลด 5MB หนึ่งครั้งตอนแรก แล้วหลังจากนั้นก็ใช้งานเหมือนตอนนี้ที่ใช้ Fuse.js ใช่ไหม
      อยากรู้ด้วยว่าจัดการภาษาที่ไม่ใช่อังกฤษได้ดีแค่ไหน และเอาไปฝึกด้วย OpenStreetMap tag wiki ได้ไหม
    • สนใจมากจริง ๆ กับการใส่ semantic search แบบง่าย ๆ ลงในแอปเดสก์ท็อปแบบ native
      อยากรู้ว่ามีการเทียบกับโมเดล embedding ขนาดจิ๋วตัวอื่นไหม ยากจะตัดสินว่าเหตุผลที่เริ่มจาก MiniLM-L6 เป็นเพราะมันเป็นโมเดลที่ดีเป็นพิเศษในระดับเดียวกันหรือไม่ เพราะตัวชี้วัดที่ให้มามีแค่ "Retrieval (SciFact NDCG@10)"
      แต่ก็ยังต่างจากประสิทธิภาพที่อ้างไว้พอสมควร บน Firefox ของ i5-4570 ได้แค่ 35 embeddings ต่อวินาที ไม่ใช่ 400 ต่อวินาที สงสัยว่าอาจตกไปใช้ path ที่ไม่ใช่ SIMD และตั้งใจจะลอง native Rust binary ด้วย
    • เพิ่ง embedding เอกสาร django ทั้งหมด กับ knowledge base ภายในบริษัท ทำให้ค้นหาจากสองแหล่งได้ทันทีแล้ว
  • เจ๋งดี แต่ถ้ามีปุ่มเริ่มเดโมบน landing page น่าจะดีมาก เปิดเว็บปุ๊บได้ยิน เสียงพัดลมหมุนอย่างบ้าคลั่ง เลยค่อนข้างตกใจ

    • เห็นด้วย ขณะเดียวกันก็รู้สึกดีที่ทำให้นึกถึงยุคที่แค่ฟังเสียงคอมพิวเตอร์ก็รู้ได้อย่างคุ้นเคยว่าตอนนี้กำลังเกิดอะไรขึ้น
    • ตกใจที่พัดลมเริ่มหมุน แต่บางทีเครื่องปิ้งขนมปังก็ทำให้ตกใจเหมือนกัน
    • ใช้ CPU cycle แบบสุดขีด ใครบอกว่า GPU เท่านั้นที่พิเศษกันนะ
  • น่าจะทำเป็นปลั๊กอินสำหรับ Astro หรือ meta-framework ทั่วไป ให้ parse ไฟล์ HTML ที่สร้างทั้งหมดโดยอัตโนมัติแล้วสร้าง ฐานข้อมูล embedding ขนาดเล็ก
    ฝั่ง frontend สามารถ lazy-load สิ่งนี้ได้ และอาจเก็บ HNSW เป็น chunk เพื่อโหลดเฉพาะส่วนที่จำเป็นต่อ query การค้นหา
    ตัวอย่างเช่นคล้าย https://pagefind.app/ แต่เป็นรูปแบบที่ให้ static vector search แบบเต็มตัว

    • สำหรับ static site generator ของเราอยากใช้ sqlite-vec แต่ตอนเช็กล่าสุดดูเหมือนยังไม่ได้ implement HNSW หรือไม่ก็การรองรับ vector search ในเบราว์เซอร์ยังไม่ดีนัก น่าจะยัง scan ทั้งตารางอยู่
      ผ่านมาหลายเดือน หลายปีแล้ว ถ้ายังเป็นแบบนั้นก็ถือว่าค่อนข้างน่าผิดหวัง เพราะดูเหมือนเป็นสัญญาณว่าขาดความสามารถในการทำโปรเจกต์ให้เสร็จจริง ๆ ถึงขั้นที่ผมเคยเสนอโปรเจกต์นั้นเป็นผู้สมัครที่ดีสำหรับทุนที่ผมสมัคร แต่ฝั่งนั้นได้ทุน ส่วนผมไม่ผ่าน
      ถ้าใครรู้วิธีแก้ดี ๆ ในพื้นที่นี้ หรือถ้าผมเข้าใจ SQLite-vec ผิด ช่วยบอกที สำหรับ SSG ของเรา ตอนนี้เกือบตัดสินใจแล้วว่าจะไปทำโครงสร้างพื้นฐานอื่นสักสองสามเดือนก่อน แล้วถ้ายังไม่เสร็จก็จะทำเอง
  • น่าจะเป็นฟีเจอร์เสริมที่เจ๋งมากสำหรับโปรเจกต์ค้นหา DuckDB HNSW ที่เคยเห็นที่นี่: https://github.com/jasonjmcghee/portable-hnsw
    จุดที่น่าสนใจมากคือการค้นหาเกิดขึ้นกับไฟล์ Parquet ที่โฮสต์แบบ static โดยใช้ HTTP range requests
    มองว่าสิ่งเหล่านี้อาจเติบโตเป็นระบบนิเวศการค้นหาที่ค่อนข้างเปิดและกระจายศูนย์ ซึ่งไม่ได้ถูกควบคุมโดยบริษัทใหญ่

    • ไอเดียคล้ายกันคือการใช้ SQLite DB บน static host ผ่าน HTTP range requests กับ WASM ก็น่าสนใจได้
      https://news.ycombinator.com/item?id=27016630
    • ไอเดียเจ๋งมาก ชอบ format ที่ client สามารถสำรวจได้ผ่าน range requests และ static hosting จริง ๆ
  • อันนี้เจ๋งจริง และอาจเป็นชิ้นส่วนที่ขาดไปของสิ่งที่เคยอยากสร้าง
    ถ้าใช้ https://github.com/npiesco/absurder-sql จะสามารถ persist corpus ต้นฉบับทั้งหมดไว้ใน IndexedDB/SQLite ภายในเบราว์เซอร์ได้
    จากนั้นก็ใช้ Ternlight ทำ การสร้าง embedding และ cache ตามต้องการ โดยไม่ต้อง index ทุกอย่างล่วงหน้า เหมือน https://weaviate.io/blog/chunking-strategies-for-rag
    แบบนั้นก็จะทำ hybrid search ได้ด้วย Reciprocal Rank Fusion ที่รวม FTS5/BM25 ของ SQLite แบบ native กับ semantic search ของ Ternlight

  • ทำได้ดีมาก
    โปรโมตว่า 7MB แต่ก็มีเวอร์ชัน mini 5MB ด้วย
    ดูเหมือน mini จะใช้ เวกเตอร์ 256 องค์ประกอบ ภายในแทน 384 เพื่อลดพื้นที่ แล้วค่อย project กลับเป็น 384 ตอนท้ายเพื่อความเข้ากันได้
    ขนาดลดลงหนึ่งในสาม แต่ loss ไม่ได้เป็นเส้นตรง ดังนั้นแม้ใช้ data path ที่เล็กกว่า ก็ดูเหมือนสูญเสียข้อมูลน้อยกว่าหนึ่งในสาม

  • โปรเจกต์เจ๋งมาก
    เคยลองทำสิ่งคล้ายกันมาก่อน: http://sol.quipu-strands.com/
    อยากโหลดโมเดล embedding ในเบราว์เซอร์ แล้วจัดเรียงข้อความตามความหมาย
    ดึง ONNX weights (MPNet, MiniLM) จาก HuggingFace ใช้ Transformers.js ทำ embedding แล้วใช้ตัวจัดกลุ่มของ scikit-learn ที่รันผ่าน pyodide ในหน้าเว็บ ทั้งหมดทำงานฝั่ง client และแปลกใจที่มันทำงานได้สมบูรณ์แบบ

  • เดโมทำงานค่อนข้างแปลก ตัวอย่างเช่นถ้าค้นหา "how to use typescript with createContext" ผลลัพธ์อันดับต้น ๆ มีแต่ รายการ typescript เลยดูเหมือน similarity search ล้มเหลว

  • ขอบคุณ โมเดล local จะนำ ความเป็นส่วนตัว มาให้ในสักวัน และผมก็รู้ use case ที่ยอดเยี่ยมซึ่งเหมาะกับโมเดล embedding เล็ก ๆ แบบนี้อยู่แล้ว คือการค้นหาราคาถูกและรวดเร็วในฐานข้อมูลสินค้า
    ในกรณีของผม การพึ่ง CPU ก็เป็นข้อดีด้วย

    • ดีเลย ถ้ามีวิธีให้ช่วยสนับสนุน หรือมี use case เฉพาะที่ควรกล่าวถึงใน roadmap ก็บอกได้เลย
  • สามารถทำ การสร้าง embedding ที่ใช้เวลา 30 วินาทีไว้ล่วงหน้าแล้วส่งไปยังเบราว์เซอร์ได้ไหม
    หลังจากนั้น inference ก็เร็วและดี

    • ได้ รัน indexing ฝั่ง server เพียงครั้งเดียว แล้วส่งเฉพาะ embeddings ไปยัง frontend ก็พอ