ブログに戻る

Whisperのハルシネーション:同じ行を繰り返し、存在しない文章を書く理由

Whisperのハルシネーションを解説します。無音に「Thank you for watching」と書いてしまう理由、同じ行を繰り返すループの原因、各対策の代償、完成したSRTの中から見つける方法まで詳しく紹介します。

2026年10月3日
Sablate Team

Whisperのハルシネーション(hallucination)とは、誰も話していないのに、モデルが書いてしまうテキストのことです。たとえば、無音の部分に現れる「Thank you for watching」、字幕のクレジット、1分間も繰り返される同じ1行などがあります。大半は、2つの設定で防げます。発話のない音声を除外することと、モデルが自分自身の出力を手がかりにしないようにすることです。ところが、標準的なWhisperのツールでは、このどちらもデフォルトで有効になっていません。

この記事では、2種類のハルシネーションの比較、それぞれが起きる理由、そのうち1つが私たちのアプリで起きたときに計測した結果、各対策の代償、openai-whisper・faster-whisper・whisper.cppの設定、そして仕上がった字幕ファイルに残っているものを見つける方法を順に説明します。

Whisperのハルシネーションを並べて比較する

幻のフレーズ繰り返しループ欠落した発話
見え方「Thank you for watching」「Thanks for watching」、字幕のクレジット1つの行が何度も書かれ、句読点がないことが多い文字起こしから文が1つ抜けている
現れる場所無音、音楽、拍手、室内の雑音どこでも。一度始まると続く小さな声で始まる冒頭、短い相づち
原因発話のない音声がデコーダーに届く各ウィンドウが、直前の出力をプロンプトとしてデコードされる特定のモデルがその音声をどう扱うか
最初の対策音声区間検出フィルター(VAD)直前テキストの参照をオフにする同じクリップを別のモデルで試す
字幕での現れ方話の合間に余分なキューが出る1つの行で止まったキュー、または文の区切りがないキュー本来あるはずの行がなく、空白になる

「欠落した発話」の列は、逆の失敗です。存在しないテキストが書かれるのではなく、発話が飛ばされます。原因も対策も異なり、Whisperモデル比較で取り上げています。以下で扱うのは、最初の2つです。

Whisperが誰も話していないテキストを書くのはなぜですか?

Whisperは音声を30秒のウィンドウごとに処理し、ウィンドウごとにデコーダーがテキストを書きます。無音かどうかを判定するチェックもありますが、確率に対する閾値でしかなく、音楽、拍手、ルームトーンのウィンドウは、しばしばこれをすり抜けて言葉として出力されます。選ばれる言葉に、原因が表れています。Whisperは、ウェブ上の音声とその文字起こしを組にした680,000時間分のデータで学習されています。文字起こしするものがないときにWhisperが書く内容については、そのテキストに多い締めの挨拶や字幕のクレジットが原因だ、という説明が一般的です。

その数は決して小さくありません。2025年の研究で、AGH University of Krakówの研究者が、ノイズ、音楽、環境音といった発話のない音声にlarge-v3を301,317回実行したところ、40.3%の割合でテキストが出力され、そのハルシネーションの9.1%はループでした。最も多かった出力は「thank you」でハルシネーション全体の24.76%、次が「thanks for watching」で10.32%でした。

実際の発話では、発生率ははるかに低くなりますが、被害はより深刻です。2024年のCareless Whisper研究では、文字起こしの約1%に、音声には存在しないフレーズや文がまるごと含まれていました。そのうち38%には、暴力の助長、事実と異なる関連づけの創作、偽りの権威の示唆といった、明白な害が含まれており、こうした現象は、長い間(ま)のある話し方をする話者に偏って起きていました。

字幕の場合、その代償ははっきりしています。ハルシネーションで生まれたフレーズは、話の合間に、書式の整った普通のキューとして現れ、視聴者はそれをセリフとして読んでしまいます。

1行の間違いが40行に増える理由

ループの原因は別のところにあります。デフォルトでは、Whisperは各ウィンドウを、自分自身の直前の出力をプロンプトとしてデコードします。そのため、コンテキストがウィンドウをまたいで引き継がれ、名前の綴りが揃い、文が途切れずにつながります。ところが、この同じ仕組みのせいで、1つの失敗したウィンドウが次のウィンドウを汚染してしまいます。デコーダーが同じ行を2回書くと、次のウィンドウはその行を前提に始まり、同じ行をまた書くのです。faster-whisperのcondition_on_previous_text(直前テキストの参照)のドキュメントには、はっきりこう書かれています。オフにすると、モデルは「繰り返しのループやタイムスタンプのずれのような、失敗のループにはまりにくくなる」。

この問題はリリース前に私たち自身のアプリでも起きたため、計測しました。モデル比較で使ったのと同じ101秒のトルコ語クリップを、RTX 3060 Ti上でfloat16を使って処理し、変えたのはこのオプション1つだけです。

large-v3、直前テキストの参照ありlarge-v3、直前テキストの参照なしmedium、直前テキストの参照ありmedium、直前テキストの参照なし
文字起こし内の文末記号の数0304730
繰り返されたフレーズ(6-gram)9000

デフォルト設定はlarge-v3を台無しにしましたが、mediumには何の影響もありませんでした。直前テキストの参照をオンにしたままだと、大きいほうのモデルは文末記号をまったく書かず、繰り返しフレーズを9件出力しました。小さいほうのモデルは、同じ音声に同じ設定でも問題ありませんでした。ある実行では、わずか0.12秒の音声に対して、1つのキューにこの内容が入っていました:

[58.26 -> 58.38]  bir level oldu bir level oldu bir level oldu bir level oldu
                  bir level oldu bir level oldu bir level oldu bir level oldu

文末記号が抜けることは、繰り返しと同じくらい深刻です。字幕ツールは、文の区切りで文字起こしをキューに分割するため、句点のない文字起こしでは分割の手がかりがなく、巨大なキューになるか、雑な行分割処理だと単語1つだけのキューになってしまいます。直前テキストの参照をオフにすると、モデルを変えなくても、句読点が戻りました。

1本のクリップはベンチマークではありません。それでも、2つのことは分かります。モデルのサイズは防御にならないこと、そして最もコストの低い対策は設定の変更だということです。

直前テキストの参照をオフにすると、何か失うものはありますか?

あります。ハルシネーションの対策にはどれも代償があり、適用する前に知っておく価値があります:

  1. ウィンドウ間の一貫性。 直前のテキストがコンテキストにないと、表記や句読点のスタイルがぶれることがあります。ドキュメントも、オフにすると「ウィンドウ間でテキストに一貫性がなくなる可能性がある」と警告しています。実際に起きるのは、長いファイルの中で、同じ名前が2通りに綴られる、といったことです。
  2. initial prompt(初期プロンプト)は最初のウィンドウまでしか効かない。 ツールは、名前、ブランド名、専門用語といった語彙のリストをinitial promptとしてWhisperに渡しますが、直前テキストの参照をオフにすると、それが影響するのは最初のウィンドウだけです。openai-whisperの--carry_initial_prompt Trueとwhisper.cppの--carry-initial-promptは、これをすべてのウィンドウに送り直します。faster-whisperでは、hotwordsオプションがその役割を担います。
  3. 繰り返しペナルティは、本当の繰り返しも消してしまう。 repetition_penaltyやno_repeat_ngram_sizeはループを抑えられますが、ループと、話者が自分で繰り返して話している箇所を区別できません。私たちのテストクリップにも、まさにそれがあります。同じ文が、意図的に2回続けて話されているのです。それを取り除いてしまうペナルティは、セリフを削っていることになります。
  4. 音声区間検出フィルターは、小さな声を切り落とすことがある。 VADは、Whisperが音声を聞く前に、どこを発話とみなすかを決めます。声が小さい出だしや、電話のように帯域が制限された音声は、単語を取りこぼしやすい場面です。AutoSubsは、まさにそのケースのために、無音スキップをオフにするスイッチを2026年9月に追加しました。
  5. hallucination_silence_thresholdは単語タイムスタンプが必要で、万能ではない。 この設定は、ハルシネーションが疑われる箇所の前後にある長い無音をスキップします。私たちのクリップでは、計測できるほどの違いは出ませんでした。作り話ではないかと疑った行は、実際の発話だったのです。

どれも、ループの代償には遠く及びません。名前の綴りの間違いは、検索と置換1回で直せます。繰り返しループは、その区間をもう一度文字起こしし直すことになります。

Whisperのハルシネーションを防ぐ方法(ツール別)

openai-whisperfaster-whisperwhisper.cppSablate
音声区間検出フィルター内蔵されていないvad_filter=True(デフォルトでオンなのは、バッチ処理のパイプラインのみ)Sileroモデルを使った--vad常にオン
直前テキストの参照をやめる--condition_on_previous_text Falsecondition_on_previous_text=False-mc 0常にオフ
ハルシネーションの疑いがある箇所のあとの無音をスキップする--hallucination_silence_thresholdhallucination_silence_threshold同等の機能なしなし
すべてのウィンドウでプロンプトを送り直す--carry_initial_prompt Truehotwords--carry-initial-promptなし
これらの設定を変更できるかできるできるできるできない

最後の3行は、Sablateの正直な弱点です。2つの対策は常にオンで、これらの設定はどれも変更できる形では提供されていません。フィルターを調整したり、プロンプトをウィンドウ間で引き継いだりしたい場合は、オープンソースのツールならそれができます。

インタビューのファイルをopenai-whisperで処理する場合:

whisper talk.mp4 --model turbo --output_format srt --condition_on_previous_text False

openai-whisperには独自の音声区間検出フィルターがないため、このコマンドで直るのはループであって、幻のフレーズではありません。後者に対する部分的な対策が、--word_timestamps Trueと組み合わせた--hallucination_silence_thresholdです。whisper.cppには両方の対策があり、そのコマンドラインツールは16ビットWAVを前提としています:

ffmpeg -i talk.mp4 -ar 16000 -ac 1 -c:a pcm_s16le talk.wav
./build/bin/whisper-cli -m models/ggml-large-v3-turbo.bin -f talk.wav -osrt -mc 0 --vad -vm models/ggml-silero-v6.2.0.bin

-mc 0は、ウィンドウ間でテキストのコンテキストを持ち越しません。Sileroモデルは、リポジトリのdownload-vad-model.shスクリプトで入手できます。Pythonでは、faster-whisperが両方を引数として受け取ります:

from faster_whisper import WhisperModel

model = WhisperModel("large-v3-turbo")
segments, info = model.transcribe("talk.mp4", vad_filter=True, condition_on_previous_text=False)

仕上がったSRTからハルシネーションを見つけるにはどうすればよいですか?

どの設定でもハルシネーションをすべて取り除けるわけではないので、ファイルを確認してください。次の3つのコマンドで、残っているもののほとんどを見つけられます。macOSとLinux、またはWindowsのGit BashとWSLで実行します:

grep -n -i -E "thank(s| you) for watching|amara\.org|subtitles by" talk.srt
awk '{ sub(/\r$/, "") } NF && !/-->/ && !/^[0-9]+$/ { if ($0 == prev) print NR": "$0; prev = $0 }' talk.srt
perl -ne 'print "$.: $_" if /(\S+(?: \S+){1,4}) \1 \1/' talk.srt

1つ目は、定番の締めの挨拶や字幕のクレジットを探します。2つ目は、直前の行とまったく同じ字幕の行を表示します。ループがキューに分割されると、たいていこのような形になります。3つ目は、2〜5語のフレーズが1つの行の中で3回繰り返されているものを見つけます。

ヒットした箇所は、結論ではなく疑問として扱ってください。人は実際に「thank you」と言いますし、同じことを繰り返して話すこともあります。キューを音声と照らして再生し、残すか削除するかを決めます。長い間(ま)や音楽の上に現れる幻の行は、どのパターンにも一致しません。それらを見つける唯一の方法は、発話のない区間にあるキューを読むことです。

どの対策を使うべきか

「講演、インタビュー、講義に字幕を付けている」 音声区間検出フィルターをオンに、直前テキストの参照をオフにします。この組み合わせは、字幕を付ける予定のあらゆる発話に対して、妥当なデフォルトです。

「録音に長い無音やBGMがある」 ここでは、フィルターがいちばん効きます。それでも、音楽の上にあるキューは必ず読んでください。

「電話の通話や、音が小さく帯域の限られた録音」 フィルターをオンにして1回、オフにして1回実行し、欠けている部分を比べます。小さな声の言葉を落としてしまうフィルターは、紛れ込んだ「thank you」1つよりたちが悪いものです。

「名前をinitial promptに頼っている」 直前テキストの参照をオフにしたうえで、上のフラグを使ってプロンプトをウィンドウごとに送り直すか、あとから検索と置換で名前を直します。

「2つの対策を入れても、行が繰り返される」 繰り返しペナルティに手を伸ばす前に、同じファイルで別のモデルを試してください。私たちのクリップはlarge-v3ではループし、mediumではしませんでした。それでもno_repeat_ngram_sizeが必要なら、取り除かれたかもしれない行がないか、通して聴いて確認してください。

Sablateでの扱い

Sablateでは、2つの対策が常にオンになっています。モデルの前段に置く音声区間検出フィルターと、直前テキストを参照しない設定です。SablateはWhisperを自分のパソコン上で実行しており、上の計測を受けて、直前テキストの参照をオフにしました。あわせて、句読点のない文字起こしを単語1つだけのキューに分割してしまっていた行分割処理も修正しました。

知っておきたい影響が2つあります。1つ目は、「Defaults → Custom vocabulary」のカスタム語彙が、initial promptとしてWhisperに渡されることです。そのため、直前テキストの参照をオフにしていると、影響が及ぶのは最初のウィンドウ、つまり発話のおよそ最初の30秒だけになります。長い録音の後半に出てくる名前は、エディタの「Find & replace」で直すことになります。この機能は、一度に1つの言語を対象に動作します。2つ目は、設定が固定されているため、調整できる項目がないことです。フィルターをオフにしたい録音は、上で紹介したオープンソースのツールを使ってください。

エディタでは、動画を再生しながらキューを順に確認できます。上のコマンドでヒットした箇所を判断するには、これが最も手早い方法です。書き出しでは、修正した字幕を.srt、.vtt、または焼き込み済みの動画として出力できます。FastとBalancedは無料プランで使え、AccurateとBestはProに含まれます。WindowsまたはMac版のSablateをダウンロードして、自分の映像で試してみてください。

まとめ

Whisperのハルシネーションには2つの形があります。発話のない音声に定型フレーズを書くものと、1つの失敗したウィンドウが次のウィンドウに影響して起きるループです。音声区間検出フィルターは前者を、condition_on_previous_textをオフにすることは後者を防ぎますが、標準のツールでは、どちらもデフォルトで有効になっていません。どちらの対策も、ループに比べれば代償は小さいものです。大きいモデルが安全なモデルとは限りません。そして、何を変更しても、ほかの誰かが見る前に、仕上がった字幕ファイルを自分で読んでください。形式そのもののチェック方法は、SRTファイルの作成ガイドにあります。