ホーム › 技術ブログ › 14
2026-10-17 #14

夜のうちにローカルLLMが字幕と突き合わせ、朝に人が直す。「博士」と「江戸川くん」の体制

テロップに正解が映っているのに、文字起こしは間違ったまま。それを1本ずつ人が探すのはやめて、Mac mini のローカルLLMに夜通し挙げさせることにした。役割の分け方と、外した測り方も残して書く。

ローカルLLMGemmaOCRlaunchd運用

こういうことで困っていませんか

音声認識のあと、LLMに文脈で直させても、固有名詞はなかなか直りません。 「学名」が「楽名」、「ハゲワシ」が「ハケワシ」のまま残ります。 ところが番組の画面を見ると、テロップにはたいてい正しい表記が出ています。 答えは画面にあるのに、文字起こしだけを見ているから届かない。

前の記事(焼き込み字幕をOCRで答え合わせする)では、怪しい箇所だけを人が選んで画面を見ていました。 それを全行に広げ、夜のあいだに機械に回させることにしました。

体制

呼び名は私が『名探偵コナン』から付けました。役割で分けています。

呼び名実体すること
江戸川くんGemma 4 31B(LM Studio・MLX 4bit)画面のテロップを読み、文字起こしと食い違う行を修正案つきで挙げる。直さない
博士Claude Code のセッション+launchd動画を取り、江戸川くんを15分の区切りごとに回し、終わったら知らせる
灰原Claude Code のセッション挙がった行を画面で1行ずつ確かめ、直して公開する

Gemma が Mac mini に載っているのは 23:45〜6:45 だけです。昼は同じ Mac で別のモデル(TypeVoice)が動くので、メモリを譲っています。

環境

Mac mini (M6 / メモリ32GB)
LM Studio 0.4.25 / mlx 1.11.0
google/gemma-4-31b  (MLX 4bit, 18.44GB)
http://localhost:1234/v1  (OpenAI互換)

江戸川くんがしていること

1本を15分ずつに区切り、区切りごとに次の5つを行います(process_job.py)。

  1. 1秒ごとに画面を撮る
  2. 画面の文字を Apple Vision で読む
  3. テロップが切り替わった秒の画面だけを Gemma に見せて、テロップを読ませる
  4. テロップと文字起こしが食い違う行を、修正案つきで挙げさせる
  5. テロップにあるのに、文字起こしのどこにも出てこない語を挙げさせる

Gemma に画面を見せるのは、Apple Vision がこの番組の字体を読み違えるからです。 テロップの「アナログゲーム」を「アナロダゲーム」、「技術ウミガメのスープ」を「技術ウミガメのスニプ」、「ロウソク」を「ロウック」と読みました。Gemma に同じ画面を見せると正しく読めました。1枚3〜4秒かかります。

どこまで拾えるようになったか

答え合わせには、私が画面から直した1本(162行)を使いました。 そのうち、テロップに正解が映っている直しは114行です。取り込み直後の文字起こしに対して、何行拾えるかを測りました。

版                                                   拾えた  直していない行を挙げた
OCR(補正なし) → Gemma が突き合わせ                      34      26
OCR に言語の補正                                       46      33
同じ直しを同じ回のほかの行へ広げる                        59      30
挙げた行の画面を Gemma が読み直す                        57      23
テロップを Gemma が画面から読む(切り替わった秒だけ)        82       8
テロップが英字ならカタカナの行を挙げる                     90      10

効いたのは、文字の読み取りを Gemma に任せたところでした。最後の版で、映っている正解の約8割を拾い、挙げた100行の9割が本物です。 「広げる」は、この版では当たりが1行増えて外れが4行増えたので外しました。

挙がったものは、そのまま入れない

江戸川くんの修正案は、半分は当たっていても、半分は作文です。だから灰原は、挙がった行を6行ずつ1枚の画像にまとめ、画面を見て決めます。

$ python3 .rv_tools/sheets.py WBecfAcYiO8
行 316 画像なし [] シート 53
716 14:00 | 楽名ですね | T: 学名 |
1598 33:18 | 死体に群がるハケワシが | T: ハゲワシ |
2351 48:48 | 入寺中中治療室みたいなところに入って | T: 出産されたときに / 死にかけた子どもを集めた |

1行目と2行目は、画面のテロップどおりに直します。3行目は、テロップが発言の要約なので、テロップの文に書き換えると「誰も言っていない文」になります。こういう行は直さず、要確認として管理画面に残します。

実際に、前の工程のLLMが、誰も言っていない「ダイアス(dais)」を9か所に書き足していたことがありました。テロップにも音声にもない語です。機械の案を入れる前に画面を見るのは、これを防ぐためです。

結果

見直しを終えた回         37本
江戸川くんが挙げた行   8,725行
画面で確かめて直した行 6,814行  (78%)

直さなかった行には、テロップが要約だった行、言い間違いをそのまま残すべき行、画面に字幕がなく確定できなかった行などがあります(内訳は数えていません)。確定できなかった行は、要確認として管理画面に色を付けて残しています。

ハマったところ

空っぽの文字起こしで精度を測っていた

直した行の文は text_corrected に入れています。直していない行は空です。それに気づかず text_corrected だけを読んで Gemma に渡していたので、ある回では1,265行中1,080行が空のまま測っていました。それまでの結果は全部捨てました。

-- 誤り
SELECT text_corrected FROM segments WHERE video_id = ?
-- 正しい
SELECT coalesce(nullif(text_corrected, ''), text) FROM segments WHERE video_id = ?

考える機能を入れたままにすると、答えが返ってこない

どのモデルも、初期では考える機能がオンです。オンのまま回すと1本46分かかり、30行ずつ42回に分けて頼んだうちの40回で、考えている途中で上限に達して答えが返ってきませんでした(Qwen3.6)。この用途では reasoning_effort: "none" で切っています。

Gemma は答えのJSONを2回書く

1回書いたあとに考え直して、もう1つ書きます。\{.*\} で取り出すと、最初の { から最後の } までをつかんで壊れていました。読めた {"errors": ...} のうち最後のものを採るように直し、読めなかった8つの答えで試したら、8つとも読めました。

止められたとき、手元を探さずに人に頼んだ

照合用の動画を取り直そうとして権限の判定に止められ、外出中の私がスマホで承認を何度も返す羽目になったことがあります。動画は取り込みのときにもう手元に残っていました。いまは取り込みのときに照合用の置き場所へ写し、夜の優先枠に自動で入れています。

まとめ

  • 答えが画面にあるなら、文字起こしの中で考えさせず、画面を読ませる
  • ローカルLLMは「挙げる」ところまで。直すのは画面を見た人
  • 役割を分けると、どこで止まったかがすぐ分かる。博士が止まれば照合が来ないし、灰原が止まれば挙がった行が溜まる
  • 精度を測る前に、測っている入力が空でないかを確かめる

← 番組の言葉を年表にする。正規表現とSQLを組み合わせ… 目次へ

ゆる言語学ラジオ・ゆるコンピュータ科学ラジオ 公認/株式会社pedantic の了承と、運営者・安東暁史との合意に基づく協力体制で運営しています。ドメイン yururadio.jp は株式会社pedanticが保有。開発・保守: 安東暁史·このサイトについて·お問い合わせ