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?

Ryzen AI Max+ 395 (ROCm)で、USBカメラ入力->YOLO11mの物体検知&Nemotron-3 Nano OmniマルチモーダルLLMの環境説明を作った

0
Last updated at Posted at 2026-05-05

LLaVA on ROCm — USB カメラ × YOLO11m × Nemotron Nano Omni × Chrome WebRTC

NucBox EVO X2 (Ryzen AI MAX+ 395 / Radeon 8060S, ROCm 7.2.1) 上で USB カメラ映像を Chrome ブラウザに低遅延 WebRTC 配信し、同じ映像に対して YOLO11m の物体検出 (30fps) と Nemotron Nano Omni による日本語キャプション (0.5fps) をリアルタイムオーバーレイするデモ。


必要なもの

項目 想定値
マシン NucBox EVO X2 (AMD Ryzen AI MAX+ 395, gfx1151, 48GB unified)
OS Ubuntu 24.04.4 LTS (HWE kernel)
ROCm 7.2.1 (/opt/rocm symlink)
Python 3.12
パッケージ管理 uv (ローカル: ~/.local/bin/uv)
USB カメラ UVC 対応のもの 1 台
Chrome 任意の最近のバージョン (同一マシンまたは LAN 内の別端末)

事前にインストール済みであることを期待:


GitHub


セットアップ手順

1. リポジトリ取得

git clone https://github.com/kotetsuy/LLaVA
cd ~/LLaVA

2. Python 仮想環境と基本依存

uv venv
uv sync

これで numpy / opencv-python / pyyaml / pyudev がインストールされ、Step 1 (USB カメラ → SHM) と Step 2 (ホットプラグ対応 CAL) が動く状態になります。

3. ROCm 版 PyTorch (Step 3 以降に必要)

PyPI の torch は CUDA 版なので使えません。AMD の ROCm wheel を直接 wget してインストール:

mkdir -p ~/wheels && cd ~/wheels
wget "https://repo.radeon.com/rocm/manylinux/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1.lw.gitff65f5bc-cp312-cp312-linux_x86_64.whl"
wget "https://repo.radeon.com/rocm/manylinux/rocm-rel-7.2.1/torchvision-0.24.0%2Brocm7.2.1.gitb919bd0c-cp312-cp312-linux_x86_64.whl"
wget "https://repo.radeon.com/rocm/manylinux/rocm-rel-7.2.1/torchaudio-2.9.0%2Brocm7.2.1.gite3c6ee2b-cp312-cp312-linux_x86_64.whl"
wget "https://repo.radeon.com/rocm/manylinux/rocm-rel-7.2.1/triton-3.5.1%2Brocm7.2.1.gita272dfa8-cp312-cp312-linux_x86_64.whl"

cd ~/LLaVA
uv pip install ~/wheels/torch-*.whl ~/wheels/torchvision-*.whl \
               ~/wheels/torchaudio-*.whl ~/wheels/triton-*.whl

4. YOLO + ONNX (退避プラン用)

uv pip install -e .[yolo,onnx]

ultralytics (YOLO11m を初回 predict 時に自動ダウンロード) と onnx / onnxruntime (CPU 版) が入ります。

5. WebRTC (aiortc + FastAPI)

uv pip install -e .[webrtc]

6. ROCm 環境変数

~/.bashrc などに追加して、新しいシェルで自動的に効くようにしておくと楽:

export HSA_OVERRIDE_GFX_VERSION=11.5.1
export ROCM_PATH=/opt/rocm
export HIP_VISIBLE_DEVICES=0

(start_all.sh は内部で再 export するので、シェル設定を忘れていても tmux セッションでは効きます。)

7. Nemotron Nano Omni GGUF の準備

mkdir -p ~/nemotron-3
cd ~/nemotron-3

# unsloth/NVIDIA-Nemotron-3-Nano-Omni-30B-A3B-Reasoning-GGUF (Q4_K_XL)
huggingface-cli download \
  unsloth/NVIDIA-Nemotron-3-Nano-Omni-30B-A3B-Reasoning-GGUF \
  NVIDIA-Nemotron-3-Nano-Omni-30B-A3B-Reasoning-UD-Q4_K_XL.gguf \
  --local-dir Nemotron-3-Nano-Omni-30B-A3B-Reasoning-GGUF

huggingface-cli download \
  unsloth/NVIDIA-Nemotron-3-Nano-Omni-30B-A3B-Reasoning-GGUF \
  mmproj-F16.gguf \
  --local-dir Nemotron-3-Nano-Omni-30B-A3B-Reasoning-GGUF

合計 ~24.5 GB。config.yamlvlm.model / vlm.mmproj のパスがこれと一致することを確認してください。

8. (任意) GPU が見えていることを確認

uv run python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))"
# True AMD Radeon Graphics

起動と停止

一括起動 (推奨)

cd ~/LLaVA
./start_all.sh

これだけで:

  1. tmux セッション llava を作成
  2. window 0: uv run capture-run (USB カメラ → SHM、Step 1+2)
  3. window 1: uv run serve (FastAPI + aiortc + YOLO bbox + VLM caption WS、Step 6+7)
  4. window 2: llama-server --reasoning off (Nemotron 常駐、Step 7b)
  5. http://localhost:8080/ がレスポンスを返すまで最大 30 秒待機
  6. Chrome を自動で開く

オプション:

./start_all.sh --no-browser     # SSH 越しなどで自動 open 不要なとき
./start_all.sh --help

セッションへの接続:

tmux attach -t llava            # ログを直接見る
# Ctrl-b 0 / 1 / 2 で window 切替
# Ctrl-b d でセッションを生かしたまま離脱

停止

./stop_all.sh

各 window に Ctrl-C を送って 5 秒待ち、そのあと tmux kill-session。万一プロセスが残っていれば SIGINT → SIGKILL の段階で後始末します。


ブラウザで確認

./start_all.sh 実行後、Chrome で http://localhost:8080/ (または LAN 内別端末から http://<NucBox の IP>:8080/) を開くと:

  • 中央の <video> に USB カメラ映像 (1280x720, ~150ms 以下の遅延)
  • 上に半透明 <canvas> で YOLO の bbox (色分け、30fps、person 92% 形式のラベル)
  • 下に半透明ボックスで Nemotron の日本語キャプション (約 50 字、2 秒ごとに更新)
  • ステータス行に WebRTC 接続状態 / bbox WS / caption の inference 時間と t/s

llama-server がモデルをロードする最初の ~10 秒は caption が (no caption yet) のまま、その後更新が始まります。


ステップごとの個別実行 (デバッグ用)

start_all.sh を使わず一つずつ起動したいとき:

# Step 1+2: capture
uv run capture-run                          # 別ターミナル
uv run shm-reader-demo --ticks 10           # SHM 読出しのみ確認
uv run shm-reader-demo --save /tmp/snap.jpg # 1 枚保存
uv run list-cameras                         # /dev/v4l デバイス一覧

# Step 3: YOLO 単独
uv run benchmark-yolo --source synthetic    # synthetic 1280x720 ノイズ
uv run benchmark-yolo --source shm          # 上の capture を起動した上で
uv run export-yolo-onnx --verify            # ONNX 退避プラン

# Step 4: VLM 単独 (mtmd-cli subprocess)
uv run benchmark-vlm --image /tmp/snap.jpg

# Step 5: YOLO + VLM 同居
uv run benchmark-concurrent --frames 600
uv run benchmark-concurrent --no-vlm        # baseline

# Step 6+7: サーバ単体起動
uv run serve                                # T2 相当
~/llama.cpp/build/bin/llama-server -m ... --mmproj ... --reasoning off  # T3 相当

トラブルシューティング

uv run capture-run でカメラが見つからない

ls /dev/v4l/by-id              # USB カメラの symlink が出るか
v4l2-ctl --list-devices        # (要 sudo apt install v4l-utils)

config.yamlcamera.preferred[].by_id が手元のカメラ名と一致しない場合は fallback: any 経由で適当に選ばれます。特定のカメラを優先したいときは by_id: usb-Vendor_Model* を編集。

connection failed が Chrome に出る

WebRTC の ICE ネゴシエーション失敗。多くは一時的で、Ctrl-Shift-R でハードリロードすれば回復します。LAN 別端末から繋がらない場合は sudo ufw allow 8080 でファイアウォール開放。

caption が空のまま ((no caption yet))

llama-server がまだモデルロード中 (~10 秒) か、--reasoning off を付け忘れたか。tmux attach -t llava → Ctrl-b 1 で serve window のログを確認:

vlm-runner: caption (1300ms) 'これは...'         ← OK
vlm-runner: empty caption after strip; raw='<think>...'  ← --reasoning off 不足

start_all.sh が「session already exists」で失敗

./stop_all.sh                  # まず停止
./start_all.sh                 # 再起動

または tmux kill-session -t llava で強制終了。

モデルロードが極端に遅い (初回 30 秒以上)

21 GB の GGUF を NVMe から SSD にコピー → ページキャッシュに乗せる時間。2 回目以降は ~10 秒に短縮されます。

YOLO の fp16 を fp32 に戻したい

config.yamlyolo.half: truefalse に。fp32 のほうが精度はわずかに上がるが、Step 5 の同居測定で fp16 のほうが VLM 側の余裕が大きくなったので fp16 を採用しています。


開発時のセルフチェック

# 全 Python ファイルの構文チェック
python3 -m compileall -q src scripts && echo OK

# モジュール import 確認 (依存解決の確認も兼ねる)
uv run python -c "from src.server.app import app; print('imports OK')"

技術詳細

このドキュメントは HANDOFFJ.md で示した設計をどう実装したか、なぜその設計を選んだか、そして実装中に判明した落とし穴を体系化したものです。導入手順は READMEJ.md を参照。


1. アーキテクチャ全体像

Pipeline architecture

Screenshot from 2026-05-05 15-53-02.png

NucBox EVO X2 (Ryzen AI MAX+ 395, gfx1151, 48 GB unified) 1 台のうえで 4 つの並走コンポーネントを動かし、Chrome へ WebRTC + WebSocket で配信します。

コンポーネント プロセス 入力 出力
Capture uv run capture-run USB カメラ SHM (1280×720 BGR letterbox 済)
YOLO11m serve 内のバックグラウンドスレッド SHM bbox JSON → /ws/bbox
VLM llama-server --reasoning off (別プロセス) serve の VlmRunner からの HTTP リクエスト (画像+プロンプト) キャプション JSON → /ws/caption
aiortc サーバ uv run serve (FastAPI + uvicorn) SHM WebRTC video track + WS broadcast

設計の鍵となる判断

  • SHM は "latest frame slot" 1 つだけ。Capture は常時上書き、読み手 (aiortc / yolo / vlm) は推論開始時にスナップショット (np.array(copy=True)) する。古いフレームでの推論積み残しを構造的に防ぐ。
  • キューにはフレームを乗せない。bbox / caption JSON だけが asyncio.Queue → WebSocket に流れる。
  • VLM へのフレーム受け渡しは JPEG 圧縮済みバイト列。SHM の生 BGR を cv2.imencode('.jpg', ...) してから llama-server の /v1/chat/completions に base64 で投げる。
  • YOLO は serve プロセス内のスレッドで動く。HANDOFF の元案は別プロセスだが、torch/CUDA は GIL を C++ 中で解放するので asyncio loop を block しない。Step 5 で fp16 推論 8 ms p50 が確認できた時点でこの判断は妥当。
  • VLM だけは別プロセス (llama-server)。理由は (a) 21 GB の GGUF を Python venv とは独立に常駐させたいこと、(b) llama.cpp のチューニングや再起動を server 本体と切り離したいこと。

2. カメラ抽象化レイヤ (CAL)

Camera abstraction layer

Screenshot from 2026-05-05 15-53-36.png

「画角の違うカメラ・差し込むポートが変わっても動く」要件を満たすため、src/capture/ に物理層 → 正規化フレーム生成までを集約しました。

2.1 デバイス検出 (device_manager.py)

  • pyudev.Context().list_devices(subsystem='video4linux') で udev に登録された v4l デバイスを列挙
  • USB の ID_VENDOR_ID / ID_MODEL_ID を取り出して vid_pid="046d:0892" のような指定にも対応
  • /dev/v4l/by-id/... の symlink からは安定 ID として by_id を取得
  • 1 つの USB カメラが index0 (映像) / index1 (メタデータ) など複数 v4l ノードを作るので、is_capture_capable() で実際に cv2.VideoCapture.read() 成功するノードに絞り込み
  • config.yamlcamera.preferred[] をパターン照合 (glob usb-Logitech* / VID:PID) → なければ fallback: any

2.2 フォーマット交渉 (format_negotiator.py)

cv2.VideoCaptureset(CAP_PROP_FOURCC, ...) ladder を MJPG → YUYV の優先順で試行し、get(CAP_PROP_*) で実際に確定した値を読み戻します。MJPEG を優先するのは USB 帯域効率が YUYV より圧倒的に良いから (1280×720@30fps が YUYV だと USB 2.0 帯域で破綻しやすい)。

2.3 letterbox 二段構成 (frame_normalizer.py)

  • CAL 出力 = 1280×720 固定 BGR letterbox 済
  • スケール = min(target_w/src_w, target_h/src_h) でアスペクト保ったまま縮小、余白は (114, 114, 114) グレーで埋める
  • pad_x / pad_y / scale / original_w / original_h を SHM ヘッダに記録 → 下流で逆変換可能
  • YOLO11m 入力 (640×640) と VLM 入力 (~448) は Ultralytics / mtmd 側で自動 letterbox。CAL では二段目を作らない (重複変換のコストを避ける)
  • bbox 座標は CAL 正規化フレーム (1280×720) 上の絶対座標で WS 送出。Chrome 側は <canvas width=1280 height=720> の固有解像度を CSS でビデオに合わせるだけ → 1 段の縮尺だけで描画可能

2.4 ホットプラグ対応 (hotplug_watcher.py, capture_session.py)

  • pyudev.MonitorObserver(filter_by='video4linux')add / remove イベントを Queue に積む
  • メイン loop は 2 状態の状態機械:
    • SEARCHING: capture 不能。30 fps で黒フレーム + connected=False を SHM に書き続ける (下流が「カメラ不在」を即座に認識できる)
    • CAPTURING: cv2.VideoCapture を別スレッド (CaptureReader) で読み、main thread が SHM へ書く
  • CaptureReadercv2.VideoCapture.read() の抜き取り時ブロックを避けるため、daemon thread で常時 read。停止時は cap.release() で stuck read を解除
  • add イベントで即時 rescan、remove 時に当該 dev_path がアクティブなら SEARCHING に遷移
  • aiortc の ShmVideoTrack は SHM 経由で間接接続なので、カメラが入れ替わっても RTCPeerConnection を切らない (Chrome の動画が止まらない)

3. SharedMemory 設計 (shm_writer.py)

3.1 レイアウト (合計 36 B + frame data)

offset size フィールド
0 8 seq_lock (uint64): even=stable / odd=writer mid-write
8 8 timestamp_ns (uint64)
16 2 original_w (uint16)
18 2 original_h (uint16)
20 2 frame_w (uint16)
22 2 frame_h (uint16)
24 2 pad_x (uint16)
26 2 pad_y (uint16)
28 4 scale (float32)
32 1 channels (uint8)
33 1 pixel_format (uint8): 0=BGR / 1=RGB
34 1 connected (uint8): 0=合成黒フレーム / 1=実映像
35 1 (padding)
36 W·H·3 frame data (uint8)

struct フォーマット: <QQHHHHHHfBBB1x (Python の struct.calcsize で 36 確認済)。

3.2 seqlock の挙動

ライター (Capture) は単一プロセス。x86_64 の整列 8 byte 書き込みは hardware atomic なのでロックフリー seqlock が成立します。

ライター:
    1. seq = next odd      (= 書き込み中マーカー)
    2. ヘッダとフレームを書き込む
    3. seq = next even     (= 完了マーカー)

リーダー:
    for retry in range(16):
        s1 = read seq
        if s1 odd: sleep(100us); continue   ← ライターと衝突中
        copy header + frame
        s2 = read seq
        if s1 == s2: success
        else: continue                       ← copy 中にライターが上書き
    return None                              ← 16 retry でも捕まえられず

3.3 「画面が一瞬黒くなる」バグの修正

初版ではリーダーがタイトに 8 retry → 失敗で None 返却 → ShmVideoTrack が黒フレームに fallback、というパスで Chrome 上に時々 1 frame の黒が出ていました。原因は:

  • ライターの "odd" 滞在時間 ≈ 500 µs (np.copyto で 2.6 MB)
  • リーダーの 8 retry はタイトループで合計 ~8 µs しか経過せず、ライターが終わる前に諦めていた

修正:

  1. read() の retry に time.sleep(100us) を挟み、最大 retry を 8 → 16 に
  2. ShmVideoTrack 側で「最後に成功したフレーム」をキャッシュし、None だったら直前フレームで埋める (TTL 1 秒で stale ガード)

これで Chrome 上の黒フレーム発生は実測 0 に。

3.4 resource_tracker パッチ

multiprocessing.shared_memory には bpo-38119 があり、attach した側でも unlink しようとして spurious warning や二重 unlink が起きます。_suppress_resource_tracker_for_shm()register / unregister をモンキーパッチして無視させる定石対応を入れています。


4. 推論バックエンド

4.1 YOLO11m (Step 3)

項目
バックエンド Ultralytics + ROCm PyTorch 2.9.1
入力 1280×720 BGR (SHM 正規化フレーム)
imgsz 640 (Ultralytics 内部で letterbox + 逆変換、bbox は元座標で返る)
量子化 fp16 (yolo.half: true)
単独 fps 97.8 fps (benchmark-yolo --source shm、ただし dedup なし、GPU 律速)
パイプライン fps 30.1 fps (benchmark-concurrent --no-vlm、カメラ 30fps 律速)

ベースラインの取り違いに注意: benchmark-yolo --source shm は SHM 重複 read で同フレーム何度も推論する → GPU 純粋スループット 97.8。一方 benchmark-concurrent --no-vlmmeta.seq で dedup → カメラレートに張り付く 30 fps。Step 5 の比較は後者を baseline に取らないと「-71% 劣化」のような誤読が起きます。

退避プラン (scripts/export_yolo_onnx.py): model.export(format='onnx', imgsz=640, simplify=True) で ONNX を吐き、onnxruntime で読める状態を維持。CPU EP で実測 15.4 fps (30 fps target には届かないので primary ではなく、graceful degradation 用)。MIGraphX EP は AMD-built ort 必須。

4.2 Nemotron-3 Nano Omni (Step 4 / 7b)

項目
モデル unsloth/NVIDIA-Nemotron-3-Nano-Omni-30B-A3B-Reasoning-GGUF, Q4_K_XL (~21 GB) + mmproj-F16 (~1.5 GB)
ランタイム llama.cpp ROCm/HIP build (llama-server 常駐)
入力 1280×720 BGR → JPEG (quality 90) → base64 → /v1/chat/completions
n_predict 128 (50 字程度の日本語キャプション、~60-100 tokens)
単独 inference ~1262 ms (Step 4 mtmd-cli) / ~1300 ms (Step 7b llama-server)
並走 inference ~1294 ms (Step 5、YOLO 同居時)、+2.5% の劣化のみ

--reasoning off 必須。Reasoning モデルなのでデフォルト (auto) では <think> タグに n_predict を全消費し、回答が空になります。同じ GGUF を mtmd-cli で使うと空 <think></think> の挙動になるので両ランタイムで違うのは要注意。

VlmServerWorker は llama-server の /v1/chat/completions レスポンスから:

  • choices[0].message.content をキャプション本文として取得
  • <think>...</think> を正規表現で除去 (保険)
  • timings.prompt_ms / predicted_ms / *_per_secondusage.prompt_tokens / completion_tokensVlmTiming に詰める

4.3 Step 5: YOLO + VLM 同居の Go/No-Go

                          YOLO alone   YOLO+VLM (fp32)   YOLO+VLM (fp16)
fps                       30.1         27.9              27.9
p50 latency (ms)          11.07        11.78             8.05
p99 latency (ms)          11.80        88.67             112.32
VLM median inf (ms)       —            1294              1151
VLM eval_tps              —            48.1              53.6

benchmark-concurrent --frames 600 の出力。fp16 が VLM 側の余裕を増やす ("YOLO が早く終わるので VLM が触れる窓が広がる") のがこのデータから読み取れる重要な発見です。p99 spike (~100 ms) は VLM の eval phase でのコンテキストスイッチ起因で、bbox 描画上は約 3 frame の stutter として現れる程度。


5. WebRTC 配信 (src/server/)

5.1 Video Track (webrtc_track.py)

class ShmVideoTrack(VideoStreamTrack):
    async def recv(self) -> VideoFrame:
        pts, time_base = await self.next_timestamp()    # aiortc が 30fps を pacing
        # SHM lazy attach (capture-run が後から起動しても自動接続)
        # SHM read → np.copyto → av.VideoFrame.from_ndarray(format='bgr24')
        # 失敗時は last_frame キャッシュ、TTL 1s で黒フォールバック
  • aiortc は CPU エンコード (VP8 デフォルト)。1280×720@30fps なら Ryzen AI MAX+ 395 で余裕
  • ROCm VCN ハードウェアエンコーダは aiortc 非対応なので使わない
  • next_timestamp() の sleep が 30 fps cadence を担保

5.2 シグナリング (app.py)

POST /offer で SDP 交換。20 行程度の最小実装:

@app.post("/offer")
async def offer(payload: SdpPayload):
    pc = RTCPeerConnection(configuration=RTCConfiguration(iceServers=[]))
    pc.addTrack(ShmVideoTrack(...))
    await pc.setRemoteDescription(RTCSessionDescription(sdp=payload.sdp, type=payload.type))
    answer = await pc.createAnswer()
    await pc.setLocalDescription(answer)
    return {"sdp": pc.localDescription.sdp, "type": pc.localDescription.type}

同一 LAN 想定で STUN / TURN なし。ホストキャンディデートだけで繋がります。config.yaml > server.ice_servers を空配列にしてあるので必要なら STUN を追加可能。

5.3 WS broadcast (ws_broadcaster.py, yolo_runner.py, vlm_runner.py)

  • WsBroadcaster: クライアント集合 + asyncio Lock + JSON broadcast。送信失敗のクライアントを自動 drop
  • YoloRunner: daemon thread。SHM read → predict → _latest = {...} を thread-safe slot で更新
  • _broadcast_bbox_loop: 30 fps の async task、frame_seq で dedup して /ws/bbox に push
  • VlmRunner: 同型、cadence 2 秒。llama-server health check → SHM attach → loop。ts_ns で dedup
  • _broadcast_caption_loop: 0.5 fps cadence

5.4 フロントエンド (src/web/)

  • <video> (WebRTC track)、<canvas> (bbox)、半透明 <div> (caption) の 3 層
  • <canvas>width=1280 height=720 固定、CSS でビデオに被せる。bbox 座標が CAL 正規化フレーム空間そのままなので 1 段スケールで描画
  • overlay.js / caption.js ともに WebSocket disconnect 時は exponential backoff で再接続 (上限 5 秒)

6. 起動・終了スクリプト

start_all.sh

tmux session llava に 3 windows:

  1. captureuv run capture-run
  2. serveuv run serve (FastAPI + YoloRunner + VlmRunner)
  3. vlm~/llama.cpp/build/bin/llama-server ... --reasoning off

ROCm 環境変数を per-pane で export するので ~/.bashrc に書き忘れていても確実に効きます。http://localhost:8080/ がレスポンスを返すまで curl で 30 秒ポーリングしてから Chrome (or chromium / xdg-open) を起動。

stop_all.sh

各 window に Ctrl-C 送信 → 5 秒待機 → tmux kill-session。万一の残留プロセスは pgrep -f "src.server.app|capture.main|llama-server" で検出して SIGINT → SIGKILL の 2 段階で後始末。


7. 実装中に判明した落とし穴 (再発防止メモ)

7.1 Capture / SHM

  • SHM seqlock の retry は sleep 必須: タイトループだとライターの 500 µs 窓を捕まえられない (3.3 節参照)
  • multiprocessing.shared_memory.SharedMemory の resource_tracker パッチ: 二重 unlink 警告を抑制 (3.4 節)
  • shm.read() の戻り値は (frame, meta) であって (ts, frame) ではないbenchmark_concurrent.py 初版で ts, frame = got と書いて ValueError: array truth value ambiguous を踏んだ
  • cv2.VideoCapture.read() は USB 抜き取り時にブロックする: 別スレッドで read、main thread はタイムアウト監視 (CaptureReader)

7.2 YOLO

  • dedup の有無でベースラインが ~3 倍違う: GPU 律速 (97.8 fps) と pipeline 律速 (30.1 fps) を混同しないこと
  • fp16 が並走時に VLM を救う: 単独 YOLO fps はカメラレートで頭打ちなのに、fp16 にすると並走時の VLM eval_tps が +12% 改善

7.3 VLM (llama.cpp)

  • llama-mtmd-cli には --no-display-prompt フラグが無い: stdout に prompt がエコーされる → Python 側で先頭 strip
  • stderr に非 UTF-8 バイトが混じる: モデルロード進捗の制御文字。subprocess.run(..., errors='replace') で救済
  • llama_perf_* ログの prompt eval timeeval time を素朴に正規表現すると両方 prompt 行に当たる: (?<!prompt )eval time の negative lookbehind が必要
  • *llama-server--reasoning auto (default) は -Reasoning モデルだと thinking が n_predict を食いつぶして content が空になる: --reasoning off を必須化

7.4 WebRTC / Frontend

  • chrome キャッシュ: caption.js を更新したのに反映されない時は Ctrl-Shift-R でハードリロード
  • ICE gathering 待たずに /offer を投げると ICE candidate が SDP に乗らない: iceGatheringState === 'complete' を await
  • status 行の + / - 符号: ベンチスクリプトで「fps が下がった」を +71.5% と表示するなど混乱の元 → 「+ = 改善 / - = 劣化」に統一

8. 関連ドキュメント

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?