Qwen3.5 9B を INT8 にして Hexagon NPU に載せる。QNN converter の signal 11 を越えるまで

本記事にはアフィリエイト広告(Amazon アソシエイト)が含まれます。

実測日: Thu May 28 2026 09:00:00 GMT+0900 (Japan Standard Time)

前回、Qwen3.5-9B を FP16 と FP32 で Hexagon NPU(HTP)に載せた。 次は INT8 である。 重みの読み出しが半分になれば decode は速くなるはずだが、量子化は変換器の中で落ちた。 本記事は、その落ち方と、回避に至るまでの試行を記録する。

対象の大きさ

ONNX を静的に走査すると、量子化の候補になる演算の規模が見える。

項目 数
Add 系の候補 6,393
Mul 系の候補 8,377
層 0 の密度 Add 264、Mul 344
層 31 の密度 Add 7、Mul 15

層ごとの密度が大きく違うので、問題を探すときは層と枝の分布で優先順位を付けた。 変換を 1 回流すと、診断だけの短い経路で平均 140 秒、本番相当で平均 517 秒かかる。 当てずっぽうの再実行は時間を失うだけなので、走査を先に固定した。

signal 11 で落ちる

量子化の本線は、converter の量子化器が encoding(各テンソルの scale と zero point)を当てはめる apply_encodings() の途中で、signal 11 で異常終了した。 止まる場所は特定の Expand の直後で、再現性があった。

まず、encoding の当てはめを飛ばす診断用の経路を用意し、保存までの道が通ることを確かめた。 ただしこの回避は診断専用で、成果物に昇格させないことを決めた。 量子化していないものを INT8 と呼ぶわけにはいかない。

encoding を自前で作る

原因は、converter が内部で作る encoding が足りないテンソルがあり、そこで量子化器が落ちることだった。 そこで、encoding を外から与える方針に変えた。 これが 15 回の作り直しになった。

版 変えたこと 結果
v1 から v6 FP16 のテンソルと _pre_reshape を除外 weight の y_scale が無いと言われる
v7 から v10 weight の入力テンソルを追加 同上
v11 形式を辞書からリストへ name が無いと言われる
v12 から v13 name を追加 y_scale が無いと言われる
v14 すべての float32 を INT8 に FP16 のテンソルは量子化できないと言われる
v15 FP16 のテンソルを除外 別の FP16 テンソルで同じ指摘
v15 と --float_bitwidth 32 float32 で統一 apply_encodings() を通過

判ったことは 3 つある。

  • encoding の形式は版 2.0.0 で、リスト形式で各要素に name と y_scale が要る。
  • FP16 のテンソルには INT8 の encoding を当てられない。 既定の --float_bitwidth 16 だと converter が float32 を内部で float16 に変えるため、どこかで必ず衝突する。 明示的に 32 を指定すると、この衝突が消える。
  • converter が reshape の前に作る _pre_reshape という内部テンソルが 3,359 個あり、親テンソルの encoding を引き継ごうとする。 除外しても別の経路で同じ指摘が出るので、FP16 を避ける側で解決するしかなかった。

通った後に残ったもの

最終版は 21,400 個の encoding を当てはめて apply_encodings() を通過した。 signal 11 は消え、量子化の上書き機構が働くことも確かめた。

その先で、backend の検証が止めた。

validateQnnOpConfig: Failed QNN validation for/wrapped_model/model/layers.3/self_attn/ExpandTensor 3962 and 3963 have mismatching datatypes. 0x232 != 0x216.

これは encoding とは別の、型の不整合である。 同種の不整合は FP16 の変換で既に直した経験があり、同じ修正で対処できる見込みだった。 進捗で言えば、量子化の本線は 99.99% から 99.995% に進んだ。 残りは型の修正と、実際の精度の確認、そして Q900 の実機での実行である。

次の 9B のために残すこと

Qwen3.5 で確定した修正は、次の 9B 系モデルでも使い回せる形にした。 ONNX の静的走査を先に固定すること、encoding の網羅を実行前に検査すること、診断用の回避を成果物にしないこと、の 3 つである。 初回の安定化までの時間を、何も無い状態から始めるより 2 割から 4 割縮めることを狙っている。

この記事で扱った機材

Crucial DDR5-5600 SO-DIMM 128 GB キット(64 GB × 2、CT2K64G56C46S5)

27B 級の重みを bf16 のまま読み込んだり、1 bit への変換の中間状態を持ったりするには、ミニ PC のメモリをこの容量まで積む必要がある。

KIOXIA NVMe SSD

変換の中間ファイル、context binary、評価の記録の置き場。モデル 1 つの作業で数百 GB を使うので、容量に余裕のあるものを選ぶ。