1 điểm bởi GN⁺ 2024-10-12 | 1 bình luận | Chia sẻ qua WhatsApp
  • 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.cllm.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.pytokenizer.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.cllm.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

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 .safetensorsconfig.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--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--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

 
GN⁺ 2024-10-12
Các ý kiến trên Hacker News
  • Thử chạy file llama3.2-1b-it-q80.lmrs dung 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 thread
    Clone lm.rs vào /tmp, build bằng RUSTFLAGS="-C target-cpu=native" cargo build --release --bin chat, rồi tải tokenizer.binllama3.2-1b-it-q80.lmrs từ Hugging Face, chạy bằng ./target/release/chat --model llama3.2-1b-it-q80.lmrs

    • Có người hỏi liệu có thể chạy bằng ./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ông
    • Khi nhập một prompt hội thoại tiếng Pháp đơn giản, ban đầu có vẻ phản hồi, nhưng ngay sau đó sụp thành đầu ra rác khó hiểu vô tận
      Do 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
    • Tò mò không biết nên hiểu mức độ thông minh của nó so với ChatGPT mới nhất như thế nào
  • 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 stdout thì 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ượng lm.rs thì tốt hơn
    Ngoài ra, thấy có đoạn dùng unsafe trong 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ần unsafe mà không quá gượng ép không

    • Gọi callback của người dùng sẽ tốt hơn buffer chuỗi
      Như 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 Python
    Hiệ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ông
    https://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ải no_std và có vẻ vẫn có vài dependency. Có lẽ ý là tất cả đều là dependency Rust

    • Đặt tiêu đề khó thật. Điều muốn nói là không có các dependency deep learning như PyTorch, CUDA, ONNX, và toàn bộ logic đều tự chứa
      Nói minh bạch thì có 5 dependency Rust cơ bản, trong đó chronoclap đú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, wide hỗ trợ SIMD, và memmap2 để memory mapping file mô hình
    • Nhìn README thì requirements.txt có 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ẫn
      Bả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
    • Ban đầu nếu tiêu đề kiểu “không phụ thuộc phần cứng” hoặc “không phụ thuộc GPU” thì có lẽ hợp lý hơn
      Đá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
    • Có cảm giác cargo của Rust giờ cũng gần như npm rồi
      Có 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

    • Tôi cũng từng làm một thứ như trải nghiệm “Hello, world” tương tự
      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 variable
      Tuy 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

    • Tôi từng thử động đến mảng này một chút
      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 offline
      Nó 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
    • Bản thân thư viện có vẻ có thể biên dịch sang WASM với rất ít thay đổi
      Các phụ thuộc bắt buộc là rayonwide hỗ trợ WASM ngay, và nếu đổi kiểu Mmap trong transformer.rs thành &[u8] thì cũng có thể loại bỏ memmap2
      Tuy 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

    • Đúng vậy. Dự án này chạy trên CPU, nên không tận dụng GPU để tính toán
      Nếu muốn thử một framework Rust hỗ trợ GPU, có thể xem Candle https://github.com/huggingface/candle/tree/main
    • Tất cả đều được triển khai trên CPU, và ít nhất hiện tại không có tăng tốc GPU nào
      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ái
      LLM 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
    • Tùy GPU mà có thể chênh 10~20 lần
      Phía Rust có các wrapper llama.cpp như llm_client của tôi, và các dự án dựa trên Candle như mistral.rs và Kalosm
      Dự á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ỏi llama.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út
    • CPU cũng đúng, nhưng điều quan trọng hơn là băng thông bộ nhớ
      Ví 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
    • Còn tùy trường hợp. Mô hình tốt thì lớn và yêu cầu bộ nhớ cũng cao
      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.cpp là gì

    • Có thể dễ tích hợp với các dự án Rust khác hơn
    • Vì ít tính năng hơn nên codebase gọn gàng hơn
  • 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

    • Nếu dùng CUDA thì sẽ đi ngược mục tiêu của dự án này, nên không phù hợp ngay từ đầu
      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 đâu
  • Tô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