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

## 目的
　Module-LLM(ax630c)やSipeed Maix4(ax650n)などのAxera-techのSOCのNPUでMeloTTSを動かすmelotts.axeraが公開されています。
 melotts.axeraには日本語のモデルも公開されています。melotts.axeraのモデルをaxmodelへの変換を行います。

## 環境

モデル変換を行った環境です
```
Ubuntu:22.04
Pulsar2:version 3.3
```

## 注記
~~Sipeed Maix4(ax650n)のモデル変換には成功しましたが、M5StackModule-LLM(ax630c)のモデル変換はエラーがでて変換できませんでした。issueを立てています。~~ (2024/12/22)
Pulsar2のバージョンとNPU Modeの組み合わせにより、M5StackModule-LLM(ax630c)のモデル変換できました。(2025/1/3追記)

| target_hardware | Pulsar2 Version | NPU Mode | Status |
|----------------|---------|-----|--------|
| AX620E| 3.2-temp-melotts(commit:93dc9c99) | NPU1 | Error |
| | | NPU2 | Success |
| | 3.3(commit:3cdead5e) | NPU1 | Error |
| | | NPU2 | Success |
| | 3.3-temp(commit:f0b32d03) | NPU2 | Error |


https://github.com/ml-inory/melotts.axera/issues/3

## MeloTTS

MeloTTSは、マサチューセッツ工科大学（MIT）とMyShell.aiが共同開発した高品質な多言語テキスト音声変換（TTS）ライブラリです。

github：MeloTTS: High-quality multi-lingual text-to-speech library by MyShell.ai Huggingface：https://huggingface.co/myshell-ai

なお、本記事はこちらをベースに翻訳・作成しました。
 基于 AX650N 部署 TTS 模型（MeloTTS）<https://zhuanlan.zhihu.com/p/8659249342>


### 特徴

- 多言語サポート
- 英語（アメリカ、イギリス、インド、オーストラリア）、スペイン語、フランス語、中国語、日本語、韓国語などの多言語をサポート。中英混合サポート(特に中国語ユーザー向けに、英単語を含む中国語テキストの発音をサポート)
- 
- オープンソースとカスタマイズ可能
-オープンソースプロジェクトとして、自由な使用とコミュニティからの貢献やカスタム開発を奨励し、継続的な改善と革新のための環境を提供

- 高い拡張性
-新しいデータセットやモデル構造の統合が容易

MeloTTSの核心は、シンプルな設計理念と強力なパフォーマンスにあります。エンドツーエンドの学習モードを採用し、深層学習モデルを通じて入力テキストを自然で滑らかな音声に変換します。モデルアーキテクチャは主にTacotron 2とWaveGlow構造を採用しています。Tacotron 2はシーケンスツーシーケンスの音響モデリングフレームワークです。
WaveGlowは波形合成ネットワークで、両者の組み合わせにより高品質な音声を生成できる構造です。MeloTTSは特に軽量化と効率性に注力しており、低リソース環境での実行が可能で、モバイルデバイスやIoTデバイスへの展開に適しています。

## 日本語のモデル変換

MeloTTSのONNXモデル取得と量子化キャリブレーションデータ取得を容易にするため、オープンソースプロジェクトを提供しています。このスクリプトを実行する前に、Pythonの仮想環境のcondaをインストールしてください。

GitHub - ml-inory/melotts.axera
https://github.com/ml-inory/melotts.axera


```bash
$ conda create -n melotts python=3.9  
$ conda activate melotts
$ git clone https://github.com/ml-inory/melotts.axera.git
$ cd model_convert 
$ sudo apt install libmecab-dev
$ pip install unidic-lite fugashi
$ pip install torch==2.1.2 torchaudio==2.1.2 --index-url https://download.pytorch.org/whl/cpu
$ pip install -r requirements.txt
```


```
$ python convert.py -l JP
-l/-- language 変換する言語モデルを指定。
現在、EN(英語)、FR(フランス語)、JP(日本語)、ES(スペイン語)、ZH(中国語)、KR(韓国語)をサポートしています。
--dec_len デコーダ入力長 128
```

最終的に、日本語のencoder-zh.onnx、decoder-zh.onnxの2つのonnxモデルファイル、量子化データセット、その他必要なファイルが生成されました。



### Pulsar2のインストール

こちらを参照して、Pulsar2をインストールします。

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

Pulsar2がインストールされている、Dockerを起動します。
ここでは、変換のできた、pulsar2のversion3.3を使用します。

```
$ sudo docker run -it --net host --rm -v $PWD:/data pulsar2:3.3
```

## Module-LLM(AX630C)のNPU用モデルへ変換

Pulsar2のbuildコマンドで、onnxモデルをModule-LLM(ax630c)のNPUに対応するaxモデルへ変換します。

```
pulsar2 build --input decoder-zh.onnx --config config_decoder_u16.json --output_dir decoder --output_name decoder.axmodel --target_hardware AX620E --npu_mode NPU2 --compiler.check 0
```

decoderフォルダの下で、decoder.axmodelが生成されています。

```
 ls decoder
build_context.json  compiler  decoder.axmodel  frontend  quant

```

## Module-LLM(AX630C)で動かすには

必要なパッケージをインストールします。
pipのパッケージのサイズがおおきいため、pipでインストールする際に、比較的容量の大きいoptフォルダにインストールします。/opt/site-packagesフォルダを指定します。

```
apt-get install libsndfile1-dev ibmecab-dev
mkdir /opt/site-packages 
pip3 install -r requirements.txt --prefix=/opt/site-packages
```

/root/.bashrc に以下の 2 行を追加した後 (実際に追加されたパスは自分で確認する必要があります)、ターミナルに再接続するか、source ~/.bashrc を実行します。

```
export PYTHONPATH=$PYTHONPATH:/opt/site-packages/local/lib/python3.10/dist-packages  
export PATH=$PATH:/opt/site-packages/local/bin
```

axモデルを呼び出して、入力した日本語からwavファイルを出力します。

```
root@m5stack-LLM# python3 melotts.py -s "あけましておめでとうございます" -e ../models/encoder-jp.onnx -d ../models/decoder-jp.axmodel --language JP

sentence: あけましておめでとうございます
sample_rate: 44100
encoder: ../models/encoder-jp.onnx
decoder: ../models/decoder-jp.axmodel
language: JP
 > Text split to sentences.
あけましておめでとうございます
 > ===========================
split_sentences_into_pieces take 4.995107650756836ms
load models take 11688.544511795044ms

Sentence[0]: あけましておめでとうございます
Load language module take 32087.883710861206ms
encoder run take 293.61ms
Decode slice[0]: decoder run take 227.86ms
Decode slice[1]: decoder run take 227.57ms
Decode slice[2]: decoder run take 227.69ms
Decode slice[3]: decoder run take 227.80ms
Save to output.wav
```


比較のため、onnxモデルを使ったCPU推論で、入力した日本語からwavファイルを出力するスクリプトもあります。
```
root@m5stack-LLM# python3 melotts_onnx.py -s "あけましておめでとうございます" -e ../models/encoder-jp.onnx -d ../models/decoder-jp.onnx --language JP


sentence: あけましておめでとうございます
sample_rate: 44100
encoder: ../models/encoder-jp.onnx
decoder: ../models/decoder-jp.onnx
language: JP
 > Text split to sentences.
あけましておめでとうございます
 > ===========================
split_sentences_into_pieces take 4.953861236572266ms
load models take 15377.976179122925ms

Sentence[0]: あけましておめでとうございます
Load language module take 31396.860599517822ms
encoder run take 267.12ms
Decode slice[0]: decoder run take 16009.63ms
Decode slice[1]: decoder run take 16278.30ms
Decode slice[2]: decoder run take 15941.68ms
Decode slice[3]: decoder run take 15868.54ms
Save to output.wav
```

onnx(CPU)とaxmodel(NPU)の推論の時間を比べると53倍くらいの差がでました。（モデル読み込みの時間は除きます）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73020/637dd9e4-d3fb-3f97-7cef-808a74d61d15.png" width="50%">



## 音声出力

出力したwavファイルをModule-LLMで再生しようとすると、以下のエラーがでました。
wavファイルとModule-LLMとで、サンプルレートとチャンネル数の組み合わせが対応していないようです。

```
# aplay -D plughw:0,1 output.wav
Playing WAVE 'output.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Mono
aplay: set_params:1416: Unable to install hw params:
ACCESS:  RW_INTERLEAVED
FORMAT:  S16_LE
SUBFORMAT:  STD
SAMPLE_BITS: 16
FRAME_BITS: 16
CHANNELS: 1
RATE: 44100
PERIOD_TIME: (125011 125012)
PERIOD_SIZE: 5513
PERIOD_BYTES: 11026
PERIODS: 4
BUFFER_TIME: (500045 500046)
BUFFER_SIZE: 22052
BUFFER_BYTES: 44104
TICK_TIME: 0
```



そこで、WAVファイルをsoxで変換し再生しました。

```
# soxがインストールされていない場合は先にインストール
apt-get install sox

# ファイルを48kHzステレオに変換
sox output.wav -r 48000 -c 2 converted_output.wav

# 変換したファイルを再生
aplay -D plughw:0,1 converted_output.wav
```