はじめに
「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)
- パススルーは Proxmox の
dev[n]:方式で(手動lxc.*は効かない) - NPU は静的シェイプ必須(動的シェイプはコンパイル不可)
-
NPU_PLATFORM=3720を明示 - Whisper は
--task ...-with-past+transformers>=5.4 - ユーザ空間の Level Zero NPU ドライバが必要
- NPU 実行は OpenVINO 同梱イメージ内で行うと確実
参考リンク
- Intel NPU / OpenVINO 設定: https://docs.openvino.ai/2026/get-started/install-openvino/configurations/configurations-intel-npu.html
- OpenVINO GenAI on NPU: https://docs.openvino.ai/2025/openvino-workflow-generative/inference-with-genai/inference-with-genai-on-npu.html
- Whisper on NPU の既知バグ(#4222): https://github.com/openvinotoolkit/openvino.genai/issues/4222
- OpenVINO Model Server(NPU 配信): https://docs.openvino.ai/2026/model-server/ovms_demos_llm_npu.html