1. はじめに
自作の歌唱コーパスは単一歌唱者で、収録時間は約47分。NNSVS の v4 音響モデルでは pitch-shift data augmentation で7倍にデータ拡張すれば辛うじて安定するが、v5 音響モデルは15倍(recipe 名の psda15)に拡張しても安定しない。それを何とか安定させようとして悪戦苦闘した記録。
実験には試行順に 実験1〜9 の連番を振る(3.2)。実験結果(3.3)は定性まとめ + lf0 / mgc[0] の定量比較に分けて記載する。同時比較したデータは v5 baseline / 実験5-7(実験6,7 は mgc[0])/ 実験9 しか残っておらず、それ以外は当時のメモと判断からの要約。
本文中の nnsvs 本体コードへのリンクは fork taroushirani/nnsvs の python3.13-support ブランチ(commit 821cf4d、2026-08-27)固定の permalink。recipe 側の config は公開リポジトリに含まれないためリンクなし。
2. v5 音響モデルの構成と問題点
構成
- モデル:
NPSSMDNMultistreamParametricModel、stream_sizes: [60,1,1,5](mgc / lf0 / vuv / bap) - サブモジュール:
- lf0:
BiLSTMResF0NonAttentiveDecoder(自己回帰、残差 F0 予測) - vuv:
FFConvLSTM - mgc / bap:
GaussianDiffusion+DiffNet(DDPM、サンプリング 100 step)
- lf0:
症状
- 長母音(ロングトーン)で F0 を保てない — ドリフト・遅いゆらぎ。全ノートの楽譜からの乖離率(off_note_rate)も高い
- mgc0のフレーム間ジッター(フレームごとに値が細かく高速にばたつく現象)
- teacher-forcing 指標(F0 RMSE / VUV err)は v4 より良いのに、自由生成では劣化
— exposure bias の典型症状
診断
- 学習時は teacher-forcing により 4 ストリームが計算グラフ上は独立している。
mgc / bap / vuv は 正解ラベルy_lf0(・y_bap)を条件に使い、lf0_modelの予測を参照しない(multistream.py#L495-L509; v4 のNPSSMultistreamParametricModelも同構造)。lf0_modelを更新するのは lf0 ストリーム自身の損失のみ。 - 唯一の結合点は「1 個の
nn.Moduleである」ことに由来する 単一 optimizer・単一グローバルclip_grad_norm_。 - v5 の diffusion 向け学習設定(
AdamWlr=1e-3、clip_norm=10、StepLR 100ep 半減、300ep)は AR な LSTM デコーダには過激。epoch 60 時点の実効 lr は v4(Adam lr=1e-4、StepLR 20ep 半減、clip_norm=1、100ep)の約 80 倍。 -
clip_grad_norm_はモデル全体のグローバルノルムに働くため、巨大なDiffNetがノルムを支配し、lf0_modelの勾配は実質クリップされない。 -
stream_wise_loss: false(v5 全 recipe の設定)では損失が全ストリーム誤差を合算し総要素数(≈67·B·T)で平均する(train_acoustic.py#L74-L103)。 mgc(60 次元)が lf0(1 次元)の約 60 倍の raw 勾配量を backward 前から供給している。 - 別の見立て: v5 のジョイント学習は lf0 を「音符追従の弱い局所解(損失地形上の谷。一度落ちると学習を続けても抜け出しにくい)」に収束させる。保守的な設定で全ストリームを回帰学習する v4 の lf0 とは別物。
3. 行った実験と結果
3.1 評価基盤
utils/f0_stability_eval.py を新規作成。TensorBoard の F0 RMSE / VUV err は teacher-forcing 下(直前フレームの正解を条件に与えた 1-step 予測)で計算されるため、exposure bias の症状(ロングトーンのドリフト、時折の音程外し)が数値に出ない。本スクリプトは 合成済み wav を楽譜と突き合わせて 自由生成時の安定性を定量化する。
共通処理
-
合成 F0: 合成 wav から
pyworld.harvestで F0 抽出(frame period 5 ms、f0_floor 100 Hz、f0_ceil 1000 Hz)。さらにpyworld.cheaptrickでスペクトル包絡を求め、フレーム毎のlog_power = log(Σ spectrogram)を得る。 - 楽譜 F0: duration調整済みHTSフルコンテキストラベルから acoustic/linguistic features を作り、楽譜のピッチ情報(MIDI ノート)を Hz 化。ノート毎に一定値の目標ピッチ列(ビブラートやしゃくり等の表情を含まない)。
- 合成 F0・楽譜 F0 とも C4 基準の cent に変換して比較。有声フレーム(
f0 > 0)のみ対象。 - 楽譜 lf0 から
note_segmentsでノート区間に分割し、以下をノート単位で計算する。
(A) 長母音メトリクス — ノート長 ≥ 0.5 s、かつ有声フレーム ≥ 2 のノートのみ
各ノートで「合成 cent − 目標 cent」の系列を作り:
-
mean_dev_cent— 上記偏差の平均。持続音での静的なピッチのずれ(バイアス)。長母音ノート全体で mean / median を報告。 -
drift_cent_per_sec— 合成 cent を時刻(秒)で線形回帰した傾き。持続音中にピッチがどれだけ滑っていくか。exposure bias によるドリフトはここに出る。長母音ノート全体で mean(abs) と median(abs) を報告(3.3 の表は median(abs))。 -
detrended_sigma_cent— 上記線形トレンドを除いた残差の標準偏差。平均やドリフトとは別の、無方向なジッター。mean / median を報告。※ 自然なビブラートもここを増やすため、値が小さいほど良いとは限らない(モデル間の相対比較用)。 -
octave_jumps— 連続する有声フレーム間で|Δcent| > 600(半オクターブ)となった回数の総和。ピッチの急な飛び・倍/半ピッチ検出。全実験で 0。 -
log_power drift/sec— フレーム毎log_powerを時刻で線形回帰した傾き。持続音中のラウドネス(音量)ドリフト = 振幅の不安定性。mean(abs) を報告。 -
log_power detrended sd— 同残差の標準偏差。ラウドネスの揺れ。
(B) 全ノートメトリクス — 中央区間に有声フレームを 1 つ以上持つ全ノート
-
中央区間: ノートから両端のマージン(
--margin-sec0.05 s、短いノートではノート長の 25 %)を除いた区間。アタック/リリースの遷移やポルタメントを除外する。 -
note_dev_cent— 中央区間の合成 cent の中央値 − 目標 cent。ノート 1 個につき 1 値で、そのノートの定常ピッチが楽譜からどれだけ外れているか。 -
off_note_rate—|note_dev_cent| > 100 cent(半音)のノートの割合(%)。明確に音程を外したノートの率。 -
dev_p95_cent— 全ノートの|note_dev_cent|の 95 パーセンタイル。少数の極端な外れ値に頑健な、ワーストケースのピッチ精度。
補助: フレーム毎 |score − pred|
- 全ノートの有声フレームで
|合成 cent − 目標 cent|を集め、median / p95 を報告(長母音限定でも中央区間限定でもない、遷移区間も含む)。 - median = 曲全体を通した典型的なフレーム単位の楽譜追従誤差。低いほど旋律線に密着。
mgc[0] 統計(音響モデル出力から直接、wav ではない)
-
std/diff_std(フレーム間差分の標準偏差)/max_abs_diff/n_outlier(|diff| > 3σ)。 -
diff_stdがフレーム間ジッターの指標で、3.3 の表で使用。
解釈上の注意
楽譜目標はノート毎一定値なので、表情豊かで自然な歌唱でもスコアは 0 にならない。これらの指標は 安定性と楽譜追従性 を測るものであり、絶対的な音質評価ではない。モデル間の相対比較に用いる。
3.2 各実験の動機と内容
各実験は前の実験の結果を受けて次を設計している(試行順)。recipe は括弧内。
実験1(学習なし)— ストリーム切り分け
動機: mgc[0] ジッターが lf0 の不安定さに引きずられて出ているのか、拡散 mgc/bap モデル固有なのかを、学習コストをかけずに判定したい。
内容: v4 の lf0 と v5 の mgc/bap を組み合わせて合成(逆の組み合わせも)。
実験2(v5lf0fix)— optimizer を穏当化
動機: v5 の学習設定(AdamW lr=1e-3・clip_norm=10・300ep)は拡散モデル向けで、AR な lf0 デコーダには実効 lr が過大。lf0 側に穏当な設定を与えればドリフトが収まるか。
内容: 学習全体を lr 2e-4 / clip_norm 1.0 に下げて v5 baseline から継続学習。
実験3(v5e2pitchreg)— 楽譜ピッチへの正則化
動機: 実験2 でドリフトは減ったが不足。楽譜ピッチへ引き戻す正則化項を足せば、持続音のドリフトをさらに抑えられるか。
内容: 実験2 + pitch_reg_weight 0.01。
注意: 当時の nnsvs 本体の
pitch_reg_weightの実装にバグがあり、正則化が
意図通りに効いていなかった可能性がある。実験3 の結果(下記)は再実行時に
異なる挙動になりうる。
実験4(v5e3warmstart)— v4 の lf0 から出発
動機: v5 のジョイント学習が lf0 を悪い局所解へ収束させている疑い。既に良好な v4 の lf0 重みから初期化すれば、良い局所解に留まれるか。
内容: 実験2 + v4 checkpoint から lf0_model を warm-start。
実験5(v5e4freeze)— lf0/vuv を凍結
動機: 実験4 で warm-start しても高 lr の継続学習で lf0 が再劣化。ならば lf0/vuv を凍結して一切動かさず、拡散 mgc/bap だけ学習すれば lf0 は v4 水準のまま保てるはず。
内容: 実験4 + lf0_model/vuv_model を freeze、diffusion mgc/bap のみ学習。
実験6(v6mixedft)— 多話者混合 DB 事前学習
動機: 本命は mgc[0] の安定化。対象 DB は pitch-shift data augmentation で約15倍にしているが、
話者・音色の多様性は素の1話者47分ぶんのままで、mgc[0] の土台には不足の可能性。多話者混合 DB で事前学習してから対象 DB へ fine-tune すれば mgc[0] が安定するか(lf0 は実験5 で解決済み扱いなので副次確認)。
内容: 混合 DB(7 話者・話者 one-hot 条件付け)で拡散音響モデルを事前学習 → 対象 DB へ fine-tune。denoise_fn 全体 + vuv.fc を unfreeze、lf0 は凍結。
実験7(v6mixedft denoiserpartial)— denoiser の出力側だけ動かす
動機: 実験6 では継続学習しても mgc[0] が収束しない(best/latest でほぼ同一)。denoise_fn 全体を動かすと揺れが残る仮説 → 出力に近い層だけ動かせば安定するか。
内容: 実験6 と同じ warm-start から、mgc/bap の denoise_fn 出力側のみ unfreeze (末尾 3 residual layer + skip/output projection、trainable 2.6M)。lf0/vuv は凍結。
実験8(v5tier1gradgroups)— サブモジュール別 grad-clip
動機: 単一グローバル clip_grad_norm_ を DiffNet が支配し、lf0 の勾配は実質クリップされない(§2 診断)。param_group と grad-clip をサブモジュール別に分ければ、lf0 に適正なクリップ・mgc/bap に緩いクリップを同時に与えられるか。混合 DB に頼らず対象 DB 単体での改善を狙う。
内容: lf0_model(clip 1.0)を mgc_model+bap_model(clip 5.0)から分離、 vuv は catch-all。v5 baseline から継続学習。
実験9(v5tier2split)— 学習自体を 2 段階に分割
動機: teacher-forcing 下で 4 ストリームは独立。学習も分ければ optimizer 干渉が完全に消える。まず lf0/vuv だけを v4 相当設定で学習し、その後 mgc/bap を学習。
内容: Stage A = lf0/vuv のみ 100ep(v5 の 300ep から warm-start)→ Stage B = mgc/bap のみ 300ep。執筆時点では継続学習中(〜185ep)。
3.3 実験結果
定性まとめ
| # | 結果 |
|---|---|
| 実験1 | mgc[0] ジッターは lf0 を差し替えても残り、拡散 mgc/bap 側にも要因あり。lf0 由来分と mgc 固有分を切り分け |
| 実験2 | lf0 ドリフトは軽減するが v4 水準には未達 |
| 実験3 | ドリフトはさらに抑制されるがビブラートも縮む。単独では不十分。ただし当時の pitch_reg_weight 実装にバグがあり結果は再現時に変わりうる(3.2 の注意) |
| 実験4 | 一旦改善するが、高 lr の継続学習で再劣化 |
| 実験5 | lf0 が v4 水準に到達。以降の標準構成 |
| 実験6 | 継続学習だけでは mgc[0] 不安定性は解消せず(best/latest でほぼ同一) |
| 実験7 | mgc[0] は実験6 比で明確に改善。ただし土台の混合 pretrain の lf0 が悪く、モデル全体の優劣・到達水準は判定不能 |
| 実験8 | mgc/bap の clip 緩和でむしろ mgc[0] が悪化 → clip は締める方向が正しいと確認。lf0 の grad norm が mgc/bap の 1/100〜1/200 であることを実測し、§2 の診断を裏付け |
| 実験9 | lf0 が v5 の局所解から出発したため v4 水準に未達(下記 lf0 表、3.4)。Stage B は学習進行中のため暫定値 |
同時比較のデータがあるのは v5 baseline / 実験5 / 実験9(いずれも f0_stability_eval.py、 dev/eval 10 発話、CPU、確率的 1 サンプル)。実験6〜7 は mgc[0] のみ別途 best checkpoint で実測。他は手元のメモからの要約。
lf0(F0)の定量比較
列は 3.1 の指標に対応: drift med|abs| = drift_cent_per_sec の median(abs)、detrended σ = detrended_sigma_cent の median、off_note = off_note_rate、dev_p95 = dev_p95_cent、frame med = 補助のフレーム毎 |score − pred| の median。すべて小さいほど安定側。
| 構成 | drift med|abs| (cent/s) | detrended σ (cent) | off_note (%) | dev_p95 (cent) | frame med (cent) |
|---|---|---|---|---|---|
| v5 baseline (300ep) | 103.5 | 70.1 | 3.1 | 70.9 | 17.5 |
| 実験5 best | 98.6 | 60.6 | 0.5 | 27.5 | 12.1 |
| 実験5 ep300 | 100.0 | 68.6 | 0.5 | 38.4 | 13.4 |
| 実験9 暫定、Stage B 〜185ep | 93.5 | 76.3 | 3.1 | 66.0 | 23.8 |
- 音程精度(
off_note/dev_p95/frame med)は実験5 が突出。これらはlf0_modelのみで決まる。 - 実験9 は音程精度で実験5 の約 2 倍悪く、v5 baseline とほぼ同等。Stage A の lf0/vuv は確定済み、Stage B は lf0 に触れないため、epoch 300 完了後もこの差は縮まらない。
- ドリフト・detrended σ(ロングトーンの遅いゆらぎ・無方向ジッター)は全構成で大差なく、共通の未解決課題として残る。
mgc[0]の定量比較
同一パイプライン合成の mgc[0](音響モデル出力から直接算出、wav ではない)。log-pow drift = log_power drift/sec の mean(abs)、mgc0 diff_std = mgc[0] のフレーム間差分の標準偏差。
| 構成 | log-pow drift | mgc0 diff_std |
|---|---|---|
| v5 baseline (300ep) | 2.91 | 0.506 |
| 実験5 best | 3.50 | 0.483 |
| 実験5 ep300 | 3.32 | 0.499 |
| 実験9 暫定 | 3.66 | 未測定 |
- 実験5・実験9 とも v5 baseline と概ね同水準。実験9 は mgc/bap を実験5 と同じ diffusion-freeze 系設定で学習しているため、mgc[0] の挙動もほぼ一致する。
- mgc[0] ジッターは「学習を続ける・optimizer を変える」だけでは動かない(実験6・実験8 も同様)。
別パイプライン(混合 DB 事前学習 → fine-tune、lf0 は両者で共通・凍結)での
実験6 vs 実験7、best checkpoint・4 発話:
| 発話 | 実験6 diff_std | 実験7 diff_std | 実験6 max_abs_diff | 実験7 max_abs_diff |
|---|---|---|---|---|
| #1 | 0.407 | 0.286 | 8.376 | 3.984 |
| #2 | 0.761 | 0.572 | 9.341 | 3.571 |
| #3 | 0.568 | 0.332 | 7.139 | 3.906 |
| #4 | 0.661 | 0.336 | 7.959 | 2.484 |
- 実験7(
denoise_fn出力側のみ unfreeze)は実験6(denoise_fn全体 unfreeze)比でdiff_std −25〜49 %、max_abs_diff −45〜69 %。lf0 は両者で共通・凍結なのでクリーンな A/B。 - 「denoiser 全体を動かすより出力側だけ触る方が mgc[0] が安定」の仮説を支持。
- ただし土台の混合 pretrain の lf0 が悪く、実験5 / v5 baseline と同一パイプラインの横並び未実施のため、モデル全体の優劣・到達水準は判定不能。
3.4 実験9が実験5に届かない理由
「v4 相当の optimizer 設定」は忠実にコピーしたが、それだけでは v4 相当にはならなかったようだ。
- 初期値: 実験9 Stage A は v5 の 300ep diffusion checkpoint(音符追従の弱い局所解)から warm-start している。v4 の lf0 は v4 レジーム(全ストリーム BiLSTM 同時学習)で得た別の局所解。
- 実効学習予算: StepLR 20ep 半減で epoch 60 以降は lr がほぼゼロ、train loss も横ばい。 約 40 実効 epoch の微小ステップでは別の局所解へ移れない。
4. 現時点の結論
- v5 のジョイント学習、および v5 の局所解からの短時間 fine-tune は、良好な lf0 を生まない。
- 実用構成は 実験5: v4 の lf0/vuv を warm-start + freeze し、diffusion mgc/bap のみ学習。
- mgc[0] 不安定性は継続学習では解消しないが、
denoise_fnの出力側のみ unfreeze する部分 fine-tune(実験7)で明確に抑えられることを確認(実験6 比 diff_std −25〜49 %)。ただし土台の lf0 が悪いため、今後追加実験を予定。 - 「v5 の
lf0_modelが原理的にダメ」なのか「v5 のジョイント学習の挙動が lf0 を悪い局所解に押し込むだけ」なのかは未切り分け。後者であれば、v4 レジームの初期値から学習すれば改善可能性がある。 - 現状で素の v5 で良好な結果を得られるのは、単一歌唱者・高品質・6.5 時間と大容量の波音リツのみでは?