- WASTE คือเอนจิน inference ที่เขียนด้วยภาษา C ซึ่งแปลงโมเดล open weight Kimi K3 แบบเต็มขนาด 2.78 ล้านล้านพารามิเตอร์โดยไม่ลดขนาด ให้เป็นคอนเทนเนอร์ 982GiB และรันบนโน้ตบุ๊กผู้บริโภคได้
- เก็บเฉพาะ trunk ที่ต้อง resident ของโมเดลไว้ในหน่วยความจำ และอ่าน น้ำหนักของ expert ประมาณ 4% ที่ถูกเปิดใช้งานต่อโทเคนจาก NVMe ส่วน RAM ที่เหลือใช้เป็นแคช expert ขนาดจำกัด
- Kimi K3 เปิดได้ด้วย RAM ขั้นต่ำ 29.05GB ที่ context 4K แต่คอนฟิกที่ใช้งานได้จริงคือ budget 46GB บน MacBook Pro 64GB ซึ่งทำได้ 0.45~0.62 tok/s
- ซ้อนทับการอ่าน expert กับการคำนวณเพื่อปรับปรุงประมาณ 1.6 เท่า และรัน router ของเลเยอร์ถัดไปล่วงหน้า 1 residual เพื่อเพิ่ม cache hit rate จาก 14% เป็น 38% โดยไม่เปลี่ยนปริมาณการอ่านรวมและ logits
- สามารถรันโมเดลขนาดใหญ่มากในเครื่องได้โดยไม่ต้องเชื่อมต่ออินเทอร์เน็ต ไม่มีค่าใช้จ่ายต่อโทเคน และไม่ส่งข้อมูลออกนอกเครื่อง แต่ต้องใช้ NVMe ภายในและพื้นที่เก็บข้อมูลประมาณ 1TB และถ้า budget RAM ตั้งแต่ 52GB ขึ้นไป อาจช้าลงอย่างรุนแรงจากการ paging ของระบบปฏิบัติการ
เป้าหมายและรูปแบบการใช้งานของ WASTE
- WASTE(Weight-Aware Streaming Tensor Engine) คือเอนจิน inference ภาษา C แบบฝังได้ที่ไม่มี dependency ภายนอกตอน runtime
- ใช้แค่
libwaste.aและไฟล์ executablewasteโดยไม่ต้องใช้ BLAS, CUDA, ONNX, Python นอกจาก libc และ pthreads - Python ใช้เฉพาะการแปลงโมเดลและการตรวจสอบเทียบกับ PyTorch เท่านั้น ไม่อยู่ในเส้นทาง inference
- public API ประกอบด้วย 26 ฟังก์ชัน รองรับการเปิดโมเดล ตั้งค่าเพดาน RAM สร้างผลลัพธ์ บันทึก session และปิดการทำงาน
- ใช้แค่
- เป้าหมายที่ตรวจสอบในปัจจุบันคือโมเดล Kimi K3 2.78T แบบเต็ม
- ต้นฉบับสาธารณะมีขนาด 1.42TB และคอนเทนเนอร์หลังแปลงมีขนาด 982GiB
- ไม่ใช่เวอร์ชัน distilled, pruned หรือย่อขนาด
- Kimi-Linear 48B ก็ใช้เอนจินและฟอร์แมตเดียวกัน โดยเป็นคอนเทนเนอร์ 19GiB, ใช้ RAM ขั้นต่ำ 1.87GB และทำได้ 10.7 tok/s
- ชื่อโปรเจกต์มาจากเป้าหมายในการลดสถานการณ์ที่นำโมเดลซึ่งรันได้บนฮาร์ดแวร์บนโต๊ะไปประมวลผลในศูนย์ข้อมูลคลาวด์ พร้อมเสียทั้งค่าโทเคนและพลังงาน
โครงสร้างการสตรีมจากดิสก์
- K3 ซึ่งเป็นโครงสร้าง Mixture of Experts เปิดใช้งานโมเดลเพียงประมาณ 4% ต่อโทเคน จึงจัดวางน้ำหนักที่ไม่ได้ใช้งานให้เข้าถึงได้เมื่อจำเป็น โดยไม่ต้อง resident ไว้ใน RAM
- คอนเทนเนอร์
.wasteประกอบด้วย JSON manifest, trunk ที่ resident และ expert bank แยกตามเลเยอร์- record ของ expert แต่ละรายการถูกจัดแนวที่ 4KiB
- วางเมทริกซ์ gate·up·down ไว้ติดกัน เพื่ออ่าน expert หนึ่งตัวได้พอดีด้วย
preadเพียงครั้งเดียว - ข้าม page cache ด้วย
F_NOCACHEบน macOS,O_DIRECTบน Linux และFILE_FLAG_NO_BUFFERINGบน Windows
- หากไม่ข้าม page cache คอนเทนเนอร์ทดสอบที่เล็กกว่า RAM จะเข้าไปอยู่ในแคชของระบบปฏิบัติการ และสร้าง hit rate ที่ไม่สามารถทำซ้ำได้กับโมเดล 982GiB
- เมื่ออ่าน record จะตรวจสอบ magic, expert ID และช่วง offset เสมอ เพื่อไม่ให้ bank ที่ถูกตัดขาดหรือรวมผิดตอบด้วยน้ำหนักที่ไม่ถูกต้อง
- การตรวจ
crc32ของ payload เปิดใช้ด้วย--verify - ค่าใช้จ่ายของการตรวจสอบอยู่ที่ประมาณ 5% สำหรับ Kimi-Linear และประมาณ 1% สำหรับ K3 โดยค่าเริ่มต้นปิดอยู่
- แนะนำให้ตรวจสอบคอนเทนเนอร์หนึ่งครั้งหากคัดลอก ดาวน์โหลด หรือวางไว้บนดิสก์ที่ไม่น่าเชื่อถือ
- trunk และ codebook ไม่มี checksum
- การตรวจ
การอ่านล่วงหน้าและการพยากรณ์ router
- เมื่อ router ของเลเยอร์หนึ่งกำหนด expert ID 16 รายการ จะขออ่านแต่ละรายการด้วย thread แยกกัน และการคำนวณจะเริ่มใช้ข้อมูลที่มาถึงก่อน
- การซ้อนทับการอ่านและการคำนวณช่วยปรับปรุง K3 ได้ประมาณ 1.6 เท่า
- งานที่ทำและสถิติแคชเหมือนกันทั้งก่อนและหลังเปิดฟีเจอร์
- ก่อนที่ hidden state จริงของเลเยอร์ถัดไปจะถูกสร้างขึ้น จะรัน router ถัดไปที่ resident อยู่ด้วย hidden state ปัจจุบันเพื่อนำ expert 6 ตัวมาไว้ล่วงหน้า
- การพยากรณ์ล่วงหน้า 1 residual มีความแม่นยำ 92% ที่ rank 1 และ 81% ใน 6 อันดับแรก
- router จริงเป็นผู้ตัดสิน expert สุดท้าย ดังนั้นผลลัพธ์จึงยังคงถูกต้องทุกประการ
- demand hit rate เพิ่มจาก 14% เป็น 38% และ จำนวนไบต์ที่อ่านรวมไม่เปลี่ยน
- ปิดได้ด้วย
WASTE_LOOKAHEAD=0
- การใช้งานเทคนิคเดียวกันกับ prefill ถูกถอดออกแล้ว
- เลเยอร์ decode ใช้ cache slot 16 ช่อง แต่เลเยอร์ chunk ใช้ประมาณ 550 ช่อง
- record ที่อ่านล่วงหน้าถูกขับออกก่อนใช้งาน ทำให้ปริมาณการอ่านเพิ่ม 6.9% และเวลาไม่ลดลง
Quantization และความถูกต้อง
- น้ำหนัก expert ถูกเก็บด้วย residual vector quantization แบบ codebook 256 รายการ 3 ขั้นสำหรับเวกเตอร์ 8 มิติ ใช้ 3.00 บิตต่อน้ำหนัก
- ไม่คืนรูปเมทริกซ์ทั้งก้อน แต่สร้างตาราง partial inner product แล้วประมวลผลแต่ละแถวด้วยการ lookup ตาราง 3 ครั้งและการบวก 2 ครั้ง
- trunk คงไว้ที่ 4 บิตและ 8 บิต
- เนื่องจากโมเดลผ่าน quantization-aware training เฉพาะกับ expert เท่านั้น ผลลัพธ์จึงพังเมื่อใช้ trunk 3 บิต
- การพยากรณ์แคชแม่นยำ แต่ throughput ก็ไม่ดีขึ้น จึงถูกถอดออก
- ทุกเลเยอร์ถูกเปรียบเทียบกับ implementation อ้างอิงของ PyTorch
- ความต่างของ logits สุดท้ายคือ
3.6e-06 - vision tower เทียบกับอ้างอิงของตัวเองอยู่ที่
2.3e-06 - การแปลง latent KV cache คง logits ที่เหมือนกันในระดับ
1.2e-05
- ความต่างของ logits สุดท้ายคือ
Budget RAM และช่วงประสิทธิภาพที่แคบ
- K3 ใช้ expert 16 ตัวในแต่ละเลเยอร์จากทั้งหมด 92 เลเยอร์ สร้าง working set 17.0GB ต่อโทเคน
- ถ้าแคชเล็กกว่าขนาดนี้ expert ที่เก็บไว้ในโทเคนหนึ่งจะถูกขับออกก่อนโทเคนถัดไป ทำให้ hit rate เป็น 0%
- ผลการวัดบนระบบ 64GB พบว่าการจัดสรร RAM เพิ่มไม่ได้ทำให้เร็วขึ้นเสมอไป
- budget 32GB·แคช 3.32GB: hit rate 0%, 0.50 tok/s
- budget 46GB·แคช 17.32GB: hit rate เดิม 17%, 0.53~0.55 tok/s
- budget 52GB·แคช 23.32GB: 0.04~0.15 tok/s โดยทำซ้ำไม่ได้
- budget 58GB·แคช 29.32GB: 0.02~0.03 tok/s
- router lookahead เพิ่ม hit rate ที่ 46GB จากประมาณ 14% เป็น 38% แต่การล่มของประสิทธิภาพที่ 52GB ขึ้นไปไม่ได้เกิดจาก cache miss แต่เกิดจาก การ paging ของระบบปฏิบัติการ
- ที่ 58GB แม้ hit rate สูงกว่า แต่ช้ากว่า 46GB ประมาณ 20 เท่า
- หลังจากทำให้ระบบเข้าสู่สถานะ paging ด้วย budget ใหญ่แล้ว การวัดที่ 46GB ก็อาจตกลงถึง 0.02 tok/s
- budget เริ่มต้นเลือกโดยลดลงเป็นหน่วย working set ต่อโทเคน ภายใต้ 7/8 ของ RAM จริง
- บน MacBook Pro 64GB ใช้ 46.24GB และจัดสรร 17.56GB ให้ expert cache
- ถ้า budget ที่ระบุต่ำกว่าขั้นต่ำ จะปฏิเสธการเริ่มต้นแทนที่จะดำเนินต่อด้วยการ swapping
- บนระบบ 128GB สามารถใช้ budget แนะนำทั้งหมดซึ่งเท่ากับ trunk และ working set 3 เท่าได้
ประสิทธิภาพ K3 และข้อกำหนดฮาร์ดแวร์
- ระบบที่วัดคือ MacBook Pro M5 Pro 64GB พร้อม SSD ภายใน
- RAM ขั้นต่ำสำหรับ context 4K: 29.05GB
- 32K: 30.54GB, 128K: 35.63GB, 1M: 83.21GB
- trunk ที่ resident: 27.28GB
- โหลดโมเดล: 20 วินาที
- decode: 0.45~0.62 tok/s ที่ budget เริ่มต้น
- prefill: chunked 0.47 tok/s, sequential 0.29 tok/s
- แม้จะเปิดโมเดลได้ด้วยขั้นต่ำ 29.05GB แต่ระบบ 32GB อาจเกิด paging อย่างหนัก ดังนั้น 64GB คือสเปกแนะนำจริง
- ในสถานะ cold อ่าน expert 17.0GB ต่อโทเคน และเมื่อ lookahead มี hit rate 38% จะอ่าน 10.5GB
- SSD ภายในวัดได้ 12.78GB/s ส่วน enclosure USB ภายนอกวัดได้ 0.94GB/s
- เพราะโทเคนหนึ่งอ่าน expert 17GB การประมวลผลเดียวกันบน storage ภายนอกจะใช้เวลาประมาณ 13 วินาที
- สามารถวางดาวน์โหลดต้นฉบับไว้บนดิสก์ภายนอกได้ แต่คอนเทนเนอร์ที่แปลงแล้วควรวางไว้บน NVMe ภายใน
- ต้องใช้ 982GiB สำหรับคอนเทนเนอร์ที่แปลงแล้ว และ 1.42TB สำหรับ staging shard ต้นฉบับ โดยพื้นที่ staging ปล่อยคืนได้หลังแปลงเสร็จ
Attention และการประมวลผล multimodal
- attention ของ K3 ผสม Kimi Delta Attention กับ gated multi-head latent attention ในอัตรา 3:1
- KDA รักษา recurrent state ขนาดคงที่แทน KV cache ที่โตขึ้นเรื่อย ๆ
- MLA แคช latent ความกว้าง 512 โดยไม่ขยาย key/value แยกตาม head
- ดูดซับ
kv_b_projเข้าไปใน query และ output เพื่อลดแคช context 4K จาก 11.25GB เหลือ 0.21GB- ลดลง 53 เท่า เมื่อเทียบกับเดิม
- ที่ 128K เลย์เอาต์แบบขยายต้องใช้ 360GB ส่วนเลย์เอาต์ latent ต้องใช้ 7.2GB
- เส้นทาง multimodal รองรับ ViT ขนาด 401M พารามิเตอร์, 27 เลเยอร์, patch 14
- การ encode ภาพ 1024 patch ใช้เวลา 15.7 วินาที
- ภาพ 896×896 ใช้ตำแหน่ง sequence 256 ตำแหน่งตามค่าเริ่มต้น
- embedding ของภาพก็ผ่าน MoE 92 เลเยอร์ด้วย ดังนั้นค่าใช้จ่ายส่วนใหญ่จึงเหมือน text prefill มากกว่า vision tower
- หากลด
max_patchesในvision.jsonลงครึ่งหนึ่ง จำนวนตำแหน่ง prompt ก็ลดลงครึ่งหนึ่งด้วย
- รองรับ PNG, JPEG, GIF, BMP, TGA, PSD และใช้ภาพได้ใน
run,chat,eval- ตำแหน่งภาพที่ encode แล้วระหว่างสนทนายังคงอยู่ใน attention state จึงไม่ต้อง encode ใหม่ในเทิร์นถัดไป
- vision tower โหลดเฉพาะเมื่อมีภาพ ใช้น้ำหนัก 434MB และหน่วยความจำที่จองรวม 1.12GB
การแปลง การรัน และเซิร์ฟเวอร์
- การ build ต้องใช้เพียง compiler C11 และ
makemake checkผ่านการตรวจ 23 รายการและข้าม 11 รายการด้วยคอนเทนเนอร์สังเคราะห์โดยไม่ต้องใช้โมเดลจริง- หากมีคอนเทนเนอร์จริงทั้งสองตัว การตรวจทั้งหมดมี 36 รายการ
- การแปลง K3 ใช้ safetensors shard 96 ชิ้นจาก moonshotai/Kimi-K3 แบบตรง ๆ
- ใช้เวลาประมาณ 4.7 ชั่วโมงบน M5 Pro เมื่อใช้ 3 process
- encoder PyTorch ล้วนใช้เวลา 23.7 ชั่วโมง
- resume ได้เป็นรายเลเยอร์ ดังนั้นเมื่อหยุดกลางคันจะประมวลผลใหม่เฉพาะเลเยอร์ที่กำลังทำอยู่
- downloader รองรับการ resume ไฟล์บางส่วน, exponential backoff และ jitter, การตรวจ
Content-Length, และการบันทึกสถานะ shard ที่เสร็จแล้ว
- CLI มี
run,chat,eval,planเป็นต้น และใช้--jsonเพื่อพิมพ์ผลลัพธ์ของeval,tokenize,plan,info,benchในรูปแบบที่เครื่องอ่านได้ serve/คือ เซิร์ฟเวอร์ HTTP ที่เข้ากันได้กับ OpenAI ซึ่งเรียก public C API ผ่าน ctypes- ให้บริการ
/v1/chat/completions,/v1/completions,/v1/models,/health - จัดการ streaming, tool definitions และผลลัพธ์, typed call arguments, JSON response schema,
tool_choice, think channel,thinking_effort, และภาพ - prompt renderer พอร์ตมาจาก
encoding_k3.pyของ K3 release และหากมีไดเรกทอรีน้ำหนัก จะเปรียบเทียบบทสนทนา 38 รายการในระดับ segment
- ให้บริการ
แพลตฟอร์มและข้อจำกัดปัจจุบัน
- macOS arm64, Linux arm64, Linux x86_64 ได้ผล 23 pass·11 skip ในการทดสอบเดียวกันที่ไม่ขึ้นกับโมเดล และผ่าน sanitizer กับ fuzz 400 รายการด้วย
- Windows x86_64 cross-compile ด้วย MinGW-w64 และตรวจสอบคอนเทนเนอร์สังเคราะห์, CLI, forward pass แล้ว แต่ยังไม่ได้รันกับคอนเทนเนอร์โมเดลจริง
- ไม่รองรับ MSVC และ Windows ARM64
- การข้าม page-cache บน Windows ตรวจสอบเฉพาะบน filesystem ของ CI และยังไม่ได้ตรวจสอบภายใต้โหลดคอนเทนเนอร์จริงที่ใหญ่กว่า RAM
- x86 SIMD เลือก AVX-512 หรือ AVX2 ตาม CPUID แต่เส้นทาง AVX-512 ยังไม่ได้รันบน CPU ที่รองรับจริง
- Metal backend แม่นยำ แต่ ช้ากว่า CPU 22% จึงปิดเป็นค่าเริ่มต้น เพราะรูปแบบงานเกิด matvec ขนาดเล็กที่มี dependency ต่อกันหลายร้อยครั้ง
- API ยังไม่คงที่ และการแปลง chat format อัตโนมัติรองรับเฉพาะ K3 ในปัจจุบัน
- Kimi-Linear รันในโหมด raw โดยไม่เดา template
- จะไม่ใส่การจัดสรรบิตแบบไม่สม่ำเสมอตาม expert
- มูลค่าของบิตที่สามต่างกันสูงสุดเพียง 1.15 เท่าระหว่าง expert ภายในเลเยอร์ และ 1.01 เท่าระหว่างเลเยอร์ จึงไม่มีประโยชน์จากการจัดสรรที่เหมาะที่สุด
- การจัดสรรตาม routing frequency ก็ช่วยลดพื้นที่เก็บข้อมูล แต่แทบไม่ลด I/O ซึ่งเป็นคอขวด
- ไลเซนส์คือ Apache 2.0
1 ความคิดเห็น
ความเห็นจาก Hacker News
เจ๋งมาก นี่ไม่ใช่โปรเจกต์ที่พยายามใช้งานได้จริงกว่าผู้ให้บริการคลาวด์ตั้งแต่ตอนนี้ แต่เป็นโปรเจกต์ที่แสดงให้เห็นถึง ขอบเขตของความเป็นไปได้
ถ้าการปรับปรุงประสิทธิภาพของโมเดลกับการเพิ่มสมรรถนะของอุปกรณ์โลคัลมาบรรจบกัน วันหนึ่งโมเดลโลคัลคุณภาพสูงก็อาจรันได้อย่างคุ้มค่าในเชิงเศรษฐกิจ
0.5 โทเคนต่อวินาที นี่ผมว่าต่อให้งานยาวก็ไม่มีประโยชน์อยู่ดี สู้จ่ายเงินซื้อ 16GB 4060 Ti สองใบแล้วทำ tensor parallelization ดีกว่า
ถ้าอีก 20 ปีข้างหน้า ก็คงเหมาะกับหุ่นยนต์ช้าๆ สไตล์ไซเบอร์พังก์ที่ใช้พลังงานแสงอาทิตย์ คอยตัดหญ้าหรือขัดทางเท้า หรือไม่ก็หุ่นยนต์ในสวนที่ไล่ทันความเร็วการเติบโตของบอนไซแบบเฉียดฉิวแล้วคอยตัดแต่งกิ่ง
มีคนบอกว่าการจ่ายค่าโทเคนแล้วให้ผู้ให้บริการ inference เป็นคนจ่ายค่าไฟเป็นความสิ้นเปลือง แต่ผมไม่เห็นว่ามันต่างจากการซื้อแตงกวาแล้วให้ชาวนาเป็นคนจ่ายค่าน้ำกับปุ๋ยตรงไหน หวังว่า LLM จะไม่ได้มานั่งหาเหตุผลเข้าข้างทีหลัง
ไอเดียนั้นน่าสนใจ และผมอยากลองกับโมเดลที่เล็กกว่านี้ ถ้าสร้างได้ 0.5 โทเคนต่อวินาทีพร้อมกับอ่านข้อมูลหลาย GB ต่อวินาทีจาก SSD มันก็ยังใหญ่เกินไปสำหรับโน้ตบุ๊กผู้บริโภคทั่วไป แต่พอเป็น โมเดลขนาด 250~500GiB มันอาจกลับกลายเป็นใช้งานได้จริงมากกว่า
ถ้าสมมติว่าใช้ไฟต่อเนื่อง 42W และค่าไฟ 20 เซนต์ต่อ kWh ก็จะอยู่ที่ ประมาณ 5 ดอลลาร์ต่อ 1 ล้านโทเคน โดยยังไม่รวมต้นทุนอื่นๆ อย่างฮาร์ดแวร์
llama.cpp มาตรฐานก็
mmapGGUF ได้อยู่แล้ว ดังนั้นส่วนที่ไม่พอดีกับหน่วยความจำก็จะอยู่บนดิสก์ และ page cache ของเคอร์เนลจะคง resident chunk ที่ใช้บ่อยไว้ให้ เลยสงสัยว่าการทำ implementation เองมีข้อดีอะไรmmapก่อน แล้วค่อยทำเองจน เร็วขึ้น 10 เท่าเหตุผลก็เหมือนที่ database engine ทำแคชของตัวเอง เคอร์เนลเพจจิงเป็นแบบอเนกประสงค์และอิงตามคำขอ แต่ถ้ารู้ access pattern จริง ก็สามารถอ่านข้อมูลที่ต้องใช้ล่วงหน้าและทำ pipeline ได้
ถ้าเป็นโมเดลที่ใส่ลง RAM ได้หมด การรัน llama-server ด้วย
--no-mmapจะดีกว่า แน่นอนว่าถ้าจะยกทั้ง Kimi K3 พร้อม context 1 ล้านโทเคนขึ้นมา ก็ต้องใช้เซิร์ฟเวอร์ 2TBREADME ให้ความรู้สึกแรงมากว่าเขียนโดย LLM เลยสงสัยว่า codebase ก็เขียนโดย LLM ด้วยไหม
ตอนนี้ผมใช้ทักษะของตัวเองไปกับการประสานงาน LLM และเอเจนต์ เพื่อเขียนโค้ดที่ดีกว่าได้เร็วขึ้นมาก นักพัฒนามีทางเลือกแค่อย่างใดอย่างหนึ่ง: ปรับตัวเข้ากับเทคโนโลยีใหม่ หรือถูกคัดออกไป
มันเลยใส่การตัดสินใจภายในที่สำคัญต่อผู้ใช้ แต่ไม่เกี่ยวอะไรกับผู้อ่านที่เห็นแค่ของสำเร็จ รวมถึงศัพท์แปลกๆ แบบ Claude ลงมาตรงๆ ผมยอมรับว่าใช้ LLM บ่อยและมันมีประโยชน์มากกับการเขียนโค้ดซับซ้อน แต่คุณภาพร่างแรกของงานเขียนนั้นแย่มาก
claudeอยู่ในรายชื่อผู้มีส่วนร่วมอยู่แล้ว ไม่ต้องเดาเลย ถ้าถึงขั้นให้ Claude ทำ commit ด้วย ก็ดูมีโอกาสต่ำที่โค้ดจะถูก ตรวจทานด้วยตัวเองถ้าเทคโนโลยีพัฒนาไปจนเลือกโมเดลให้เหมาะกับงานได้อย่างแม่นยำ มูลค่าของมันก็อาจสูงขึ้นมาก เราอาจจินตนาการถึงอนาคตที่ในกระบวนการค้นหาอัตโนมัติ เปิดใช้โมเดลใหญ่แค่วันละ 30 นาที แล้วใช้ โมเดลขนาดเล็ก ตลอดเวลาที่เหลือ