複数ASRモデルを組み合わせた高品質訓練データ作成パイプライン——Whisper×Qwen3×LLMの設計思想と実装

この記事でわかること:Whisper・Qwen3-ASR・LLMを組み合わせて、単一モデルでは得られない高精度なASR訓練データを生成する2段階パイプラインの設計思想と実装ポイントを解説します。強制アライメント(Forced Alignment)・差分マージ・チャンキング戦略など、実運用上のノウハウを理論から丁寧に説明します。

ASRモデルのファインチューニングにおいて、最大のボトルネックはデータ品質です。生音声をそのまま使うと、誤字・タイムスタンプのズレ・過度なフィラーなどのノイズが混入し、学習が安定しません。

本記事では「Whisper」「Qwen3-ASR」「LLM」の3モデルを連携させ、相互補完によって高品質なASR訓練データを生成するパイプラインの設計と実装を詳解します。さらに後段の「チャンク分割スクリプト」まで含めた2段階の構成を取り上げ、ASRデータセット作成の実践的なノウハウを共有します。


なぜ単一モデルでは不十分なのか

Whisper(OpenAI)はロバストな多言語対応に強みを持ち、特に漢字変換や固有名詞の表記精度が高いのが特徴です。アナウンサーの読み上げや専門用語が多い音声では、Whisperの漢字出力が他のモデルより正確なケースが多く見られます。ただし、長時間音声でのドリフト(繰り返し生成)が発生しやすく、CER全体で見ると不利になることがあります。

一方、Qwen3-ASRはLLMベースの構造により文全体のCERはWhisperより低い傾向がありますが、入力長の制約から長尺音声を単独で扱うのが難しく、チャンク境界をまたぐ文の結合にも課題があります。

この2つをアンサンブルし、差異をLLMで調停することで、両者の弱点を補い合う設計がこのパイプラインの核心です。


パイプライン全体像

パイプラインは大きく2つのスクリプトに分かれます。

text
[Step 1] 高精度転写+統合
  音声ファイル
    → Whisper 転写(テキスト+タイムスタンプ)
    → Qwen3-ASR 転写(チャンキング戦略)
    → LLM 差分マージ(統合テキスト生成)
    → Forced Aligner(単語レベルタイムスタンプ)
    → JSON 出力

[Step 2] チャンク分割と品質フィルタリング
  JSON(merged text+alignment)
    → 文レベル分割
    → 20〜30秒チャンク化
    → ffmpeg 音声切り出し
    → Whisper 品質チェック(CER計算)
    → manifest.jsonl 出力

Step 1:高精度転写+統合

Whisper転写

WhisperはHugging Face Transformersパイプライン経由で動かすのが一般的です。openai/whisper-large-v3-turboを使い、temperaturenum_beamsrepetition_penaltyを調整してドリフトを抑制します。

Whisperの最大の強みは漢字・固有名詞の変換精度です。エンドツーエンドのエンコーダ・デコーダ構造で大規模な多言語コーパスで学習されているため、地名・人名・専門用語などの漢字表記が他のモデルより正確なケースが多い傾向があります。ただしCER全体ではLLMベースのQwen3-ASRに劣ることが多く、単体では訓練データとして不十分な場面が出てきます。

出力はテキストと、セグメント単位のタイムスタンプstart_time / end_time)のペアです。このタイムスタンプが後続のQwen3チャンキングの基準になります。

Qwen3-ASRのチャンキング戦略

Qwen3-ASRはLLMベースのため、入力長に制約があります。無闇に固定長で切ると文の途中でチャンクが終わり、前後の文脈が失われて精度が落ちます。

この問題を解決するのがWhisperタイムスタンプ準拠のチャンキングです。

text
考え方:
1. Whisperが出力したセグメント境界(文末付近)を尊重する
2. 目標チャンク秒数(デフォルト25秒)に近くなるよう
   複数セグメントをまとめる
3. 許容範囲は目標の70%〜130%(17.5〜32.5秒)
4. 上記で境界が決まらない場合は固定長フォールバック

この戦略により、「文の途中でチャンクが途切れる」問題を回避しつつ、Qwen3の文脈理解能力を最大限に活かせます。

ポイント:Qwen3はチャンクをまたぐ文の結合が苦手なので、チャンク境界を"意味的に安全な区切り"に揃えることが精度向上に直結します。Whisperのセグメント境界はその近似として機能します。

LLMによる差分マージ

WhisperとQwen3の転写結果をdiff(差分)形式でLLMに渡し、「どちらの表現を採用するか」を判断させます。

差分の表記ルールは次のとおりです。

text
プレーンテキスト   → 両モデルが一致(そのまま採用)
(w: text)         → Whisperのみに存在
(q: text)         → Qwen3のみに存在
(w: text|q: text) → 両モデルが異なる表現(置換候補)

LLMへの指示例(概略):

text
以下のdiff形式の転写を読み、最も自然で正確な日本語転写を出力してください。
- (w: ...) や (q: ...) の選択根拠は不要。最終テキストのみ出力。
- フィラー(えー、あの)は文脈に応じて保持または削除。
- 句読点(。、)は自然な位置に補完。
- 固有名詞はより正確な方を優先。

長文は**チャンク分割(デフォルト5000文字)**してLLMに渡し、Geminiを使う場合は並列処理で高速化します。

注意:LLMは生成AIのため、稀に転写にない内容を追加したり文を要約してしまうことがあります。プロンプトで「転写テキストのみ出力」「要約や意訳は厳禁」を明示し、出力後にCER(文字誤り率)で品質確認することを推奨します。

強制アライメント(Forced Alignment)

LLMが統合したmerged textは、元の音声とのタイムスタンプが失われています。強制アライメント(Forced Alignment)とは、テキストを固定として音声に当てはめ、各単語・文字の発話時刻を推定する技術です。

text
通常のASR:  音声 → テキスト(タイムスタンプ付き)
強制アライメント: 音声+テキスト → タイムスタンプ

このパイプラインではQwen Forced Alignerを使い、単語レベルのタイムスタンプを取得します。OOM(メモリ不足)が発生した場合のフォールバック戦略も用意されています。

text
フォールバック優先順位:
1. Forced Aligner が利用可能 → 使用(最高精度)
2. OOM 発生時 → Qwen3 のタイムスタンプから線形補間で予測
3. Aligner 未使用時 → Qwen3 タイムスタンプをそのまま使用

予測アライメントはdifflib.SequenceMatcherでソーステキスト(Qwen3の転写)とターゲットテキスト(merged)を照合し、文字位置と時刻を線形補間で対応付けます。精度は落ちますが、GPU OOMが多い長尺音声でも処理を続けられる実用的な設計です。

Step 1の出力JSON構造

json
{
  "audio_path": "/path/to/audio.mp3",
  "status": "ok",
  "qwen_whisper_diff": "(w: こんにちは|q: こんにちわ)、今日は...",
  "metadata": {
    "audio_duration_sec": 123.456,
    "cer": {
      "pairwise": {
        "merged_vs_whisper": 0.04,
        "merged_vs_qwen": 0.03,
        "qwen_vs_whisper": 0.06
      }
    }
  },
  "whisper": { "text": "...", "timestamps": [...] },
  "qwen":    { "text": "...", "timestamps": [...] },
  "merged":  { "text": "...", "provider": "openai", "model": "gpt-4o-mini" },
  "alignment": {
    "items": [{ "text": "こんにちは", "start_time": 0.0, "end_time": 0.8 }],
    "source": "qwen_forced_alignment_merged"
  }
}

cer.pairwiseの値を見ることで、3つのモデル間の不一致度が一目でわかります。merged_vs_whisperが大きくmerged_vs_qwenが小さければ、LLMはQwen3寄りの判断をしたことがわかります。


Step 2:チャンク分割と品質フィルタリング

文レベルへの分割

alignment.itemsは単語単位のタイムスタンプですが、そのままではチャンクを切りにくいため、まず文レベルに再構成します。

句読点(。!??!)で分割し、1文がmax秒を超える場合は読点(、,)でソフト分割、さらに超える場合は文字単位で強制分割します。各分割後の時刻は文字数比例の線形補間で割り当てます。

python
# 文字数比例で時刻を配分するイメージ
total_chars = len(text)
for i, char in enumerate(text):
    ratio = i / total_chars
    timestamp = start + ratio * (end - start)

チャンク化(20〜30秒)

ASRモデル(特にWhisper系)は30秒以下の音声を前提に設計されているため、訓練データも同様の長さに揃える必要があります。

チャンク分割のスコアリング:

text
スコア = abs(chunk_duration - target_sec)  # 目標秒数との差
文末(。!?)で終わる → -0.35 ボーナス
次セグメントとのギャップが大きい → -0.1/秒 ボーナス(最大1秒)

スコアが最小の分割点を選ぶことで、自然な文末かつ目標長に近いチャンクを優先的に生成します。短すぎるチャンクは前後にマージし、長すぎるチャンクは再帰的に句読点分割します。

設計の妙:単純に「N秒ごとに切る」のではなく、文末スコアとギャップスコアを組み合わせることで「文の途中で切れる不自然なチャンク」を避けています。これが学習データの品質に直結します。

句読点の前チャンクへの移動

チャンク先頭が句読点になる場合(例:。おはよう)、その句読点を前チャンクの末尾に移動します。

text
Before:
  チャンク1: "こんにちは"
  チャンク2: "。今日もいい天気ですね"

After:
  チャンク1: "こんにちは。"
  チャンク2: "今日もいい天気ですね"

これは一見細かい処理ですが、テキスト側の自然さと音声切り出し精度(句読点直後から音が始まるため)の両方に影響します。

音声切り出しとWhisper品質チェック

ffmpegで各チャンクを切り出します。

bash
ffmpeg -y -ss {start} -i {source} -t {duration} \
       -ac 1 -ar 16000 {output.wav}

モノラル変換(-ac 1)と16kHzサンプリング(-ar 16000)はWhisper系モデルの入力仕様に合わせています。

切り出した音声を再度Whisperで転写し、mergedテキストとのCER(文字誤り率)を計算します。CERが高いチャンクは、タイムスタンプのズレや強制アライメントの失敗が疑われるため、後処理でフィルタリングできます。

CER計算時のテキスト正規化:

python
def normalize_for_cer(text):
    # Unicode NFKC正規化(全角・半角統一)
    text = unicodedata.normalize("NFKC", text)
    # Letter/Numberカテゴリの文字のみ抽出
    # → 漢字・ひらがな・カタカナ・英数字が対象
    return "".join(c for c in text if unicodedata.category(c)[0] in ("L", "N"))

注意:CER計算では句読点・記号を除外しています。句読点の有無でCERが振れることを防ぐためですが、逆に句読点の品質は別途確認が必要です。

manifest.jsonl の構造

json
{
  "sample_name": "dataset_0001",
  "audio_path": "/output/audio/dataset_0001.wav",
  "text": "今日はとてもいい天気ですね。散歩日和です。",
  "start_time": 12.5,
  "end_time": 37.6,
  "duration_sec": 25.1,
  "source_audio_path": "/input/episode001.mp3",
  "source_json_path": "/input/episode001.json",
  "cer": 0.021,
  "whisper_text": "今日はとてもいい天気ですね。散歩日和です。",
  "error_metric": "cer"
}

実装上の重要な設計判断

メモリ管理

GPU OOMは長尺音声処理では避けられません。このパイプラインは各ステージでOOMを検出し、自動的に軽量フォールバックに切り替える設計になっています。

python
try:
    result = forced_aligner.align(audio, text)
except torch.cuda.OutOfMemoryError:
    _clear_torch_memory()
    result = predict_alignment_from_qwen(qwen_timestamps, merged_text)

torch.cuda.empty_cache()だけでなく、モデルオブジェクトの明示的な削除(del model)も組み合わせることでVRAMリークを防ぎます。

テキスト正規化

日本語ASRデータでよく問題になるノイズの例:

  • 全角スペース( )→ 半角に統一
  • 省略記号()→ 削除
  • 連続する空白 → 1文字に統一
  • 典型的なハルシネーション(「ご視聴ありがとうございました」など)→ 除外リストで削除

特に定型文の除外は、動画や番組エンディングで繰り返し出現するテキストが訓練データに混入するのを防ぐための重要なフィルタです。


このアーキテクチャの応用範囲

このパイプラインの設計思想(複数モデルの差分をLLMで調停する)は、ASRデータ作成に限らず応用が効きます。

  • 多言語ASR:言語ヒント(--language zhなど)を変えるだけで中国語・英語にも対応
  • 話者ダイアライゼーション:alignment.itemsに話者IDを付与すれば話者分離訓練データにも転用可能
  • 低リソース言語:Whisperが得意な言語とQwen3-ASRを使い分けることでカバレッジを拡大

一次情報として:Forced Alignerの精度はモデルと音声のドメインに大きく依存します。ドラマ音声(BGM・効果音混在)と会議音声(マイク近接)ではアライメントの信頼性が大きく変わります。CER分布をヒストグラムで確認し、外れ値チャンクを学習から除外することを強く推奨します。


FAQ

Q1. WhisperとQwen3-ASRでどちらが精度が高いですか?

全体的なCER(文字誤り率)ではQwen3-ASRの方が低い傾向があります(こちらの記事を参考)。ただしWhisperは漢字変換と固有名詞の正確さで優位なケースが多く、専門用語や人名・地名が頻出する音声ではWhisperの出力が最終的に採用されやすいです。両者が得意な領域が異なるため、qwen_vs_whisper CERが高いファイルほどLLMマージの恩恵が大きくなります。

Q2. LLMのAPI費用はどのくらいかかりますか?

gemini-flashを使用した際は数時間の音声で1000~2000円かかってしまったので、オープンソースのLLMを使用することをお勧めします。

Q3. 強制アライメントが失敗しやすい条件は何ですか?

主に以下の3つです。①BGMや効果音が大きい音声(音楽番組、ドラマなど)、②話速が極端に速いまたは遅い音声、③mergedテキストとQwen3転写の乖離が大きい音声(LLMが大幅に書き換えた場合)。フォールバックで線形補間を使う場合、タイムスタンプの誤差が数百ミリ秒程度生じることがあります。

Q4. チャンクの目標秒数(25秒)はなぜこの値ですか?

Whisperの最大入力長が30秒であることが主な理由です。30秒未満でチャンクを切ることで、品質チェック時のWhisper再転写でスキップされるチャンク(29秒超過)を最小化できます。また、あまり短いと文の断片が多くなり、モデルの長距離依存学習に不利なためです。

Q5. 日本語以外の言語でも使えますか?

--language引数を変えるだけで対応言語を切り替えられます。ただし、句読点による文分割のロジック(SENTENCE_END_RE)は日本語・中国語・英語などの句読点を想定しているため、アラビア語やデーヴァナーガリーなど独自の文末表現を持つ言語では、正規表現のカスタマイズが必要になる場合があります。

Q6. GPU(VRAM)はどの程度必要ですか?

Whisper large-v3-turboで約6GB、Qwen3-ASR(モデルサイズによる)で4〜16GB、Forced Alignerで2〜4GB程度が目安です。すべてを同時に動かす設計ではなく、ステージごとにモデルをロード・アンロードするため、1枚のGPUで順次処理できます。ただし24GBのVRAMがあると余裕を持って動作します。


まとめ

この2段階パイプラインの要点を整理します。

フェーズ技術解決する問題
Whisper転写Whisper large-v3-turbo長尺安定・タイムスタンプ基準点
Qwen3転写Whisper境界準拠チャンキング文末整合・LLM文脈活用
LLMマージdiff形式+LLM調停モデル間の不一致を最善解に統合
強制アライメントQwen Forced Aligner+フォールバック単語レベルタイムスタンプ付与
チャンク化スコアベース文末分割20〜30秒自然チャンク生成
品質フィルタWhisper再転写+CER低品質チャンクの検出・除外

単一モデルに頼るデータ作成では避けられない精度の天井を、複数モデルのアンサンブルとLLM調停という設計で突破するのがこのパイプラインの本質です。ASRファインチューニング用データセットの品質で悩んでいる方の参考になれば幸いです。

Contact

仕事の依頼などのお問い合わせはこちら

新規プロジェクトのご相談、開発のご依頼、協業のご相談などがあれば、お気軽にご連絡ください。

関連するブログ

この記事に近いテーマのブログをピックアップしています。