- lm.rs là một dự án chạy suy luận mô hình ngôn ngữ cục bộ trên CPU bằng Rust, hướng tới hiện thực mã tối giản thực hiện toàn bộ suy luận mà không cần thư viện ML
- Dự án lấy cảm hứng từ llama2.c và llm.c của Karpathy; ban đầu chỉ hỗ trợ Google Gemma 2 nhưng sau đó đã mở rộng sang Llama 3.2 và hỗ trợ đầu vào hình ảnh với PHI-3.5
- Thay đổi mới nhất đã triển khai xử lý theo lô, giúp tốc độ mã hóa hình ảnh nhanh hơn tối đa khoảng 3 lần, và Llama 3.2 1B chạy ở 50 tok/s trên máy 16 lõi của tác giả
- Có thể tải các mô hình đã chuẩn bị sẵn từ Hugging Face; README khuyến nghị dùng Q8_0 và cho biết lượng tử hóa Q4_0 vẫn đang được cải thiện
- Người dùng có thể tải mô hình định dạng LMRS và tokenizer để build ngay, hoặc chuyển đổi trực tiếp các tệp mô hình gốc từ Hugging Face bằng export.py và tokenizer.py để chạy
Mục tiêu của lm.rs
- lm.rs là một triển khai suy luận mô hình ngôn ngữ cục bộ chạy trên CPU được viết bằng Rust
- Mục tiêu là hiện thực mã tối giản có thể thực hiện toàn bộ suy luận của mô hình ngôn ngữ trên CPU mà không cần thư viện ML
- Dự án lấy cảm hứng từ llama2.c và llm.c của Karpathy
- README cho biết mã hiện tại “không tối giản đến vậy”, và một số phần vẫn còn chỗ để tối ưu và cải thiện
- Dự án cũng là dịp để tác giả lần đầu thử dùng Rust
Các mô hình được hỗ trợ và mở rộng đa phương thức
- Ban đầu dự án chỉ hỗ trợ mô hình Google Gemma 2, nhưng sau đó đã bổ sung hỗ trợ Llama 3.2
- Gần đây, tùy chọn dùng hình ảnh đã được thêm vào thông qua PHI-3.5
- Các hạng mục hỗ trợ đang được nhấn mạnh hiện nay
- Hỗ trợ đa phương thức thông qua mô hình PHI-3.5-vision
- Hỗ trợ mô hình chỉ văn bản PHI-3.5-mini
- Tài nguyên liên quan
Hiệu năng và các mô hình đã chuẩn bị sẵn
- Tin mới nhất là xử lý theo lô đã được triển khai, giúp tốc độ mã hóa hình ảnh tăng tối đa khoảng 3 lần
- Llama 3.2 1B chạy ở 50 tok/s trên máy 16 lõi của tác giả
- Có thể tải các mô hình và tokenizer đã chuẩn bị sẵn từ Hugging Face
- Việc đo tốc độ được thực hiện trên AMD Epyc 16 lõi
- README khuyến nghị dùng Q8_0, còn lượng tử hóa Q4_0 vẫn đang được cải thiện
-
Bảng mô hình đã chuẩn bị sẵn
- Gemma 2 2B IT Q4_0: 1.39G, 20 tok/s
- Gemma 2 2B IT Q8_0: 2.66GB, 24 tok/s
- Gemma 2 9B IT Q4_0: 4.91GB, 7 tok/s
- Gemma 2 9B IT Q8_0: 9.53GB, 8 tok/s
- Llama 3.2 1B IT: 4.94GB, 21 tok/s
- Llama 3.2 1B IT Q8_0: 1.27GB, 50 tok/s
- Llama 3.2 3B IT Q4_0: 1.71GB, 17 tok/s
- Llama 3.2 3B IT Q8_0: 3.31GB, 19 tok/s
- PHI 3.5 IT Vision Q8_0: 4.28GB, 17 tok/s
- PHI 3.5 IT Mini Q8_0: 3.94GB, 18 tok/s
Quy trình chuyển đổi mô hình
- Nếu tải mô hình lượng tử hóa và tokenizer đã chuẩn bị sẵn từ Hugging Face thì có thể bỏ qua quá trình chuyển đổi
- Nếu muốn tự chuyển đổi mô hình do Google hoặc Meta công bố trên Hugging Face, cần cài thêm các phụ thuộc Python
pip install -r requirements.txt
- Tải các tệp .safetensors và config.json từ trang mô hình gốc để sử dụng
- Với các mô hình đa phương thức như PHI3.5 Vision, còn cần cả tệp config của CLIP
export.py chuyển đổi trọng số bfloat16 sang định dạng LMRS
python export.py --files [ordered .safetensor files] --config [model config.json] --save-path [name and path to save] --type [model type (GEMMA/LLAMA/PHI)]
- Để xuất bản lượng tử hóa, dùng các cờ --quantize và --quantize-type
- Kích thước mô hình lượng tử hóa int8 có thể giảm từ khoảng 9.8G xuống khoảng 2.5G tùy theo kích thước nhóm
- Mô hình đa phương thức phải bao gồm đối số --vision-config
tokenizer.py chuyển đổi mô hình tokenizer sang định dạng tokenizer LMRS
python tokenizer.py --model-id [huggingface model_id] --tokenizer-type [type of the tokenizer (GEMMA/LLAMA/PHI)]
Build và chạy
- Mã Rust được biên dịch bằng
cargo, và README nêu rõ cần truyền cờ target-cpu
RUSTFLAGS="-C target-cpu=native" cargo build --release --bin chat
- Để kích hoạt tính năng đa phương thức, thêm đối số --features multimodal
- Chạy mặc định bằng cách chỉ định tệp trọng số mô hình
./target/release/chat --model [model weights file]
- Có thể dùng thêm các đối số như tokenizer, temperature, top-p, show-metrics...
- Xem các đối số khả dụng bằng --help
- Với mô hình đa phương thức, chỉ định đường dẫn ảnh bằng đối số --image
- Khi dùng PHI3.5-vision, README khuyến nghị temperature 0
Chạy backend cho WebUI
- Để chạy backend cho WebUI, biên dịch với tính năng backend
RUSTFLAGS="-C target-cpu=native" cargo build --release --features backend --bin backend
- Backend đa phương thức kích hoạt tính năng backend-multimodal
- Backend chạy bằng cách chỉ định tệp trọng số mô hình
./target/release/backend --model [model weights file]
- Có thể thay đổi IP và cổng bằng --ip và --port
- Cũng có thể dùng thêm các cờ như temperature
- Để tương thích đa phương thức, dùng cờ --multimodal
- Sau khi chạy, có thể kết nối qua giao diện web
Trạng thái TODO và giấy phép
- Các mục đã hoàn thành
- Bổ sung các phương pháp sampling khác
- Trong hạng mục kiểm thử mô hình 9B và 27B, kiểm thử 9B đã hoàn tất, còn 27B được ghi chú là sẽ quá chậm
- Song song hóa vòng lặp multi-head attention
- Bổ sung chỉ số hiệu năng
- Hỗ trợ lượng tử hóa int8, int4
- Các mục còn lại
- Tính năng cung cấp system prompt
- Giấy phép là MIT
1 bình luận
Các ý kiến trên Hacker News
Thử chạy file
llama3.2-1b-it-q80.lmrsdung lượng 1,2GB trên MacBook M2 64GB thì cảm thấy khá nhanh, và theo Activity Monitor, nó dùng CPU 1000% trên 13 threadClone
lm.rsvào/tmp, build bằngRUSTFLAGS="-C target-cpu=native" cargo build --release --bin chat, rồi tảitokenizer.binvàllama3.2-1b-it-q80.lmrstừ Hugging Face, chạy bằng./target/release/chat --model llama3.2-1b-it-q80.lmrs./target/release/chat --model llama3.2-1b-it-q80.lmrs --show-metricsđể xem số token mỗi giây đạt được là bao nhiêu khôngDo vấn đề định dạng nên chỉ giữ lại một phần, nhưng nó có dạng một chuỗi dài các từ ngẫu nhiên cứ tiếp tục kéo dài
Bài viết rất hay, và có thể dùng một phần mã nguồn trong lớp học khi giải thích transformer thực sự hoạt động ra sao
Mã cụ thể và chi tiết hơn các hình minh họa attention head. Tuy nhiên, nếu thư viện in trực tiếp ra
stdoutthì có thể làm hỏng đầu ra của các ứng dụng, chẳng hạn trình soạn thảo văn bản cung cấp kiểm tra style; vì vậy có lẽ nên ghi vào buffer chuỗi của một instance logging gắn với đối tượnglm.rsthì tốt hơnNgoài ra, thấy có đoạn dùng
unsafetrong model reader để ép căn chỉnh dữ liệu, nên cũng tò mò liệu có thể làm được không cầnunsafemà không quá gượng ép khôngNhư vậy có thể xử lý theo kiểu hiển thị log trong GUI
Đã làm khá nhiều công cụ Rust cho các tác vụ LLM, gồm cả nạp mô hình
Có các chức năng như tự động chọn mô hình lượng tử hóa lớn nhất tùy theo bộ nhớ khả dụng, trích xuất tokenizer từ
gguf, hay đưa prompt vào. Dùng cái này có vẻ có thể loại bỏ một số dependency PythonHiện tại là để hỗ trợ
llama.cpp, nhưng cái này cũng khá thú vị. Cũng tò mò liệu có kế hoạch hỗ trợ ràng buộc ngữ pháp (grammar) khônghttps://github.com/ShelbyJenkins/llm_client
Cụm no dependency trong tiêu đề chưa rõ ràng lắm
Lúc mới nhìn, tôi tưởng có thể là
no_std, nhưng thực ra không phảino_stdvà có vẻ vẫn có vài dependency. Có lẽ ý là tất cả đều là dependency RustNói minh bạch thì có 5 dependency Rust cơ bản, trong đó
chronovàclapđúng ra nên được xử lý bằng feature flag cho chức năng chat. 3 cái còn lại là các crate tiện ích để khai thác thêm một chút hiệu năng phần cứng:rayonđể dễ song song hóa,widehỗ trợ SIMD, vàmemmap2để memory mapping file mô hìnhrequirements.txtcó vẻ cần PyTorch và nhiều dependency Python, và đó cũng là chỗ duy nhất trên trang xuất hiện từ “dependency”, nên cách diễn đạt trong tiêu đề khá gây nhầm lẫnBản thân dự án dường như chỉ dùng phụ đề “Minimal LLM inference in Rust”. Nhìn lịch sử Git thì tài khoản đăng bài này là contributor nhưng có vẻ không phải tác giả chính, nên nếu giải thích chính xác zero dependencies nghĩa là gì thì sẽ hữu ích
Đáng tiếc là HN đôi khi xóa từ trong tiêu đề mà chẳng có lý do hay sự minh bạch nào rõ ràng
cargocủa Rust giờ cũng gần nhưnpmrồiCó 16 dependency thì không hiểu sao có thể gọi là không có dependency được
Trước đây từng làm một thứ tương tự, nhưng so với mã C/C++ chạy trên CPU thì hiệu năng chưa thỏa mãn
Điều đó cũng có nghĩa là tôi chưa thật sự biết cách làm Rust chạy nhanh. Sẽ rất hay nếu có benchmark cho nhiều implementation Rust
Việc triển khai suy luận LLM có vẻ có thể trở thành “Hello, world!” mới cho các lập trình viên nghiêm túc
https://github.com/gip/yllama.rs
https://github.com/crabml/crabml
Tôi đã dùng trực tiếp một số lệnh SIMD, và hiệu năng có vẻ có thể ngang với
llama.cpp. Điểm cốt lõi có lẽ là dùng SIMD trong phép nhân ma trận lượng tử hóa và khi chia việc giữa các thread thì dùng vòng lặp busy-wait thay vì condition variableTuy nhiên tôi đã không cập nhật một thời gian vì không có thời gian tiếp tục làm suy luận mô hình lượng tử hóa trên GPU bằng Vulkan
Việc dự án đã dùng Dioxus khá thú vị, và tôi tò mò liệu WASM có thể được đưa vào lộ trình không
Nếu có thể chạy một LLM nhẹ như RWKV trong trình duyệt, trình duyệt có thể mở ra một nhóm tính năng mới mà không cần gọi SaaS API
https://github.com/maedoc/rwkv.js
Tôi đã dùng
Rwkv.cppđược biên dịch bằng Emscripten, nhưng phần tokenizer vẫn chưa giải quyết ổn. Dù vậy, 1.6B RWKV6 có vẻ đủ dùng cho trình duyệt offlineNó không đủ năng lực cho chat thông thường, nhưng có thể khá đủ cho các mục đích như RAG
Các phụ thuộc bắt buộc là
rayonvàwidehỗ trợ WASM ngay, và nếu đổi kiểuMmaptrongtransformer.rsthành&[u8]thì cũng có thể loại bỏmemmap2Tuy nhiên RWKV có kiến trúc hoàn toàn khác nên sẽ phải triển khai lại toàn bộ, và khả năng nó được đưa vào lộ trình có vẻ rất thấp
Tôi tò mò liệu tất cả các triển khai này có chỉ giới hạn ở CPU không
Ý là nếu có GPU tốt thì có đúng là nên tìm lựa chọn khác không
Nếu muốn thử một framework Rust hỗ trợ GPU, có thể xem Candle https://github.com/huggingface/candle/tree/main
Nếu mục tiêu là chạy thực tế, dù chỉ dùng CPU thì có lẽ vẫn nên dùng lựa chọn thay thế, tức
llama.cpp. Dự án này gần với tài liệu giáo dục hơn, cho thấy bên trong hoạt động thế nào khi loại bỏ các tầng phức tạp của hệ sinh tháiLLM trông như ma thuật về mặt hiệu quả, nhưng xét từ góc độ mã nguồn thì khá đơn giản
Phía Rust có các wrapper
llama.cppnhưllm_clientcủa tôi, và các dự án dựa trên Candle nhưmistral.rsvà KalosmDự án của tôi cũng đang định cung cấp triển khai
mistral.rs, nhưng vẫn chưa chuyển hoàn toàn khỏillama.cpp. Một triển khai Rust hoàn chỉnh có nhiều lợi thế, chẳng hạn rút ngắn thời gian cài đặt. Hiện crate của tôi phải clone và build, nên dù đã tự động hóa trên macOS, Windows, Linux, thời gian build vẫn tăng thêm khoảng 1 phútVí dụ RTX 3090 có băng thông bộ nhớ gần 1TB/s. Để bắt kịp mức đó, cần ít nhất 12 kênh DDR5 ở mức proof-of-concept nhanh nhất trên Trái Đất
Nếu có GPU rời, dùng một triển khai tận dụng nó sẽ là một thế giới hoàn toàn khác. Lý do các con số suy luận LLM trên Apple Silicon ấn tượng cũng là nhờ kiến trúc bộ nhớ băng thông cao hợp nhất CPU-GPU; theo tôi nhớ là khoảng 400GB/s
Ngay cả 4090 cũng không có nhiều bộ nhớ lắm theo chuẩn LLM. GPU chắc chắn nhanh hơn, nhưng có khả năng không đưa được mô hình lớn lên đó
Tôi tò mò giá trị của nó so với
llama.cpplà gìRất hay, và chúc mừng vì đã tạo thư viện Rust đầu tiên, nhưng để dùng cục bộ nghiêm túc thì hỗ trợ Metal/CUDA là bắt buộc
Tuy tôi không phải tác giả chính mà là người đóng góp, tôi đang thử nghiệm để có được một mức tăng tốc GPU nhất định bằng
wgpu. Tác giả chính muốn kiểm soát độ phức tạp, nên chưa rõ thực tế sẽ đi được đến đâuTôi thấy sự nhiệt huyết của cộng đồng Rust trong việc viết lại gần như mọi thứ vừa thú vị vừa đáng cảm kích