動画制作

Whisperのハルシネーション:無音で同じ言葉が続く原因と除き方|206分の文字起こしで実際に外した方法【2026年】

編集責任者佐藤翔太株式会社UrakaTA 代表取締役記事はAIが社内の記録から下書きし、別のAIが公式情報で照合、公開後に編集責任者が確認しています。
音の波形の静かな部分に浮かぶ小さな吹き出しを、編集者が大きな虫眼鏡で見ながらピンセットで取り除いている様子
イラスト:AIで作成

この記事の結論

Whisperが無音の区間で同じ言葉を並べるのはハルシネーション(幻聴)です。既定の設定では、無音らしくてもモデルが自信を持って書いた区間は残ります。文字起こしの後に no_speech_prob 0.6以上の区間と決まった言葉の連打を外し、外した区間は映像と突き合わせると安全です。

目次7項目
  1. Whisperのハルシネーション:無音の区間で同じ言葉が続く症状
  2. 幻聴を外す対処手順:no_speech_prob 0.6と定型句で後から除く
  3. 裏方で実際に起きた例:206分の素材で出た「I can't」の連打
  4. 再発防止:文字起こしとサムネイルを並べてカットを決める
  5. 業務で使うときの注意:字幕や議事録に幻聴を残さない
  6. まとめ
  7. よくある質問

【無料・メールだけ】Claude Code 2daysセミナー(第二の脳構築講座)の次回開催の案内を受け取る →

Whisperのハルシネーションで無音の区間に同じ言葉を繰り返すとき、僕は設定を変えた記録の無いまま文字起こしを済ませました。そのあとで no_speech_prob が0.6以上の区間と決まった言葉の連打を外しています。旅行で撮った206分の動画素材を文字起こししたとき、僕の会社でも「ん ん ん」が続けて出ています。

原因はWhisperの仕組みにあり、既定の設定のままだと無音の区間が残ることがあります。症状の見分け方、後から外す手順、公式にある設定の選択肢を順に書きます。

Whisperのハルシネーション:無音の区間で同じ言葉が続く症状

症状は、誰も話していない区間に、音声に無い言葉や同じ言葉の連打が入るものです。話し声のある区間は正しく書けているので、全文を流し読みすると見落とします。

ここでいうハルシネーションとは、音声に無い言葉を、モデルがもっともらしく書き出してしまう現象です。音声認識では「幻聴」と呼ぶと分かりやすいと思います。

僕の会社で実際に出たのは、次の3つの形でした。英語の言葉、日本語の相づち、意味の通らない単語が、どれも続けて出ています。

I can't I can't…/ん ん ん/櫻かい

どれも、声のない区間や環境音だけの区間で起きました。見分ける目安を、原因と対処の早見表にします。

見え方よくある原因まずやること
無音の区間に短い言葉が並ぶ無音らしい区間を、モデルが自信を持って書いたno_speech_prob を見て0.6以上なら外す
同じ言葉や同じ文が何度も続く前の区間の文字起こしを次の区間へ引き継いだ決まった言葉の連打をパターンで外す
話していない場面の字幕に意味不明な単語環境音を言葉として読んだ映像のサムネイルと突き合わせる

僕なら、まず1行目の no_speech_prob から見ます。数字で機械的に外せるので、人の目で確かめる範囲を最初に狭められるためです。

既定では、無音らしくても「自信がある」区間は消えない

既定の設定では、無音の確率が高いだけでは区間は消えません。公式のコードは、2つの条件がそろったときだけ、その区間を無音として飛ばしています。

no_speech_prob とは、その区間に話し声が無い確率として、モデルが区間ごとに出す値です。0から1の間で、1に近いほど「声が無い」と見ています。

公式の transcribe.py では、no_speech_threshold の既定値が0.6、logprob_threshold の既定値が-1.0です。

飛ばすのは、no_speech_prob が0.6を超え、かつ平均の対数確率が-1.0以下のときだけです。

コードには、平均の対数確率が十分に高ければ no_speech_prob が大きくても飛ばさない、という処理が入っています。無音らしくても、モデルが自信を持って書いた区間は残るわけです。

幻聴が文字起こしに残るのはこの形だと、僕は読んでいます。後で書く僕の会社の外し方は、0.6という同じ数字を、自信の条件なしで当てた形になります。

同じ言葉が続くのは、前の区間の結果を次へ渡すから

同じ言葉が何度も続くのは、前の区間の文字起こしを次の区間の手がかりにしているためです。この動きを決めているのが condition_on_previous_text で、既定値は True です。

公式の説明では、これを切ると区間どうしの文のつながりが崩れることがある一方で、くり返しの輪に入りにくくなるとされています。つまり、くり返しの起きやすさは既定の動きと関係しています。

くり返しを見張る仕組みもあります。compression_ratio_threshold(既定値2.4)は、書き出した文を gzip で縮めたときの比率が2.4を超えたら、失敗として扱う値です。

失敗と判定された区間は、temperature を0.0から1.0まで0.2刻みで上げて書き直します。同じ言葉が多い文はよく縮むので、この比率でくり返しを見つける作りです。

幻聴を外す対処手順:no_speech_prob 0.6と定型句で後から除く

いちばん手早いのは、文字起こしの後で、no_speech_prob が0.6以上の区間と決まった言葉の連打を外す方法です。僕の会社ではこの方法で対応しました。

  1. 動画から音声を16kHzのwavに書き出してから、文字起こしにかける
  2. テキストだけでなく、区間の情報(segments)ごと保存する
  3. no_speech_prob が0.6以上の区間を外す
  4. 実際に出た言葉の連打(ん ん ん など)をパターンで外す
  5. 外した区間と残した区間を、映像のサムネイルと突き合わせる

2番目が抜けると、後から外せません。mlx-whisper の公式コードでも、区間ごとの情報に no_speech_prob が入っていることを確かめました。

考え方を示すために、この記事用に短い例を書きました。僕の会社で動かしたスクリプトそのものではないので、語のリストや数字は自分の素材に合わせて変えてください。

import re

PHRASES = ["I can't", "ん"]  # 実際に出た言葉を足していく

def keep(seg):
    if seg["no_speech_prob"] >= 0.6:
        return False
    text = seg["text"].strip()
    for p in PHRASES:
        pattern = "(?:" + re.escape(p) + r"[\s、。…]*)" + "{2,}"
        if re.fullmatch(pattern, text):
            return False
    return True

# result は mlx_whisper.transcribe(...) の戻り値
segments = [s for s in result["segments"] if keep(s)]

5番目は省かないでください。外す処理は機械なので、本当に「ん」と相づちを打った区間まで消えることがあります。

公式にある設定の選択肢(僕の会社では未検証)

文字起こしの段階で幻聴を減らす選択肢も、公式のコードにあります。ただし、僕の会社にはどれも既定値から変えて試した記録がありません。下は公式の既定値と働きです。

引数既定値公式のコードでの働き
condition_on_previous_textTrue前の区間の結果を次の手がかりにする。False でくり返しの輪に入りにくくなる
no_speech_threshold0.6logprob_threshold と組み合わせて、無音として飛ばすかを決める
logprob_threshold-1.0平均の対数確率がこれより低い区間を失敗として扱う
compression_ratio_threshold2.4gzip で縮めた比率がこれより高い区間を失敗として扱う
hallucination_silence_thresholdNoneword_timestamps が True のときだけ、幻聴らしい区間の前の長い無音を飛ばす

hallucination_silence_threshold には注意が要ります。コマンドの説明では --word_timestamps True が必要とされていますが、付け忘れてもエラーにはならず、黙って使われません。

僕なら、設定を変える前に後から外す方法で様子を見ます。設定は文字起こし全体の結果を変えるので、変えたあとに全文を比べ直す手間がかかるためです。

裏方で実際に起きた例:206分の素材で出た「I can't」の連打

2026年9月7日、僕の会社では旅行で撮った動画素材102本・206分から、長尺の動画の下書きを作っていました。文字起こしの幻聴が出たのは、その解析の工程です。

解析は、ffprobe で素材を一括で調べ、コンタクトシートを作る順番でした。そのあと音声を16kHzのwavにして mlx_whisper で文字起こしし、最後に silencedetect で無音の区間を出しています。

文字起こしは Whisper large-v3-turbo を MLX で動かし、206分を約6分で処理しています。

速さは十分でしたが、文字起こしの全文には I can't I can't…、ん ん ん、櫻かい の連打が混ざっていました。

どれも、無音や環境音の区間で出た幻聴です。社内の記録には、原因を「Whisper の無音・環境音への幻聴」、対処を「no_speech_prob<0.6 +定型句パターンで除外」と残しています。0.6未満の区間を残す、という意味です。

カットの判断は、文字起こしだけで決めず、必ずサムネイルと突き合わせると決めました。何区間を外したかの数は、記録に残していません。

同じ素材には、音声は入っているのに、絵が真っ暗な30分のクリップもありました。文字起こしだけを見ると使えそうに見えるので、絵の確認を先にする決まりにしています。

この素材から下書き全体をどう組んだかは、CapCutの下書きをClaude Codeで作った実録に書きました。この記事では、文字起こしの幻聴だけを扱っています。

再発防止:文字起こしとサムネイルを並べてカットを決める

再発を防ぐいちばんの手は、文字起こしとサムネイルを並べて見る工程を、手順から外さないことです。文字起こしは便利ですが、無音の区間ほど信用できません。

サムネイルは、全カットを1枚に並べたコンタクトシートで先に見ています。ffmpeg の -skip_frame nokey でキーフレームだけを使うと、206分の素材が約4分で済みました。

全フレームを読む作り方だと、1分に4本ほどしか進まず、206分の素材では1時間ほどかかる見込みでした。見る工程が重いと省きたくなるので、速さも再発防止の一部です。

外す言葉のリストは、素材ごとに育てる前提で持っておくのがよいと僕は考えています。英語の「I can't」が日本語の素材に出たように、どの言葉が出るかは事前に読めません。

silencedetect で出した無音の区間と、幻聴を外した区間を重ねて見る使い方もできます。ただ、僕の会社でこの照合をした結果は記録していないので、効果は書けません。

業務で使うときの注意:字幕や議事録に幻聴を残さない

業務で困るのは、幻聴がそのまま字幕や議事録に残り、人の目を通らずに外へ出る場合です。お客さまに渡す文字起こしは、無音の区間を中心に一度は人が読みます。

外す処理をかけても、元の文字起こしは区間の情報ごと残しておきます。本当の短い返事まで外していたと気づいたとき、元に戻せるようにするためです。

翻訳にも使うなら、モデルの選び方に気をつけてください。公式の README では、turbo のモデルは翻訳の作業向けには学習されていないと書かれています。

音声を社外に出したくない場合は、Mac の上で動かす方法があります。導入の手順と、Apple シリコンでしか入らない点はmlx-whisperの確認手順と文字起こしの始め方にまとめています。

この記事の既定値は、2026年11月6日に公式のコードで確かめたものです。版が上がると変わる可能性があるので、使う前に自分の環境の transcribe の説明を見てください。

まとめ

  • Whisperのハルシネーションは、無音や環境音の区間に音声に無い言葉が入る症状で、同じ言葉の連打になりやすい
  • 既定では no_speech_prob が0.6を超えても、モデルに自信があれば区間は消えない
  • 文字起こしの後で、no_speech_prob 0.6以上と決まった言葉の連打を外すと手早い
  • condition_on_previous_text などの設定は公式の選択肢で、僕の会社では未検証
  • 僕なら、まず区間の情報ごと保存し、外した区間をサムネイルと突き合わせる工程から始めます

よくある質問

Whisperのハルシネーションは設定だけで止められますか?

止められるとは言い切れません。公式には condition_on_previous_text などの選択肢がありますが、僕の会社では設定を変えて比べた記録がありません。文字起こしの後に、無音の確率と決まった言葉で外す方法で対応しています。

no_speech_prob はどこで確かめられますか?

transcribe の戻り値の segments に、区間ごとに入っています。OpenAI の whisper と mlx-whisper のどちらのコードにも、この値を区間に入れる処理がありました。テキストだけを書き出さず、区間の情報ごと保存しておくと後から外せます。

hallucination_silence_threshold だけ指定すれば効きますか?

効きません。公式のコードでは、word_timestamps を有効にしたときだけ、この値が使われます。コマンドの説明にも `requires --word_timestamps True` と書かれています。既定値は None で、何もしない状態です。

引用元・参考にした記事

  1. openai/whisper出典GitHub(OpenAI の公式リポジトリ)参照
  2. whisper/transcribe.py出典GitHub(OpenAI の公式リポジトリ)参照
  3. mlx-examples/whisper(README)出典GitHub(ml-explore の公式リポジトリ)参照
  4. mlx_whisper/transcribe.py出典GitHub(ml-explore の公式リポジトリ)参照