そんな今日この頃の技術ネタ

本家側に書くほどでもない小ネタ用

Ubuntu on EVO-X2、llama.cpp ServerでGemma4 31BのMTPを動かす

前の記事でvLLmでGemma4のMTPを動かしていたんだけど、LLM運用以外にもサーバを使いたい身としてはVRAMがあればあるだけ食べてしまうvLLMはちょっと辛かったんですな。

で、一旦はOllamaでオフィシャルに配布されているQwen3.6:27bのMTPイメージに切り替えていたんだけど、こっちは手軽だし性能面も悪く無いものの応対の日本語がちょっと微妙・・・

そんなわけで紆余曲折、単純に執筆時点ではOllama公式ではmtpなgemma4イメージが上がってない+OpenClaw等で恒常的に利用するので単独で駐在してた方が都合が良いということで、今時点ではllama.cpp serverでGemma4を動かすという方向で落ち着いている。


環境としてはUbuntu 24.04でRocm7.2.4を導入済み、できる限りサーバ的なものはDockerで運用する方針。

llama.cppは高頻度で更新されるが、ちょいちょい仕様変更があったりバグったりというのがあるので、雑にlatestタグに任せるのではなくちゃんとバージョンを明記して運用した方が良い。

(まさに直近でもb9737以降しばらくGemma4 MTPで上手く起動できないバグがあり、直近のb9935でようやっと動きそうだったのもあって、記録としてこの記事を書こうと思った)

以下のような.envでバージョン更新だったりモデルファイルだったりを指定。

# === Release Tags ===
TAG_STABLE=server-rocm-b9737
TAG_BLEEDING=server-rocm-b9935

# 現在アクティブにするタグをここでスイッチ
LLAMA_IMAGE_TAG=${TAG_BLEEDING}

# 同じUnslothのリポジトリを指定
LLAMA_MODEL=unsloth/gemma-4-31B-it-qat-GGUF
LLAMA_MODEL_ALIAS=gemma-4:31b

# ファイル名(または相対パス)を直接指定するために環境変数を分ける
LLAMA_MAIN_FILE=gemma-4-31B-it-qat-UD-Q4_K_XL.gguf
LLAMA_DRAFT_FILE=mtp-gemma-4-31B-it.gguf

# コンテキストサイズ
LLAMA_CTX_SIZE=131072
LLAMA_N_PARALLEL=1

# === ROCm Prefill 高速化チューニング ===
# 全体の論理バッチを大きくして並列演算を促進
LLAMA_BATCH_SIZE=4096
# GPUの演算効率(行列サイズ)に最適化
LLAMA_UBATCH_SIZE=512

# === CPUオーバーヘッド削減 ===
# GPU完全オフロード時はCPU側を絞った方がオーバーヘッドが減る
LLAMA_THREADS=4
LLAMA_THREADS_BATCH=8

# === Gemma 4 最適化サンプリング ===
LLAMA_TEMP=1.0
LLAMA_TOP_K=64
LLAMA_TOP_P=0.95
LLAMA_MIN_P=0.05
LLAMA_REPEAT_PENALTY=1.0

以下のようなcompose.ymlで運用。

services:
  llama-cpp-gpu:
    image: ghcr.io/ggml-org/llama.cpp:${LLAMA_IMAGE_TAG:-server-rocm}
    ports:
      - "10000:10000"
    volumes:
      - llama-cache:/app/models
    environment:
      - LLAMA_MODEL=${LLAMA_MODEL}
      - LLAMA_N_GPU_LAYERS=${LLAMA_N_GPU_LAYERS:--1}
      - LLAMA_CTX_SIZE=${LLAMA_CTX_SIZE:-32768}
      - LLAMA_BATCH_SIZE=${LLAMA_BATCH_SIZE:-2048}
      - LLAMA_UBATCH_SIZE=${LLAMA_UBATCH_SIZE:-1024}
      - LLAMA_THREADS=${LLAMA_THREADS:-16}
      - LLAMA_N_PARALLEL=${LLAMA_N_PARALLEL:-8}
      - LLAMA_TEMP=${LLAMA_TEMP:-0.8}
      - LLAMA_TOP_K=${LLAMA_TOP_K:-40}
      - LLAMA_TOP_P=${LLAMA_TOP_P:-0.95}
      - LLAMA_MIN_P=${LLAMA_MIN_P:-0.05}
      - LLAMA_REPEAT_PENALTY=${LLAMA_REPEAT_PENALTY:-1.1}
      - LLAMA_CACHE=models
    command: >
      -hf ${LLAMA_MODEL}
      -m ${LLAMA_MAIN_FILE}
      -hfd ${LLAMA_MODEL}
      --model-draft ${LLAMA_DRAFT_FILE}
      --spec-type draft-mtp
      --spec-draft-n-max 4
      --alias ${LLAMA_MODEL_ALIAS}
      --host 0.0.0.0
      --port 10000
      --n-gpu-layers 99
      --n-gpu-layers-draft 99
      --ctx-size ${LLAMA_CTX_SIZE}
      --fit off
      --batch-size ${LLAMA_BATCH_SIZE}
      --ubatch-size ${LLAMA_UBATCH_SIZE}
      --threads ${LLAMA_THREADS}
      --threads-batch ${LLAMA_THREADS_BATCH}
      --parallel ${LLAMA_N_PARALLEL}
      --temp ${LLAMA_TEMP}
      --top-k ${LLAMA_TOP_K}
      --top-p ${LLAMA_TOP_P}
      --min-p ${LLAMA_MIN_P}
      --repeat-penalty ${LLAMA_REPEAT_PENALTY}
      --cont-batching
      --mlock
      --jinja
      --flash-attn on
      -ctk q8_0
      -ctv q8_0
      --reasoning-format auto
    restart: unless-stopped
    devices:
      - /dev/kfd:/dev/kfd
      - /dev/dri:/dev/dri
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    ulimits:
      memlock:
        soft: -1
        hard: -1
volumes:
  llama-cache:

まさに直近の更新によってMTPが高速化していて、(自前スクリプトでの雑測定だけど)vLLMで15 Token/sec弱だったのが、llama.cpp serverでは20→ちょっと設定見直して 22 Token/sec前後まで上がってくれた。

MoEモデルの水準からするとそれでも遅くは感じられるが、OpenClawで実際に運用すると安定して自己メンテナンスできるのはこの位のサイズのDenseモデルからだなという体感があるので、現時点での速度と能力の実用バランスを考えるとこれで運用するのが良い塩梅だと思う。


追記: ROCm7.14がリリースされたので・・・

公式イメージはこの記事の記述時点ではベースがROCm7.2.1なのだが、せっかくサーバの方を7.14にアプグレしたので対応するイメージを自前で作ってみる。

rocm.docs.amd.com

Dockerfile: 公式のイメージの記述を踏襲しつつ、ROCm7.14に変更。

ARG UBUNTU_VERSION=24.04
ARG ROCM_VERSION=7.14.0
ARG AMDGPU_VERSION=7.14

# 1. UIのビルドステージ
FROM docker.io/node:24 AS web
WORKDIR /app
RUN apt-get update && apt-get install -y git && git clone --depth 1 https://github.com/ggml-org/llama.cpp.git .
WORKDIR /app/tools/ui
RUN npm ci && npm run build

# 2. 本体ビルドステージ
FROM docker.io/rocm/dev-ubuntu-${UBUNTU_VERSION}:${ROCM_VERSION}-full AS build
ARG ROCM_DOCKER_ARCH='gfx908;gfx90a;gfx942;gfx1030;gfx1100;gfx1101;gfx1102;gfx1151;gfx1150;gfx1200;gfx1201'
ENV AMDGPU_TARGETS=${ROCM_DOCKER_ARCH}

RUN apt-get update && apt-get install -y build-essential cmake git libssl-dev curl libgomp1
WORKDIR /app
RUN git clone --depth 1 https://github.com/ggml-org/llama.cpp.git .
COPY --from=web /app/tools/ui/dist tools/ui/dist

RUN HIPCXX="$(hipconfig -l)/clang" HIP_PATH="$(hipconfig -R)" \
    cmake -S . -B build \
    -DGGML_HIP=ON \
    -DGGML_HIP_ROCWMMA_FATTN=ON \
    -DAMDGPU_TARGETS="$ROCM_DOCKER_ARCH" \
    -DGGML_BACKEND_DL=ON \
    -DGGML_CPU_ALL_VARIANTS=ON \
    -DCMAKE_BUILD_TYPE=Release \
    -DLLAMA_BUILD_TESTS=OFF \
    && cmake --build build --config Release -j$(nproc)

RUN mkdir -p /app/lib && find build -name "*.so*" -exec cp -P {} /app/lib \;
RUN mkdir -p /app/full && cp build/bin/* /app/full

# 3. 実行用ベースステージ
FROM docker.io/rocm/dev-ubuntu-${UBUNTU_VERSION}:${ROCM_VERSION}-full AS base
RUN apt-get update && apt-get install -y libgomp1 curl ffmpeg \
    && apt-get autoremove -y && apt-get clean -y \
    && rm -rf /tmp/* /var/tmp/* \
    && find /var/cache/apt/archives /var/lib/apt/lists -not -name lock -type f -delete \
    && find /var/cache -type f -delete
COPY --from=build /app/lib/ /app

# 4. サーバー実行ステージ
FROM base AS server
ENV LLAMA_ARG_HOST=0.0.0.0

# 🌟 Issue #25807 対策:ROCm 7.14 で変更されたライブラリパスを通す
ENV LD_LIBRARY_PATH=/opt/rocm/lib:/opt/rocm/core-7.14/lib:/app:${LD_LIBRARY_PATH}

COPY --from=build /app/full/llama-server /app/llama-server
WORKDIR /app
HEALTHCHECK CMD [ "curl", "-f", "http://localhost:8080/health" ]
ENTRYPOINT [ "/app/llama-server" ]

compose.local.yml: イメージ部分だけオーバーライドする設定を作成。

services:
  llama-cpp-gpu:
    build:
      context: .
      dockerfile: Dockerfile
      target: server
      args:
        ROCM_VERSION: "7.14.0"
        AMDGPU_VERSION: "7.14"
        ROCM_DOCKER_ARCH: 'gfx1151'
    # 元ファイルに設定された外部イメージ(ghcr.io)を無視し、ローカルビルドした独自タグに差し替えます
    image: llama-cpp-server:rocm7.14-local

以上で起動。

$ docker compose -f compose.yml -f compose.local.yml up --build -d

ひとまず今のところは問題なく動いてそう。

ベンチマーク的には、ほんのちょっと恩恵あったか?という感じ。まあ作るだけ作ったけど、あっても微差なんで普通にrocm-server-*として提供されているのを大人しく利用するのが良さそう。

$ for i in $(seq 1 10); do ./bench.sh ;done

Tokens/s: 21.836889040832848

Tokens/s: 23.360779183154488

Tokens/s: 22.426950598457406

Tokens/s: 24.485984078146974

Tokens/s: 36.570018013590904

Tokens/s: 24.22252425616665

Tokens/s: 22.600159632681795

Tokens/s: 24.018720292803533

Tokens/s: 25.29314483156075

Tokens/s: 22.28074483950202 

MTPの性質なのかたまに外れ値的に早い時が存在するので、それこそ平均ではなく中央値でみるのが良いのかもしれない。