- lm.rs は、RustでCPU上のローカル言語モデル推論を実行するプロジェクトで、MLライブラリなしで推論全体を実行する最小コード実装を目指している
- Karpathyの llama2.c と llm.c に着想を得ており、当初は Google Gemma 2 のみをサポートしていたが、Llama 3.2 と PHI-3.5 の画像入力サポートまで拡張された
- 最新の変更で バッチ処理 が実装され、画像エンコーディング速度が最大約3倍高速化され、Llama 3.2 1B は作者の16コアマシンで 50 tok/s で動作する
- 用意済みのモデルは Hugging Face から入手でき、README では Q8_0 の使用を推奨し、Q4_0 量子化はまだ改善中だとしている
- ユーザーは LMRS 形式のモデルとトークナイザーをダウンロードしてすぐにビルドするか、Hugging Face の元モデルファイルを export.py と tokenizer.py で変換して実行できる
lm.rs が目指すもの
- lm.rs は Rust で書かれたローカルCPUベースの言語モデル推論実装である
- 目標は、MLライブラリなしでCPU上で言語モデルの 完全な推論 を実行する最小コード実装である
- Karpathy の llama2.c と llm.c に着想を得ている
- README では、現在のコードは「そこまで最小ではなく」、一部のコードには最適化や改善の余地があると述べている
- このプロジェクトは、作者が初めて Rust を使ってみるきっかけでもあった
サポートモデルとマルチモーダル拡張
- 当初は Google Gemma 2 モデルのみをサポートしていたが、その後 Llama 3.2 モデルのサポートが追加された
- 最近では PHI-3.5 を通じて画像利用オプションが追加された
- 現在強調されているサポート項目
- PHI-3.5-vision モデルによるマルチモーダル対応
- PHI-3.5-mini テキスト専用モデル対応
- 関連リソース
性能と用意済みモデル
- 最新ニュースとして バッチ処理 が実装され、画像エンコーディング速度が最大約3倍向上した
- Llama 3.2 1B は作者の 16コアマシン で 50 tok/s で動作する
- 用意済みのモデルとトークナイザーは Hugging Face から入手できる
- 速度測定は 16コア AMD Epyc で行われた
- README では Q8_0 の使用を推奨し、Q4_0 量子化はまだ改善中だとしている
-
用意済みモデル一覧
- 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
モデル変換の流れ
- 用意済みの量子化モデルとトークナイザーを Hugging Face から取得すれば、変換工程を省略できる
- Google または Meta が Hugging Face に公開したモデルを直接変換するには、追加の Python 依存関係をインストールする必要がある
pip install -r requirements.txt
- 元モデルのページから .safetensors と config.json ファイルをダウンロードして使用する
- PHI3.5 Vision のようなマルチモーダルモデルには CLIP の config ファイルも必要である
export.py は bfloat16 重みを 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)]
- 量子化版を書き出すには --quantize と --quantize-type フラグを使う
- int8 量子化モデルのサイズは、グループサイズに応じて約 9.8G から約 2.5G まで縮小できる
- マルチモーダルモデルでは --vision-config 引数を含める必要がある
tokenizer.py はトークナイザーモデルを LMRS トークナイザー形式に変換する
python tokenizer.py --model-id [huggingface model_id] --tokenizer-type [type of the tokenizer (GEMMA/LLAMA/PHI)]
ビルドと実行
- Rust コードは
cargo でコンパイルし、README では target-cpu フラグの指定を明記している
RUSTFLAGS="-C target-cpu=native" cargo build --release --bin chat
- マルチモーダル機能を有効にするには --features multimodal 引数を追加する
- 基本実行はモデル重みファイルを指定して行う
./target/release/chat --model [model weights file]
- 追加引数として tokenizer、temperature、top-p、show-metrics などを使える
- 使用可能な引数は --help で確認する
- マルチモーダルモデルでは --image 引数で画像パスを指定する
- PHI3.5-vision 使用時、README では temperature 0 を推奨している
WebUI バックエンドの実行
- WebUI 用バックエンドを実行するには、backend 機能付きでコンパイルする
RUSTFLAGS="-C target-cpu=native" cargo build --release --features backend --bin backend
- マルチモーダルバックエンドでは backend-multimodal 機能を有効にする
- バックエンドはモデル重みファイルを指定して実行する
./target/release/backend --model [model weights file]
- --ip と --port で IP とポートを変更できる
- temperature などの追加フラグも使用できる
- マルチモーダル互換性には --multimodal フラグを使用する
- 実行後、Web インターフェースに接続できる
TODO 状態とライセンス
- 完了した項目
- 別のサンプリング方法の追加
- 9B と 27B モデルのテスト項目のうち 9B テスト完了、27B は遅すぎる見込みと表示
- マルチヘッドアテンションループの並列化
- 性能指標の追加
- int8、int4 量子化 対応
- 残っている項目
- ライセンスは MIT である
1件のコメント
Hacker Newsのコメント
M2 64GB MacBookで1.2GBの
llama3.2-1b-it-q80.lmrsを動かしてみたところ、かなり速く感じられ、Activity Monitor上では**13スレッドでCPU 1000%**を使っていた/tmpにlm.rsをクローンし、RUSTFLAGS="-C target-cpu=native" cargo build --release --bin chatでビルドした後、Hugging Faceからtokenizer.binとllama3.2-1b-it-q80.lmrsを取得し、./target/release/chat --model llama3.2-1b-it-q80.lmrsで実行した./target/release/chat --model llama3.2-1b-it-q80.lmrs --show-metricsで実行して、秒間トークン数がどれくらい出るか確認できるかと尋ねているフォーマットの都合で一部だけ残したが、長いランダムな単語列が延々と続く形だった
記事は非常によく書かれており、授業でTransformerが実際にどのように動作するかを説明する際に、ソースコードの一部を使えそう
アテンションヘッドの図よりも、コードのほうが具体的で細部まで示されている。ただしライブラリが
stdoutへ直接出力すると、テキストエディタでスタイルチェックを提供するようなアプリケーションの出力を壊す可能性があるため、lm.rsオブジェクトに紐づいたロギングインスタンスの文字列バッファへ書き込むほうがよさそうまた、モデルリーダーでデータアラインメントを強制するために
unsafeを使っている箇所が見えるが、無理をせず**unsafeなし**で可能なのかも気になるそうすればログをGUIに表示するといった処理ができる
モデル読み込みと複数のLLM作業向けRustツールをかなり作ってある
利用可能なメモリに応じて最大の量子化モデルを自動選択したり、
ggufからトークナイザを抽出したり、プロンプトを投入したりする機能がある。これを使えば一部のPython依存を取り除けそう現在は
llama.cpp対応用だが、これもかなり興味深い。**文法制約(grammar)**対応の計画があるのかも気になるhttps://github.com/ShelbyJenkins/llm_client
タイトルのno dependencyという表現はあまり明確ではない
最初に見たときは
no_stdかもしれないと思ったが、実際にはno_stdではなく、依存関係もいくつかあるように見える。おそらく全部Rustの依存関係という意味ではあり得る率直に言うと、基本のRust依存が5つあり、そのうち
chronoとclapはチャット機能用として機能フラグで扱うのが妥当。残り3つはハードウェア性能を少し引き出すためのユーティリティクレートで、並列化を容易にするrayon、SIMDを助けるwide、モデルファイルのメモリマッピング用のmemmap2であるrequirements.txtにPyTorchと複数のPython依存が必要であるかのように見え、ページ内で「dependency」という単語が見える場所もそこだけなので、タイトルの表現はかなり混乱を招くプロジェクト自体は単に「Minimal LLM inference in Rust」という副題を使っているようだ。Gitの履歴を見ると、この投稿をしたアカウントはコントリビューターではあるが主著者ではなさそうで、zero dependenciesが正確に何を意味するのか説明してくれると助かる
残念ながらHNは、特に理由や透明性もなくタイトルから単語を削除することがよくある
cargoも、今ではほとんどnpmのようになったのかと思ってしまう依存関係が16個あるのに、どうして依存性なしと言えるのか分からない
以前に似たようなものを作ってみたことがあるが、CPUで動くC/C++コードと比べると性能は物足りなかった
Rustを高速にする方法をちゃんと分かっていなかったということでもある。複数のRust実装のベンチマークがあるとよさそう
LLM推論の実装は、本気のプログラマにとって新しい「Hello, world!」になり得るように見える
https://github.com/gip/yllama.rs
https://github.com/crabml/crabml
一部のSIMD命令を直接使っており、性能は
llama.cppに合わせられそうだった。肝は量子化行列乗算でのSIMD利用と、スレッド間で作業を分割するときに条件変数ではなくビジーウェイトループを使うことのようだただし、GPU上でVulkanによる量子化モデル推論を続けて作業する時間がなく、しばらく更新できていなかった
すでに Dioxus を使っている点が興味深く、ロードマップに WASM も入り得るのか気になる
ブラウザで RWKV のような軽量 LLM を動かせるなら、SaaS API を呼び出さなくても、ブラウザが新しい機能カテゴリを開ける可能性がある
https://github.com/maedoc/rwkv.js
Emscripten でコンパイルした
Rwkv.cppを使ったが、まだトークナイザ部分はうまく解決できていない。それでも 1.6B RWKV6 はオフラインのブラウザ専用としては十分使いものになりそう通常のチャットには能力不足だが、RAG のような用途にはかなり十分かもしれない
必須依存の
rayonとwideは WASM をそのままサポートしており、transformer.rsのMmap型を&[u8]に変えればmemmap2も取り除けるただし RWKV はまったく別の構造なので全体を新しく実装する必要があり、ロードマップに入る可能性は非常に低そう
これらの実装はすべて CPU 限定 なのか気になる
良い GPU があるなら別の代替を探すべきなのかを尋ねている
GPU をサポートする Rust フレームワークを試してみたいなら、Candle https://github.com/huggingface/candle/tree/main を見てみる価値がある
実際に実行する目的なら、CPU だけを使うとしても代替、つまり
llama.cppを使うほうがよさそう。このプロジェクトは、エコシステムの複雑なレイヤーを取り払ったときに内部がどう動いているかを示す教育用資料に近いLLM は効果の面では魔法のように見えるが、コードの観点ではかなり単純
Rust 側には私の
llm_clientのようなllama.cppラッパーがあり、Candle ベースのプロジェクトとしてmistral.rsと Kalosm がある私のプロジェクトでも
mistral.rs実装を提供しようとしているが、まだllama.cppから完全には移行できていない。完全な Rust 実装には、インストール時間を短くできるなど大きな利点がある。今の私のクレートはクローンしてビルドする必要があり、macOS、Windows、Linux で自動化はされているものの、ビルド時間が約1分増えるたとえば RTX 3090 はほぼ 1TB/s のメモリ帯域幅を持っている。これに匹敵するには、地球上で最速級の概念実証レベルの DDR5 を少なくとも12チャネル使う必要がある
外付け GPU があるなら、それを活用する実装を使うほうがまったく別世界。Apple Silicon の LLM 推論数値が印象的なのも、CPU-GPU 統合の高帯域メモリ構造があるためで、記憶では約400GB/sだった
4090 でさえ LLM 基準ではメモリがそれほど多いわけではない。GPU のほうが速いだろうが、大きなモデルは載せられない可能性が高い
これが
llama.cppと比べてどんな価値があるのか気になる素晴らしいし、初めての Rust ライブラリを作ったこともおめでとう。ただ、本格的なローカル利用には Metal/CUDA サポート が必須
ただし主著者ではないがコントリビューターとして、
wgpuである程度の GPU アクセラレーションを得る実験をしている。主著者が複雑さを抑えたいと考えているので、実際にどこまで行くかはわからないRust コミュニティがほとんどすべてを再実装しようとする 情熱 は興味深く、ありがたく感じる