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?

Intel Meteor Lake の NPU を Proxmox コンテナにパススルーして使ってみた ― embeddings は CPU 比 2.4 倍、LLM 生成は遅かった

0
Posted at

はじめに

「AI PC」世代の CPU には NPU(Neural Processing Unit)が載っています。手元のミニ PC(Beelink GTi14 Ultra / Intel Core Ultra 7 155H = Meteor Lake)にも Intel AI Boost NPU が搭載されているのですが、Proxmox 上のコンテナからは見えていませんでした。

本記事では NPU を非特権 LXC コンテナにパススルーし、次の用途で実際に計測した結果を共有します。

  • A. embeddings(RAG のベクトル化)
  • B. 音声認識(Whisper)
  • C. NPU と内蔵GPU の同時実行
  • (参考)LLM 生成

結論を先に言うと、Meteor Lake の NPU は「embeddings」と「音声認識」では CPU より明確に速く、LLM のトークン生成では内蔵GPU に大きく負ける、という結果でした。

関連記事:同じマシンの内蔵GPU で Ollama / llama.cpp(Vulkan・SYCL・OpenVINO) / OVMS を比較した記事も書いています。本記事はその「NPU編」です。

TL;DR

用途 NPU の結果 CPU 比
embeddings (bge-small) 126.9 texts/s (7.9 ms/text) 約 2.4 倍
Whisper (base, 20秒音声) 0.42s (RTF 0.021) 約 1.5 倍
LLM 生成 (1.2B) 9.5 tok/s GPU の 1/4〜1/5
NPU + GPU 同時 合計 +10%(コンテンション) —

NPU の実用的な使い道は embeddings と音声認識でした。

検証環境

項目 内容
マシン Beelink GTi14 Ultra(DMI: AZW GTi14)
SoC Intel Core Ultra 7 155H(Meteor Lake, 6P+8E+2LP-E = 16C/22T)
NPU Intel AI Boost(NPU 3720, PCI 8086:7d1d)
内蔵GPU Intel Arc Graphics(8 Xeコア)
ホスト Proxmox VE(カーネル 7.x-pve)
ゲスト 非特権 LXC(Ubuntu 24.04.4 LTS, 20GB RAM, 22 vCPU)
コンテナ Docker(LXC 内でネスト実行)

NPU の位置づけ

ユニット 概算性能
NPU (AI Boost) 約 11 TOPS
内蔵GPU (Arc 8 Xe) 約 18 TOPS
CPU (22 スレッド) 約 5 TOPS

NPU は 生のスループットでは内蔵GPU より非力です。売りは低消費電力(Intel の資料では GPU の半分以下の消費電力)で、「GPU が別作業で埋まっている間の常時 AI」に向きます。

1. NPU を LXC にパススルーする

ハマりどころ①:手動の lxc.* では動かない

最初、よくある手順で lxc.cgroup2.devices.allow と lxc.mount.entry を手書きしたところ、デバイスノードは見えるのに root でも開けない(Permission denied)状態になりました。デバイスの所有者が nobody:nogroup になり、デバイス cgroup に反映されていなかったためです。

正解は、Proxmox の dev[n]: ショートカットを使うこと。/dev/dri のパススルーと同じ仕組みで、ノード生成と cgroup 許可をまとめて面倒見てくれます。

# /etc/pve/lxc/<CTID>.conf
dev0: /dev/dri/renderD128
dev1: /dev/dri/card0
dev2: /dev/accel/accel0    # ← NPU を追加(これだけ)

ハマりどころ②:非特権 LXC は自作 mknod 不可

非特権 LXC では mknod が Operation not permitted で弾かれます。デバイスノードを自分で作ることはできないので、必ずホスト側の設定変更が必要です。

手順

# ホスト(Proxmox)で
ls -l /dev/accel/accel0                 # 存在確認(261:0)
# /etc/pve/lxc/<CTID>.conf に dev2: /dev/accel/accel0 を追記
pct snapshot <CTID> pre-npu             # 念のためスナップショット
pct reboot <CTID>

再起動後、ゲストで /dev/accel/accel0 が root:root で見え、開けるようになります。

ls -l /dev/accel/accel0     # crw-rw---- root root 261, 0
head -c0 /dev/accel/accel0 && echo OPEN_OK

ハマりどころ③:NPU ドライバ(ユーザ空間)が必要

カーネル側は intel_vpu がロード済みでも、ユーザ空間の Level Zero NPU ドライバ(libze_intel_npu.so) が無いと使えません。今回は OpenVINO 同梱イメージ(後述)にドライバが入っていたため、それを利用しました。

# 例:イメージからドライバを取り出す
ls /usr/lib/x86_64-linux-gnu/libze_intel_npu.so*

2. 環境構築(OpenVINO GenAI + optimum-intel)

モデルの OpenVINO IR 変換と推論に Python 環境を用意します。落とし穴が多かったので、バージョン固定が重要です。

python3 -m venv /opt/ov-venv
/opt/ov-venv/bin/pip install torch --index-url https://download.pytorch.org/whl/cpu
/opt/ov-venv/bin/pip install openvino openvino-genai "optimum-intel[openvino]" \
    "transformers>=5.4,<5.6" soundfile librosa

注意:torch と torchvision のバージョンがずれると operator torchvision::nms does not exist で transformers の import が壊れます。CPU 版をセットで入れ直すこと。また transformers は 5.4 以上 5.6 未満に固定します(後述)。

3. A. embeddings を NPU で動かす

bge-small-en-v1.5 を IR にエクスポートします。

/opt/ov-venv/bin/optimum-cli export openvino \
  --model BAAI/bge-small-en-v1.5 --task feature-extraction \
  /opt/ov-models/bge-small-ov

ハマりどころ④:NPU は静的シェイプ必須

エクスポート直後のモデルは動的シェイプ([?,?])で、NPU コンパイラに拒否されます(INT64_MAX の次元エラー)。入力長を固定して reshape します。

import openvino as ov
core = ov.Core()
m = core.read_model("/opt/ov-models/bge-small-ov/openvino_model.xml")
# 入力を [1,128] に固定
m.reshape({i.get_any_name(): [1, 128] for i in m.inputs})
# NPU プラットフォームを明示(Meteor Lake = 3720)
cm = core.compile_model(m, "NPU", {"NPU_PLATFORM": "3720"})

計測結果:

実行先 スループット レイテンシ
NPU 126.9 texts/s 7.9 ms/text
CPU 52.4 texts/s 19.1 ms/text

NPU は CPU の約 2.4 倍。埋め込みの一致度(cosine)も 0.99994 で、実質同一の出力が得られました。RAG のベクトル化を NPU に逃がせば、内蔵GPU を LLM に空けられます。

4. B. Whisper を NPU で動かす

whisper-base をエクスポートします。ここが一番ハマりました。

/opt/ov-venv/bin/optimum-cli export openvino \
  --model openai/whisper-base \
  --task automatic-speech-recognition-with-past \
  /opt/ov-models/whisper-past

ハマりどころ⑤:--task ...-with-past が必須

--task automatic-speech-recognition(without past)でエクスポートすると、decoder に beam_idx が含まれず、WhisperPipeline が Port for tensor name beam_idx was not found で失敗します。-with-past を付けると decoder に beam_idx が入り、動くようになります。

ハマりどころ⑥:transformers のバージョン

Whisper の NPU 実行は、エクスポート時の transformers >= 5.4 が事実上必須です(古い 4.x でエクスポートした IR は NPU で invalid unordered_map key を起こす既知バグがあります)。transformers>=5.4,<5.6 に固定しましょう。

実行と結果(20.06 秒の音声、3 回計測の最速値):

実行先 処理時間 RTF 文字起こし
NPU 0.42s 0.021 CPU と完全一致
CPU 0.65s 0.033 —

NPU は CPU の約 1.5 倍速(20 秒の音声を 0.42 秒で文字起こし)。RTF 0.02 は「実時間の 1/50」で、常時文字起こしには十分です。

import openvino_genai as ov_genai
pipe = ov_genai.WhisperPipeline("/opt/ov-models/whisper-past", "NPU", NPU_PLATFORM="3720")
print(pipe.generate(wav).texts[0])

5. C. NPU と内蔵GPU の同時実行

「GPU で LLM を回しつつ、NPU で別の小型モデルを回す」構成を検証しました。NPU に 1.2B、GPU に 4B を載せ、同時にリクエストを投げます。

構成 スループット
NPU 単独(1.2B) 8.7 tok/s
GPU 単独(4B) 13.9 tok/s
同時実行 合計 15.3 tok/s(NPU 6.5 + GPU 8.8)

同時にすると各ストリームは 25〜37% 低下し、合計も GPU 単独比 +10% 程度にとどまりました。内蔵GPU と NPU がメモリ帯域や CPU を共有するため、コンテンションが発生しています。

→ スループットを稼ぐ目的ではなく、「GPU を対話用に占有し、NPU で裏方処理(embeddings・ASR)を回す」という分離用途が現実的です。

6. 参考:NPU で LLM を生成すると

同じ仕組みで 1.2B の LLM を NPU 実行すると 9.5 tok/s。内蔵GPU の Vulkan 実行(同じ 1.2B で 45.9 tok/s)の 約 1/5 でした。4B クラスは NPU のグラフに載らず Compute error で失敗します。

NPU は LLM の生成速度目的では割に合いません。 モデルの対応範囲も限定的(対称 INT4 中心、コンテキスト上限が小さい)です。

まとめ

用途 NPU の評価 根拠
Embeddings / RAG ◎ CPU 比 2.4 倍、低電力
ASR (Whisper) ○ CPU 比 1.5 倍、常時処理向き
LLM 生成 △ GPU の 1/5、モデル対応も限定的
GPU との同時 △ 合計の伸びは限定的

Meteor Lake の NPU は「埋め込み」と「音声認識」に使うのが結論です。どちらも GPU から切り離せるため、「GPU は LLM、NPU は RAG のベクトル化と文字起こし」という役割分担が有効です。

得られた教訓(Meteor Lake NPU)

  1. パススルーは Proxmox の dev[n]: 方式で(手動 lxc.* は効かない)
  2. NPU は静的シェイプ必須(動的シェイプはコンパイル不可)
  3. NPU_PLATFORM=3720 を明示
  4. Whisper は --task ...-with-past + transformers>=5.4
  5. ユーザ空間の Level Zero NPU ドライバが必要
  6. NPU 実行は OpenVINO 同梱イメージ内で行うと確実

参考リンク

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?