Ternlight - โมเดล embedding ขนาด 7MB ที่ทำงานในเบราว์เซอร์ (WASM)
(ternlight-demo.vercel.app)- ทำ 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แล้ว importembed,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,similarcosineSim(embed('reset my password'), embed('I forgot my password'))→ 0.88similarรองรับการค้นหา 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 ตั้งแต่ต้นเพื่อรักษาคุณภาพ
- weights ทั้งหมดเป็นค่าใดค่าหนึ่งใน
-
One bundle
- รวมโมเดล + BERT tokenizer + เอนจินไว้ใน
.wasmไฟล์เดียว - ไม่มีขั้นตอน postinstall หรือ runtime fetch
- รวมโมเดล + BERT tokenizer + เอนจินไว้ใน
-
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 ความคิดเห็น
ความคิดเห็นบน 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 แบบไหนบ้าง
อยากรู้ว่าจะช่วยให้ผู้ใช้พิมพ์ "pancake" แล้วหา crêpe เจอได้ไหม โดยไม่ต้องเขียนรายการพจนานุกรมแบบชัดเจนว่า "pancake = crêpe"
ถ้าเข้าใจถูก โครงสร้างคือไลบรารีจะดาวน์โหลด 5MB หนึ่งครั้งตอนแรก แล้วหลังจากนั้นก็ใช้งานเหมือนตอนนี้ที่ใช้ Fuse.js ใช่ไหม
อยากรู้ด้วยว่าจัดการภาษาที่ไม่ใช่อังกฤษได้ดีแค่ไหน และเอาไปฝึกด้วย OpenStreetMap tag wiki ได้ไหม
อยากรู้ว่ามีการเทียบกับโมเดล embedding ขนาดจิ๋วตัวอื่นไหม ยากจะตัดสินว่าเหตุผลที่เริ่มจาก MiniLM-L6 เป็นเพราะมันเป็นโมเดลที่ดีเป็นพิเศษในระดับเดียวกันหรือไม่ เพราะตัวชี้วัดที่ให้มามีแค่ "Retrieval (SciFact NDCG@10)"
แต่ก็ยังต่างจากประสิทธิภาพที่อ้างไว้พอสมควร บน Firefox ของ i5-4570 ได้แค่ 35 embeddings ต่อวินาที ไม่ใช่ 400 ต่อวินาที สงสัยว่าอาจตกไปใช้ path ที่ไม่ใช่ SIMD และตั้งใจจะลอง native Rust binary ด้วย
เจ๋งดี แต่ถ้ามีปุ่มเริ่มเดโมบน landing page น่าจะดีมาก เปิดเว็บปุ๊บได้ยิน เสียงพัดลมหมุนอย่างบ้าคลั่ง เลยค่อนข้างตกใจ
น่าจะทำเป็นปลั๊กอินสำหรับ Astro หรือ meta-framework ทั่วไป ให้ parse ไฟล์ HTML ที่สร้างทั้งหมดโดยอัตโนมัติแล้วสร้าง ฐานข้อมูล embedding ขนาดเล็ก
ฝั่ง frontend สามารถ lazy-load สิ่งนี้ได้ และอาจเก็บ HNSW เป็น chunk เพื่อโหลดเฉพาะส่วนที่จำเป็นต่อ query การค้นหา
ตัวอย่างเช่นคล้าย https://pagefind.app/ แต่เป็นรูปแบบที่ให้ static vector search แบบเต็มตัว
ผ่านมาหลายเดือน หลายปีแล้ว ถ้ายังเป็นแบบนั้นก็ถือว่าค่อนข้างน่าผิดหวัง เพราะดูเหมือนเป็นสัญญาณว่าขาดความสามารถในการทำโปรเจกต์ให้เสร็จจริง ๆ ถึงขั้นที่ผมเคยเสนอโปรเจกต์นั้นเป็นผู้สมัครที่ดีสำหรับทุนที่ผมสมัคร แต่ฝั่งนั้นได้ทุน ส่วนผมไม่ผ่าน
ถ้าใครรู้วิธีแก้ดี ๆ ในพื้นที่นี้ หรือถ้าผมเข้าใจ SQLite-vec ผิด ช่วยบอกที สำหรับ SSG ของเรา ตอนนี้เกือบตัดสินใจแล้วว่าจะไปทำโครงสร้างพื้นฐานอื่นสักสองสามเดือนก่อน แล้วถ้ายังไม่เสร็จก็จะทำเอง
น่าจะเป็นฟีเจอร์เสริมที่เจ๋งมากสำหรับโปรเจกต์ค้นหา DuckDB HNSW ที่เคยเห็นที่นี่: https://github.com/jasonjmcghee/portable-hnsw
จุดที่น่าสนใจมากคือการค้นหาเกิดขึ้นกับไฟล์ Parquet ที่โฮสต์แบบ static โดยใช้ HTTP range requests
มองว่าสิ่งเหล่านี้อาจเติบโตเป็นระบบนิเวศการค้นหาที่ค่อนข้างเปิดและกระจายศูนย์ ซึ่งไม่ได้ถูกควบคุมโดยบริษัทใหญ่
https://news.ycombinator.com/item?id=27016630
อันนี้เจ๋งจริง และอาจเป็นชิ้นส่วนที่ขาดไปของสิ่งที่เคยอยากสร้าง
ถ้าใช้ 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 ก็เป็นข้อดีด้วย
สามารถทำ การสร้าง embedding ที่ใช้เวลา 30 วินาทีไว้ล่วงหน้าแล้วส่งไปยังเบราว์เซอร์ได้ไหม
หลังจากนั้น inference ก็เร็วและดี