Skip to content

Latest commit

 

History

History
364 lines (241 loc) · 26.4 KB

development-notes.md

File metadata and controls

364 lines (241 loc) · 26.4 KB

改良の経緯と測定記録

English | 日本語

最終更新: 2026-09-28

この文書は、Realtime Narration Videoの応答速度、連続再生、実写キャラクターの口の動きを改善する過程で行った試験と判断を記録したものです。節11までの測定は2026-09-04時点の履歴で、現行仕様は節10と節14に反映しています。数値は開発環境での実測値であり、GPU、バックエンド、入力画像、発話内容、TTS負荷によって変動します。

1. 検証した処理構成

処理は次の順で進みます。

  1. OpenAI互換LLMからストリーミング応答を受信
  2. 受信途中で文節を確定し、TTSを並列実行
  3. 完成したWAVをLTX-2.5 Audio-to-Videoへ渡す
  4. 動画生成中も後続文節のTTSを進める
  5. LTX生成音声を元のTTS音声へ差し替える
  6. 完成動画をブラウザーで先読みして順次再生

LLMとTTSは十分高速で、主な待ち時間はLTX動画生成です。動画生成ジョブは単一GPU上で実質的に直列となります。

2. 初期タイムライン

初期の代表測定は、入力「おはよう」、出力2チャンク、384×512・16fps・81フレーム・8 steps相当です。

経過時間 処理
0.000秒 LLM開始
0.291秒 LLM最初の文字を受信
0.319秒 1番目のTTS開始
0.426秒 2番目のTTS開始
0.442秒 LLM応答完了
1.282秒 1番目の音声完成
1.283秒 1番目の動画生成開始
2.758秒 2番目の音声完成
5.239秒 1番目の動画完成
9.248秒 2番目の動画完成

この測定では、2番目の動画は1番目の再生終了より約0.99秒早く完成しました。LLM受信とTTSは並列に進んでおり、動画生成がボトルネックであることを確認しました。

3. 応答速度の改善

3.1 ストリーミング分割

当初は句点まで待つため、長い応答でTTS開始が遅れていました。LLM受信中に句点・読点・文字数を見て文節を確定し、TTSを先行させました。

その後、単純な文字数分割によって次の問題が発生しました。

  • 「できます|か?」のように短い語尾が孤立する
  • 「指|している」のように動詞の活用途中で切れる
  • 「チェックしてみる|といいですよ。」のように意味が分断される
  • 5秒動画の無音部分を挟み、続きの発話まで不自然な間ができる

現在は句読点を優先し、約22〜26文字まで語尾を待ちます。また、「している」「します」「される」「できますか」など、よくある日本語の活用語尾を語幹から切り離さない保護を入れています。

読点で終わる導入部を即時確定すると初動は速くなりますが、意味の続きまで5秒空く場合があります。反対に次の文節を待つと初回応答が遅くなります。この点は、速度と自然な間の明確なトレードオフです。

3.2 TTS並列化

LLMの全文受信を待たず、確定した文節ごとにTTSタスクを開始しました。複数のTTSは並列に進み、動画生成中にも後続音声を準備します。

短いWAVをそのままLTXへ渡すと音声範囲エラーになるため、LTX条件用WAVだけを動画尺よりわずかに長く無音パディングします。最終MP4には元のTTS波形を使用します。

3.3 ジョブ監視間隔

Gatewayジョブのポーリング間隔を0.5秒から0.1秒へ変更しました。

  • 理論上の平均検知短縮: 約0.20秒
  • 理論上の最大検知短縮: 約0.40秒

実測では動画生成時間そのもののばらつきが大きく、単独効果は明瞭に分離できませんでした。プッシュ型完了通知も検討しましたが、0.1秒ポーリングから得られる追加効果は平均約0.05秒のため未採用です。

4. 解像度の比較

ターン先頭だけ288×384へ下げ、384×512・4 stepsと比較しました。初期のアプリ実測は、異なる時間帯、GPU負荷、キャッシュ・アロケータ状態、LLMプロンプトを含む非統制の比較でした。

4.1 初期のアプリ観測(参考値)

指標 384×512・4 steps平均 288×384・4 steps平均 差
音声完成 1.066秒 0.972秒 -0.094秒
動画処理全体 3.613秒 3.677秒 +0.064秒
サーバー生成本体 3.299秒 3.358秒 +0.059秒
送信から動画完成 4.679秒 4.650秒 -0.029秒

この結果だけを見ると解像度低下の効果はありませんが、同一構成でも2.4〜4.5秒の変動が観測されており、測定差はその雑音の内側でした。そのため、この表は解像度の性能判断には使用しません。

4.2 サーバー側の統制測定

同一seed、同一音声、4 steps、81フレームに固定し、両解像度を交互に5本ずつ、計2セット実行して段階別に測定しました。

段階 384×512 288×384 差
合計 3.457秒 3.108秒 -0.349秒
pipe(denoise含む) 2.622秒 2.270秒 -0.352秒
text encode 0.134秒 0.134秒 ±0秒
mp4 encode 0.400秒 0.400秒 ±0秒
A2V前処理 0.100秒 0.104秒 +0.004秒

統制後は、288×384が約0.35秒、約10%高速でした。短縮分はほぼすべてpipe内にあり、画素数減少によるdenoiseのトークン数削減が効いています。

アプリ側で一時的に逆転して見えた主因は、別時間帯のGPU負荷やキャッシュ状態をまたいで平均を比較したことです。LLMが生成するプロンプト長の差も数十msの変動要因になります。画素数を約44%減らした効果がなかったのではなく、約0.11秒以下の観測差を、それより大きい実行時変動から分離できていませんでした。

画質確認後、アプリはいったん384×512へ戻しました。その後の統制測定とアプリ環境でのA/B再測定を受け、ターン先頭だけ288×384を再採用しました。

4.3 アプリ環境でのA/B再測定

アプリと同じGatewayクライアントを使い、直近セッションの同一キャラクター画像と同一条件音声を再利用して再測定しました。LLMとTTSは測定経路から除外し、seed 1004、4 steps、81フレーム、modality scaleなしを固定しました。A/Bは各5回で、近接した比較を維持しながら開始順を反転しています。

指標 A: 384×512 B: 288×384 Bの差
受付から完了検知・平均 3.466秒 3.175秒 -0.291秒
受付から完了検知・中央値 3.457秒 3.167秒 -0.290秒
サーバー生成・平均 3.357秒 3.069秒 -0.288秒
サーバー生成・中央値 3.356秒 3.062秒 -0.294秒

この再測定でも288×384が約0.29秒、約8%高速となり、サーバー側の統制測定を再現しました。

本測定前の予備セットでは、最初の384×512だけ17.317秒、以降は約3秒台でした。これはA/B差ではなく、測定開始時のバックエンド・コールドスタートです。両解像度が準備済みとなった次セットを上表の採用値にしました。実利用の起動直後レイテンシを評価するときは、このコールドスタートを別指標として測る必要があります。

再現用スクリプトはscripts/benchmark_resolution.pyです。画像と条件用WAVを指定すると、各実行値、平均、中央値をdata/benchmarksのJSONへ保存します。生成要求はGPUキュー待ちと409競合を避けるため直列に送ります。

5. steps削減

ターン先頭だけstepsを8から6、さらに4へ下げました。後続は8 stepsを維持します。

5.1 6 stepsの代表測定

指標 値
LLM最初の文字 0.141秒
音声完成 1.018秒
サーバー生成本体 3.520秒
動画処理全体 3.917秒
送信から動画完成 4.935秒

5.2 4 stepsの測定

384×512・4 stepsでの先頭完成時間は4.842秒と4.516秒、平均4.679秒でした。6 stepsの代表測定と比べると、動画処理部分は約0.3〜0.6秒短縮しました。目視で問題となる画質低下がなかったため、ターン先頭4 stepsを採用しました。

後続動画は8 stepsのままでも、当時の測定では先頭動画終了まで約1.2〜1.7秒の先読み余裕がありました。

6. 動画サーバー側の改善

動画サーバー側では、前処理、エンコード、キュー処理などの改善が行われました。denoiseは約2秒を占め、8 stepsがカーネル起動律速に近い状態でした。

CUDA Graphsおよびtorch.compileは大きな改善候補ですが、次の理由で現時点では未対応です。

  • 解像度やフレーム数ごとのグラフ管理が必要
  • 初回コンパイルとウォームアップが初動を悪化させる可能性
  • グラフ不成立、再コンパイル、追加VRAMの検証が必要
  • 今期の低リスク改善範囲を超える

6.1 stepsパラメータ有効化後の性能

2026-09-04朝までのサーバーでは、クライアントが指定したstepsが蒸留8ステップ固定経路で黙殺されていました。それ以前に観測した「4 stepsの短縮」には、LLM・TTS・サーバー改修・実行時変動が混在しています。サーバー修正後は蒸留sigma列を指定数へ間引く処理が実装され、ログのdistilled sigma subsample: 4/8 stepsで適用を確認できます。

288×384・81フレームのサーバー実測は次のとおりです。

steps サーバー生成 8 steps比
8 3.20秒 基準
6 2.68秒 -16%
4 2.19秒 -31%
4 + modality_scale=1.3 約2.9秒 口動作優先時

これにより、ターン先頭4 stepsが初動短縮の最大要因になりました。後続は時間方向の品質を優先して8 stepsを維持します。

7. 実写キャラクターの発話検証

7.1 3Dと実写の違い

3Dキャラクターでは全チャンクで口が動きました。一方、実写画像では同じAudio-to-Video経路でも、表情や瞬きだけが変化して口が閉じたままになる場合がありました。

この結果から、音声アップロード、TTS、A2Vモード、音声差し替え経路の欠落ではなく、実写画像で画像同一性が音声条件より強く働くことが主因と推定しました。

7.2 発話プロンプト

発話文字列を単なる文脈ではなく、プロンプト先頭の完全一致の台詞として渡すよう変更しました。さらに、各音節に合わせた口・歯・顎の明確な動きを指示しました。

効果は確認できましたが、実写では常に成功するわけではありませんでした。

7.3 seed試験

seed 1001、1004などを比較し、1004で明瞭な発話が複数回確認されました。しかし、同じ1004でも失敗例が出たため、seedだけで成功を保証できないことが分かりました。

現在の実写優先モードでは、観測上最も安定していた1004を既定値とし、UIで指定したseedを全チャンクへ適用します。標準モードでは指定seedを基準にチャンクごとに変えます。

7.4 最終フレーム連結

当初は同一ターン内で前動画の最終フレームを次動画の開始画像に使っていました。実写では、発話後の無音区間にある閉じた口が次動画へ強く継承され、後続ほど口が動かない傾向が見られました。

連結を外して全チャンクを同じ画像から生成すると、ターン中の切り替えは目立たず、後続の発話成功率が改善しました。実写優先モードでは登録時に作る発話用アンカーを毎回使用します。3D・イラスト向け標準モードでは、映像連続性を優先してターン内連結を残しています。

7.5 音声条件強度

サーバー側で音声条件強度を比較した結果です。

設定 生成時間 評価
既定 1.0 3.16秒 十分な速度余裕
audio_modality_scale=1.3 4.84秒 当時は有効と誤認。A2Vではno-op
audio_guidance_scale=1.3 5.16秒 A2Vでは実効なしの疑いが強く不採用
両方1.5 6.78秒 コスト増のみのため不採用

サーバー側の後続検証で、A2Vの音声は凍結されるためaudio_modality_scaleは音声予測にしか作用せず、出力がbit一致するno-opだったことが判明しました。口領域へ実際に作用するのは映像側のmodality_scaleです。現在は無効なaudio_modality_scaleとaudio_guidance_scaleを送信しません。

現在はキャラクター種別をユーザーが選択します。

モード scale seed 参照画像 用途
標準(イラスト・3D) なし 可変 ターン内で最終フレーム連結 速度と連続性を優先
実写・高速 準備だけmodality_scale=1.3 UI指定(既定1004) 毎回発話用アンカー 初動と連続再生を優先
実写・口動作優先 会話もmodality_scale=1.3 UI指定(既定1004) 毎回発話用アンカー 発話成功率を優先

7.6 キャラクター登録時の発話用アンカー

実写では参照画像の口形が生成結果を強く拘束します。このため登録時にLTXをnvfp4-fastで事前ロードし、入力画像と固定音声「あー、あー、あー、あー。」から選択中の本来の解像度・8 stepsで準備動画を生成します。高解像度でも口が安定して開いていた0.75秒地点をcharacter-speaking.pngとして抽出し、会話時は元画像ではなくこの口開き画像を毎回参照します。準備生成はモデルロードと推論経路のウォームアップも兼ねます。

低解像度・4 steps版の実装確認では、バックエンド再起動後の初回登録は19.9秒(準備動画生成17.0秒)、ロード済み状態では約3.2〜3.6秒でした。その後、全チャンクで使う参照画質を優先し、アンカー生成を選択解像度・8 stepsへ変更しました。384×512の実測生成は4.94秒で、4 stepsでは開口しない試行があったのに対して8 stepsでは明瞭な開口を確認しました。初回の遅延ロードを会話開始前へ移す方針は同じです。発話用フレームの抽出は、短いMP4で先頭キーフレームへ戻らないようFFmpegの-ssを入力後に置き、正確な0.75秒地点をデコードします。

384×512の実利用確認では、低解像度アンカー版より顔・口・髪の解像感が改善し、発話中の口動作も維持できました。会話開始後の先頭完成は2.618秒、後続の再生余裕は1.496秒で、高解像度アンカー生成による会話レイテンシ増加はありませんでした。この結果を受け、アンカーは当時の全11公開動画プロファイルで選択解像度・8 stepsを使う構成を正式採用しました。現在は同じ方針を24プロファイルへ拡張しています。

準備後の会話E2Eでは、先頭288×384・4 steps・modality_scale=1.3のサーバー生成が2.805秒でした。旧audio_modality_scale=1.3使用時の約4.2〜4.8秒から大きく短縮しています。

口開きアンカーだけで発話を維持できるか確認するため、実写の既定を高速モードにしました。準備生成には引き続きmodality_scale=1.3を使いますが、会話生成ではscaleを送りません。比較用に、会話にも1.3を使う「口動作優先」をUIから選べます。

高速モードのE2E確認では、先頭288×384・4 stepsがサーバー生成2.174秒、アプリ処理全体2.469秒、後続384×512・8 stepsがサーバー生成3.427秒、アプリ処理全体3.640秒でした。両動画とも発話区間の明確な口の開閉をフレーム列で確認しました。後続は5秒の再生尺に対して約1.36秒の生成余裕があります。

8. UIと再生の改善

発話後の固定尺無音を動的に短縮

LTXの動画尺は約5秒で固定されるため、1.45秒程度の短い発話では次の発話まで約3.5秒の無音が残っていました。後続動画を生成するバッファとしては有効ですが、後続がすでに完成していても動画終了を待つ実装だったため、会話の間として不自然でした。

再生中の動画がspeech_durationへ達し、次チャンクがplayableになった時点で残りの無音区間を打ち切るよう変更しました。次チャンクが未完成の場合は現在の5秒動画をそのまま維持し、完成通知後に即座に切り替えます。この制御はターン内の全境界に適用されるため、3発話目以降も準備状況に応じて連続発話します。

  • キャラクター画像とTXTをドラッグ&ドロップ対応
  • 動画領域下にチャット入力を配置
  • Enter送信、Shift+Enter改行
  • 表裏2つのvideo要素で次動画を先読み
  • 初回生成待ちに元キャラクター画像を表示
  • 動画再生開始時に待機画像からフェード
  • ターン送信時にも元画像へ戻し、前ターン最終フレームの残留を隠す
  • 待機画像を動画枠へ中央クロップしてサイズを一致
  • 設定変更時に「設定を更新」ボタンを再有効化
  • 実写向け対処をユーザー選択にし、3Dへ不要なレイテンシを加えない
  • UI表示を日本語・英語で切り替え、選択をブラウザーへ保存
  • 会話言語をUIから独立させ、自動・日本語・英語に応じてLLMプロンプトと文分割規則を変更

9. 発生した主な不具合

HTTP 409

Gateway管理外のバックエンドプロセスや、競合する生成要求があると409になるため、Gatewayによる排他管理へ統一しました。

短い音声の範囲エラー

LTXが必要とする長さまで条件用WAVだけを無音パディングして解消しました。

フレーム抽出失敗

短い動画でffmpeg -sseof -0.08が出力を作らない場合があり、取得位置とファイル存在確認を修正しました。

20fpsで101フレームを送る422

3秒/5秒比較機能で秒数×fps+1と計算した結果、20fps・5秒が101フレームとなり、LTXの8n+1制約に違反しました。比較機能を削除し、現在はプロファイル定義済みの97フレームを使用します。

10. 現在の採用構成

  • LLMストリーミング受信中にTTS開始
  • TTSタスクを並列実行
  • 動画生成と後続TTSをパイプライン化
  • 全動画約5秒
  • 動画stepsはUI指定(1〜12、既定4)。ターン先頭はmin(4, steps)、後続は指定値
  • 全プロファイルでターン先頭だけ低解像度、後続は選択解像度
  • Gatewayポーリング100ms
  • キャラクター種別をユーザーが選択
  • 実写高速(既定): 先頭はstartupプロファイル、後続は選択プロファイル、UI指定seed(既定1004)、会話scaleなし、毎回発話用アンカー
  • 実写口動作強調: 上記に加えて会話もmodality_scale=1.3
  • 標準: 選択プロファイル、UI指定seed+チャンク番号、modality scaleなし、ターン内最終フレーム連結
  • LTX生成音声を元TTS音声へ差し替え
  • 待機プールは初期3本から設定数3〜7(既定5)へ漸進成長、6本以上はランダム再生
  • 会話開始フレームと終了姿勢の錨止めを個別に切り替え
  • リアルタイム優先リースで長尺の別バックエンドジョブをターン中に排除
  • 設定、待機プール、発話アンカーをキャラクタープリセットとして即時復元

10.1 全プロファイルの先頭用解像度

口開きアンカーは全チャンクで再利用するため、全24公開プロファイルで選択プロファイルの本来の解像度・8 stepsで生成します。各ターン先頭動画だけは、選択画角に近い32の倍数の低解像度とmin(4, video_steps)を使います。fpsとフレーム数は選択プロファイルから変えません。現行24プロファイルの対応表はテクニカルガイドを参照してください。

元の縦横比を32の倍数で正確に縮小できない画角は、視覚的な切り替えを抑える範囲で最も近い比率へ丸めています。先頭から後続への見た目と速度はプロファイルごとに実利用で確認します。

11. 最新高速化の総括

2026-09-04の最終構成では、次の改善を組み合わせました。

  1. AivisSpeech EngineをGPU1で動作させ、起動後にウォームアップ
  2. キャラクター登録時にLTXをロードし、初回モデルロードを会話開始前へ移動
  3. 実写画像から口開き発話用アンカーを生成
  4. A2Vで無効なaudio_modality_scale系を削除
  5. 準備生成だけ有効なmodality_scale=1.3を使い、会話の高速モードでは省略
  6. サーバーで有効化された4 stepsをターン先頭へ適用
  7. 全プロファイルで先頭だけ低解像度化
  8. 後続TTSと動画生成を先行させ、5秒の再生時間内に次動画を完成

実写・384×512選択・高速モードの代表E2E測定です。先頭は288×384・4 steps、後続は384×512・8 stepsです。

指標 高速化途中 最終構成1回目 最終構成2回目
LLM最初の文字 0.160秒 0.115秒 0.139秒
先頭TTS 0.703秒 0.231秒 0.224秒
先頭サーバー生成 4.451秒 2.170秒 2.057秒
送信から先頭動画完成 5.657秒 2.734秒 2.657秒
後続サーバー生成 5.009秒 3.359秒 3.292秒
先頭終了時の後続余裕 -0.206秒 1.436秒 1.507秒

最終構成では初回動画が約2.7秒で完成し、後続動画も先頭終了の約1.4〜1.5秒前に完成しました。2回とも口の動作を目視確認しています。無音区間では口開きアンカーの影響で口が少し開いたままになる場合がありますが、閉口したまま発話する失敗より影響が小さいと判断して高速モードを既定にしました。

アプリ外の固定入力テストでは、会話scaleなしの先頭がサーバー生成2.174秒、アプリ処理全体2.469秒でした。サーバー申告値以外の約0.19〜0.30秒は、条件音声アップロード、HTTP往復、100msポーリング、MP4ダウンロード、音声差し替えmuxで構成されます。

全プロファイル展開時には16fps横型、20fps横型、24fps横型、24fps縦型で準備生成を確認しました。ロード済み状態の登録処理は約3.2〜3.5秒でした。

12. 残る課題

  • LTX単体では実写の完全なリップシンクを保証できない
  • modality_scale=1.3は口動作と引き換えに4 stepsで約0.7秒の生成コストがある
  • 長時間会話ではGPUキューの変動で動画間ギャップが発生し得る
  • 日本語の句読点/ひらがな節境界のヒューリスティックは形態素解析ほど確実ではない
  • 確実な実写リップシンクには、生成後の専用リップシンク工程が必要
  • CUDA Graphsは48GB級構成の候補。32GB構成では多shapeのcaptureごとにメモリが増えるため無効化。torch.compileは引き続き検討候補
  • LLMの発話内容から感情・表情・ジェスチャーを抽出して動作へ反映する処理は今後の課題

13. 測定値の読み方

session.jsonには、LLM開始・初回文字・完了、TTS開始・音声完成、動画開始・完成、生成秒数、使用プロファイル、steps、seed、フレーム数、音声scaleを保存します。

比較時は送信から動画完成までの総時間だけでなく、次を分離して評価してください。

  • LLM初回文字まで
  • TTS所要時間
  • 音声完成から動画完成まで
  • サーバー申告の生成時間
  • ダウンロードとmuxの後処理時間
  • 先頭動画完成から後続動画完成までの間隔
  • 先頭動画終了時点の先読み余裕

異なるサーバー改修、steps、解像度、音声scaleが混在した値を直接比較すると誤った結論になりやすいため、同一条件で複数回測定する必要があります。

解像度のように効果が実行時変動より小さい項目は、同一入力を固定し、比較対象を交互に実行してください。別時間帯の平均同士は傾向確認に限り、採否の根拠にはしません。

14. 2026-09-14〜09-28の追補

9月4日の速度検証後、長時間利用と外部GUI統合を重視して次を追加しました。

  • 公開動画プロファイルを16/20/24fpsの縦横24種に拡張し、すべてに低解像度startupプロファイルを用意
  • 生成stepsをUIから1〜12で変更可能にし、先頭だけmin(4, steps)とする統一ルールへ変更
  • 待機プールを初期3本の漸進成長式にし、上限7本、既定5本、6本以上のランダム再生、非表示/無操作時の補充休止を追加
  • 待機フレームから発話を開始するアンカーと、最終発話を待機開始ポーズへ戻すFLF錨止めを追加
  • キャラクタープリセット、セッション復元、チャンク個別ダウンロード、外部GUI用CORSを追加
  • Gatewayロードにstrategy=coresidentを使い、会話中はリアルタイム優先リースでH3などの長尺ジョブを排除
  • SSE切断後5秒で放棄されたターンを自動キャンセルし、不要なGPU稼働を防止

32GB級構成ではnvfp4-32gbを使い、未使用upsamplerの非ロードとText Encoderの一部CPU退避により常駐実測約28.8GBに収めました。空きVRAM 31GBの制限下で、512×384・20fps・97フレーム・4 stepsは定常3.5〜4秒(実測3.8秒)です。ただしヘッドルームは約2GBのため、動画エンジンはGPUを専有し、TTSとLLMはCPUまたは別ホストで動かします。32GB構成では多shape時のメモリ蓄積を避けるためCUDA Graphを無効化します。