PyTorch/TensorFlow で書かれた音声スピーチ系ネットワークを C++ でモバイルで推論するためのメモ

## 背景

PyTorch/TensorFlow で書かれた音声スピーチ系ネットワーク(e.g. Tacotron, FastSpeech)をモバイルで推論したい.
TensorFlow-Lite などでは RNN 系などの対応が十分ではないので変換がうまくいかない.
(少なくとも TensorFlow tacotron は .pb から toco での変換は r1.14 時点では失敗する)

libtensorflow を使う手もあるが, やりたいことに対して無駄が多すぎるし, ビルドも手間であり, また libtensorflow は Android には正式対応していない(モバイルでのオフィシャルな対応は tensorflow-lite に移行したっぽい)

libtorch もなかなかうまくいかない(ScriptModule で記述しても一部変換できない op がある)

tvm などの他のモバイル推論に特化したライブラリでも, RNN 系や音声系の対応は(2019 年 9 月時点)行われていないか, 非常に限られた対応となっている.

多少演算に時間がかかったとしても, モバイルで動かしたい.

## Tacotron 系

T.B.W.

## Transformer 系

使っているネットワーク op はそれほど多いわけではない.
たとえば, PyTorch 実装の FastSpeech(Transformer TTS) https://github.com/xcmyz/FastSpeech では, pytorch の op は

* torch.bmm(batch matmul)
* nn.Softmax
* nn.Conv1d
* nn.Linear
* nn.Dropout
* nn.LayerNorm
* nn.Embedding
* nn.ModuleList

あたりくらいしか使っていない.

waveglow を CUDA だけ(cuDNN は使っているが)で動かしていたりする既存の取り組みがある.

https://github.com/Saurabh-29/Waveglow_Inference_in_CUDA

=> C++ で自作したい! 

## PyTorch

推論(forward, inference)についての実装です.

### torch.bmm

https://pytorch.org/docs/stable/torch.html#torch.bmm

行列をまとめて matmul すれば OK


### nn.SoftMax, nn.Conv1d, nn.Linear

通常の NN のように実装すればよい. 特に特殊なことはなし.

### nn.Embedding

https://pytorch.org/tutorials/beginner/nlp/word_embeddings_tutorial.html

word embedding などに使われる.

実装としては, int の配列(`[x]`)と重み行列(`W`)を受け取り, float の配列(2 次元行列)を返す.

一番簡単なのは, `W[x]` な感じで配列ルックアップする.

重み行列が大きく, データが sparse な場合は `std::unordered_map` などを使ってルックアップするといいかも?(GPU で動かす場合はハッシュ or 二分木探索とか?) 

Chainer では `EmbedID` https://docs.chainer.org/en/stable/reference/generated/chainer.links.EmbedID.html に相当. ちなみに Chainer では `initialW` が与えられなかった場合, 正規分布の乱数で weight 行列を初期化している.


## nn.Dropout

推論の場合はなにもしない(input をそのまま返す)

参考までに, dropout を実装したい場合は,

Chainer と TensorFlow の Dropout 実装メモ
https://qiita.com/syoyo/items/8b459cc5524eff6f54f3

を参考に実装する. PyTorch(ATen での実装) はノイズ(乱数)にベルヌーイ分布を使っている.

## nn.LayerNorm

https://pytorch.org/docs/stable/nn.html#torch.nn.LayerNorm

Aten に CPU 実装があるのでそれを参考に推論を実装.
特段特殊なことはしていない.

## nn.ModuleList

Module(op)をリストで保持できるようにしたもの.


## MultiHeadAttension など

pytorch には `nn.MultiheadAttention` が 1.2 からあります(`nn.Transformer` も 1.2 から導入). 必要に応じて参考にしましょう.

https://pytorch.org/docs/master/nn.html#multiheadattention


# その他

音声系は, ニューラルネットワークモジュールのほかにも, text の処理や wav の処理, fft などいろいろ周辺のコードも必要です.

## librosa 相当や, FFT 処理

https://github.com/lighttransport/nanosnap

nanosnap にご期待ください.
`stft`, `mfcc` などがあります.
(少なくとも現時点で Tacotron のモバイル実行で必要なものはあるはず)

## wav 入出力

nanosnap か, nanosnap でも使っている dr_wav がおすすめ.

https://github.com/mackron/dr_libs

## inflect

inflect の C++ 実装がほしい(とくに `number_to_words`)

https://github.com/syoyo/tacotron-tts-cpp/tree/master/experiment/number_to_words

ご期待ください.


## PyTorch のメモ

`nn.init.normal_` など, アンダースコアで終わっている関数は in-place 関数.
PyTorch では in-place 関数の利用は推奨されないが, 乱数などでウェイトを初期化したいときとかは in-place 便利.

What does the underscore suffix in PyTorch functions mean?
https://stackoverflow.com/questions/52920098/what-does-the-underscore-suffix-in-pytorch-functions-mean


## 参考文献

その他参考になる文献です.

作って理解する Transformer / Attention
https://qiita.com/halhorn/items/c91497522be27bde17ce


## TODO

* [ ] AMDGraphX あたりでよろしく演算グラフを quantize して最適化する(https://github.com/ROCmSoftwarePlatform/AMDMIGraphX/wiki/Getting-started:-using-the-new-features-of-MIGraphX-0.4#bert-natural-language-processing-nlp-model)
* [ ] Torch model から直接 C++ コードを吐くのはどうか?
  * [ ] chainer2tflite の参考 : https://qiita.com/syoyo/items/e84d66b2a938843c1594  
  * [ ] Torch JIT やら onnx に吐いてから変換は RNN 系が自動展開されてしまうのでうまくいかなさそう(すくなくとも torch jit はダメだった)