Hiroshiba / vv_core_inference

VOICEVOXのコア内で用いられているディープラーニングモデルの推論コード
MIT License
29 stars 5 forks source link

複数のモデルを1つとして扱いたい #6

Closed Hiroshiba closed 1 year ago

Hiroshiba commented 1 year ago

複数のonnxを1つのonnxにしたいのですが、可能なのか・妥当そうかを議論したいです。

目的

decoderは学習時はspectrogram生成とvocoderに分かれていて、onnx化するときにまとめられます。 モデルサイズが大きいのはvocoderだけで、前者のspectrogram生成はかなり小さいです。 vocoderは汎用化が可能で、spectrogram生成と1:1対応する必要はありません。

複数のspectrogram生成モデルと1つのvocoderをまとめて1つのonnxを作れれば、容量を格段に抑えることができそうです。 今現在VOICEVOXのモデルは10種類ほどあり、1つあたり60MBほどの容量があります。 仮に1つにまとめられれば500MBほど容量削減できそうです。

また、それにともなってyukarin_s(duration生成)、yukarin_sa(イントネーション生成)もまとめると便利そうです。 (現状のコアはduration生成・イントネーション生成・decoderが1:1:1対応している前提なため)

これらのことが可能なのか、他に良い解決策がないのかを議論したいです。

実装方法

今は1つのpytorchモデルから1つのonnxを作っていますが、これを複数のpytorchモデルを与えられるようにするのが妥当かなと思っています。

話者番号ごとにどのモデルで推論するか割り振れる必要があります。 それぞれのモデルの話者数も受け取って話者数のcumsumと比較してモデルに割り振るのが簡単で実現可能なのかなと思っています。

その他

onnxをまとめるのではなく、コア側のdecoderをspectrogram生成とvocoderに分けてそれぞれで推論する方法もあるとは思います。 ただ、コア側を変えるのは割と大変なことと、onnx側でまとめるとファイル数削減にもなって扱いやすそうというのもあって、モデル側をなんとかしたいなと思っています。

Hiroshiba commented 1 year ago

そんな難しくないのかなと思っているのですが、例えばnn.Moduleをlistとして持つモデルはonnxにできない、みたいな制約がありそうで調査が必要かもです。

Yosshi999 commented 1 year ago

onnxファイルをいじってspectrogram生成モデルの頭(すなわちdecoder.onnxの頭)でIf分岐してそれぞれのバージョンにデータを振り分けるというのは可能なように思えます

似たようなことをやろうとしているissueがありますね:https://github.com/microsoft/onnxruntime/issues/11367

Hiroshiba commented 1 year ago

ありがとうございます!!勉強になります。

issueの中身を見てみた感じ、if用のノードをonnxファイルとして追加し、enable_cacheinputを与えて分岐させる感じでしょうか。 spectrogram生成モデルの番号を与えて推論、とかだったら可能そうですね! ただ用途を考えると、spectrogram生成モデル番号用の引数を追加しちゃうとonnxモデルのインターフェースが変わるので、可能なら避けたみがあります・・・!

今は1つのpytorchモデルから1つのonnxモデルに変換してますが、これを複数のpytorchモデルから1つのonnxモデルを作るのとかは難しそうでしょうか・・・?

def init(models: list[nn.Module], speaker_nums: list[int]):
  self.models = models
  self.speaker_sumsum = torch.cumsum(speaker_nums)
  ...

def forward(x, speaker_id: int):
  model_index = (self.speaker_sumsum > speaker_id).nonzero()[0]
  return self.models[model_index](x)

みたいなコードを想像しています!

Yosshi999 commented 1 year ago

確かに上のリンクではenable_cacheを追加していますが、今使っているonnxモデルはspeaker_idが与えられているので、これを使って分岐をすることは可能だと思います。

pytorchレベルで分岐するのもtorchscriptを経由する(yukarin_saはこれをやってます)なら可能な気がしますが、やってみないとわからないですね...

Hiroshiba commented 1 year ago

なるほどです・・・! やり方はともかく可能そうっぽいので、vocoder統一化の実験がうまくいくか試してみたいと思います、ありがとうございます!!

Yosshi999 commented 1 year ago

これにチャレンジして気づいたことをメモします

Hiroshiba commented 1 year ago

なるほどです・・・!!! jit化って変なとこに落とし穴あったりしますよね・・・。謎エラー、なるほどです。 メモ本当にありがとうございます!!!

Hiroshiba commented 1 year ago

こちら完璧に動作して、コアの0.13.3、エディタの0.13.4に取り込まれました! @Yosshi999 さん、ありがとうございます!!!

というのと、また別に大量のLOGが出てくるというところがユーザーにとって気になるっぽく、できればなんとかしたなと感じています。 ちょっと別issue作ってみます!