Module-LLMのNPU用モデルへ変換する(Qwen2編)

## 目的
　Module-LLM(ax630c)のNPUでQwen2を動かすために、Qwen2のLLMモデルをaxmodelへの変換を行います。
　QwenはアリババグループのQwenチームが開発した大規模言語モデルおよび大規模マルチモーダルモデルシリーズです。現在、言語モデルはQwen2バージョンにアップグレードされています。言語モデルもマルチモーダルモデルも、大規模な多言語・マルチモーダルデータで事前学習され、高品質なデータで微調整することで人間の嗜好に近づけています。

本記事はこちらを元に作成しました。

https://pulsar2-docs.readthedocs.io/en/latest/appendix/build_llm.html


## Qwen2とは

Qwen2の最新バージョンは、以下の機能を備えています：

- 0.5B、1.5B、7B、57B-A14B、72Bの5つのモデルサイズがあります。
-  各サイズにベースおよびコマンド微調整モデルを提供し、コマンド微調整モデルが人間の嗜好に合うように調整されています、
- ベースおよびコマンド微調整モデルの多言語サポートをしています。
- すべてのモデルで32K長のコンテクストを安定的にサポートしています
- ツールコール、RAG（Retrieval Augmented Text Generation）、ロールプレイング、AIエージェントなどのサポートしています。
- 最近、Hugging Faceの共同設立者兼CEOであるClem Delangueは、6月26日にXプラットフォーム上で、AliCloudのオープンソースQwen命令微調整モデルであるQwen2-72Bがオープンソースモデルのリストのトップになったとツイートしました。HuggingFaceからの第三者によるお墨付きは、Qwen2の技術の高さを再確認するものです。


https://link.zhihu.com/?target=https%3A//github.com/QwenLM/Qwen2

https://link.zhihu.com/?target=https%3A//qwen.readthedocs.io/zh-cn/latest/index.html



## 対応しているモデル

pulsar2ツールを使ってHuggingfaceのモデル（*.safetensorまたはpytorch_model.bin）をaxmodelに変換するための基本操作を紹介します。
以下のHuggingfaceのモデルが今回紹介する変換に対応しています。
今回のサンプルモデルはQwen2-0.5B-Instructです。

- Llama2、Llama3、Llama3.2
- TinyLlama-1.1B
- Qwen1.5、Qwen2、Qwen2.5
- Phi2、Phi3
- MiniCPM、MiniCPM-V 2.0
- SmolLM
- ChatGLM3
- OpenBuddy


## Pulsar2のインストール

まずPulsar2 ドキュメントの「開発環境準備」セクションに従って、dockerイメージをインストールしPulsar2のdocker環境に入っていることを前提とします。

https://qiita.com/nnn112358/items/37a4dac27bca23ec7754#pulsar2%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB

https://pulsar2-docs.readthedocs.io/en/latest/user_guides_quick/quick_start_prepare.html#dev-env-prepare

## ax-llm-buildプロジェクトのダウンロードと実行

AXERA-TECHのax-llm-buildプロジェクトをダウンロードします。


```bash
git clone https://github.com/AXERA-TECH/ax-llm-build.git
```

## ax-llm-buildとは

Pulsar2ツールチェインでは、CVモデルの変換には、pulsar2 llm_buildコマンドを使いましたが、LLMモデルの変換には、pulsar2 llm_buildコマンドを使います。

```bash
usage: pulsar2 llm_build [-h] [--input_path INPUT_PATH] [--output_path OUTPUT_PATH] [--prefill_len PREFILL_LEN]
                        [--parallel PARALLEL] [--model_config MODEL_CONFIG] [--kv_cache_len KV_CACHE_LEN]
                        [--post_topk POST_TOPK] [--post_weight_type {bf16,s8}] [-t {fp16,bf16,fp32}]
                        [-w {fp16,bf16,fp32,s8,s4}] [-c CHECK_LEVEL] [--chip {AX620E,AX650}] [--prompt PROMPT]

オプション引数:
-h, --help            ヘルプメッセージを表示して終了
--input_path INPUT_PATH
                        モデルまたはnpyファイルのパス
--output_path OUTPUT_PATH
                        出力するax_modelのパス
--prefill_len PREFILL_LEN
                        プリフィルのトークン長
--parallel PARALLEL   ビルド並列数
--model_config MODEL_CONFIG
                        設定ファイル
--kv_cache_len KV_CACHE_LEN
                        kv_cacheの長さ
--post_topk POST_TOPK
                        モデル出力のインデックスと確率
--post_weight_type {bf16,s8}
                        ポスト重み型
-t {fp16,bf16,fp32}, --hidden_state_type {fp16,bf16,fp32}
                        隠れ状態のデータ型
-w {fp16,bf16,fp32,s8,s4}, --weight_type {fp16,bf16,fp32,s8,s4}
                        重みのデータ型
-c CHECK_LEVEL, --check_level CHECK_LEVEL
                        チェックレベル 0:実行 1:レイヤーチェック 2:計算 1+1
--chip {AX620E,AX650}
                        チップ
--prompt PROMPT       check_level==2の場合のプロンプト
```


## Qwen2-0.5B-Instructのダウンロード:

huggingface_hubから、Qwen2-0.5B-InstructのLLMモデルをダウンロードします。

```bash
cd ax-llm-build
pip install -U huggingface_hub
huggingface-cli download --resume-download Qwen/Qwen2-0.5B-Instruct --local-dir Qwen/Qwen2-0.5B-Instruct
```

## LLMモデルの変換

Qwen2-0.5B-InstructのLLMモデルをpulsar2 llm_buildでAXモデルへの変換を行います。

```bash
pulsar2 llm_build --input_path Qwen/Qwen2-0.5B-Instruct/ --output_path Qwen/Qwen2-0.5B-w8a16/ --kv_cache_len 1023 --model_config config/qwen2-0.5B.json --hidden_state_type bf16 --weight_type s8
```

pulsar2 llm_buildでAXモデルへの変換のログです。

```bash
pulsar2 llm_build --input_path Qwen/Qwen2-0.5B-Instruct/ --output_path Qwen/Qwen2-0.5B-w8a16/ --kv_cache_len 1023 --model_config config/qwen2-0.5B.json --hidden_state_type bf16 --weight_type s8 --parallel 8 --chip AX620E
Config(
    model_name='Qwen2-0.5B-Instruct',
    model_type='qwen2',
    num_hidden_layers=24,
    num_attention_heads=14,
    num_key_value_heads=2,
    hidden_size=896,
    intermediate_size=4864,
    vocab_size=151936,
    rope_theta=1000000.0,
    max_position_embeddings=32768,
    rope_partial_factor=1.0,
    rms_norm_eps=1e-06,
    norm_type='rms_norm',
    hidden_act='silu',
    hidden_act_param=0.03,
    scale_depth=1.4,
    scale_emb=1
)
2024-08-22 16:16:04.364 | SUCCESS  | yamain.command.llm_build:llm_build:100 - prepare llm model done!
building llm decode layers   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 24/24 0:05:03
building llm post layer   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1/1 0:01:25
2024-08-22 16:22:33.485 | SUCCESS  | yamain.command.llm_build:llm_build:160 - build llm model done!
2024-08-22 16:22:47.861 | SUCCESS  | yamain.command.llm_build:llm_build:337 - check llm model done!

```

## 埋め込み処理と最適化

これらのファイルは、Qwen2モデルの埋め込み（embedding）処理に関連するツールとスクリプトです。

1. `fp32_to_bf16` は、バイナリファイルですが、その主な機能は:
- 32ビット浮動小数点数(float32)のデータを16ビット脳浮動小数点数(bfloat16)形式に変換するツールです
- これにより、モデルのサイズを削減しながら、数値精度を維持することができます

2. `embed-process.sh` は、以下の3つのステップで埋め込み処理を実行するシェルスクリプトです:
 埋め込みの抽出、埋め込みデータの前処理、float32からbfloat16への変換を行います。

この一連の処理は、モデルの埋め込み層を最適化し、メモリ使用量を削減しながら推論性能を維持することを目的としています。bfloat16形式は機械学習モデルでよく使用される最適化手法の1つです。


```bash
python tools/extract_embed.py --input_path Qwen/Qwen2-0.5B-Instruct/ --output_path Qwen/Qwen2-0.5B-w8a16/
python tools/embed-process.py --input Qwen/Qwen2-0.5B-w8a16/model.embed_tokens.weight.npy --output Qwen/Qwen2-0.5B-w8a16/model.embed_tokens.weight.float32.bin
chmod +x ./tools/fp32_to_bf16
./tools/fp32_to_bf16 Qwen/Qwen2-0.5B-w8a16/model.embed_tokens.weight.float32.bin Qwen/Qwen2-0.5B-w8a16/model.embed_tokens.weight.bfloat16.bin
```


## 出力されるファイル

model.embed_tokens.weight.bfloat16.bin, qwen_p128_l0.axmodel ~ qwen_p128_l23.axmodel, qwen_post.axmodel がボード上で動作するために必要です。

```bash
root@xxx:/data/ax-llm-build# tree Qwen/Qwen2-0.5B-w8a16
Qwen/Qwen2-0.5B-w8a16
├── model.embed_tokens.weight.bfloat16.bin
├── model.embed_tokens.weight.float32.bin # temp file, it can be deleted
├── model.embed_tokens.weight.npy # temp file, it can be deleted
├── qwen2_p128_l0_together.axmodel
├── qwen2_p128_l10_together.axmodel
...
├── qwen2_p128_l8_together.axmodel
├── qwen2_p128_l9_together.axmodel
└── qwen2_post.axmodel
```


## 補足

こちらで変換するQwen2のモデルは、pulsar2の開発者であるqqc 氏によって、実行するバイナリとあわせて公開されています。単にModule-LLMでお試しする場合はこちらのファイルの入手が推奨です。

<blockquote class="twitter-tweet"><p lang="en" dir="ltr"><a href="https://t.co/Bmlc5O70oh">https://t.co/Bmlc5O70oh</a> We will periodically update some commonly used open-source models. Welcome to use them.such as YOLO11、YOLO World v2、DepthAnything……</p>&mdash; qqc (@qqc1989) <a href="https://twitter.com/qqc1989/status/1855507945989038424?ref_src=twsrc%5Etfw">November 10, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 参考