2 điểm bởi GN⁺ 5 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • colibrì là một engine thuần C được tạo để chạy GLM-5.2 744B MoE trên máy tiêu dùng với khoảng 25GB RAM, hoạt động không cần GPU bằng cách streaming các expert được định tuyến từ đĩa
  • Kiến trúc cốt lõi là giữ phần dense khoảng 17B tham số trong RAM ở dạng int4, dùng 9,9GB, còn 21.504 expert định tuyến được đặt trên đĩa khoảng 370GB và đọc khi cần
  • Phần triển khai bao gồm GLM-5.2 forward, MLA attention và KV-cache nén, router kiểu DeepSeek-V3, MTP speculative decoding, kernel lượng tử hóa int8/int4/int2, tokenizer BPE cấp byte
  • Trên môi trường WSL2 12 core, 25GB RAM, NVMe VHDX, thời gian load khoảng 30 giây, peak RSS khi chat khoảng 20GB, cold decode đọc đĩa khoảng 11GB mỗi token với tốc độ 0,05~0,1 tok/s
  • Với NVMe nhanh và RAM lớn hơn, cache và pinning rất quan trọng; trong đo đạc thực tế của cộng đồng, môi trường Apple M5 Max 128GB đạt 1,06 tok/s khi tắt MTP

Vấn đề colibrì muốn giải quyết

  • colibrì tự mô tả là “Tiny engine, immense model”, một engine chạy mô hình GLM-5.2 744B-parameter MoE trên máy tiêu dùng có khoảng 25GB RAM
  • Runtime là thuần C, không phụ thuộc Python, BLAS hay GPU
  • Engine gồm một file C đơn c/glm.c khoảng 1.300 dòng và các header nhỏ
  • Ví dụ chạy là ./coli chat; ví dụ đầu ra hiển thị colibrì v1.0 — GLM-5.2 · 744B MoE · int4 · streaming CPU, thời gian chuẩn bị khoảng 32 giây và 9,9GB resident memory

Cách chạy MoE dựa trên streaming từ đĩa

  • Mô hình 744B Mixture-of-Experts của GLM-5.2 chỉ kích hoạt khoảng 40B tham số cho mỗi token
  • Các expert định tuyến thay đổi theo từng token tương ứng khoảng 11GB
  • Phần dense thường trú trong RAM
    • Bao gồm attention, shared experts, embeddings
    • Khoảng 17B tham số
    • Dùng khoảng 9,9GB RAM ở định dạng int4
  • Expert định tuyến được lưu trên đĩa
    • Gồm 75 MoE layer × 256 expert và MTP head
    • Tổng cộng 21.504 routed experts
    • Khoảng 19MB cho mỗi expert ở định dạng int4
    • Tổng dung lượng đĩa khoảng 370GB
  • Việc load expert sử dụng per-layer LRU cache, optional pinned hot-store và OS page cache

Các tính năng chính đã triển khai

  • GLM-5.2 forward được triển khai theo cấu trúc glm_moe_dsa và đã được xác minh token-exact so với oracle transformers
    • Teacher-forcing 32/32 trên mô hình tiny-random
    • Greedy generation 20/20
  • MLA attention bao gồm q/kv-LoRA và partial RoPE xen kẽ
    • KV-cache nén dùng 576 float mỗi token
    • Nhỏ hơn 57 lần so với 32.768 float/token truyền thống
    • GLM-5.2 có 64 head và không có GQA
  • Triển khai router sigmoid kiểu DeepSeek-V3
    • noaux_tc
    • routed_scaling_factor
    • shared expert
    • first-3-dense layers
  • Native MTP speculative decoding dùng multi-token-prediction head của GLM-5.2 (layer 78) để tạo draft token, rồi main model xác minh bằng batched forward
    • MTP head phải là int8
    • Với int4, draft acceptance giảm xuống 0~4% nên speculation không hoạt động
    • Với int8, acceptance đo được là 39~59%, 2,2~2,8 token/forward
    • Giữ lossless cả khi sampling thông qua rejection sampling
    • Trong cold cache, draft đã xác minh có thể định tuyến thêm expert, khiến expert-loads/token tăng từ khoảng 660 lên khoảng 1100
    • Trường hợp này có thể mất nhiều thời gian hơn trước khi cache và pin được warm-up, nên có adaptive guard và tùy chọn DRAFT=0
  • True sampling hỗ trợ temperature và nucleus
    • Mặc định là 0,7 / 0,90
    • Thiết lập chính thức 1,0 / 0,95 được giải thích là sẽ sampling nhiễu lượng tử hóa ở phần tail trong môi trường int4
  • Kernel integer-dot dùng Q8_0-style int8 activations và AVX2 maddubs
    • Matmul int8 nhanh hơn 1,4~2,5 lần
    • Hiệu năng đo được là 119 GFLOP/s
    • int4 nhanh hơn 1,8 lần trong batch
    • int4 single-row đo được chậm hơn nên được giữ ở f32
  • MLA weight absorption tránh tái tạo k/v theo từng token khi decode
    • Query hấp thụ kv_b, còn context được projection sau attention
    • Ngay cả trong môi trường forced absorption, cũng đã xác minh với TF 32/32 và generation 20/20
  • Expert readahead đọc block tiếp theo bằng WILLNEED trong khi nhân một expert block
  • Kernel lượng tử hóa hỗ trợ int8, packed int4, packed int2, per-row scales, AVX2 và dequant-on-use
  • DSA sparse attention đang được phát triển
    • lightning-indexer weights được trích xuất khoảng 108GB từ FP8 repo
    • indexer forward được lên kế hoạch cho bước tiếp theo
    • Cho đến lúc đó, attention là dense và exact với context ≤ 2048 token
  • Trong prefill và MTP verification, Batch-union MoE chỉ đọc một lần các expert unique trong batch và áp dụng cho tất cả position được định tuyến đến expert đó
  • Tokenizer là byte-level BPE tokenizer được triển khai bằng C, dùng regex Unicode-property kiểu GPT-2 và 320k merge
  • Cơ chế an toàn RAM tự động điều chỉnh kích thước expert cache khi khởi động dựa trên MemAvailable
    • Phản ánh peak projection của working set, KV, MTP row và reconstruction buffer
    • Được thiết kế để kernel OOM-killer không kích hoạt
  • Offline converter được cung cấp dưới dạng c/convert_fp8_to_int4.py
    • Tải từng shard GLM-5.2 FP8 một
    • Dequant bằng block scale 128×128 rồi requantize sang container của engine
    • Xóa shard trong khi xử lý, nên toàn bộ checkpoint FP8 756GB không cần cùng lúc tồn tại trên đĩa
    • Quá trình chuyển đổi có thể tiếp tục lại

Hiệu năng cơ bản đo được và các giới hạn

  • Môi trường phát triển là WSL2, 12 core, 25GB RAM, NVMe qua VHDX
  • Các số đo như sau
    • Kích thước mô hình int4 container: khoảng 370GB
    • Resident RAM: 9,9GB
    • Thời gian load: khoảng 30 giây
    • Peak RSS khi chat: khoảng 20GB, auto-capped
    • Chi phí cold decode: khoảng 11GB disk reads mỗi token
    • Giới hạn random disk của VHDX: khoảng 1GB/s
    • Tốc độ cold decode: khoảng 0,05~0,1 tok/s
    • MTP speculation: 2,2~2,8 tok/forward với int8 head
  • Dự án nêu rõ đây không phải hệ thống nhanh, và giải thích rằng warm cache, pinned hot experts, MTP giúp giảm useful-response latency
  • Có hai lưu ý liên quan đến SSD
    • colibrì streaming là read-only nên bản thân việc đọc không ảnh hưởng đáng kể đến độ mòn SSD
    • Nếu thiếu RAM gây ra swap traffic, sẽ phát sinh ghi và có thể làm mòn ổ
    • Full read duty cycle kéo dài có thể làm nóng ổ giá rẻ, nên cần theo dõi nhiệt độ và health

Tải mô hình và chạy

  • Mô hình GLM-5.2 int4 đã chuyển đổi sẵn được cung cấp trên Hugging Face
  • Nếu nhận mô hình đã chuyển đổi sẵn, có thể bỏ qua bước chuyển đổi FP8 → int4
  • Chạy bằng cách trỏ COLI_MODEL tới thư mục mô hình
COLI_MODEL=/path/to/GLM-5.2-colibri-int4 ./coli chat
  • Quick start thực hiện kiểm tra gcc/OpenMP, build và self-test bằng ./setup.sh trong thư mục c
  • Nếu tự chuyển đổi, dùng ./coli convert --model /nvme/glm52_i4
    • Cần đường dẫn ext4/NVMe còn khoảng 400GB dung lượng trống
    • Việc chuyển đổi cần Python và torch, safetensors, huggingface_hub, numpy
    • Bản thân runtime engine là thuần C, Python chỉ được dùng cho converter một lần
  • Các tùy chọn hữu ích như sau
    • --temp T: sampling temperature, mặc định 0,7 và nucleus 0,90; 0 là greedy
    • --topp 0.7: adaptive expert top-p, giảm 30~40% sử dụng đĩa
    • --ngen N: số token tối đa cho mỗi câu trả lời
    • AUTOPIN=0: tắt learning cache auto-pin
    • THINK=1: bật GLM-5.2 reasoning block
    • DRAFT=n: độ sâu MTP draft
    • TF=1: xác minh teacher-forcing

Learning cache và kỳ vọng theo phần cứng

  • Learning cache ghi các expert được định tuyến trong quá trình sử dụng thực tế vào .coli_usage cạnh mô hình
  • Khi khởi động, tự động pin các expert hot nhất vào phần RAM còn dư
  • Mô tả dự án cho biết colibrì càng dùng nhiều càng nhanh hơn
  • Môi trường yêu cầu là Linux hoặc WSL2, gcc hỗ trợ OpenMP, AVX2, tối thiểu 16GB RAM, local NVMe chứa mô hình int4 khoảng 370GB
    • Khuyến nghị đường dẫn ext4
    • Hướng dẫn không dùng network/9p mount
  • Trình tự kiểm thử là build và self-test, đo đĩa bằng iobench, kiểm tra tok/s, expert hit-rate, RSS trong chat, ghi nhận expert usage rồi pinning, chạy benchmark chất lượng
  • Dự đoán theo phần cứng là ước tính, không phải số đo
    • Máy phát triển WSL2 VHDX, khoảng 1GB/s, 25GB RAM: 0,05~0,1 tok/s cold
    • Native Linux, PCIe4 NVMe random 3~5GB/s, 32GB: 0,5~1 tok/s
    • PCIe5 NVMe hoặc 2×NVMe RAID0 8~12GB/s, 64GB, PIN khoảng 40GB: 2~4 tok/s
    • 128~256GB RAM, 12 core, hot experts cached: 2~4 tok/s
    • Cùng mức RAM với 24~32 core hoặc AVX-512/VNNI kernels: 5~15 tok/s

Benchmark cộng đồng

  • Trong môi trường Intel Core Ultra 7 270K Plus, 24 thread, WSL2, 24GB RAM, NVMe VHDX, thiết lập mặc định ghi nhận 0,07 tok/s
    • disk iobench: 1,96GB/s buffered, 2,74GB/s O_DIRECT
    • expert hit: 3~4%
    • RSS: 14,1GB
  • Trong cùng môi trường, --topp 0.7 ghi nhận 0,11 tok/s
    • expert hit: 11%
    • RSS: 14,7GB
    • Tốc độ end-to-end tăng 1,6 lần
  • Môi trường Apple M5 Max, 18 core, macOS, 128GB unified memory, SSD trong ghi nhận 1,06 tok/s
    • disk iobench: 14,2GB/s O_DIRECT
    • default, tắt MTP
    • expert hit: 23%
    • RSS: 21,8GB
  • Trên máy 24GB RAM, expert cache bị auto-cap ở 2 slot mỗi layer, nên dù đĩa nhanh hơn 2~2,7 lần so với máy phát triển, decode vẫn ở trạng thái cold
  • Trên máy ít RAM, RAM cap trở thành nút thắt thay vì đĩa
  • Số đo M5 Max là một trường hợp mô hình 744B đạt khoảng 1 tok/s trên SSD laptop; đĩa 14GB/s chuyển nút thắt trở lại RAM budget và kernel

Benchmark chất lượng và các phép đo còn lại

  • Chi phí của lượng tử hóa int4 đối với độ chính xác vẫn chưa được đo
  • Benchmark harness đã được triển khai, nhưng trên đĩa khoảng 1GB/s của máy phát triển, chạy đầy đủ mất một phần đáng kể của một ngày
  • Các lệnh benchmark được cung cấp chạy hellaswag, arc_challenge, mmlu
cd c
./coli bench
./coli bench hellaswag --limit 200
./coli bench mmlu arc_challenge --ram 100
  • Đầu ra là accuracy theo từng task, dùng log-likelihood scoring và phong cách EleutherAI-harness
  • Điểm công khai của full-precision GLM-5.2 được ghi là khoảng 85~95% trên các task đó
  • Nếu int4 container nằm trong khoảng chênh lệch vài điểm, lượng tử hóa được xác nhận; nếu không, sẽ có cơ sở để đầu tư vào mixed hoặc grouped-scale quantization

Cấu trúc kho lưu trữ và giấy phép

  • Các file chính như sau
    • c/glm.c: engine bao gồm GLM-5.2 forward, streaming MoE, MTP, serve mode
    • c/st.h: safetensors reader dựa trên preadfadvise, không dùng mmap
    • c/tok.h: C byte-level BPE tokenizer
    • c/coli: CLI chat, run, bench, convert, info
    • c/iobench.c: parallel disk microbenchmark theo cảm nhận của engine
    • c/convert_fp8_to_int4.py: converter FP8 → int4 an toàn cho đĩa
    • c/make_glm_oracle.py: tiny-random oracle generator dùng cho validation
    • c/olmoe.c: engine Stage-A và validation target đầu tiên
  • Tên colibrì bắt nguồn từ loài chim ruồi, có thể bay treo tại chỗ với trọng lượng nhỏ và ghé thăm nhiều bông hoa mỗi ngày
  • Giấy phép dự án là Apache 2.0, còn weights GLM-5.2 được Z.ai phát hành theo MIT

1 bình luận

 
Ý kiến trên Hacker News
  • Điều tôi tò mò nhất là khi dùng thực tế thì nó ở mức đo được bằng token/giây, hay gần như 1 token mỗi phút
    LLM cục bộ dù chậm như 1 token/giây vẫn khá hữu ích nếu để nó xử lý dự án qua đêm rồi 6~8 giờ sau kiểm tra kết quả
    Ngược lại, 0,05~0,1 token/giây trên phần cứng cấu hình tối thiểu trong liên kết có vẻ gần như không có ứng dụng nào dùng được
    Dù vậy, ý tưởng tổng thể rất hay, và tôi mong sẽ có thêm nhiều nỗ lực để chạy cục bộ các mô hình cỡ 350B~900B trên phần cứng người bình thường có thể chi trả, dù chỉ ở mức 1 token/giây
    Tôi thích hướng “đã có SSD NVMe đọc nhanh, có mô hình khổng lồ trên đĩa cục bộ, vậy đừng nạp toàn bộ mà cứ khi cần thì đọc 11GB/token”

    • Buồn cười là nhờ Claude Cowork mà chúng ta đã quen với việc chờ thời gian phản hồi
      Giờ tôi nghĩ muộn nhất khoảng năm 2028 thì mình sẽ chạy được cục bộ
      Ngân sách dưới 10.000 USD, và tôi muốn chạy một mô hình có thể sánh với các mô hình hàng đầu hiện nay
    • Với mô hình cục bộ chậm hoặc một số dự án, giao diện chat có thể là cách tiếp cận sai, có lẽ dạng như hệ thống ticket sẽ phù hợp hơn
      Trước khi bỏ tiền vào Mac Studio hay GPU, tôi đã xác định cách kiểm chứng ý tưởng này bằng phần cứng đang có, và có lẽ tuần này có thể làm được một bản proof-of-concept
      Đây không phải ý tưởng mới, chỉ là cần thời gian để làm cho nó chạy đúng với quy trình làm việc của tôi
    • Trong README có benchmark chạy Colibrì trên nhiều phần cứng, và tôi từng thấy thời gian khá tốt
      Họ vẫn đang cố cải thiện thêm
    • Với phần lớn dự án, dùng cloud cung cấp GLM 5.2 miễn phí sẽ thực tế hơn
      1 token mỗi phút là con số quá nhỏ ngay cả khi so với hạn mức dùng miễn phí
    • Tôi tự hỏi thời nay “người bình thường” có kham nổi 24GB RAM và SSD NVMe 0,5TB không
  • Tôi đang làm một thứ tương tự nhắm tới macOS trên Apple Silicon
    Hướng đi là dùng Unsloth split GGUF, giữ phần đã nén thường trú trong unified memory, kernel Metal native, và KV nén native chỉ dùng RAM
    Có lẽ trên 128GB sẽ hợp lý hơn máy 64GB của tôi, và khi sẵn sàng tôi định đưa lên GitHub

    • Tôi có một MacBook Pro M4 128GB đang chờ, nên rất sẵn lòng thử nghiệm
  • Ở mảng tạo ảnh/video tôi cũng từng dùng chiến lược tương tự với https://github.com/cretz/thinfer
    Phần lớn công việc nằm ở nhánh video
    Vì luôn cần một inference engine có thể đưa trọng số vào/ra theo cơ chế LRU đúng lúc cần, cuối cùng tôi đã vibe code một công cụ nhận --vram-budget và giữ trong giới hạn đó
    Hóa ra chi phí di chuyển các byte mmap vào/ra VRAM khá rẻ so với tính toán, và nếu kết hợp pipeline với double buffering thì gần như lúc nào điểm nghẽn cũng là tính toán chứ không phải bộ nhớ
    Tất nhiên là tôi đang dùng các mô hình nhỏ hơn nhiều

    • Có vẻ bạn đã đưa vào rất nhiều mô hình như krea, wan, hunyan
      Tôi tò mò liệu bạn có tạo một harness chung để chạy được tất cả không
      Cũng tò mò mô hình nào bám sát ngân sách VRAM nhất quán hơn
  • llama.cpp hỗ trợ nhiều kiểu lượng tử hóa 4-bit trở xuống và mặc định mmap mô hình, nên không cần giữ toàn bộ trọng số trong bộ nhớ
    Hệ điều hành sẽ lấy từ lưu trữ khi cần
    Việc triển khai bằng rất ít mã và không phụ thuộc là rất hay, nhưng tôi tò mò liệu thực tế có lợi thế hiệu năng không

  • Khá tuyệt
    Tuần này tôi đã thử GLM 5.2 và cũng thấy rất ấn tượng
    Ở công ty, trước khi bắt đầu dự án tiếp theo, chúng tôi đang thử chạy cục bộ trên phần cứng cực đắt, nên thật thú vị khi thấy có người chạy bản phát hành mô hình nguồn mở khổng lồ này trên máy trung bình
    Dù hiện chưa thực dụng lắm, đây vẫn là công việc tốt

    • Tò mò đó là phần cứng gì
  • Tôi không rành cấu trúc bên trong LLM, nhưng tò mò liệu có thể dùng cấu trúc tương tự cho các mô hình khác có “lớp” hay không
    Liệu có thể khi một lớp xử lý xong thì loại lớp đó khỏi RAM, nạp lớp tiếp theo từ đĩa rồi kích hoạt nó trên kết quả của lớp đầu tiên không

  • Máy của tôi có RAM 64GB và VRAM 24GB, nên tôi tò mò liệu có thể điều chỉnh để nó dùng nhiều RAM hơn không
    Hoặc cũng có thể chạy Gemma/Qwen trên GPU rồi để GLM-5.2 ủy quyền các tác vụ nhỏ hơn cho chúng
    Có lẽ sẽ phải huấn luyện lại GLM-5.2 ở một mức độ nào đó
    Tôi cũng tò mò liệu dùng nhiều đĩa song song để tăng băng thông có giúp tăng tốc không
    Cảnh báo hao mòn SSD nói rằng cold start nặng ở khoảng 11GB/token đọc ngẫu nhiên, bản thân việc đọc là an toàn nhưng page cache của hệ điều hành có thể tạo ghi, và dùng quá mức có thể đẩy nhanh hao mòn SSD giá rẻ
    Một cách an toàn có thể là tạo phân vùng riêng cho trọng số mô hình và đặt ở chế độ chỉ đọc
    Tôi không biết page cache theo từng phân vùng hay từng đĩa, nhưng nếu theo đĩa thì cũng có thể định dạng data.iso chỉ đọc như một phân vùng và mount nó như đĩa

    • Tôi đang dùng một laptop nhỏ
      Nếu có thêm đĩa thì thực sự có thể thử nghiệm
      Khi có benchmark, nếu gửi pull request hoặc issue thì chúng ta có thể cùng làm tiếp
    • Tôi tò mò liệu câu “page cache của hệ điều hành có thể tạo ghi” có phải ảo giác không
      Tôi đang bỏ sót điều gì? Vì sao đọc lượng lớn lại tạo ghi?
    • Có thể là một ý hay, và chúng ta có thể cùng làm
  • Trên trang có cảnh báo hao mòn SSD https://github.com/JustVugg/colibri#ssd-wear-warning
    Nếu dùng PC desktop tự ráp thì có thể thay SSD, nhưng người dùng có SSD hàn chết thì phải làm sao?
    Nên tránh các ứng dụng kiểu này, hay cứ chấp nhận khả năng thiết bị lưu trữ hỏng sớm và tiếp tục?
    Có lẽ nên dùng thiết bị lưu trữ ngoài như SSD tiêu hao

    • Tránh là đúng
      Laptop có SSD hàn chết thì phải theo dõi mức sử dụng thật kỹ và cẩn thận
      Dự án này trông giống thí nghiệm hơn là thứ dành cho mọi người chạy, nhưng vẫn khá tuyệt
    • Theo tôi hiểu thì cảnh báo nói về việc swap out khi dùng quá nhiều bộ nhớ
      Không cần suy nghĩ mê tín ở đây
      Hoạt động đĩa, đặc biệt là ghi, có thể đo được
      Ví dụ trên Linux có thể dùng iostat hoặc vmstat
    • Đây là cảnh báo khá thận trọng
      Ứng dụng không thực hiện ghi, nên thực tế bản thân ứng dụng không làm mòn SSD
      Phần còn lại gần với các nguyên tắc quản lý chung không liên quan đến ứng dụng
    • Với mục đích đó thì có lẽ dùng ổ ngoài là đúng
  • Về kỹ thuật thì ấn tượng, nhưng tôi tò mò liệu thực tế có dùng được một cách thực dụng không

  • Thật ra tôi cũng đang làm thứ tương tự, nhưng đã chọn hướng mmap toàn bộ mô hình vào bộ nhớ để tránh dùng thêm RAM
    Tôi cũng nhờ Claude triển khai Medusahttps://arxiv.org/abs/2401.10774 cho mô hình, để thử nhận lợi ích của dự đoán nhiều token mà không cần nạp thêm mô hình vào bộ nhớ
    Vừa về đến nhà nên bổ sung thêm: tất cả chỉnh sửa đều đang làm trong llama.cpp, và mục tiêu cuối cùng là đưa nó lên những nơi như máy tính single-board xử lý file mmap qua NVMe
    Dựa trên các thử nghiệm trước, giới hạn lý thuyết của cấu hình hiện tại có vẻ khoảng 1,8 token/giây, nhưng các head Medusa bổ sung chưa được huấn luyện hoàn toàn, và thú thật tôi cũng không chắc nó có được tính là token sinh ra hay không
    Cuối cùng ý tưởng có vẻ tương tự, nhưng tôi vẫn chưa biết viết parser/runner LLM từ đầu, nên thay vì chỉ định giữ gì trong bộ nhớ, tôi giao việc đó cho kernel Linux
    Cuối cùng, tôi đã giới hạn mức dùng llama.cpp ở 16GB trong tổng 32GB của mình, nên có lẽ còn có thể giảm thấp hơn

    • Colibrì vẫn cần cải thiện liên tục, nên nếu có ý tưởng hay thứ gì khác thì pull request, issue, benchmark đều được hoan nghênh
    • Đây chính là cách tiếp cận mà tôi đã tò mò