0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Radeon AI Pro R9700 (Ubuntu 26.04 + ROCm) による vLLM や llama.cpp の稼働のたぶん最短手順

0
Last updated at Posted at 2026-09-28

ningentteiina.png
いまどき珍しい!!この記事は人間によって書かれています。
明らかに不必要な情報も多分に含むので死ぬほど忙しい人は適宜読み飛ばしてね!


この記事は Radeon AI Pro R9700 シングルカードでLLMを稼働させるために書いた記事だけど、同じ gfx1201 の Radeon RX 9070 XT (VRAM 16GB) でも実質同一製品みたいなもんだからこの記事使えると思うよ。

マルチGPUの人は知らん。


(参考までに) 構成

電源: CORSAIR RM750e 2025モデル (750W ATX, PCIe 5.1 準拠)
M/B: Asrock B850I Lightning Wifi (mini-ITX, B850, PCIe 5.0x16)
CPU: AMD Ryzen 5 7600 (7600X じゃなくて 7600, TDP 65W, 3.8 GHz 6c/12t)
RAM: DDR5-5600 32GB (16GB x 2)
DISK1: NVMe Gen.4x4 2TB QLC SSD (OS, LLMモデル, 他スクリプト全部用)
DISK2: SATA 6Gbps 1TB SSD (使ってない)
GPU: XFX AMD Radeon AI Pro R9700 (VRAM:32GB, gfx1201)
CASE: CORSAIR Obsidian Series 250D (10年以上前の使い古しのMini-ITX用ケース)

稼働成功までの最短手順 (vLLM、llama.cpp両方)

                 カ     ラララ     下
 コココココ  コココココ  カカカカカ           下
     コ      コ    カ カ  ラララララ    下
     コ      コ    カ カ      ラ  下 下 下
     コ      コ   カ  カ     ラ    下下下
 コココココ  コココココ  カ  カ    ララ      下

 OS初期設定、ROCmで稼働させるための下準備

  1. UEFI BIOSの設定
    1.1. (UEFI BIOS上で)マザーボードの時刻合わせ
    1.2. (UEFI BIOS上で)CPU内蔵のグラフィック(iGPU)をどうするか決める
    1.3. (UEFI BIOS上で)CPUのダウンクロック、機能有効化・無効化など
    1.4. (UEFI BIOS上で)dGPU関連の設定
  2. Ubuntu 26.04 Desktop クリーンインストール
    2.1. 気を抜いてグラフィカルじゃなくてCLIで起動した?日本語文字化けするでしょ?
    2.2. sudo systemctl isolate graphical.target で一時的にグラフィカルに
  3. 初期設定
    3.1. apt更新、wifi設定、IP設定、SSH設定、ファイヤーウォール設定
    3.2. CPU電力設定
    3.3. ユーザー設定
  4. LACTインストール、GRUBの編集
    4.1. githubのLACTリポジトリからdebファイルダウンロードしてインストール
    4.2. LACTでGPUの電力210W制限、ファンコントロールカーブ設定、ダウンクロック設定
    4.3. 起動時オーバークロック設定自動反映のためにGRUB編集
    4.4. !!再起動で確認!!
  5. ROCm 10の公式ドキュメントを見てROCm 10.0インストール
    5.1. 上のセレクターの罠に注意して、自分のGPUとOSが選択されていることを確認
    5.2. gfx1201のものだけをインストール
    5.3. rocm-smi lact cli での情報取得
    5.4. LDの設定
    5.5. !!再起動で確認!!
  6. AI稼働、管理、カスタマイズに必要なものをインストール
    6.1. 必要最低限なもの
    6.2. あったら便利なもの
  7. オープンウェイト(AIモデル)の事前ダウンロード
    7.1. モデル用ディレクトリの作成
    7.2A. vLLM用モデルダウンロード (Hugging Face: アカウント不要 - safetensors)
    7.2B. llama.cpp用モデルダウンロード (Hugging Face: アカウント不要 - gguf)
    7.3. (参考情報) Hugging Faceでのオープンウェイト(AIモデル)の探し方
  8. vLLMの稼働
    8.1. (vLLM) docker pullでエンジン取得(ダウンロード)
    8.2. (vLLM) docker compose用設定ファイルの作成
    8.3. (vLLM) vLLM起動
    8.4. (Open WebUI) docker pullでOpen WebUI取得(ダウンロード)
    8.5. (Open WebUI) docker compose用設定ファイルの作成
    8.6. (Open WebUI) 起動
    8.7. vLLM + Open WebUIで動作確認
  9. llama.cppの稼働
    9.1. (llama.cpp) docker pullでエンジン取得(ダウンロード)
    9.2. (llama.cpp) docker compose用設定ファイルの作成
    9.3. (llama.cpp) llama.cpp起動
    9.4. llama.cpp 動作確認
    9.5. マルチモデル動的切り替え設定
    9.5.1. マルチモデル用設定ファイル(.ini)作成
    9.5.2. マルチモデル切り替え用docker-compose.yml作成
    9.5.3. マルチモデル切り替え可能llama.cpp起動

1. [UEFI BIOSの設定]

だいたいのマザーボードは初回電源投入時はDDR5 DRAMのトレーニングから始まるから5分~20分くらい(DRAMの搭載量による)、画面になにも映らなくても問題ない。ってのを知らずに初心者が騒ぐから箱にデカデカと書いておいて欲しいんだよね・・・。あとマザーボード上のPOSTランプちゃんと見ろ。

ディスクにブートできるものが入ってなかったら勝手にUEFI BIOSの画面に入るはず。
OS勝手に起動しちゃう場合は、電源入れてすぐに [F2]キー と [DEL]キー を交互に連打してれば、よほど特殊なマザーボードじゃない限りUEFI BIOSの画面に入ることができる。

1.1. [(UEFI BIOS上で)マザーボードの時刻合わせ]

Ubuntuのインストール時にマザーボード上の日付を見る処理が入るらしく、リリース日より前の日付が入っていた場合はエラーを吐いて止まる。

日付が過去だとこんなエラーが出るっていう画像

こんな風な画面が出て [CTRL][ALT][DEL] しか利かなくなるので、予めUEFI BIOS画面から日付と時刻を設定しておく必要がある。

1.2. [(UEFI BIOS上で)CPU内蔵のグラフィック(iGPU)をどうするか決める]

最近のCPUは最廉価品以外はiGPU(内蔵グラフィックユニット)がCPUに搭載されているはずなので、画面出力をdGPU(外付けGPU)に頼るって人はiGPUを不活性化できる。iGPUを不活性化しても消費電力にはほぼ影響はなく、熱もCPUのものに比べれば微々たるもの。おまけに削減できるシステムDRAM量も512MBくらいしかないので放っておいてもいい。

ただ、iGPUが居ると居ないでROCmでの検出に差異が出てくる点だけ留意しておくこと。(GPUの参照番号が変わる)

1.3. [(UEFI BIOS上で)CPUのダウンクロック、機能有効化・無効化など]

お好みで省電力化、オーバークロックなどいろいろ設定しておく。

下記の項目について 必ず ON/Enable/有効化 しておくこと。
ASRock以外のマザーボードであれば、BIOS項目の便利な検索機能がついているはずなので、それを使うことで比較的簡単に項目が見つかる。

  • Resizable BAR
    Resizable BAR、Smart Access Memory、Radeon SAM、ReBAR とか呼び方はベンダー様々
    機能の 有効化は必須
    旧来は256MB以下のブロックサイズで管理していたものを、より大きなブロックサイズで管理できるようにする技術らしい
    CPU ⇔ GPU間のRAM(メモリ)の確保・受け渡しに必要と考えてもらえればいい
    LLMの論理/物理バッチサイズ(batch-size、ubatch-sizeなど)に影響

  • Above 4G Decoding
    4GB以上のデコードを扱えるようにする技術(そのまんま)
    機能の 有効化は必須
    ベンダーによっては こいつとResizable BARが連動 してしまっているために Resizable BAR を有効化したい場合は Above 4G Decoding も有効化する必要がある。逆も然り。単体機能におけるLLMへの影響は不明。おそらく影響ある。

UEFI BIOS項目上に上記が見当たらない場合は見逃しているか検索できないほど深い階層にあるかだと思う。
マザーボードが古くてUEFI BIOSを更新できない場合はパフォーマンスダウン覚悟で諦めよう。

ちなみにCPUのオーバークロック、HyperThreading/SMTの有効化・無効化、クロック固定化などはLLMにおいて現状ほぼ意味が無い or 体感できない程度の影響しかないのでオススメしない。能力を向上させるより、できるだけ機能を維持したまま省電力化(ダウンクロック)するほうが得策っぽい。

2. [Ubuntu 26.04 Desktop クリーンインストール]

え?WindowsでUbuntuインストールするためのUSBメモリを作るところから説明してほしい?

  1. Rufus のポータブルバージョンをダウンロード (https://rufus.ie/ja/#download)
  2. Ubuntu Desktop ISOイメージをダウンロード (https://releases.ubuntu.com/resolute/)
  3. Rufusを実行
    3.1. 中身を吹き飛ばす生贄となるUSBメモリを接続→ドライブレターを指定
    3.2. UbuntuのISOイメージを指定
    3.3. USBメモリの中身を吹き飛ばしてUbuntuのインストールイメージ書き込み
  4. USBメモリ取り外してAIサーバー機に接続

UEFI BIOSの設定が終わったら再起動する前に作成したインストール用USBメモリ接続しておいて、再起動して設定を反映させ、Ubuntu Desktopのインストール。
いうて画面指示のとおりに言語選んでキーボード選んで・・・とするだけなので割愛。

オフィスソフトなどを入れるかどうか聞いてくるので、オフィスソフトなどを入れないほうを選ぶ。

2.1. [気を抜いてグラフィカルじゃなくてCLIで起動した?日本語文字化けするでしょ?]

ちょっと知識や経験があるのをイキりたい年頃のキミのことだから、ちょっとGUIを楽しんでから「俺は玄人だから」って早々に起動モードをCUIに設定して再起動しちゃったんじゃない?LACTのファンコントロール設定とかGUIじゃないと難しいよ?CPU電圧とか温度とかも簡単に見れなくない?大丈夫そ?

日本語文字化けしてるから fbterm とかインストールして無理矢理頑張ればなんとかなるかも!

2.2. [sudo systemctl isolate graphical.target で一時的にグラフィカルに]

ログインさえできるなら sudo systemctl isolate graphical.target で一時的にグラフィカルに切り替えできるよ!
isolate 指定だから再起動したらまたCUIに戻るし玄人気取りのキミも安心!

逆にグラフィカルからCUIに切り替えたいときは sudo systemctl isolate multi-user.target ね!

3. [初期設定]

3.1. [apt更新、wifi設定、IP設定、SSH設定、ファイヤーウォール設定]

[apt更新]

まずは手始めに、

sudo apt update
sudo apt upgrade

でアップデートを行うか、せっかくGUIで接続してるからGUI上でシステム更新を実施

[WiFi設定]

WiFiについては無効化したり、有効化してパスワード保存しておいたり等
GUIじゃないとアクセスポイントで何を拾うかとかが見辛いので、ここで設定しておくことをオススメする

[IP設定]

IP固定化、DHCP、DNSの設定など

[SSH設定]

お好みで

[ファイヤーウォール設定]

Ubuntuデフォルトでは、iptables や nftables じゃなくて ufw が入っている。

ufw は最初は無効化されているので、GUI上で gufw をインストールするか、sudo ufw enableで有効化する必要がある。

ufwはCLIでも iptables や nftables より簡単に穴をあけることができるのでおすすめ。

  • TCP:8081に穴をあけたいとき
    sudo ufw allow 8081/tcp
    (上記コマンド1回だけで、IPv4/IPv6両方に穴をあける)
  • 現在のufwの状況を確認したいとき
    sudo ufw status numbered
    (numbered はルールの番号を表示するもの: ルール削除時に必要)
  • 穴をあけたルールを消したいとき
    sudo ufw delete [番号]
    ([番号]は、sudo ufw status numbered で表示された番号)
    (allowはIPv4/IPv6両方同時で、deleteは一方のみ 残ったほうは再度deleteで削除する)
  • 設定変更即時反映
    sudo ufw reload

3.2. [CPU電力設定]

ノートPCにインストールしない限りは、デフォルトの電力設定がバランス(performance-blance)になっていて
CPUコアの周波数が高めの状態で維持される。これだと待機時に無駄に電力を食うことになるので cpupower などを使用してうまいことコントロールするか、電源設定を省電力に変えてあげるようにする。

ただ、電源設定を省電力に変えると、今度は上限周波数が3.8GHzくらいに下がるのでそれはそれで・・・。

最悪電源管理の仕組みを「tlp」などに変えてもいい。

あと、ノートPCとかのバッテリー内蔵デバイスにUbuntuをインストールした人 は、設定のどこかに「電源ボタンを押したときの動作」とか「画面を閉じたときの動作」とかを選ぶ画面があって、そこに「自動サスペンド」という 自動的に電源を切る機能 が デフォルトでON になってるはずなので OFFにすることを忘れず に。

3.3. [ユーザー設定]

LLMを稼働させるユーザーを決めておき、基本的にそのユーザーでvLLMやllama.cppを管理するようにする。

なぜそうしないといけないかというと、Linux上のROCmは /dev/kfd と /dev/dri の中に本体があって、そのディレクトリ配下は root と videoグループ や renderグループ しか変更できないようになっているから。

workstation:~$ ls -la /dev/dri
total 0
drwxr-xr-x  3 root root        140 Sep 19 12:50 ./
drwxr-xr-x 19 root root       4820 Sep 19 12:50 ../
drwxr-xr-x  2 root root        120 Sep 19 12:50 by-path/
crw-rw----  1 root video  226,   0 Sep 19 12:50 card0
crw-rw----  1 root video  226,   1 Sep 19 12:50 card1
crw-rw----  1 root render 226, 128 Sep 19 12:50 renderD128
crw-rw----  1 root render 226, 129 Sep 19 12:50 renderD129

workstation:~$ ls -la /dev/kfd
crw-rw---- 1 root render 234, 0 Sep 19 12:50 /dev/kfd

 ▲/dev/kfd と /dev/driの権限

なので、LLMを稼働させるユーザーを決めてから

sudo usermod -aG render,video [ユーザー]

としてROCmを操作できるようにしておく必要がある。
(もちろん、root 以外ならUbuntuインストール時に作成した管理アカウントでもいい)

4. [LACTインストール、GRUBの編集]

4.1. [githubのLACTリポジトリからdebファイルダウンロードしてインストール]

GitHubのLACTリポジトリ( https://github.com/ilya-zlobintsev/LACT/releases/ )より latest タグのついたreleaseを確認し、おそらくその中にあるUbuntuのdebファイルを入手
image.png

ダウンロードしたdebファイルを管理者アカウントでダブルクリックして実行するか、
sudo dpkg -i [ダウンロードしたdebファイル] で 実行してインストール
(DLするファイルを間違えてない限り何もエラーは出ないはず)

4.2. [LACTでGPUの電力210W制限、ファンコントロールカーブ設定、ダウンクロック設定]

このへんは保証とか12V-2x6ケーブルの都合もあるしお好み(自己責任)で。
積んでる電源の許容電力や排熱やパーツの温度上昇による寿命低下のことを考えて、オーバークロックするよりもダウンクロックすることをオススメしたい感。

image.png
▲電力制限 -30%(210W)、最大GPU周波数 -500MHz、GPU電圧降下指定 -25mV

電力制限と最大GPU周波数はLLM稼働には大きく影響せず、最大まで下げても体感するほどの差は出なかった。
ただし、GPU電圧降下指定はある閾値以下になると極端に影響が出てくるので慎重に!!
個体差もあると思うけど、だいたい-80mVくらいからLLM生成が不安定になり稼働に失敗するようになる。

最大VRAM周波数はLLM生成の要なのでとくに触ってない。
何がどう影響するか勉強不足の人はOCもDCもせずに画面を指をくわえて見ておくだけでいい。


image.png
▲ファンコントロールカーブ

こちらも 個人のお好み(自己責任) で。デフォルトプロファイルでは煩すぎて常用できないと判断し、いろいろ試した上で上記のようになった。長い目で見ないとあってるかどうか誰にもわからんと思う。
XFXのものはだいたい上の画像の通りにちょっと味付けした程度で常用可能だと感じた。

実験する際は [static]モード に合わせて自分でパーセンテージいじって [Apply] して、「これはうるさい」「これはギリ耐えれる」などを見極める。

LLM稼働の際、応答するトークン量が多いと75度~90度くらいまで平気で温度上昇するので、その辺を加味して調整するといい。温度上昇してから1分経過しても1℃も温度が低下していないなら、それはファン回転数が低すぎる。

4.3. [起動時オーバークロック設定自動反映のためにGRUB編集]

OCとかDCすると、なんかLACTの画面上部に何か警告みたいなのが出てくる。
これはたぶん(文字が切れてて読めない)、「再起動時に自動的に設定を反映するために、設定ファイルを権限を持ってる人が編集してください!!」っていう文言だと思う。

いわれたとおり、GRUBの起動設定にamdgpuのOC設定上書きマスクを追加する。

sudo /etc/default/grub

設定ファイルの GRUB_CMDLINE_LINUX_DEFAULT の部分が

...
GRUB_DISTRIBUTOR=`( . /etc/os-release && echo ${NAME} )`
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX=""
...

デフォルトでは上記のようになってると思うので、それを

...
GRUB_DISTRIBUTOR=`( . /etc/os-release && echo ${NAME} )`
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash amdgpu.ppfeaturemask=0xffffffff"
GRUB_CMDLINE_LINUX=""
...

こう書き換える。(splashのうしろにamdgpu.ppfeaturemask=0xffffffffを追加)

書き換えが終わったら、

sudo update-grub

で設定を反映する。
GRUBファイルの設定ミスったらいろいろ大変なことになるのでコピペミスなど無いように。
(ここまでの手順ならまだクリーンインストールやり直しでもそこまで痛手ではないと思う)

4.4. [!!再起動で確認!!]

Ubuntuを再起動して、LACTを開いてOC設定やファンコントロールに設定が反映されているかを確認。

ちなみに起動時コンソールに、
image.png
こんな文字列が出てきたら読み込み(というか設定の反映)に失敗してるらしいけど、この表示が出てきてても普通に設定反映されてるし・・・正直よくわからん。
気になった人は sudo journalctl -xe とか sudo dmesg で調査してみて。

5. [ROCm 10の公式ドキュメントを見てROCm 10.0インストール]

ここからROCm 10のインストール。
(https://rocm.docs.amd.com/en/docs-10.0.0/install/rocm.html)

宗教上の理由でROCm 7を使い続けるひとはこの手順には従わず、公式のROCm 7の手順を見てすすめてね!ぶっちゃけ今のところROCm 7でもROCm 10でもそこまで大差ないからROCm 10だけでいいぞ!

5.1. [上のセレクターの罠に注意して、自分のGPUとOSが選択されていることを確認]

ROCm 10のインストール方法ページの上部のセレクターの部分で、下記のように選ぶ
(https://rocm.docs.amd.com/en/docs-10.0.0/install/rocm.html)

Device family:AMD Radeon
Use case: Compute
Radeon GPU: RX 9070XT もしくは AI Pro R9700
Operation system: Ubuntu
Ubuntu version: 26.04
Installation method: apt

image.png

「Use case:」を「Compute + graphics」にしたままだと、amdgpu-installという全く別のソリューションを紹介されるので間違わないように。

5.2. [gfx1201のものだけをインストール]

公式に提示されている通りにコピペしてインストールして、正しくインストールされたかどうかを確認する

  1. sudo apt install libatomic1 libquadmath0
  2. Configure permissions for GPU access はすでに「3.3. ユーザー設定」で実施済みのはず
  3. Register ROCm repositories (GPGキー入れてリポジトリ読み込み)
  4. Install ROCm packages (sudo apt install amdrocm10.0-gfx1201)
  5. rocminfo
  6. amd-smi version

5.3. [rocm-smi lact cli での情報取得]

通常、CPU内蔵のiGPUを有効にしたままの状態であれば、rocminfoの確認では

0:CPU
1:dGPU(Radeon AI Pro R9700)
2:iGPU(AMD Radeon Graphics)

となるはず。

もしCPUがROCmに拾われないタイプだったり、iGPUが無効化されていたら

0:dGPU(Radeon AI Pro R9700)

という結果になる。

LACT CLIで温度やファンの状態を確認する場合は、dGPUにあった番号を指定する。
index番号でdGPUが1なら、
lact cli -g 1 stats で GPU温度やファン状態が読める。

image.png

また、もし電力制限などを設定しているのであれば、
rocm-smi を実行することで
電力制限がうまくdGPUに反映されていることを確認することができる。

image.png

5.4. [LDの設定]

起動時にROCmのライブラリを読み込んでおくようにするため、昔は
~/.bashrc とかに export LD_LIBRARY_PATH=/opt/rocm/lib:$LD_LIBRARY_PATH
とか書いて読み込むように設定してたらしいけど、今はそれは推奨されてないらしい。
(ユーザースクリプトで動くから微妙だとか、衝突するからだとか)

なのでおとなしく、/etc/ld.so.conf.d/の中にroot権限でファイル作って
ldconfigでライブラリパスを読むようにする。

echo '/opt/rocm/lib' | sudo tee /etc/ld.so.conf.d/AMD_ROCm.conf

これで /etc/ld.so.conf.d/AMD_ROCm.conf というファイルが作成されたはず。

5.5. [!!再起動で確認!!]

再起動した後に、一般ユーザーで
ldconfig -v | grep -A3 '/etc/ld.so.conf.d/AMD_ROCm.conf' を実行すると
下記のような表示が出て、適切に読み込まれていることがわかるはず。

image.png

6. [AI稼働、管理、カスタマイズに必要なものをインストール・設定]

6.1. [必要最低限なもの]

パッケージ名 導入方法 何に使う?
python3.14-venv apt huggingface_hub(hfコマンド)の環境づくりのために必要
ca-certificates apt 証明書やssl関連、dockerの公式インストール手順に入っているため
curl, wget apt GPG鍵の取得など、WEB上ソースからのダウンロード全般、GET/POSTリクエスト
huggingface_hub pyenv+pip Hugging Faceからのオープンウェイト(AIモデル)のダウンロード
docker 公式リポジトリ+apt vLLM, llama.cpp, OpenWebUI, SearXNGなどのコンテナ管理
  • まず、python3.14-venv、ca-certificates、curl、wgetをインストール
sudo apt install python3.14-venv ca-certificates curl wget
  • 次に、huggingface_hubをインストール(下記を説明文を飛ばして1行ずつ実行)
# ホームディレクトリに移動
cd ~

# python仮想環境を作成(.pyenv)
python3 -m venv .pyenv

# python仮想環境を有効化(プロンプトの左になんか変なのが出る)
source .pyenv/bin/activate

# python仮想環境に、pipでhuggingface_hubをインストール
pip install --upgrade huggingface_hub

# 一度python仮想環境から出る
deactivate

# python仮想環境を再度有効化(プロンプトの左になんか変なのが出る)
source .pyenv/bin/activate

# hfコマンドの実体をwhichコマンドで取得し、その中身を変数HF_CMDBINに入れる
HF_CMDBIN=$(which hf)

# python仮想環境から出る
deactivate

# ホームディレクトリの.bash_aliasesというファイルに「# huggingface_hub」と追記
echo '# huggingface_hub' | tee -a ~/.bash_aliases

# ホームディレクトリの.bash_aliasesというファイルに変数HF_CMDBINの中身(つまりhfの実体)を追記
echo "alias hf='${HF_CMDBIN}'" | tee -a ~/.bash_aliases

# 自分のログイン時の環境設定を再読み込み(.bash_aliasesがついでに読まれる)
source ~/.bashrc

# hf --helpでhfコマンドのオプションが表示されるか確認(表示されないならhfコマンドが使えない)
hf --help

上記は何をしているかというと、python仮想環境を作ってhuggingface_hubをインストールして、インストールしたhfコマンドの実体を今後「hf」と打つだけで使えるように .bash_aliases というエイリアス(別名)をまとめるファイルに書き込んで、再読み込みして、ちゃんと実行できるか確認してるかんじ。「毎回python仮想環境に入らないとhfコマンドが使えない」ってのが不便なので、python仮想環境外でも直接hfコマンドを実行できるようにしているだけ。

  • 次は、Dockerのインストール

dockerは公式 ( https://docs.docker.com/engine/install/ubuntu/ ) の手順からインストール
書いてあるコマンドを打っていくだけで導入できる。

6.2. [あったら便利なもの]

パッケージ名 導入方法 何に使う?
tree apt ディレクトリのファイルツリーを表示できる(モデル管理に便利かも)
lsof apt ポート開放とどのプロセスが握っているかを簡単に把握するために使う(ssコマンド使い慣れてるなら不要かも)
vim apt 高機能版vi(nanoエディタを使っている場合は不要かも)

導入コマンド:

sudo apt install tree lsof vim

7. [オープンウェイト(AIモデル)の事前ダウンロード]

ここからは Qwen とか Gemma4 とか呼ばれてるオープンウェイト(AIモデル)を管理する手順。
どんなふうに管理するかまだよくわかってない人向けなんで、オープンウェイト管理のプロはこの項番をスルーして、自分の感性に従ってもらっていいと思う。(だいたいそういういらん自我を出した人はなんか道が逸れて後々失敗するまでがセット)

7.1. [モデル用ディレクトリの作成]

「3.3.ユーザー設定」 で LLMを稼働させるユーザー を決めてるはずなので、そのユーザーでログインして作業を進める。え?何? LLMを稼働させるユーザー まだ決めてないの?じゃあ今ログインしてるユーザーでええんとちゃう?知らんけど。でも 「rootユーザーでLLM稼働」 だけはやめておいたほうがいいと思う。

# ホームディレクトリ以下に `ai/models/config` ディレクトリを作成 (-pオプションで存在しない階層自動作成)
mkdir -p ~/ai/models/config

今後、この ~/ai/models 以下 (実体は /home/[ユーザー名]/ai/models) に
huggingfaceでダウンロードしたオープンウェイトを蓄えていくようにする。
もし、NVMe SSDなどの都合でパーティション分けたいとかがあればシンボリックリンク貼るとかで管理したほうがいいと思う。

config ディレクトリはマルチモデルロード用途であとで使う。

7.2A. [vLLM用モデルダウンロード (Hugging Face: アカウント不要 - safetensors)]

(vLLMを意地でも動かしたい人用:llama.cppだけでいい人はこの手順いらん)

まず、ダウンロードしたいオープンウェイト(safetensors形式)をブラウザで探して、
「リポジトリ名」 をコピーする。

今回は、google/gemma-4-12B-it-qat-w4a16-ct (Gemma4 12B 4bit量子化モデル)
https://huggingface.co/google/gemma-4-12B-it-qat-w4a16-ct
で説明するので、なんかよくわからん人はそのまま同じコマンド入れていったらいい。

image.png
▲リポジトリ名のなんか右側にあるなんとも形容しがたいコピーボタンでコピー

これでリポジトリ名がコピーできてるはずなので、下記のようなコマンドでウェイトを所定の位置にダウンロード。

# huggingface_hubのコマンド(hf)でローカルディレクトリにファイル丸ごとダウンロード
hf download google/gemma-4-12B-it-qat-w4a16-ct --local-dir ~/ai/models/google/gemma-4-12B-it-qat-w4a16-ct

要するに、

hf download <リポジトリ名> --local-dir ~/ai/models/<リポジトリ名>

で ~/ai/models/ にウェイト( gemma-4-12B-it-qat-w4a16-ct )をパブリッシャー( google )ごとに分けてダウンロードしてる。各ディレクトリは hf コマンドで勝手に作成される。
これで同じモデル名の量子粒度の違いとかでバッティングするとかもなくなると思う。

もちろんダウンロードにはそれなりに時間かかるので待つ。
(ときどきhuggingfaceの応答がめっさ遅くなるので、もうそれは待つしかない・・・)

ダウンロードが完了したら、下記のようなディレクトリ構成になってるはず。(tree ~/ai)

/home/<ユーザー名>/ai
└── models
    └── google
        └── gemma-4-12B-it-qat-w4a16-ct
            ├── README.md
            ├── chat_template.jinja
            ├── config.json
            ├── generation_config.json
            ├── model.safetensors
            ├── processor_config.json
            ├── recipe.yaml
            ├── tokenizer.json
            └── tokenizer_config.json

この手順を使って、試したいオープンウェイトをダウンロードしていく。

7.2B. [llama.cpp用モデルダウンロード (Hugging Face: アカウント不要 - gguf)]

(llama.cppで4bit量子化済みのGGUFファイルを利用する人用)

まず、ダウンロードしたいオープンウェイト(safetensors形式)をブラウザで探して、
「リポジトリ名」 をコピーする。

今回は、meta-models/Muse-Glimmer-30B-GGUF (MuseGlimmer 30B 4bit量子化モデル)
https://huggingface.co/meta-models/Muse-Glimmer-30B-GGUF
で説明するので、なんかよくわからん人はそのまま同じコマンド入れていったらいい。

image.png
▲リポジトリ名のなんか右側にあるなんとも形容しがたいコピーボタンでコピー

これでリポジトリ名がコピーできてるはずなので、下記のようなコマンドで、目的のウェイトのみを選択的に ダウンロード。

# huggingface_hubのコマンド(hf)でローカルディレクトリに選択したGGUFファイルをダウンロード
hf download meta-models/Muse-Glimmer-30B-GGUF \
    --local-dir ~/ai/models/meta-models/Muse-Glimmer-30B-GGUF \
    --include "*Q4_K_M.gguf"

(同じリポジトリ内に異なる量子化のggufファイルをたくさん置いているものが多いので、--includeオプションを使用して目的のものだけをダウンロードしないとディスクがパンクする)

image.png

--include オプションで "*Q4_K_M.gguf" とすることで、接尾辞が Q4_K_M.gguf となっているファイルを1回のコマンドで全部ダウンロード可能。

マルチモーダル(画像読み取り・TTS・映像読み取り)モデルでは mmproj とかいう接頭辞でウェイトが分割されているものが多い。

MTPやDFlashで知られる投機的推論用ドラフトネットワークも本体とは切り離されていることが多いので、使用する場合はダウンロードしておく必要がある。

もちろんダウンロードにはそれなりに時間かかるので待つ。
(ときどきhuggingfaceの応答がめっさ遅くなるので、もうそれは待つしかない・・・)

ダウンロードが完了したら、下記のようなディレクトリ構成になってるはず。(tree ~/ai)

/home/<ユーザー名>/ai
└── models
    └── meta-models
        └── Muse-Glimmer-30B-GGUF
            ├── Muse-Glimmer-30B-KQuant-17GB-Q4_K_M.gguf
            ├── dflash-Muse-Glimmer-30B-Q4_K_M.gguf
            └── mmproj-Muse-Glimmer-30B-Q4_K_M.gguf

この手順を使って、試したいオープンウェイトをダウンロードしていく。

7.3. [(参考情報) Hugging Faceでのオープンウェイト(AIモデル)の探し方]

Hugging Faceでオープンウェイトを探す場合は、だいたい検索テキストにモデル名接頭辞を入れたら出てくる。

image.png

だいたいは公式が出しているウェイトを選択して、

image.png

右側にある「Quantizations」の右側にある「NNN models」をクリックして、

image.png

絞り込みの検索ボックスに自分の目的のものを打ったら、

image.png

だいたいそれぞれLLMエンジンのリポジトリがあるので、それを使う
(llama.cppならggml-orgのものを使えばハズレはない)

不用意になんかへんなところからダウンロードして使っても、今は何も起きないとは思うけど・・・。
まぁ自己防衛のために有名どころ(unsloth、ggml-org、各ベンダー公式とか)しか使わないようにしておいたほうがいいと思う。

8. [vLLMの稼働]

(vLLMを使いたい人用 : llama.cppだけを使う人はこの項番スルーでOK)

8.1. [(vLLM) docker pullでエンジン取得(ダウンロード)]

vLLM公式の書いている通り( https://github.com/vllm-project/vllm/releases )に、ROCm用のdockerイメージを取得する。もしバージョン指定するのであれば、latest の部分を v0.30.0 とかに変えればOK。

え?何?ROCm公式( https://rocm.docs.amd.com/projects/ai-ecosystem/en/latest/inference/vllm.html )に書いてるvLLM?(rocm/vllm:rocm10.0.0_ubuntu24.04_py3.14_pytorch_2.12.0_vllm_0.27.0)
あれはROCm公式が勝手に動くって言ってるだけの別物なので消しちゃってOK。

docker pull vllm/vllm-openai-rocm:latest

ちなみに、latestって書いてるからって起動時に自動的に更新されるってわけではなく、「その時最新のものが一度だけ取得される」っていう意味なので、アプデされたものを試したい場合は毎回上記コマンドを実行して最新バージョンをダウンロードしてくる必要がある。

8.2. [(vLLM) docker compose用設定ファイルの作成]

「3.3.ユーザー設定」 で LLMを稼働させるユーザー を決めてるはずなので、そのユーザーのコントロール下のいうほど深くないところにディレクトリを作成し、その中に空の docker-compose.yml を作成する。

今回は、/home/<ユーザー名>/A-dock/vllm-gemma4-12B-INT4/docker-compose.yml として作成。

# docker compose管理用のディレクトリを作成 (-p オプションで存在しない階層を自動作成)
mkdir -p ~/A-dock/vllm-gemma4-12B-INT4

# 何もしないコマンド「:」の結果を、~/A-dock/vllm-gemma4-12B-INT4/docker-compose.yml に追記
# つまり、何も書かれていないファイルを作成(もしすでに存在しても空行を追加するだけ)
: >> ~/A-dock/vllm-gemma4-12B-INT4/docker-compose.yml

作成したdocker-compose.ymlに、vLLM起動用のあれこれを追加して、上書き保存
(viエディタでもnanoエディタでも好きなの使って)

services:
  vllm:
    container_name: vLLM-gemma4-12B-INT4
    image: vllm/vllm-openai-rocm:latest

    entrypoint:
      - vllm
      - serve

    stdin_open: true
    tty: true
    restart: unless-stopped

    network_mode: host
    ipc: host

    devices:
      - /dev/kfd:/dev/kfd
      - /dev/dri:/dev/dri

    group_add:
      - video

    cap_add:
      - SYS_PTRACE

    security_opt:
      - seccomp=unconfined

    ulimits:
      memlock:
        soft: -1
        hard: -1

    environment:
      VLLM_CACHE_ROOT: "/root/.cache/vllm"
      HF_HOME: "/root/.cache/huggingface"
      HF_HUB_OFFLINE: "1"
      TRANSFORMERS_OFFLINE: "1"

    volumes:
      - "${HOME}/.cache:/root/.cache"
      - "${HOME}/ai/.cache/vllm:/root/.cache/vllm"
      - "${HOME}/ai/models:/app/models"

    command:
      - "/app/models/google/gemma-4-12B-it-qat-w4a16-ct"
      - --host
      - "0.0.0.0"
      - --port
      - "8118"
      - --enable-auto-tool-choice
      - --tool-call-parser
      - "gemma4"
      - --reasoning-parser
      - "gemma4"
      - --max-model-len
      - "32768"
      - --kv-cache-dtype
      - "fp8"
それぞれのパラメータの指定内容の説明 (クリックで展開...)

1. 基本情報・識別設定 (Identity & Basic)

パラメータ名 設定値/内容 説明(初心者向け)
container_name vLLM-gemma4-12B-INT4 コンテナの名前。後からこの名前を指定して操作する。
image vllm/vllm-openai-rocm:latest docker pullで指定したイメージ名。このイメージのコンテナを起動するという意味。
entrypoint (配列) コンテナが起動したときに、最初に実行するプログラムを上書き定義。「vllm serve」を実行するように指定。
stdin_open: true, tty: true (値なし) コンテナを対話モードで動かすための設定。(なくても動くけど念のため)
restart: unless-stopped (値なし) コンテナは「手動で止める(stop)まで」起動し続ける、という意味。ホスト再起動時に自動起動はしない。手動起動させる必要がある。

2. ハードウェア・ネットワークアクセス設定 (Hardware & Network Access)

パラメータ名 設定値/内容 説明(初心者向け)
network_mode host ホストOSと同じネットワークを使う設定です。これにより、コンテナが使うポートを外部から直接アクセスさせる。
ipc host ホストOSと 同じ内部通信環境(IPC) を共有。リソース共有設定。
devices /dev/kfd, /dev/dri GPUや関連ハードウェアへのアクセス権を与える。AMD GPU (ROCm) が動作するために必須のインターフェース。
group_add video コンテナ内のプロセスに「ビデオ関連のグループ」の権限を追加。
cap_add SYS_PTRACE プロセスの監視やデバッグに必要な特殊なシステム権限を付与。プロセスがkillされたりしないように。
security_opt seccomp=unconfined コンテナへのセキュリティ制限(サンドボックス)の一部を解除。
ulimits memlock: ... OSによってメモリが勝手に解放されるのを防ぐ。

3. データ永続化と環境変数 (Data Persistence & Environment)

パラメータ名 設定値/内容 説明(初心者向け)
environment 各種変数の設定 コンテナ内部で使用する環境変数。
---- VLLM_CACHE_ROOT vLLMのキャッシュを/root/.cache/vllmに設定。
---- HF_HOME huggingfaceの(ダウンロード)キャッシュ場所を /root/.cache/huggingfaceに設定。
---- HF_HUB_OFFLINE、TRANSFORMERS_OFFLINE すでにダウンロード済みのウェイトを使いますよ指定。
volumes (パスのマッピング) データを永続的に保存する場所を設定。ローカルPC上のフォルダを、コンテナ内の特定の場所に紐づけ。これによりコンテナを削除してもデータは残るし、コンテナからホスト上のファイルの直接読み書きが可能になる。
---- /root/.cache コンテナの/root/.cacheをホスト上の実体${HOME}/.cacheにマッピング。
---- /root/.cache/vllm コンテナの/root/.cache/vllmをホスト上の実体${HOME}/ai/.cache/vllmにマッピング。
---- /app/models コンテナの/app/modelsをホスト上の実体${HOME}/ai/modelsにマッピング。

4. 実行されるコマンド (The Command)

コマンド要素 設定値/内容 説明(初心者向け)
モデルの指定 /app/models/google/... 動作させるモデルのコンテナ上でのパス。コンテナの/app/modelsはホスト上の実体${HOME}/ai/modelsにマッピングされているため、実際に開かれるのは${HOME}/ai/models/google/...となる。
--host "0.0.0.0" (引数) vLLMをすべてのネットワークインターフェースで待機させるように指示。(外部からの接続を受け付けるため)
--port "8118" (引数) vLLMの待ち受けポートを8118番に設定。
--enable-auto-tool-choice, --tool-call-parser, --reasoning-parser (引数) vLLM公式のレシピ( https://recipes.vllm.ai/Google/gemma-4-12B-it )を参照して、自分の手持ちモデルにあわせてそれっぽいのを設定。
--max-model-len "32768" (引数) 一度に処理できる文章(トークン)の最大長を32,768個に設定。
--kv-cache-dtype "fp8" (引数) AIが計算過程で使う**キャッシュデータの精度(データ型)**をFP8に指定。

8.3. [(vLLM) vLLM起動]

起動はめちゃくちゃ時間がかかる
(キャッシュ設定がうまくいっていれば、なぜか3回目以降に起動が早くなる)

何かひとつパラメータを変える度にキャッシュが効かなくなって、起動時間が初回同様になる。

Gemma4 12B 4bit量子化クラスのモデルを起動した場合は以下のとおり。
(vllm-openai-rocm:v0.29)

段階 起動時間 短縮量(分) 短縮の割合(%)
初回起動 43分 - -
2回目 31分 12分 約27.9%
3回目 9分 22分 約71.0%
4回目 8分 1分 約10.8%

起動にはびっくりするくらい時間かかるので、気長に待つこと。
(何も出力されずvLLMコンパイルに1分)
(vLLMのロゴが出るまでに1分30秒)
(完全起動するまでにGPUが不定期に鳴き声を上げるはず)
(完全起動時はログに Application startup complete. が出力される)
(すべての起動が完了したら、dキーを押してログ閲覧をデタッチするといい)
(ログ見るのが面倒ならはじめから docker compose up -d とすればOK)

参考:初回起動ログ (クリックで展開...)
workstation:~/A-dock/vllm-gemma4-12B-INT4$ docker compose up
Attaching to vLLM-gemma4-12B-INT4
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 01:31:13 [api_utils.py:347] 
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 01:31:13 [api_utils.py:347]        █     █     █▄   ▄█
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 01:31:13 [api_utils.py:347]  ▄▄ ▄█ █     █     █ ▀▄▀ █  version 0.29.0
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 01:31:13 [api_utils.py:347]   █▄█▀ █     █     █     █  model   /app/models/google/gemma-4-12B-it-qat-w4a16-ct
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 01:31:13 [api_utils.py:347]    ▀▀  ▀▀▀▀▀ ▀▀▀▀▀ ▀     ▀
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 01:31:13 [api_utils.py:347] 
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 01:31:13 [api_utils.py:286] non-default args: {'model_tag': '/app/models/google/gemma-4-12B-it-qat-w4a16-ct', 'enable_auto_tool_choice': True, 'tool_call_parser': 'gemma4', 'host': '0.0.0.0', 'port': 58000, 'model': '/app/models/google/gemma-4-12B-it-qat-w4a16-ct', 'max_model_len': 32768, 'reasoning_parser': 'gemma4', 'kv_cache_dtype': 'fp8'}
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 01:31:13 [model.py:684] Resolved architecture: Gemma4UnifiedForConditionalGeneration
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 01:31:13 [model.py:2021] Using max model len 32768
vLLM-gemma4-12B-INT4  | (APIServer pid=1) [transformers] `SiglipImageProcessorFast` is deprecated. The `Fast` suffix for image processors has been removed; use `SiglipImageProcessor` instead.
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 01:31:34 [cache.py:335] Using fp8 data type to store kv cache. It reduces the GPU memory footprint and boosts the performance. Meanwhile, it may cause accuracy drop without a proper scaling factor
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 01:32:38 [arg_utils.py:2929] Raising max_num_batched_tokens from 2048 to 2496 to accommodate 'video' input for prefix-LM model /app/models/google/gemma-4-12B-it-qat-w4a16-ct.
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 01:32:38 [scheduler.py:277] Chunked prefill is enabled with max_num_batched_tokens=2496.
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 01:32:38 [config.py:252] Gemma4 model has heterogeneous head dimensions {'sliding_attention': 256, 'full_attention': 512}. FA4 not available, forcing TRITON_ATTN backend.
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 01:32:38 [kernel.py:369] Final IR op priority after setting platform defaults: IrOpPriorityConfig(rms_norm=['native'], fused_add_rms_norm=['native'])
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:34:51 [core.py:123] Initializing a V1 LLM engine (v0.29.0) with config: model='/app/models/google/gemma-4-12B-it-qat-w4a16-ct', speculative_config=None, tokenizer='/app/models/google/gemma-4-12B-it-qat-w4a16-ct', skip_tokenizer_init=False, tokenizer_mode=auto, revision=None, tokenizer_revision=None, trust_remote_code=False, dtype=torch.bfloat16, max_seq_len=32768, download_dir=None, load_format=auto, tensor_parallel_size=1, pipeline_parallel_size=1, data_parallel_size=1, decode_context_parallel_size=1, dcp_comm_backend=ag_rs, disable_custom_all_reduce=True, quantization=compressed-tensors, quantization_config=None, enforce_eager=False, enable_return_routed_experts=False, kv_cache_dtype=fp8, device_config=cuda, structured_outputs_config=StructuredOutputsConfig(backend='auto', disable_any_whitespace=False, disable_additional_properties=False, reasoning_parser='gemma4', reasoning_parser_plugin='', enable_in_reasoning=False), observability_config=ObservabilityConfig(show_hidden_metrics_for_version=None, otlp_traces_endpoint=None, collect_detailed_traces=None, per_request_spec_decode_metrics='none', kv_cache_metrics=False, kv_cache_metrics_sample=0.01, cudagraph_metrics=False, enable_layerwise_nvtx_tracing=False, enable_mfu_metrics=False, enable_mm_processor_stats=False, enable_logging_iteration_details=False, jit_monitor_mode='warn', jit_monitor_verbose=False), seed=0, served_model_name=/app/models/google/gemma-4-12B-it-qat-w4a16-ct, enable_prefix_caching=True, enable_chunked_prefill=True, pooler_config=None, compilation_config={'mode': <CompilationMode.VLLM_COMPILE: 3>, 'debug_dump_path': None, 'cache_dir': '', 'compile_cache_save_format': 'binary', 'backend': 'inductor', 'custom_ops': ['+sparse_attn_indexer', 'none'], 'ir_enable_torch_wrap': True, 'splitting_ops': ['vllm::unified_attention_with_output', 'vllm::unified_mla_attention_with_output', 'vllm::mamba_mixer2', 'vllm::mamba_mixer', 'vllm::short_conv', 'vllm::qwen4_exp_compute_ple_ngram_ids', 'vllm::qwen4_exp_ple_short_conv', 'vllm::qwen4_exp_qsa_with_output', 'vllm::linear_attention', 'vllm::qwen_gdn_attention_core', 'vllm::qwen_gdn_attention_core_fused_norm_packed', 'vllm::gdn_attention_core_xpu', 'vllm::olmo_hybrid_gdn_full_forward', 'vllm::sparse_attn_indexer', 'vllm::rocm_aiter_sparse_attn_indexer', 'vllm::deepseek_v4_attention', 'vllm::hpc_rope_norm_forward', 'vllm::unified_kv_cache_update', 'vllm::unified_mla_kv_cache_update'], 'compile_mm_encoder': False, 'cudagraph_mm_encoder': False, 'encoder_cudagraph_token_budgets': [], 'encoder_cudagraph_max_vision_items_per_batch': 0, 'encoder_cudagraph_max_frames_per_batch': None, 'compile_sizes': [], 'compile_ranges_endpoints': [2496], 'inductor_compile_config': {'enable_auto_functionalized_v2': False, 'combo_kernels': True, 'benchmark_combo_kernel': True}, 'inductor_passes': {}, 'cudagraph_mode': <CUDAGraphMode.FULL_AND_PIECEWISE: (2, 1)>, 'cudagraph_num_of_warmups': 1, 'cudagraph_capture_sizes': [1, 2, 4, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128, 136, 144, 152, 160, 168, 176, 184, 192, 200, 208, 216, 224, 232, 240, 248, 256, 272, 288, 304, 320, 336, 352, 368, 384, 400, 416, 432, 448, 464, 480, 496, 512], 'cudagraph_copy_inputs': False, 'cudagraph_specialize_lora': True, 'use_inductor_graph_partition': False, 'pass_config': {'fuse_norm_quant': False, 'fuse_act_quant': False, 'fuse_attn_quant': False, 'enable_sp': False, 'fuse_gemm_comms': False, 'enable_qk_norm_rope_fusion': False, 'fuse_rope_kvcache_cat_mla': False, 'fuse_act_padding': False, 'fuse_qk_norm_rope_kvcache': False}, 'max_cudagraph_capture_size': 512, 'dynamic_shapes_config': {'type': <DynamicShapesType.BACKED: 'backed'>, 'evaluate_guards': False, 'assume_32_bit_indexing': False}, 'local_cache_dir': None, 'fast_moe_cold_start': False, 'static_all_moe_layers': []}, kernel_config=KernelConfig(ir_op_priority=IrOpPriorityConfig(rms_norm=['native'], fused_add_rms_norm=['native']), enable_flashinfer_autotune=True, enable_cutedsl_warmup=True, enable_jit_warmup=True, enable_bf16x3_router_gemm=False, moe_backend='auto', linear_backend='auto')
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) [transformers] `SiglipImageProcessorFast` is deprecated. The `Fast` suffix for image processors has been removed; use `SiglipImageProcessor` instead.
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:35:06 [parallel_state.py:1775] world_size=1 rank=0 local_rank=0 distributed_init_method=file:///tmp/vllm_dist_ae25a998b17e4d51a8c72e11562c0c79 backend=nccl
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:35:06 [parallel_state.py:2119] rank 0 in world size 1 is assigned as DP rank 0, PP rank 0, PCP rank 0, TP rank 0, EP rank N/A, EPLB rank N/A
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:35:06 [gpu_worker.py:429] Using V2 Model Runner
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:35:11 [model_runner.py:382] Loading model from scratch...
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:35:11 [kernel.py:369] Final IR op priority after setting platform defaults: IrOpPriorityConfig(rms_norm=['native'], fused_add_rms_norm=['native'])
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:35:11 [compressed_tensors_wNa16.py:137] Using RDNAHybridW4A16LinearKernel for CompressedTensorsWNA16
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:35:15 [rocm.py:638] Using TRITON_ATTN backend (selected via --attention-backend).
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) WARNING 09-19 01:35:15 [activation.py:444] [ROCm] PyTorch's native GELU with tanh approximation is unstable with torch.compile. For native implementation, fallback to 'none' approximation. The custom kernel implementation is unaffected.
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) WARNING 09-19 01:35:17 [compilation.py:1354] Op 'sparse_attn_indexer' doesn't exist (or wasn't imported/registered), enabling with '+sparse_attn_indexer' has no effect
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:35:17 [weight_utils.py:863] Filesystem type for checkpoints: EXT4. Checkpoint size: 9.56 GiB. Available RAM: 24.82 GiB.
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:35:17 [weight_utils.py:886] Auto-prefetch is disabled because the filesystem (EXT4) is not a recognized network FS (NFS/Lustre). If you want to force prefetching, start vLLM with --safetensors-load-strategy=prefetch.
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) 
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:35:48 [default_loader.py:430] Loading weights took 31.18 seconds
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:36:06 [model_runner.py:404] Model loading took 8.1 GiB memory and 56.323656 seconds
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:36:06 [utils.py:306] Using LBNHC KV cache layout.
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:38:06 [encoder_runner.py:131] Encoder cache will be initialized with a budget of 2496 tokens, and profiled with 1 video items of the maximum feature size.
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) /usr/local/lib/python3.12/dist-packages/torch/_dynamo/variables/functions.py:2459: UserWarning: Dynamo detected a call to a `functools.lru_cache`-wrapped function at 'rdna_hybrid_w4a16.py:550'. Dynamo ignores the cache wrapper and directly traces the wrapped function. Silent incorrectness is only a *potential* risk, not something we have observed. Enable TORCH_LOGS=+dynamo for a DEBUG stack trace.
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) 
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) This call originates from:
vLLM-gemma4-12B-INT4  | (EngineCore pid=53)   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/kernels/linear/mixed_precision/rdna_hybrid_w4a16.py", line 550, in apply_weights
vLLM-gemma4-12B-INT4  | (EngineCore pid=53)     cu_count = num_compute_units()
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) 
vLLM-gemma4-12B-INT4  | (EngineCore pid=53)   torch._dynamo.utils.warn_once(msg)
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:39:32 [backends.py:1094] Using cache directory: /root/.cache/vllm/torch_compile_cache/2de10a5e3a/rank_0_0/backbone for vLLM's torch.compile
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:39:32 [backends.py:1155] Dynamo bytecode transform time: 66.70 s
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:43:36 [backends.py:393] Compiling a graph for compile range (1, 2496) takes 241.15 s
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:44:16 [backends.py:920] collected artifacts: 49 entries, 6 artifacts, 19517618 bytes total
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:44:16 [decorators.py:719] saved AOT compiled function to /root/.cache/vllm/torch_compile_cache/torch_aot_compile/a5e330549b40ca74053a3dc7c84804e8a2df982231f026651078726291798656/rank_0_0/model
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:44:16 [monitor.py:53] torch.compile took 351.11 s in total
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:44:24 [monitor.py:81] Initial profiling/warmup run took 7.80 s
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) 
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) 
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:56:51 [model_runner.py:960] Graph capturing finished in 740 secs, took 5.17 GiB
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:56:53 [gpu_worker.py:625] Available KV cache memory: 13.22 GiB
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:56:53 [gpu_worker.py:640] CUDA graph memory profiling is enabled (default since v0.21.0). The current --gpu-memory-utilization=0.9200 is equivalent to --gpu-memory-utilization=0.7555 without CUDA graph memory profiling. To maintain the same effective KV cache size as before, increase --gpu-memory-utilization to 1.0000. To disable, set VLLM_MEMORY_PROFILER_ESTIMATE_CUDAGRAPHS=0.
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:56:54 [kv_cache_utils.py:2032] GPU KV cache size: 370,208 tokens, Maximum concurrency for 32,768 tokens per request: 11.30x
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:56:55 [kernel_warmup.py:124] JIT kernel warmup starting.
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:56:55 [kernel_warmup.py:134] JIT kernel warmup finished in 0.00s.
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 01:56:55 [ll_bf16.py:30] cuteDSL (CUTLASS Python) not available, ll_bf16_gemm disabled
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) 
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) 
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 02:12:10 [model_runner.py:960] Graph capturing finished in 916 secs, took 5.22 GiB
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 02:12:10 [gpu_worker.py:797] CUDA graph pool memory: 5.22 GiB (actual), 5.24 GiB (estimated), difference: 0.02 GiB (0.4%).
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 02:12:10 [gpu_worker.py:860] Free memory on device (31.79/31.86 GiB) on startup. Desired GPU memory utilization is (0.92, 29.31 GiB). Actual usage is 10.45 GiB for consumed memory (weights + non-torch), 5.64 GiB for peak activation, and 5.22 GiB for CUDAGraph memory. Replace gpu_memory_utilization config with `--kv-cache-memory=8435354543` (7.86 GiB) to fit into requested memory, or `--kv-cache-memory=11100750848` (10.34 GiB) to fully utilize gpu memory. Current kv cache memory in use is 13.22 GiB.
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 02:12:58 [jit_monitor.py:85] Kernel JIT monitor activated; monitored JIT compilations during inference will use mode=warn.
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 02:13:00 [torch_utils.py:277] Reducing Torch threads from 12 to 1 for serving. Set OMP_NUM_THREADS in the external environment to override.
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 02:13:00 [core.py:361] init engine (profile, create kv cache, warmup model) took 2213.38 s (compilation: 351.11 s)
vLLM-gemma4-12B-INT4  | (EngineCore pid=53) INFO 09-19 02:13:02 [kernel.py:369] Final IR op priority after setting platform defaults: IrOpPriorityConfig(rms_norm=['native'], fused_add_rms_norm=['native'])
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:02 [entry.py:135] Supported tasks: ['generate']
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:04 [parser_manager.py:36] "auto" tool choice has been enabled.
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:13 [hf.py:547] Detected the chat template content format to be 'openai'. You can set `--chat-template-content-format` to override this.
vLLM-gemma4-12B-INT4  | (APIServer pid=1) WARNING 09-19 02:13:14 [base.py:278] Multi-modal warmup failed
vLLM-gemma4-12B-INT4  | (APIServer pid=1) WARNING 09-19 02:13:14 [base.py:290] Readonly multi-modal warmup failed
vLLM-gemma4-12B-INT4  | (APIServer pid=1) WARNING 09-19 02:13:14 [model.py:1759] Default vLLM sampling parameters have been overridden by the model's `generation_config.json`: `{'temperature': 1.0, 'top_k': 64, 'top_p': 0.95}`. If this is not intended, please relaunch vLLM instance with `--generation-config vllm`.
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [entry.py:139] Starting vLLM server on http://0.0.0.0:58000
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:61] Available routes are:
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /openapi.json, Methods: HEAD, GET
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /docs, Methods: HEAD, GET
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /docs/oauth2-redirect, Methods: HEAD, GET
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /redoc, Methods: HEAD, GET
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /load, Methods: GET
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /version, Methods: GET
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /health, Methods: GET
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /metrics, Methods: GET
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /tokenize, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /detokenize, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /v1/models, Methods: GET
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /ping, Methods: GET
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /ping, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /invocations, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /v1/chat/completions, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /v1/chat/completions/batch, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /v1/responses, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /v1/responses/{response_id}, Methods: GET
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /v1/responses/{response_id}/cancel, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /v1/completions, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /v1/messages, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /v1/messages/count_tokens, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /generative_scoring, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /scale_elastic_ep, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /is_scaling_elastic_ep, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /v1/chat/completions/render, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /v1/messages/render, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /v1/completions/render, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /v1/chat/completions/derender, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /v1/completions/derender, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO 09-19 02:13:14 [launcher.py:70] Route: /inference/v1/generate, Methods: POST
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO:     Started server process [1]
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO:     Waiting for application startup.
vLLM-gemma4-12B-INT4  | (APIServer pid=1) INFO:     Application startup complete.

docker composeは、「起動するコンテナのディレクトリに移動してdocker compose」 するか、「ディレクトリ移動せずdocker composeファイルを指定して起動」 するかのいずれかの方法で起動する。

方法1:<コンテナのディレクトリに移動してから起動する場合>

# cdコマンドでdocker-compose.ymlファイルがあるディレクトリに移動
cd ~/A-dock/vllm-gemma4-12B-INT4/

# docker composeコマンドでコンテナを起動(初回はup, 2回目以降はrestart)
# 毎回upでもいいけど、なんとなくキャッシュ消えそうでいやなので2回目以降はrestartしてる
docker compose up

停止する場合は、ディレクトリそのままで

docker compose stop

方法2:<-fオプションでdocker-compose.ymlファイルを指定して起動する場合>

# docker composeコマンドでコンテナを起動(初回はup, 2回目以降はrestart)
# 毎回upでもいいけど、なんとなくキャッシュ消えそうでいやなので2回目以降はrestartしてる
docker compose -f ~/A-dock/vllm-gemma4-12B-INT4/docker-compose.yml up

停止する場合は、

docker compose -f ~/A-dock/vllm-gemma4-12B-INT4/docker-compose.yml stop

8.4. [(Open WebUI) docker pullでOpen WebUI取得(ダウンロード)]

Open WebUI公式( https://docs.openwebui.com/getting-started/quick-start/ )に書いてある通りに、dockerイメージを取得する。公式ではバージョン指定するみたいなことを書いてるけど、無視してmainブランチをそのまま引っ張てくるほうが楽。

docker pull ghcr.io/open-webui/open-webui:main

8.5. [(Open WebUI) docker compose用設定ファイルの作成]

「3.3.ユーザー設定」 で LLMを稼働させるユーザー を決めてるはずなので、そのユーザーのコントロール下のいうほど深くないところにディレクトリを作成し、その中に空の docker-compose.yml を作成する。

今回は、/home/<ユーザー名>/A-dock/open-webui/docker-compose.yml として作成。

# docker compose管理用のディレクトリを作成 (-p オプションで存在しない階層を自動作成)
mkdir -p ~/A-dock/open-webui

# 何もしないコマンド「:」の結果を、~/A-dock/open-webui/docker-compose.yml に追記
# つまり、何も書かれていないファイルを作成(もしすでに存在しても空行を追加するだけ)
: >> ~/A-dock/open-webui/docker-compose.yml

作成したdocker-compose.ymlに、OpenWebUI起動用のあれこれを追加して、上書き保存
(viエディタでもnanoエディタでも好きなの使って)

WEBUI_SECRET_KEY には、openssl rand -hex 32コマンドを実行して出力されたなんかグチャグチャの文字列を設定しとけって公式に書いてる。

services:
  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    ports:
      - "8181:8080"
    volumes:
      - open-webui:/app/backend/data
    extra_hosts:
      - host.docker.internal:host-gateway
    environment:
      - WEBUI_SECRET_KEY=**************************************
      - OPENAI_API_BASE_URL=http://host.docker.internal:8181/v1
    restart: unless-stopped

volumes:
  open-webui:

8.6. [(Open WebUI) 起動]

docker composeは、「起動するコンテナのディレクトリに移動してdocker compose」 するか、「ディレクトリ移動せずdocker composeファイルを指定して起動」 するかのいずれかの方法で起動する。

方法1:<コンテナのディレクトリに移動してから起動する場合>

# cdコマンドでdocker-compose.ymlファイルがあるディレクトリに移動
cd ~/A-dock/open-webui

# docker composeコマンドでコンテナを起動(起動は早いので毎回upでいい)
docker compose up

停止する場合は、ディレクトリそのままで

docker compose stop

方法2:<-fオプションでdocker-compose.ymlファイルを指定して起動する場合>

# docker composeコマンドでコンテナを起動(初回はup, 2回目以降はrestart)
# 毎回upでもいいけど、なんとなくキャッシュ消えそうでいやなので2回目以降はrestartしてる
docker compose -f ~/A-dock/open-webui/docker-compose.yml up

停止する場合は、

docker compose -f ~/A-dock/open-webui/docker-compose.yml stop

8.7. [vLLM + Open WebUIで動作確認]

まず、AI(vLLM)側と、OpenWebUI側待ち受けポートが正しくオープン状態になっているかどうかを確認

# lsofコマンドがインストールされてるなら
lsof -i
# lsofコマンドがインストールされてないなら
ss -tulnp | grep LISTEN

もし、前述のconfigのまま設定したのであれば、8118(vLLM) と 8181(Open WebUI)が開いているはず。
開いてなければそもそも起動できていないか、何らかの制限によりポートを開けることができていないので
sudo journalctl -xeなどで確認。

もし、ホスト(dockerコンテナを動かしている端末)とは別のローカルネットワーク端末でAIを使おうとしてるなら
ufwなどで外部から接続を許可するように穴をあけておく必要がある。(ファイヤーウォール設定)

ポートが正しく待ち受け状態となっているなら、ブラウザから http://<vLLM稼働サーバーのローカルIPアドレス>:8181/ にアクセスすることで、OpenWebUIの初期設定画面につながるはず。

あとはOpenWebUIの設定画面からvLLMの待ち受けポートに対して接続するように設定すれば問題なくAIチャット画面が出来上がる。vLLMへの接続についてはOpen WebUIを参照( https://docs.openwebui.com/getting-started/quick-start/connect-a-provider/ )。たぶんConnectionでOpenAI APIをONにして、vLLMのエンドポイント( http://host.docker.internal:8118/v1 )とかを設定するだけでよかったはず。

image.png

バグなのか仕様なのかよくわからんけど、初回のプロンプトは
(例えば「テスト」などと打ち込んでも)一切応答は返ってこない。
vLLMのログ上では応答しているような素振りは見せるものの、やはり画面上に応答は返ってこない。

おそらく、
Triton kernel JIT compilation during inference: kernel_unified_attention. This causes a latency spike; consider extending warmup to cover this shape/config.
という部分で何かを待っている状態と思われる。

一度プロンプトに打ち込んでから生成を停止ボタンで停止し、
しばらく待ってから新しいプロンプトで実行することでやっと応答が返ってくる状態になる。

こんなんじゃ実務では使えない。vLLMはCUDA用と割り切って窓から投げ捨ててみんなllama.cppにしよ!

9. [llama.cppの稼働]

(vLLMだけを極めたい人はこの項番スルーでOK)

9.1. [(llama.cpp) docker pullでエンジン取得(ダウンロード)]

llama.cppのGitHub上のドキュメント( https://github.com/ggml-org/llama.cpp/blob/master/docs/docker.md )にある通り、ROCm用のフルイメージを取得。

docker pull ghcr.io/ggml-org/llama.cpp:full-rocm

9.2. [(llama.cpp) docker compose用設定ファイルの作成]

「3.3.ユーザー設定」 で LLMを稼働させるユーザー を決めてるはずなので、そのユーザーのコントロール下のいうほど深くないところにディレクトリを作成し、その中に空の docker-compose.yml を作成する。

今回は、/home/<ユーザー名>/A-dock/llamacpp-Muse-Glimmer-30B/docker-compose.yml として作成。

# docker compose管理用のディレクトリを作成 (-p オプションで存在しない階層を自動作成)
mkdir -p ~/A-dock/llamacpp-Muse-Glimmer-30B

# 何もしないコマンド「:」の結果を、~/A-dock/llamacpp-Muse-Glimmer-30B/docker-compose.yml に追記
# つまり、何も書かれていないファイルを作成(もしすでに存在しても空行を追加するだけ)
: >> ~/A-dock/llamacpp-Muse-Glimmer-30B/docker-compose.yml

作成したdocker-compose.ymlに、llama.cpp起動用のあれこれを追加して、上書き保存
(viエディタでもnanoエディタでも好きなの使って)

services:
  server:
    image: ghcr.io/ggml-org/llama.cpp:full-rocm
    container_name: llamacpp-server-rocm
    restart: unless-stopped
    network_mode: host
    ipc: host
    devices:
      - /dev/kfd:/dev/kfd
      - /dev/dri:/dev/dri
    group_add:
      - video
    cap_add:
      - SYS_PTRACE
    security_opt:
      - seccomp=unconfined
    ulimits:
      memlock:
        soft: -1
        hard: -1

    environment:
      HF_HOME: "/app/models/"

    volumes:
      - "${HOME}/ai/models/meta-models/Muse-Glimmer-30B-GGUF:/app/models"

    command:
      - --server
      - -m
      - "/app/models/Muse-Glimmer-30B-KQuant-17GB-Q4_K_M.gguf"
      - --mmproj
      - "/app/models/mmproj-Muse-Glimmer-30B-Q4_K_M.gguf"
      - --spec-draft-model
      - "/app/models/dflash-Muse-Glimmer-30B-Q4_K_M.gguf"
      - --spec-type
      - "draft-dflash"
      - --spec-draft-n-max
      - "15"
      - -a
      - "Muse-Glimmer-30B"
      - --gpu-layers
      - "99"
      - --gpu-layers-draft
      - "99"
      - -c
      - "224000"
      - -np
      - "4"
      - --threads
      - "6"
      - --batch-size
      - "512"
      - --ubatch-size
      - "256"
      - --load-mode
      - "mlock"
      - --flash-attn
      - "on"
      - --jinja
      - --temp
      - "1.0"
      - --top-p
      - "0.95"
      - --top-k
      - "64"
      - --min-p
      - "0.05"
      - --cache-type-k
      - "q8_0"
      - --cache-type-v
      - "q8_0"
      - --host
      - "0.0.0.0"
      - --port
      - "8181"
それぞれのパラメータの指定内容の説明 (クリックで展開...)

1. 基本情報・識別設定 (Identity & Basic)

パラメータ名 設定値/内容 説明(初心者向け)
container_name llamacpp-server-rocm コンテナの名前。後からこの名前を指定して操作する。
image ghcr.io/ggml-org/llama.cpp:full-rocm docker pullで指定したイメージ名。このイメージのコンテナを起動するという意味。
restart: unless-stopped (値なし) コンテナは「手動で止める(stop)まで」起動し続ける、という意味。ホスト再起動時に自動起動はしない。手動起動させる必要がある。

2. ハードウェア・ネットワークアクセス設定 (Hardware & Network Access)

パラメータ名 設定値/内容 説明(初心者向け)
network_mode host ホストOSと同じネットワークを使う設定です。これにより、コンテナが使うポートを外部から直接アクセスさせる。
ipc host ホストOSと 同じ内部通信環境(IPC) を共有。リソース共有設定。
devices /dev/kfd, /dev/dri GPUや関連ハードウェアへのアクセス権を与える。AMD GPU (ROCm) が動作するために必須のインターフェース。
group_add video コンテナ内のプロセスに「ビデオ関連のグループ」の権限を追加。
cap_add SYS_PTRACE プロセスの監視やデバッグに必要な特殊なシステム権限を付与。プロセスがkillされたりしないように。
security_opt seccomp=unconfined コンテナへのセキュリティ制限(サンドボックス)の一部を解除。
ulimits memlock: ... OSによってメモリが勝手に解放されるのを防ぐ。

3. データ永続化と環境変数 (Data Persistence & Environment)

パラメータ名 設定値/内容 説明(初心者向け)
environment 各種変数の設定 コンテナ内部で使用する環境変数。
---- HF_HOME huggingfaceの(ダウンロード)キャッシュ場所を /root/.cache/huggingfaceに設定。
volumes (パスのマッピング) データを永続的に保存する場所を設定。ローカルPC上のフォルダを、コンテナ内の特定の場所に紐づけ。これによりコンテナを削除してもデータは残るし、コンテナからホスト上のファイルの直接読み書きが可能になる。
---- /app/models コンテナの/app/modelsをホスト上の実体${HOME}/ai/modelsにマッピング。

4. llama.cpp起動コマンドパラメータ

パラメータ 値 説明
--server - llama.cpp を HTTP API サーバーとして起動。
-m / --model /app/models/Muse-Glimmer-30B-KQuant-17GB-Q4_K_M.gguf メインで使う大言語モデルのGGUFファイルを指定。
--mmproj /app/models/mmproj-Muse-Glimmer-30B-Q4_K_M.gguf 画像などのマルチモーダル入力に使う投影モデル。Vision対応用。
--spec-draft-model /app/models/dflash-Muse-Glimmer-30B-Q4_K_M.gguf Speculative Decodingで使うドラフト用の小さいモデル。高速化のため。
--spec-type draft-dflash Speculative Decodingの方式を指定。ここでは draft-dflash。
--spec-draft-n-max 15 ドラフトモデルが一度に作るトークンの最大数。大きいほど速いが重い。
-a / --alias Muse-Glimmer-30B サーバー上でモデルを識別するための名前。
--gpu-layers 99 モデルのレイヤーをGPUに載せる数。99 = ほぼ全レイヤーをGPUに載せる。ROCM使用。
--gpu-layers-draft 99 ドラフトモデルのレイヤーもGPUに載せる。
-c / --ctx-size 224000 コンテキスト長。モデルが一度に覚えておけるトークン数。224k = 非常に長い会話が可能。
-np 4 並列処理数。推論で使うストリーム数。
--threads 6 CPUスレッド数。GPU主体だがCPU補助に使う。
--batch-size 512 一度に処理するトークンのバッチ上限。大きいほど効率が良い。
--ubatch-size 256 マイクロバッチサイズ。内部的な処理単位。
--load-mode mlock モデルをメモリにロックしてスワップさせない。高速・安定。
--flash-attn on Flash Attentionを有効化。長いコンテキストでの計算を高速化。
--jinja - 出力テンプレートに Jinja2 を使う。ChatML形式対応。
--temp 1.0 温度パラメータ。0に近いほど決定的、大きいほどランダム。
--top-p 0.95 Nucleus Sampling。確率上位95%のトークンだけを候補にする。
--top-k 64 各ステップで候補にする最大トークン数。
--min-p 0.05 候補トークンの最小確率しきい値。低品質トークンを除去。
--cache-type-k q8_0 KVキャッシュのK側圧縮形式。q8_0 = 8bit量子化で省メモリ。
--cache-type-v q8_0 KVキャッシュのV側圧縮形式。同上。
--host 0.0.0.0 サーバーが待ち受けるIPアドレス。0.0.0.0 = 全ネットワークインターフェース。
--port 8181 サーバーが待ち受けるポート番号。

参考にしたもの:https://github.com/cypriennn/llamacpp-example

command:部分の指定パラメータについてここに書くとグチャグチャになるので、
とりあえずは「これで動いた!」としてもらって、ひとまず先にすすめてもらったほうがいい。
(あとでどうせマルチモデル用に書き直すことになると思う)

9.3. [(llama.cpp) llama.cpp起動]

llama.cppはvLLMと違って一瞬で起動するので、臆することなくパラメータを変更して最適化を目指すことができる

docker composeは、「起動するコンテナのディレクトリに移動してdocker compose」 するか、「ディレクトリ移動せずdocker composeファイルを指定して起動」 するかのいずれかの方法で起動する。

方法1:<コンテナのディレクトリに移動してから起動する場合>

# cdコマンドでdocker-compose.ymlファイルがあるディレクトリに移動
cd ~/A-dock/llamacpp-Muse-Glimmer-30B

# docker composeコマンドでコンテナを起動(起動は早いので毎回upでいい)
docker compose up

停止する場合は、ディレクトリそのままで

docker compose stop

方法2:<-fオプションでdocker-compose.ymlファイルを指定して起動する場合>

# docker composeコマンドでコンテナを起動(初回はup, 2回目以降はrestart)
# 毎回upでもいいけど、なんとなくキャッシュ消えそうでいやなので2回目以降はrestartしてる
docker compose -f ~/A-dock/llamacpp-Muse-Glimmer-30B/docker-compose.yml up

停止する場合は、

docker compose -f ~/A-dock/llamacpp-Muse-Glimmer-30B/docker-compose.yml stop

9.4. [(llama.cpp) llama.cpp 動作確認]

待ち受けポートが正しくオープン状態になっているかどうかを確認

# lsofコマンドがインストールされてるなら
lsof -i
# lsofコマンドがインストールされてないなら
ss -tulnp | grep LISTEN

もし、前述のconfigのまま設定したのであれば、8181 が開いているはず。
開いてなければそもそも起動できていないか、何らかの制限によりポートを開けることができていないので
sudo journalctl -xeなどで確認。

もし、ホスト(dockerコンテナを動かしている端末)とは別のローカルネットワーク端末でAIを使おうとしてるなら
ufwなどで外部から接続を許可するように穴をあけておく必要がある。(ファイヤーウォール設定)

ポートが正しく待ち受け状態となっているなら、ブラウザから http://<llama.cpp稼働サーバーのローカルIPアドレス>:8181/ にアクセスすることで、WebUIの初期設定画面につながるはず。

image.png

あとは何か打ち込んで、LLMからの応答があるかどうかを確認。
ついでにllama.cppのログを見て、どのくらいの応答速度が出ているのかを見ておくと調整の効果を見極めやすい。

9.5. [(llama.cpp) マルチモデル動的切り替え設定]

前述の仮設定は「1つのモデルを読み込んだ状態で、そのモデルしか使用できない」というもの。
いくらllama.cppは高速起動するといっても、正直これからいろいろテストしていく上で何回もllama.cppを再読み込みするのは非常に億劫なので、 「複数のモデル・設定を先に定義しておいて、チャット画面(WebUI)上で切り替えられるように」 しよう。

9.5.1. [(llama.cpp) マルチモデル用設定ファイル(.ini)作成]

モデル用ディレクトリの作成で ~/ai/models/config ディレクトリを生成しているはずなので、その中にマルチモデル用の事前設定ファイルを作成する。

# また例の如く「:」コマンドで空ファイル作成
: >> ~/ai/models/config/multi-models.ini

このiniファイルの中に、本来であればllama.cppと一緒に渡すモデル起動パラメータを列挙していく。

ただし、引数としてパラメータを渡す場合と若干の違いがあるので注意すること。
公式にも一応書き方が載ってるので参考に。( https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md#model-presets )

一応簡単にルールを書くと、

  • あくまでもコンテナベース(ファイルパス、ディレクトリパスはコンテナ基準のものを指定)
  • 引数のように前にハイフン「-」をつける必要がない
  • 省略形(--ctx-size → -c など)もそのまま使える(もちろん前のハイフン「-」は不要)
  • パラメータはシングルクォートやダブルクォートで囲う必要はない(JSONパラメータはJSONのルールに従う)
  • 1行につき1パラメータ(らしい)
  • 引数が無いもの(--jinja や --flash-attn)は on を指定

ということらしい。

参考までに、こちらの multi-models.ini を内容を提示しておく。


この記事書いた人の multi-models.ini (クリックで展開...)
[*]
batch-size = 2048
ubatch-size = 512
load-mode = mlock
flash-attn = on
cache-type-k = q4_0
cache-type-v = q4_0
chat-template-kwargs = {"reasoning_effort": "low"}

[ggml-org/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-GGUF]
alias = Nemotron-3.5-Lightning-30B-A3B(NVIDIA)
model = /app/models/ggml-org/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-GGUF/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Q4_0.gguf
spec-draft-model = /app/models/ggml-org/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-GGUF/mtp-NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Q4_0.gguf
spec-type = draft-mtp
spec-draft-n-max = 4
gpu-layers = 99
gpu-layers-draft = 99
ctx-size = 262144
jinja = on
temp = 0.95
top-p = 0.95
min-p = 0.05
repeat-last-n = 64
repeat-penalty = 1.07
presence-penalty = 0.00
frequency-penalty = 0.00

[ggml-org/Qwen3.8-27B-GGUF]
alias = Qwen3.8-27B(Alibaba)
model = /app/models/ggml-org/Qwen3.8-27B-GGUF/Qwen3.8-27B-Q4_K_M.gguf
mmproj = /app/models/ggml-org/Qwen3.8-27B-GGUF/mmproj-Qwen3.8-27B-Q8_0.gguf
spec-draft-model = /app/models/ggml-org/Qwen3.8-27B-GGUF/mtp-Qwen3.8-27B-Q4_0.gguf
spec-type = draft-mtp
spec-draft-n-max = 4
gpu-layers = 512
gpu-layers-draft = 512
ctx-size = 262144
temp = 0.95
top-p = 0.95
top-k = 20
min-p = 0.05
repeat-last-n = 64
repeat-penalty = 1.07
presence-penalty = 0.00
frequency-penalty = 0.00

[ggml-org/gemma-4-12B-it-GGUF]
alias = gemma4 12B instruction-tuned(Google)
model = /app/models/ggml-org/gemma-4-12B-it-GGUF/gemma-4-12B-it-Q4_0.gguf
mmproj = /app/models/ggml-org/gemma-4-12B-it-GGUF/mmproj-gemma-4-12B-it-Q8_0.gguf
spec-draft-model = /app/models/ggml-org/gemma-4-12B-it-GGUF/mtp-gemma-4-12B-it-Q4_0.gguf
spec-type = draft-mtp
spec-draft-n-max = 4
ctx-size = 262144
temp = 0.95
top-p = 0.95
top-k = 64
min-p = 0.05
repeat-last-n = 64
repeat-penalty = 1.07
presence-penalty = 0.00
frequency-penalty = 0.00

[ggml-org/gemma-4-31B-it-GGUF]
alias = gemma4 31B instruction-tuned(Google)
model = /app/models/ggml-org/gemma-4-31B-it-GGUF/gemma-4-31B-it-Q4_0.gguf
mmproj = /app/models/ggml-org/gemma-4-31B-it-GGUF/mmproj-gemma-4-31B-it-Q8_0.gguf
spec-draft-model = /app/models/ggml-org/gemma-4-31B-it-GGUF/mtp-gemma-4-31B-it-Q4_0.gguf
spec-type = draft-mtp
spec-draft-n-max = 4
ctx-size = 262144
temp = 0.95
top-p = 0.95
top-k = 64
min-p = 0.05
repeat-last-n = 64
repeat-penalty = 1.07
presence-penalty = 0.00
frequency-penalty = 0.00

[ggml-org/gemma-4-E4B-it-GGUF]
alias = gemma4 E4B instruction-tuned(Google)
model = /app/models/ggml-org/gemma-4-E4B-it-GGUF/gemma-4-E4B-it-Q4_0.gguf
mmproj = /app/models/ggml-org/gemma-4-E4B-it-GGUF/mmproj-gemma-4-E4B-it-Q8_0.gguf
spec-draft-model = /app/models/ggml-org/gemma-4-E4B-it-GGUF/mtp-gemma-4-E4B-it-Q4_0.gguf
spec-type = draft-mtp
spec-draft-n-max = 4
ctx-size = 262144
temp = 0.95
top-p = 0.95
top-k = 64
min-p = 0.05
repeat-last-n = 64
repeat-penalty = 1.07
presence-penalty = 0.00
frequency-penalty = 0.00

[meta-models/Muse-Glimmer-30B-GGUF]
alias = MuseGlimmer 30B(Meta)
model = /app/models/meta-models/Muse-Glimmer-30B-GGUF/Muse-Glimmer-30B-KQuant-Dynamic-Q4_K_XL.gguf
mmproj = /app/models/meta-models/Muse-Glimmer-30B-GGUF/mmproj-Muse-Glimmer-30B-Q4_K_M.gguf
spec-draft-model = /app/models/meta-models/Muse-Glimmer-30B-GGUF/dflash-Muse-Glimmer-30B-Q4_K_M.gguf
spec-type = draft-dflash
spec-draft-n-max = 7
gpu-layers = 99
gpu-layers-draft = 99
ctx-size = 262144
jinja = on
temp = 0.95
top-p = 0.95
top-k = 64
min-p = 0.05
repeat-last-n = 64
repeat-penalty = 1.07
presence-penalty = 0.00
frequency-penalty = 0.00

[unsloth/Devstral-Small-2-24B-Instruct-2512-GGUF]
alias = Devstral Small 2 24B Instruct-2512(MinstralAI)
model = /app/models/unsloth/Devstral-Small-2-24B-Instruct-2512-GGUF/Devstral-Small-2-24B-Instruct-2512-UD-Q4_K_XL.gguf
mmproj = /app/models/unsloth/Devstral-Small-2-24B-Instruct-2512-GGUF/mmproj-BF16.gguf
gpu-layers = 999
ctx-size = 262144
jinja = on
min-p = 0.01
repeat-last-n = 64
repeat-penalty = 1.07
presence-penalty = 0.00
frequency-penalty = 0.00

[llm-jp/llm-jp-4-8b-thinking-gguf]
alias = LLM-jp-4 8B thinking(LLM-jp)
model = /app/models/llm-jp/llm-jp-4-8b-thinking-gguf/llm-jp-4-8b-thinking-Q4_K_M.gguf
ctx-size = 262144
jinja = on
repeat-last-n = 64
repeat-penalty = 1.01
presence-penalty = 0.00
frequency-penalty = 0.00

[llm-jp/llm-jp-4-32b-a3b-thinking-gguf]
alias = LLM-jp-4 32B A3B thinking(LLM-jp)
model = /app/models/llm-jp/llm-jp-4-32b-a3b-thinking-gguf/llm-jp-4-32b-a3b-thinking-Q4_K_M.gguf
ctx-size = 262144
jinja = on
repeat-last-n = 64
repeat-penalty = 1.01
presence-penalty = 0.00
frequency-penalty = 0.00
この記事書いた人の モデルディレクトリツリー (クリックで展開...)

LLM-jp-4のモデルはなんかバグってて、WebUI上からはストレートに使えない。
同じ被害にあってる人の調査内容が大変参考になる:https://naokifkuma3-hash.github.io/llm-jp4-llamacpp-note

/home/u0x4bc6aa3290cf-ai/ai
└── models
    ├── config
    │   └── multi-models.ini
    ├── ggml-org
    │   ├── NVIDIA-Nemotron-3.5-Lightning-30B-A3B-GGUF
    │   │   ├── NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Q4_0.gguf
    │   │   └── mtp-NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Q4_0.gguf
    │   ├── Qwen3.8-27B-GGUF
    │   │   ├── Qwen3.8-27B-Q4_K_M.gguf
    │   │   ├── mmproj-Qwen3.8-27B-Q8_0.gguf
    │   │   └── mtp-Qwen3.8-27B-Q4_0.gguf
    │   ├── gemma-4-12B-it-GGUF
    │   │   ├── gemma-4-12B-it-Q4_0.gguf
    │   │   ├── mmproj-gemma-4-12B-it-Q8_0.gguf
    │   │   └── mtp-gemma-4-12B-it-Q4_0.gguf
    │   ├── gemma-4-31B-it-GGUF
    │   │   ├── gemma-4-31B-it-Q4_0.gguf
    │   │   ├── mmproj-gemma-4-31B-it-Q8_0.gguf
    │   │   └── mtp-gemma-4-31B-it-Q4_0.gguf
    │   └── gemma-4-E4B-it-GGUF
    │       ├── gemma-4-E4B-it-Q4_0.gguf
    │       ├── mmproj-gemma-4-E4B-it-Q8_0.gguf
    │       └── mtp-gemma-4-E4B-it-Q4_0.gguf
    ├── google
    │   └── gemma-4-12B-it-qat-w4a16-ct
    │       ├── README.md
    │       ├── chat_template.jinja
    │       ├── config.json
    │       ├── generation_config.json
    │       ├── model.safetensors
    │       ├── processor_config.json
    │       ├── recipe.yaml
    │       ├── tokenizer.json
    │       └── tokenizer_config.json
    ├── llm-jp
    │   ├── llm-jp-4-32b-a3b-thinking-gguf
    │   │   └── llm-jp-4-32b-a3b-thinking-Q4_K_M.gguf
    │   └── llm-jp-4-8b-thinking-gguf
    │       └── llm-jp-4-8b-thinking-Q4_K_M.gguf
    ├── meta-models
    │   └── Muse-Glimmer-30B-GGUF
    │       ├── Muse-Glimmer-30B-KQuant-17GB-Q4_K_M.gguf
    │       ├── Muse-Glimmer-30B-KQuant-Dynamic-Q4_K_XL.gguf
    │       ├── dflash-Muse-Glimmer-30B-Q4_K_M.gguf
    │       └── mmproj-Muse-Glimmer-30B-Q4_K_M.gguf
    └── unsloth
        └── Devstral-Small-2-24B-Instruct-2512-GGUF
            ├── Devstral-Small-2-24B-Instruct-2512-Q4_K_M.gguf
            ├── Devstral-Small-2-24B-Instruct-2512-UD-Q4_K_XL.gguf
            └── mmproj-BF16.gguf

18 directories, 33 files

9.5.2. [(llama.cpp) マルチモデル切り替え用docker-compose.yml作成]

マルチモデル切り替え用のdocker compose管理ディレクトリと、docker-compose.ymlを作成する。
すでに何か同じ形式のものを作ってるのであればそれをコピーしてもいいかも。

今回は空ディレクトリ ~/A-dock/llamacpp-MultiModelSelect を作成し、その中に docker-compose.yml を作成する。

# ディレクトリ作成
mkdir -p ~/A-dock/llamacpp-MultiModelSelect

# docker-compose.yml作成
: >> ~/A-dock/llamacpp-MultiModelSelect/docker-compose.yml

モデルの設定はすべて前述のINIファイルに書いているので、docker-compose.ymlの内容はシンプル。

services:
  server:
    image: ghcr.io/ggml-org/llama.cpp:full-rocm
    container_name: llamacpp-server-rocm
    restart: unless-stopped
    network_mode: host
    ipc: host
    devices:
      - /dev/kfd:/dev/kfd
      - /dev/dri:/dev/dri
    group_add:
      - video
    cap_add:
      - SYS_PTRACE
    security_opt:
      - seccomp=unconfined
    ulimits:
      memlock:
        soft: -1
        hard: -1

    environment:
      HF_HOME: "/app/models/"

    volumes:
      - "${HOME}/ai/models/:/app/models"

    command:
      - --server
      - --host
      - "0.0.0.0"
      - --port
      - "8181"
      - --threads
      - "6"
      - --parallel
      - "3"
      - --models-preset
      - "/app/models/config/multi-models.ini"
      - --models-max
      - "1"
      - --ui-mcp-proxy

--models-presetに、コンテナから見たINIファイルのファイルパスを指定
--models-maxに、同時に読み込むモデルの最大数を指定(VRAMが許す限り、2とか3とか指定できる)

そのほかは /app/models に割り当てるディレクトリをホスト上の ~/ai/models に変えただけ。

INIファイルを指定しただけでは起動時にモデルは読み込まれず、WebUIなどであとからモデルを指定して読み込むことになる。
利便性を考えるのであれば、このコマンドパラメータで デフォルトで読み込んでおくモデル を指定しておくべき。そうすることでWebUI起動直後にお気に入りのモデルを読み込んだ状態にできる。


9.5.3. [(llama.cpp) マルチモデル切り替え可能llama.cpp起動]

あとは普通にdocker compose upで起動すれば、WebUI上からINIファイルで指定したモデルを選ぶことができるようになっている。モデルのサイズにもよるけど、だいたい30秒もあればモデルをロード可能。
(もしファイルパスとかパラメータがミスってたら、モデル読み込みの段階でエラーが出る)

image.png

同じモデルでパラメータの違いを見極めたりする場合に非常に有用なので、マルチモデル切り替え用設定は作っておくべきだと思う。(ちな k_top とか min_p とかの温度パラメータ系は標準のWebUIの設定で動的に変更できる)


ちなみに前述の設定では「NVIDIA-Nemotron-3.5-Lightning-30B-A3B」が一番応答が早い(70~80 token/s)。
「Gemma4シリーズ」(30~45 token/s)
「Devstral small 2 24B」(25~35 token/s)
「MuseGlimmer 30B」(25~35 token/s)
「Qwen3.8 27B」(25~30 token/s)
「LLM-jp-4シリーズ」(0 token/s というか動かない)

日本語応答の正確さは「Gemma4シリーズ」と「Qwen3.8 27B」に軍配があがり、「NVIDIA-Nemotron-3.5-Lightning-30B-A3B」や「Devstral small 2 24B」はいまいち。

「Gemma4シリーズ」は平気で噓をつく。

巷ではQwen 3.8 Flash-Nextを動かすという潮流が来ているようなので、今後もパラメータなどをいじって挑戦していく予定。


Radeon AI Pro R9700を導入してからやったこと(失敗含む)

下記は失敗を含む手順です。
この記事読む人のために私がたくさん失敗しておきました。
同じ轍を踏む必要はありません。

  1. Ubuntu 26.04 Desktop セットアップ (Serverでやると時間かかるのでDesktopオススメ)
  2. 初期設定
    2.1. ユーザー、M/Bドライバ自動インストール、apt更新、wifi設定、IP設定など
  3. LACTインストール、GRUBの設定
    3.1. githubのLACTリポジトリのリリース(https://github.com/ilya-zlobintsev/LACT/releases/)からUbuntu 26.04用のdebファイルダウンロードしてインストール
    3.2. LACTでGPUの電力210W制限、ファンコントロールカーブ設定、ダウンクロック設定
    3.3. GRUB編集 (GPUダウンクロック設定を書き込むフラグつけないと起動時に反映されない)
  4. ROCm 10の公式ドキュメントを見てROCm 10.0インストール
  5. dockerインストール、huggingface_hubインストール、AI用ディレクトリ設定とか
  6. vLLM(pip)インストール
    6.1. venv作成、uvインストール、vLLM(pip)設定
    6.2. HuggingFaceツールのインストール、モデルダウンロード
    6.3. 起動、しかしいろいろ足りないといわれ断念
  7. ROCm 10の公式ドキュメントを見てvLLM 0.27(docker)インストール
    7.1. docker pull、rocm/vllm:rocm10.0.0_ubuntu24.04_py3.14_pytorch_2.12.0_vllm_0.27.0実行
    7.2. HuggingFaceツールでモデルダウンロード
    7.2.1. Qwen3.8 flash-next ダメ OOM
    7.2.2. Qwen3.6 27B ダメ 量子化対応していない
    7.2.3. gemma4 26B A4B ダメ 量子化対応していない
    7.2.4. RedHatAI/gemma-4-12B-it-FP8-Dynamic ダメ gemma4のツールが動作しない
    7.2.5. mistralai/Ministral-3-14B-Instruct-2512 OK
    ただし、--enforce-eagerを入れないと起動時に
    Hip error: 'operation not permitted when stream is capturing'(900) at /__w/rockrel/rockrel/rocm-libraries/projects/hipblaslt/library/src/amd_detail/hipblaslt.cpp:164
    でフリーズする
    7.2.5.1 バックエンドをAITER固定 ダメ
    7.2.5.1 バックエンドをROCM ATTN固定 ダメ
  8. ROCm 10の公式ドキュメントを無視してvLLM 0.29(docker)インストール
    8.1. docker pull、vllm/vllm-openai-rocm:v0.29.0実行
    8.2. 怒られ (rocm/vllmはbashベース、vllm/vllm-rocmは vllm servベース)
    8.2.1. docker-compose.ymlを書き直して再実行→動いた(mistralai/Ministral-3-14B-Instruct-2512)が遅くて化け化けで品質最悪
    8.2.2. gemma4 26B A4B ダメ 量子化対応していない
    8.2.3. gemma4 12B INT4 動いた 妥当な速さ
    8.2.4. gemma4 31B INT4 ダメ OOM
    8.3. gemma4 12B INT4に絞ってkv-cacheをfp8にしたり、いろいろと調整したり、最終的に--max-model-lenは51200にした
  9. Open WebUI(docker)を導入
    9.1. vLLMに接続できるが、Open WebUIに接続できない→Open WebUI dockerコンテナとホストが同ポートだと不具合?→dockerコンテナとホストのポートを分ける→OK
  10. WebSearch機能を構築→面倒なのでOllamaのAPIキーを作成してそっちに流した
  11. AIチャットシステムは完成したが、使ってなくてもCPUがずっと唸っている
    11.1. topで見るとプロセスはvLLMのエンジンでCPUの1コアだけ(固定ではなくランダムに)100%に張り付いている
    11.1.1. 環境変数 VLLM_SLEEP_WHEN_IDLE=1 でvLLM起動→効果なし
    11.1.2. 環境変数 GPU_MAX_HW_QUEUES=1 でvLLM起動→効果なし 明確に応答が遅くなる
    11.1.3. ROCm 10特有の事象なのか、それともvLLMの事象なのか切り分けする→ROCm 10.0をアンインストール
  12. ROCm 7.14.1の公式ドキュメントを見てROCm 7.14.1をインストール
    12.1. 使ってたvLLM(docker)をそのまま起動→CPU1コア100%張り付きは改善しない
    12.2. 環境変数 ROCR_VISIBLE_DEVICES="1" でvLLM起動→むしろROCmデバイスがなぜか消失して起動しなくなる→再起動で改善
    12.3. 環境変数 GPU_MAX_HW_QUEUES=8 でヤケクソテスト→効果なし 明確に応答が遅くなる
  13. llama.cpp(docker)
    13.1. docker pull、ghcr.io/ggml-org/llama.cpp:full-rocm実行
    13.2. https://github.com/cypriennn/llamacpp-example を参考にROCm用のモデル変換・量子化・サーバー起動のスクリプトとdocker composeを書く
    13.3. meta-models/Muse-Glimmer-30B-GGUFをダウンロードしてllama.cpp serverを起動 あっけなく動く しかもCPU100%の暴走がない

失敗から得た知見

  • 想像通り、LLM稼働中でもCPUはほとんど使わないので4スレッドもあれば十分と感じた
    • LLMだけを動かすのであれば、多コアCPUは宝の持ち腐れになりそう
  • gfx1201は ROCm 7 でも ROCm 10 でも LLM はぜんぜんOK
    • 現時点でぜんぜん問題なくほとんどの機能が動作する
  • vLLM (pip vllm-0.27.1.dev5)
    • めちゃくちゃ苦労して、(やっぱダメか・・・)ってなるのが好きな人向け
    • 環境依存が多すぎるためかストレートに成功せず
    • いろいろ足りないといわれ、追加インストール→やっぱり無理で環境がどんどん汚れていく
    • WEB上の記事もバージョン依存で見てもあまり参考にならないものが多い
  • vLLM (docker rocm/vllm:rocm10.0.0_ubuntu24.04_py3.14_pytorch_2.12.0_vllm_0.27.0)
    • ROCm公式が勝手に 「自分できます!やらせてください!」 って言ってるだけ
    • huggingfaceで公開されているだいたいのモデルがそつなく 動かない
    • 起動までにクソほど時間がかかる (調子が良いと1時間越えも狙える しかも毎回)
    • 起動に成功したら漏れなく CPUの1コアが使用率100%に張り付く オマケ付き
      • しかもこれが 「正常」 らしい
    • 公式のvLLMとは違い、エントリーポイントが bash ベースという混乱を招く仕様
  • vLLM (docker vllm/vllm-openai-rocm:v0.29.0)
    • ROCm公式が出してるやつよりは使える
    • huggingfaceで公開されているだいたいのモデルがそこそこ動いているように見える
    • 起動までにクソほど時間がかかる (20分越えが当たり前の世界 しかも毎回)
    • 起動に成功したら漏れなく CPUの1コアが使用率100%に張り付く
      • もちろんこれが 「正常」 とのこと
    • 更新速度が異常に早いので、最新版ではそつなく動くようになってる といいな・・・
  • Open WebUI (docker)
    • そつなく動く
    • だいたいの人にとっては動作確認用
      • HermesAgentやOpenClawとかのAIエージェントフロントエンド使うなら不要
    • llama.cppならserver機能にWebUIを含んでるのでそもそも不要
  • SearXNG (docker)
    • そつなく動く
    • AIから使うと爆速でIPレピュテーションが下がっていくので自ホストする場合は精神衛生上良くない
    • Ollamaなどの無料のWeb (Search/Fetch) APIを使うほうが良いかもしれない
  • llama.cpp (docker ghcr.io/ggml-org/llama.cpp:full-rocm)
    • 神といわざるを得ない
    • もう全部こいつ1人でいい
    • 一発で動くし起動も30秒以内に終わる
    • huggingfaceで公開されているどんなモデルもOOMにならない限りそつなく動く
    • server機能にWebUIを含んでるので動作確認に使うOpen WebUIが不要
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?