whisper の幻覚で、動画終盤のアドバイスが落ちていた

whisperModel
large-v3-turbo
whisperOption
-mc 0
videoMinutes
13
transcribeSeconds
105

13 分の動画の文字起こしの末尾が「アイスクリームを作ります。」という同じ行の 14 回の繰り返しになっていて、そこに入っていたワンポイントアドバイスが丸ごと落ちていました。記事はその文字起こしから書いていたので、一番いい話が最初から存在しないことになっていました。

何が起きていたか

whisper.cpp は音声を 30 秒の窓に切ってデコードします。既定の --max-context -1(短縮形は -mc)は、直前の窓のテキストを次の窓のプロンプトとして渡します。文脈が繋がる代わりに、一度おかしな出力が出るとデコーダが自分の出力に引きずられて、同じ行を出し続けます。

既定では直前の窓の出力が次の窓に渡って末尾が幻覚になり、-mc 0 では窓ごとに独立してデコードされる様子の比較図 既定(--max-context -1) 窓 1 0–30 秒 窓 2 30–60 秒 最後の窓 動画の末尾 … 直前の窓の出力を次の窓へ渡す 「アイスクリームを作ります。」 同じ行が 14 回、アドバイスは消失 -mc 0 を付けた場合 窓 1 0–30 秒 窓 2 30–60 秒 最後の窓 動画の末尾 窓ごとに独立してデコードする 幻覚なし 終盤のアドバイスが全部残る
渡す文脈を切るだけで、末尾の繰り返しは消えます。

3 つの設定を比べた

この値を 0 にして持ち越しを切り、窓ごとに独立してデコードさせるのが -mc 0 です。同じ動画、同じモデル(large-v3-turbo)で 3 通りを比べました。

設定 行数 結果
既定(--max-context -1) 167 末尾が幻覚、終盤のアドバイスが消失
--vad 53 幻覚は消えるが、屋外の雑音下で発話まで削る
-mc 0 140 幻覚なし、終盤のアドバイスが全部残る

-mc 0 の所要時間は 1 分 45 秒でした。transcribe.ts にこの指定を入れて、キャッシュしてあった文字起こしを取り直したものに差し替えています。

--vad でも幻覚は止まりますが、行数が 3 分の 1 以下になりました。屋外の雑音下では発話まで削られます。

気づけなかった理由

文字起こしは日本語として読めてしまうので、欠けている箇所は目に入りません。実際この動画からは、ラリー中の独り言を記事の結論に据えていました。意味が映像側にしかない一言で、前後も同じ行の繰り返しです。意味を補おうとするたびに、言っていないことを書いていました。

同じ文字起こしから、存在しない言葉も作っていました。

書いていた語 実際
出し球 文字起こしは「1球目玉を出します」で、誰も言っていない語
ローブ ロブ
反面 半面
スピードアップ スピードショット(競技で使う語)

直したこと

文体のブレだけは機械判定にできました。scripts/lib/tone.ts が句点で終わる文を見て常体の語尾を落とし、npm run validate から呼びます。体言止めは許容して、「」『』“” の中、> の引用行、コードブロック、HTML、リンクの URL は検査から外します。入れた時点で、Instagram の下書き 1 件と X の下書きに常体が残っていたのが見つかりました。

残りは文章の規則として書き足すところまでです。プレー中の独り言を結論にしない、元の発言に無い言葉を作らない、専門語は競技で使う語を使う、手順を渡すときは目的まで書く。

同じ週に変えたところ

配色をアースカラーにしました。コントラスト比は本文 13.23 / 13.89、補助文字 5.11 / 6.49、アクセント 5.39 / 8.66 で、ライトとダークのどちらも 4.5 以上です。CSS だけ変えると OGP が旧配色のまま残るので、scripts/brand/build.ts の定数も一緒に直しています。

main へ直接 push する運用に変えました。push がそのままリリースになり、公開サイトと Inbox の両方がデプロイされて未適用の D1 マイグレーションも本番に当たります。inbox/ に触れたときだけ push 前に確認します。