ailia LLM NPU(QNN)ガイド

ailia LLM の QNN バックエンドを使用して、Qualcomm SoC の NPU で LLM / VLM を高速に推論する方法を説明します。

概要

ailia LLM 1.5.0 以降は Qualcomm SoC の NPU(Hexagon)を使用した高速推論に対応しています。NPU 向けには、llama.cpp とは異なるアイリア社独自の実装を搭載しています。

NPU を使用することで、高速・低消費電力に LLM を実行可能です。テキストの LLM だけでなく、画像を入力する VLM(Vision Language Model)や音声入力にも対応し、NPU で処理できます。また、Snapdragon 7s など、ミドルレンジの端末でも Gemma 4 E2B や E4B を実行可能です。

実測値では、Android 端末上で Prefill を最大 4.2 倍高速化し、Prefill 中の CPU 使用率を 1/30 以下に抑えられます(Snapdragon 8+ Gen 1、2,048 トークン入力時の同端末 CPU 比)。

QNN(Qualcomm AI Engine Direct)は Qualcomm が提供するバックエンド API です。ailia LLM では、llama.cpp のモデル形式である GGUF を PC 上で SoC 専用の QNN Model(.qnn)に変換し、端末上の ailia LLM で NPU を使って実行します。端末には GGUF は不要で、QNN Model のみを配置します。

GGUF
llama.cpp 互換のモデルファイル(Q4 量子化)
↓
ailia LLM Compiler(PC)
QNN API でグラフを構築し、SoC 別の Context Binary を生成
↓
QNN Model(.qnn)
SoC 固有・自己完結のモデルファイル
↓
ailia LLM(Device)
Context Binary を復元し、Hexagon NPU で Prefill / Decode を実行
注意: QNN バックエンドは ailia LLM 1.5.0 以降で利用できます。

対応プラットフォーム

Windows on Arm では QNN 対応の ailia LLM ARM64 ビルドと、端末の SoC に一致する .qnn を使用します。Snapdragon X Plus 実機と QCS6490 実機(Windows 11 IoT Enterprise)で E2B のテキスト・画像・音声パッケージの動作を確認しています。異なる SoC 向けのモデルは流用できません。

注意: QNN(QAIRT)の対応バージョンは ailia SDK と同じ 2.47.0.260601 です。QNN Model は QNN SDK のバージョンにも依存するため、異なるバージョンの QNN ランタイムと組み合わせないでください。

対応モデル

現在は Gemma 4 E2B と Gemma 4 E4B(いずれもテキスト・画像・音声入力)に対応しています。画像・音声入力では、テキスト用の QNN Model に加えて、画像 / 音声エンコーダ(mmproj)の QNN Model を使用します。対応するモデルは SoC の Hexagon バージョンによって異なります。

Hexagon バージョンSoC の例対応モデルコンテキスト長
v68QCS6490Gemma 4 E2B8K
v69Snapdragon 8 Gen 1 / 8+ Gen 1Gemma 4 E2B8K
v73 以降Snapdragon 7s Gen 3、8 Gen 2 以降Gemma 4 E2B / E4B8K
注意: Hexagon v68 / v69 には 2 GB のサイズ制約があるため、E2B のみ対応です。v73 以降にはこの制約がないため E4B にも対応します。コンテキスト長は変換済みモデルではいずれも 8K ですが、ailia LLM Compiler での変換時の設定で変更できます。

QNN Model はモデルと Snapdragon の機種(SoC)ごとに別ファイルになります。変換済みのモデルファイルを提供していますので、対象端末の SoC に対応したファイルをご利用ください。

ファイル内容
gemma4-<model>-<soc>.qnn(例: gemma4-e2b-sm8475.qnn)テキストモデル(Prefill + Decode)
gemma4-<model>-<soc>-mmproj.qnn(例: gemma4-e2b-sm8475-mmproj.qnn)画像 / 音声エンコーダ(mmproj)

モデルのダウンロード

変換済みの Gemma 4 の QNN Model は下記からダウンロードできます。VLM / 音声入力を使用する場合は、テキストモデルと mmproj の両方をダウンロードしてください。E4B は Hexagon v73 以降のみ対応です。

SoC端末の例モデルファイル内容サイズ
sm8475
(Hexagon v69 / FP16)
Snapdragon 8+ Gen 1E2Bgemma4-e2b-sm8475.qnnテキストモデル(Prefill + Decode)3.60 GB
gemma4-e2b-sm8475-mmproj.qnn画像 / 音声エンコーダ(mmproj)1.10 GB
sm7635
(Hexagon v73 / Int16)
Snapdragon 7s Gen 3E2Bgemma4-e2b-sm7635.qnnテキストモデル(Prefill + Decode)3.56 GB
gemma4-e2b-sm7635-mmproj.qnn画像 / 音声エンコーダ(mmproj)0.63 GB
E4Bgemma4-e4b-sm7635.qnnテキストモデル(Prefill + Decode)5.54 GB
gemma4-e4b-sm7635-mmproj.qnn画像 / 音声エンコーダ(mmproj)0.64 GB
sc8380xp
(Hexagon v73 / FP16)
Surface(Snapdragon X Plus)E2Bgemma4-e2b-sc8380xp.qnnテキストモデル(Prefill + Decode)3.61 GB
gemma4-e2b-sc8380xp-mmproj.qnn画像 / 音声エンコーダ(mmproj)1.09 GB
E4Bgemma4-e4b-sc8380xp.qnnテキストモデル(Prefill + Decode)5.58 GB
gemma4-e4b-sc8380xp-mmproj.qnn画像 / 音声エンコーダ(mmproj)1.10 GB
qcs6490
(Hexagon v68 / Int16)
QCS6490E2Bgemma4-e2b-qcs6490.qnnテキストモデル(Prefill + Decode)3.60 GB
gemma4-e2b-qcs6490-mmproj.qnn画像 / 音声エンコーダ(mmproj)0.63 GB

いずれも ailia LLM 1.5.0(QAIRT 2.47.0.260601)向けで、コンテキスト長は 8192、AR-256 / AR-1 構成です。

その他の SoC について: 現在モデルファイルを公開しているのは sm8475、sm7635、sc8380xp、qcs6490 です。その他の SoC 向けの変換については、対象の SoC(ailiaLLMGetQNNModelName API で取得できる名称)を添えて ailia までお問い合わせください。
注意: QNN Model は Hexagon のバージョンだけでなく、QNN SDK のバージョンと SoC のモデル ID にも依存します。異なる SoC 向けのファイルを流用しないでください。実行中の端末に対応する SoC 名は ailiaLLMGetQNNModelName API で取得できます(例: "sm8475")。

プロジェクト設定

以下はアプリで事前コンパイル済みの .qnn を実行するための設定です。モデルをコンパイルする手順ではありません。QNN ランタイムはモデル生成時と同じ QAIRT 2.47.0.260601 を使用してください。

Windows on Arm:QNN ランタイムの取得

Qualcomm AI Engine Direct SDK から QAIRT 2.47.0.260601(配布 ZIP)を取得します。取得に Qualcomm アカウントが必要な場合があります。配布ファイルの場所と Hexagon バージョンの選び方は ailia SDK の QNN セットアップを参照してください。Snapdragon X Plus(Hexagon v73)の例では、SDK 内の lib/aarch64-windows-msvc にある QNN DLL と、lib/hexagon-v73/unsigned にある NPU 側の .so および .cat を使用します。

QNN 対応の Windows ARM64 版では、ailia_llm.dll と ailia_llm_qnn.dll、共有 GGML/llama DLL、SDK の QNN DLL、NPU 側の .so と .cat をアプリから参照できる場所に配置します。Qualcomm NPU ドライバも必要です。ailia SDK 用の ailia_qnn と ailia LLM 用の ailia_llm_qnn は別のプラグインです。ビルドと配置の詳細は Windows ARM64 QNN 手順を参照してください。

Android:アプリへの組み込み

Android プロジェクトの app/src/main/jniLibs/arm64-v8a/ に、配布物の libailia_llm.so、libailia_llm_qnn.so、同梱された libggml*.so / libllama*.so と CPU variant、および対象 Hexagon 世代の QNN Stub / Skel を配置します。libailia_llm.so をロードすると、QNN プラグインは必要時に遅延ロードされます。SDK の再配布条件を確認してください。

app/src/main/AndroidManifest.xml の <application> に FastRPC の宣言と native library の抽出を追加します。Skel が実ファイルとして見える必要があります。

<application android:extractNativeLibs="true" ...>
    <uses-native-library android:name="libcdsprpc.so" android:required="false" />
</application>

app/build.gradle の dependencies に、同じ世代の QNN ランタイムを追加します。

dependencies {
    implementation 'com.qualcomm.qti:qnn-runtime:2.47.0'
}

.qnn は assets 内のパスを直接指定できません。初回起動時にアプリのプライベートストレージへコピーするかダウンロードし、その実ファイルパスを以下の API に渡してください。

評価版アプリ

Android 向けの評価版 APK は ailia-models-kotlin-qnn.apk です。

Windows on Arm 向けの評価版アプリは ailia-models-flutter-qnn-v261008.zip です。Snapdragon X Plus と QCS6490 で動作します。

コードサンプル:SoC 名の取得とモデルのオープン

まず SoC 名(例: sm7635)を取得し、同じ SoC 用にコンパイルした gemma4-e2b-<soc>.qnn などのテキストモデルを選びます。返る文字列はファイル名全体ではなく SoC 名です。n_ctx=0 はパッケージに埋め込まれたコンテキスト長を選択します。モデル未公開の SoC に別 SoC 用のファイルを流用しないでください。画像・音声入力には、テキストモデルを開いた後、同じモデル・SoC の -mmproj.qnn を通常のプロジェクタ API で追加します。

以下は Android のテキストモデル例です。モデルファイルは前述のプライベートストレージ内にあるものとします。Windows on Arm の QNN 対応ビルドでも同じ API を使用し、ファイルパスを Windows の配置先に置き換えます。QNN プラグインがない場合は UNIMPLEMENTED、未対応 SoC の場合は OTHER_ERROR になります。

C

#include "ailia_llm.h"
#include <stdio.h>

/* model_dir: アプリのプライベートストレージ。成功時は呼び出し側が Destroy する。 */
int open_qnn_model(const char *model_dir, struct AILIALLM **out) {
    if (model_dir == NULL || out == NULL) return AILIA_LLM_STATUS_INVALID_ARGUMENT;
    *out = NULL;
    const char *soc = NULL; /* ライブラリ所有。解放しない */
    int status = ailiaLLMGetQNNModelName(&soc);
    if (status != AILIA_LLM_STATUS_SUCCESS) return status;
    char path[512];
    int n = snprintf(path, sizeof(path), "%s/gemma4-e2b-%s.qnn", model_dir, soc);
    if (n < 0 || (size_t)n >= sizeof(path)) return AILIA_LLM_STATUS_INVALID_ARGUMENT;
    struct AILIALLM *llm = NULL;
    status = ailiaLLMCreate(&llm);
    if (status != AILIA_LLM_STATUS_SUCCESS) return status;
    status = ailiaLLMOpenModelFileA(llm, path, 0);
    if (status != AILIA_LLM_STATUS_SUCCESS) { ailiaLLMDestroy(llm); return status; }
    *out = llm;
    return AILIA_LLM_STATUS_SUCCESS;
}

Python

from pathlib import Path
import ailia_llm

soc = ailia_llm.AiliaLLM.get_qnn_model_name()
model_path = Path("/data/user/0/your.package/files") / f"gemma4-e2b-{soc}.qnn"
llm = ailia_llm.AiliaLLM()
llm.open(str(model_path), n_ctx=0)

Flutter

import 'package:ailia_llm/ailia_llm_model.dart';

final soc = AiliaLLMModel.getQNNModelName();
final model = AiliaLLMModel();
model.open('/data/user/0/your.package/files/gemma4-e2b-$soc.qnn', 0);
// 使用後は model.close();

JNI(Kotlin)

context は Android の Activity または Application の Context です。

import axip.ailia_llm.AiliaLLM

val soc = AiliaLLM.getQNNModelName()
val llm = AiliaLLM()
val modelPath = "${context.filesDir}/gemma4-e2b-$soc.qnn"
llm.openModelFile(modelPath, 0)
// 使用後は llm.close()

Unity(C#)

using System;
using System.IO;
using UnityEngine;
using ailiaLLM;

string soc = AiliaLLMModel.GetQNNModelName();
var llm = new AiliaLLMModel();
if (!llm.Create()) throw new Exception("ailiaLLMCreate failed");
string modelPath = Path.Combine(Application.persistentDataPath, $"gemma4-e2b-{soc}.qnn");
if (!llm.Open(modelPath, 0)) throw new Exception(llm.GetErrorDetail());
// 使用後は llm.Dispose();

アーキテクチャ詳細

QNN Model は Prefill 用の固定長グラフ(AR-N)と Decode 用のグラフ(AR-1)を同じ QNN Context に格納し、重みと KV キャッシュを共有します。重みは変換時に 4bit(W4)に量子化され、FP16 モデルでは活性値と KV キャッシュは FP16 で処理されます。

ailia LLM Compiler は FP16 と Int16 の両方に対応しており、対象の SoC に応じて変換時に切り替えられます。Snapdragon 7s Gen 3 の NPU は Hexagon v73 ですが、FP16 に対応していないため、ailia LLM では Int16 に量子化したモデルを使用します。Qualcomm の Snapdragon 7s Gen 3 製品資料(PDF、2 ページの Artificial Intelligence)では、NPU の対応精度として INT4、INT8、INT16 が記載されており、FP16 は含まれていません。Hexagon のバージョンだけでなく、SoC ごとの対応精度に合わせてモデルを選択してください。

ailia LLM は QNN Model を mmap で読み込み、QNN Context を復元して Hexagon NPU(HTP)上で実行します。Prefill は固定長の AR-N グラフで処理し、プロンプトが AR の倍数でない場合は最後のスライスをパディングします。Decode は同じ Context 内の AR-1 グラフで処理し、Prefill 後の KV キャッシュのコピーは発生しません。VLM では画像のパッチ化と位置埋め込みを CPU で行い、16 層の Vision Transformer からプーリング、プロジェクションまでを 1 つの QNN グラフで実行します。音声入力には Audio グラフを使用し、音声エンコーダを NPU で実行します。また、層別トークン埋め込み(PLE)のフロントエンドも各 Transformer グラフ内で実行し、CPU 側にはトークン行の参照とメディア埋め込みの差し替えのみを残しています。これにより、入力処理を CPU で行う場合に比べて Int16 モデルの Prefill が改善します。

QNN Model(.qnn)SoC 固有・自己完結のモデルファイル
AR-N グラフ
Prefill(Transformer + LM head)
+
AR-1 グラフ
Decode(Transformer + LM head)
+
共有 static weights(W4)
AR-N / AR-1 で同じ重みを参照
+
Vision グラフ
画像エンコーダ(mmproj.qnn)
+
Audio グラフ
音声エンコーダ(mmproj.qnn)
→
mmap / Context 復元
ailia LLM(Device)実行時のソフトウェアスタック
ailia LLM
トークナイズ / プロンプト分割 / サンプリング
↓
ailia_llm_qnn
Context 復元、KV キャッシュ管理(プラグイン)
↓
QNN(HTP バックエンド)
Qualcomm AI Engine Direct
↓
libcdsprpc.so
FastRPC(CPU と NPU 間の RPC インタフェース)
↓
Hexagon NPU
HVX(ベクトル演算ユニット)/ HMX(行列演算ユニット)

ベンチマーク

Snapdragon 8+ Gen 1(SM8475)、Snapdragon 7s Gen 3(SM7635)、Snapdragon X Plus(SC8380XP)、QCS6490 で Gemma 4 E2B / E4B を測定した Decode / Prefill スループット、Prefill 中の CPU 使用率、VLM・音声入力の応答時間、メモリ使用量は ailia LLM NPU(QNN)ベンチマーク を参照してください。

制約

他の手法との比較

Android の Qualcomm SoC で LLM を実行する主な手法と ailia LLM の比較です。ailia LLM は NPU を使用でき、Gemma 4 を NPU で実行でき、Hexagon v69 以降の比較的古い SoC にも対応します。

手法NPU 対応Gemma 4 対応対応 SoC(Android)備考
llama.cpp×○制約なし(CPU 実行)CPU 実行です。実験的な Hexagon バックエンドがありますが、NPU 側のライブラリ(Skel)が署名されていないため一般の端末では実行できません。画像エンコーダは CPU で実行されます。
QNN(Qualcomm AI Engine Direct)○×Hexagon v65 以降(Snapdragon 845 以降)低レベルの推論 API で、LLM のランタイムは提供されません。
QNN GenAI(Qualcomm Genie / GenieX)○×Hexagon v79 以降(Snapdragon 8 Elite 以降)公式の対応 Android SoC は Snapdragon 8 Elite 系のみで、Hexagon v79 以降が必須です。Gemma 4 の NPU 用(QAIRT)モデルは提供されていません。
LiteRT-LM NPU○×Snapdragon 8 Gen 2 / 8 Gen 3 / 8 Elite(SM8550 / SM8650 / SM8750)Qualcomm 向けに公開されている NPU モデルは Gemma3-1B(4bit、コンテキスト長 1,280)のみです。Gemma 4 の NPU モデルは Google Tensor と Intel 向けにのみ提供されています。テキスト専用で、画像・音声入力には対応していません。
ailia LLM○○Hexagon v69 以降(Snapdragon 8 Gen 1 以降、7s Gen 3 など)Snapdragon 8+ Gen 1 などの古い SoC でも NPU を使用でき、Gemma 4 E2B / E4B をテキスト・VLM・ALM とも NPU で実行できます。Int16 への量子化に対応しており、FP16 に非対応の Snapdragon 7s でも実行可能です。