Closed Yosshi999 closed 4 weeks ago
テストはどういったものを書きましょうか。デグレ防止という意味だと現状のtest_util
では不十分だと思います(decode()
のapiは変わっていないが実装をseekable版に書き換えているので)。
全体を一度で変換した場合とチャンクに区切って変換して最後に合わせた場合とを比較するというテストならいくらか簡単だと思います。ただ、現状decodeに対応するseekable版が存在しない(audio_query
を入力とするsynthesisに対してseekable_synthesis+renderはあるが、f0
を直で入れるseekable版は無い)ので、testcase内でそういったものを実装することになり、APIのテストのカバレッジ向上という観点ではちょっと微妙になります
旧decode
版からデグレっていないことを確認するテストについては、mainブランチにスナップショットテスト的なものを…と思いましたが、 #851 はもうマージしちゃってますね。
いくつかのWAVの完全一致を手元で手動で…とか?(テストはテストで将来のために作るとして)
テスト
あっ @qryxip さんの いくつかのWAVの完全一致を手元で手動で…とか?
で、一番気になる部分の確認は全く問題なさそうだと気づきました!!
@Yosshi999 さんの作ってくださったpython版サンプルが2つとも実装されているので、これでチェックするのが簡単&安心できそうと思いました! 想像してる手順はこんな感じ:
.zip
にまとめるてGithubコメントにドラッグ・アンド・ドロップすれば共有できます)みたいな・・・!WAVフォーマットがちょっと変わるとかは問題なさそう。
予感として、DirectMLだと何かしらの計算誤差が出るかも。 cpuだと少なくとも同じマシン上では計算誤差が出ないかも。もしかしたら出るかも。
いくつかのWAVの完全一致を手元で手動で
ゆくゆくはcrates/test_util
でデグレチェック用の音声を作成しておきたいですね。確かopencvではテスト用のデータがリポジトリに保存されており、特定の環境変数を付けてテストを回すとテスト用データが書き出され、デフォルトではテスト用データを現在の実装の出力と比較するといったことをやっていたと思います。(つまり最初の一回は手動で実行してテスト用データをアップロードし、以降はCIでデグレチェックを回しています)
example/python/run.py
で生成した音声の比較結果を載せます。生成した音声はスクリプトのデフォルトで、mainブランチの音声、このPRでの音声、このPRでストリーミング処理したときの音声です。
compare main-7dd6738.wav and streaming_decoder-a5a08e8-streaming.wav
diff max: 3.7331134e-05
diff mean: 1.78254e-07
compare main-7dd6738.wav and streaming_decoder-a5a08e8.wav
diff max: 3.7331134e-05
diff mean: 1.7770306e-07
compare streaming_decoder-a5a08e8.wav and streaming_decoder-a5a08e8-streaming.wav
diff max: 2.6855618e-05
diff mean: 2.3077598e-08
比較スクリプト、音声はこちら↓ compare.zip
@Yosshi999 比較ありがとうございます!!
別リポジトリに切り出すかどうか置いといて、「これが作られるべき」なものをファイル保存しといてそれと比較するテストのことをスナップショットテストと呼んだりします! 実際Python版のエンジンでは音声のスナップショットテストをしているので、もし参考になれば! https://github.com/VOICEVOX/voicevox_engine/blob/dd7a7a7cd1ebe6004a57e6f7d5af2a37bcc4a33e/test/e2e/single_api/tts_pipeline/test_synthesis.py#L43 (たしかlinuxとmacとwindowsで、float32だかfloat64だかの差で計算結果が異なるので、その微差は許容するようなテストになってたと思います)
比較結果なんですが、完全一致しない感じなんですね!!!!!! 以前のものと今のものが合わない・今のものもrenderかどうかで合わない、という感じでしょうか。 これって理論上は完全一致するはず・・・・・という理解あってそうでしょうか。 だとしたら何でずれてるんだろ。。。。。 計算するたびに値がずれるとか・・・・・?(だとしたらもうしょうがなそうなので)
誤差については完全に謎です。トリミングしたことで(このPRの実装ではsynthesisで全体を生成したときもトリミングが入ります)計算順序がずれたのかなと思いますが、確実なことは言えないです よく考えたらonnxを分割する前のmain branch 991fbc8とも比較すべきなので後でやります
なるほどです!! 僕もありえるとしたら計算順序の違いだろうなという気はしています! 何か確認できる方法があればいいけど、なければもうエイヤで決めてしまうしかなさそう!!
とりあえずこちらの数値誤差であれば、まあおそらく問題ないだろうとは感じています・・・! もしかしたらマージ後にでも確認させていただくかもです! 仮に何かしら耳にわかるレベルのエラーがあったとしても、よほどなことがない限り利便性を加味するとそのまま実装した方が絶対いいと思ってます・・・!!
991fbc8 -> 7dd6738 (decodeモデル分割)では変化なし
compare main-991fbc8.wav and main-7dd6738.wav
diff max: 0.0
diff mean: 0.0
現在のPRコミットabdc696でPADDING_SIZEを0にしてデフォルトのテキストを生成したり「あ」を生成したりしてみましたがエラーにはなりませんでした(workaroundのときに言われていたように音声冒頭が切れてはいました)
何度考えてもこの機能楽しみですねぇ!!!
991fbc8 -> 7dd6738 (decodeモデル分割)では変化なし
・・・!!!! あれ、なるほどです! てっきりここでずれたのだと思っていたのですが、完全一致するんですね!!
これって一応確認すると、以前はdecode.onnxだけでdecode推論してたのを、sosoa.onnxとvocoder.onnxの部分に分けてdecode相当を推論しても、結果が完全一致したってことですよね。
もう1つ確認すると、このコメントの↓の結果って、どちらも「sosoaとvocoderでdecode相当を推論してる」という認識であってそうでしょうか? 👀
compare main-7dd6738.wav and streaming_decoder-a5a08e8.wav
もしあってたら、原因箇所はonnxruntimeではないはずなので、原因はかなり絞れそうだなと!!! (おそらくこのPR内の、decodeと保存の経路のどこか)
パッと思いつくのはwave化するときにfloat32にしてるかfloat64にしてるかとか、flameの丸め込み方向が変わったとかがありえそう! ちょっと大変だけど1つ1つ数値をダンプしてったり関数を戻したりすればどこに原因があるかは特定できそう!!!
https://github.com/VOICEVOX/voicevox_core/commit/991fbc83a15439d45961803d593048c139f8fa77 -> https://github.com/VOICEVOX/voicevox_core/commit/7dd6738a45ad2152c2c373488b106f784cf82bcb (つまり#851 による変更)ではおっしゃる通りdecode.onnxをふたつに分割し、中間物(sosoaのoutput)はそのままvocoderに入力しています。この変更で誤差が生じなかったのはこちら の
この分割自体は出力の変化は発生していません
での言及を再現したものになってます。
mainとこのPR(streaming無し)で初めて誤差が発生していますが、おそらくこれはsynthesize時に中間物をそのまま入れるのではなく、「再生領域全体」を指定してrender()に投げているために、始端終端のパディング量がMARGINに切り揃えられたことが原因と考えられます。もちろんvocoderのreceptive fieldの幅から考えれば変化しないはずですが、、、計算順じゃないかな〜とは思います。 PRとPR(streamingあり)での変化も、本来は等価なので誤差が発生しないはずですが、謎です。
main->PR(streaming無し)での変化がどうしても気になるなら、実装を修正してsynthesize関数の挙動をmain branchのものと完全に一致させることはできます。ただrender_all関数を生やすみたいな感じで実行パスが一個増えるのを保守の観点でどう捉えるかになります
あーーーーーーーーーーーーーーーーーなるほどです!!!!! おっしゃっていたことを完全に理解しました!!!!
確かにパディングの長さが違うというか、入力が異なるので何らかの何らかが発生してずれうる気がします!!! 切り揃えられるてるから全体入力が同じじゃない点が盲点でした、すごくよくわかりました!!!
おっしゃる通り実装を以前のに合わせれば、つまり余分に計算して切り揃えれば完全一致する予感がありますが、少なくとも実装を戻す必要はないと思います! 一度実装を戻してから完全一致するか確認できると一番安心ですが、まーーーーー別にいいかなと!!! 実際に製品版で精製してみた音声がなんか違和感あったら確認する、みたいな感じで・・・!!!
いやーーすみません、認識ずれていてお手数おかけしてしまいました、ありがとうございました!! あとはエラーのとこが良い感じにできればマージの方向が良さそう!!
@Yosshi999 このPRのdescriptionの"Solve"ですが、おそらく正しいのは"Resolve"かと思います。"Solve"だとissueはlinkされず、PRをマージしても自動で閉じられません。 なので今私が手動で #853 をlinkしてからマージしようと思います。
もし意図と外れているのであれば #853 をre-openして頂ければ!
内容
ストリーミング処理をするための、中間表現の出力
seekable_synthesis
と 時間区間を指定して音声(16bit PCMバッファ)を出力するrender
の実装 元のsynthesis
はWAVバイナリを出力していたが、ストリーミング処理ではWAVの結合が面倒なのでPCMを出す形にした。関連 Issue
Solve #853
その他