GEMINI LABEN
OMNI — gemini-omni-flash-preview は9月30日で廃止されます。残り19日です。移行を扱う記事を書くなら、実質これが最後の機会になりますMIGRATE — 移行先は8月27日に GA となった gemini-omni-1.1-flash。extend で末尾を延長でき、2枚の画像を渡せばそのあいだを補間しますRESOLUTION — video_config の resolution は 360p / 720p / 1080p / 4k から選べます。1080p と 4K はアップスケーリングによる生成ですSAMPLING — temperature と top_p、top_k は非推奨として整理されました。揺れを抑えたいなら、プロンプトと構造化出力の側で決めることになりますTRANSCRIBE — gemini-3.5-transcribe は85言語以上の発話単位の言語判定、話者分離、単語単位のタイムスタンプ。カスタム語彙は最大1,000語までですFLASH — Gemini 3.8 Flash は9月2日に GA。9月3日には Lyria 3.5 がパブリックプレビューとなり、テキストと画像から楽曲を生成しますOMNI — gemini-omni-flash-preview は9月30日で廃止されます。残り19日です。移行を扱う記事を書くなら、実質これが最後の機会になりますMIGRATE — 移行先は8月27日に GA となった gemini-omni-1.1-flash。extend で末尾を延長でき、2枚の画像を渡せばそのあいだを補間しますRESOLUTION — video_config の resolution は 360p / 720p / 1080p / 4k から選べます。1080p と 4K はアップスケーリングによる生成ですSAMPLING — temperature と top_p、top_k は非推奨として整理されました。揺れを抑えたいなら、プロンプトと構造化出力の側で決めることになりますTRANSCRIBE — gemini-3.5-transcribe は85言語以上の発話単位の言語判定、話者分離、単語単位のタイムスタンプ。カスタム語彙は最大1,000語までですFLASH — Gemini 3.8 Flash は9月2日に GA。9月3日には Lyria 3.5 がパブリックプレビューとなり、テキストと画像から楽曲を生成します
記事一覧/API / SDK
API / SDK/2026-09-11中級

文字起こしの固有名詞が直らないとき、カスタム語彙に何を入れて何を諦めるか

gemini-3.5-transcribe の custom_vocabulary は最大1,000語ですが、枠を埋めることが目的ではありません。誤認ログの正解と出力の対から、枠を渡す語だけを選び直す60行のスクリプトと、その判断基準をまとめます。

Gemini API237gemini-3.5-transcribe2文字起こし2カスタム語彙固有名詞個人開発118

作業の合間に録った音声メモを文字起こしにかけて、最初に目に入ったのが「歌川ひろしげ」でした。

浮世絵の壁紙アプリで配信する作品を選びながら、思いついたことを口で残しております。絵師の名前と作品名がそのまま作業の単位になりますので、そこが崩れると、あとで読み返しても何の話だったのか辿れません。

custom_vocabulary という指定があることは知っておりました。最大1,000語まで積めると書かれています。ですから私は、思いつく限りの絵師名と作品名を並べたリストを作って、そのまま渡しました。

結果は芳しくありませんでした。直った語もあれば、直らない語もあり、なぜそうなるのかが分からないままでした。

一覧を先に作ったところで、枠の使い道を間違えました

いま振り返ると、順序が逆だったのです。

私は「この音源に出てきそうな固有名詞」を思い出しながらリストを作りました。頭の中の在庫を書き出す作業です。けれども文字起こしが必要としているのは、在庫ではありません。実際に落ちた語です。

上限が1,000語と書かれていると、埋められるだけ埋めたくなります。ただ、同じサイトの別記事でも触れましたが、実務で守るべき予算は1,000ではなく、その十分の一くらいの感覚です(gemini-3.5-transcribe の smart モードでは、話者ラベルも単語タイムスタンプも返ってきませんに、モードごとの組み合わせと併せて書いております)。上限に当たる前に、効かない語で薄まるほうが先に来ます。

語彙は思い出して書くものではなく、落ちた語を拾って書くものです。 この一文に辿り着くまで、私は同じリストを三度書き直しました。

誤認ログは「正解と出力の対」だけで足ります

拾うためには、落ちたことが分かる記録が要ります。といっても大掛かりなものは不要でした。

必要なのは二つだけです。その音源で本当は何と言っていたか(ref)と、文字起こしが何と返したか(hyp)。この対を、短い区間ごとに並べておきます。

{
  "terms": ["歌川広重", "喜多川歌麿", "東洲斎写楽", "葛飾北斎", "名所江戸百景", "浮世絵", "壁紙"],
  "pairs": [
    {"ref": "歌川広重の名所江戸百景から三枚選びました",
     "hyp": "歌川ひろしげの名所江戸百景から三枚選びました"},
    {"ref": "喜多川歌麿の美人画を壁紙にします",
     "hyp": "北川うたまろの美人画を壁紙にします"},
    {"ref": "東洲斎写楽は役者絵だけで十ヶ月です",
     "hyp": "東洲斎写楽は役者絵だけで十ヶ月です"},
    {"ref": "葛飾北斎の富嶽三十六景は別の回にします",
     "hyp": "葛飾北斎の富嶽三十六景は別の回にします"},
    {"ref": "歌川広重の雨の表現を残したいので浮世絵の粒を潰しません",
     "hyp": "歌川ひろ重の雨の表現を残したいので浮世絵の粒を潰しません"},
    {"ref": "喜多川歌麿の線は細いので拡大時に壁紙が眠くなります",
     "hyp": "喜多川うたまろの線は細いので拡大時に壁紙が眠くなります"},
    {"ref": "名所江戸百景の橋の回を先に出します",
     "hyp": "名所江戸百景の橋の回を先に出します"}
  ]
}

ref を作る手間を心配される方がいらっしゃるかもしれません。私も最初は身構えました。ただ、全文を書き起こす必要はないのです。固有名詞を含む区間だけで十分ですし、一本の音源から五つも取れれば、もう傾向は見えてきます。

terms のほうは、最初に作った在庫のリストをそのまま流用できます。捨てずに済みます——ただし、渡す先が変わります。渡す先は API ではなく、次のスクリプトです。

候補を並べ替える60行

やっていることは単純です。terms の各語について、ref に何回出たか、hyp に何回残ったかを数えて、差を取ります。差が出た語だけが候補になります。

import json, sys, unicodedata
from collections import defaultdict
 
BUDGET = 100  # 実際に積む上限(1,000 は API 側の上限です)
 
def norm(s):
    return unicodedata.normalize("NFKC", s).strip()
 
def build(pairs, terms, budget=BUDGET):
    seen = defaultdict(int)   # 音源に登場した回数
    miss = defaultdict(int)   # 認識結果に残らなかった回数
    for ref, hyp in pairs:
        r, h = norm(ref), norm(hyp)
        for t in terms:
            n = r.count(norm(t))
            if n == 0:
                continue
            seen[t] += n
            got = h.count(norm(t))
            if got < n:
                miss[t] += n - got
 
    rows = []
    for t in terms:
        if seen[t] == 0:
            rows.append((t, 0, 0, None, "音源に出ていない"))
            continue
        rate = miss[t] / seen[t]
        if miss[t] == 0:
            rows.append((t, seen[t], 0, 0.0, "そのまま通る"))
        else:
            rows.append((t, seen[t], miss[t], rate, "候補"))
 
    cand = [r for r in rows if r[4] == "候補"]
    cand.sort(key=lambda r: (-r[2], -r[3], r[0]))
    picked = [r[0] for r in cand[:budget]]
    dropped = [r for r in rows if r[4] != "候補"]
    return picked, cand, dropped
 
if __name__ == "__main__":
    data = json.load(open(sys.argv[1], encoding="utf-8"))
    picked, cand, dropped = build(
        [(d["ref"], d["hyp"]) for d in data["pairs"]], data["terms"]
    )
    print(f"候補 {len(cand)} 語 / 採用 {len(picked)} 語 / 除外 {len(dropped)}\n")
    print("順位  誤認  出現  誤認率  語")
    for i, (t, s, m, rate, _) in enumerate(cand, 1):
        print(f"{i:>3}   {m:>3}   {s:>3}   {rate:>5.2f}  {t}")
    print("\n-- 枠を渡さなかった語 --")
    for t, s, m, rate, why in dropped:
        print(f"  {t}{why})")
    print("\ncustom_vocabulary =", json.dumps(picked, ensure_ascii=False))

norm() で NFKC 正規化をかけているのは、同じ語が半角と全角で別物として数えられるのを防ぐためです。ここを省くと、数字やアルファベットを含む語で数が合わなくなります。

並べ替えの鍵は sort の行です。誤認の回数を第一の基準にして、同数なら誤認で割ります。率だけで並べると、一度しか出ていない語が上位を占めてしまうのです。何度も落ちる語のほうが、直したときの効き目が大きくなります。

上の入力をそのまま渡すと、こう出ます。

候補 2 語 / 採用 2 語 / 除外 5 語

順位  誤認  出現  誤認率  語
  1     2     2    1.00  喜多川歌麿
  2     2     2    1.00  歌川広重

-- 枠を渡さなかった語 --
  東洲斎写楽(そのまま通る)
  葛飾北斎(そのまま通る)
  名所江戸百景(そのまま通る)
  浮世絵(そのまま通る)
  壁紙(そのまま通る)

custom_vocabulary = ["喜多川歌麿", "歌川広重"]

七語を並べて、枠を渡す価値があったのは二語でした。

選んだ語を、どこに置くか

スクリプトが返すのは値であって、設定そのものではありません。custom_vocabularytranscription_config の直下に置きます。language_codes と同じ階層で、話者や単語タイムスタンプの設定が入る mode オブジェクトの中ではありません。私はここを何度か取り違えました。

transcription_config = {
    "language_codes": ["ja-JP"],
    "custom_vocabulary": picked,          # スクリプトの出力をそのまま
    "mode": {"type": "verbatim",
             "timestamp_granularities": ["word"]},
}

言語が分かっているなら、語彙と併せて language_codes も指定しておくほうが素直です。語のリストだけでは手掛かりとして弱く、言語を固定しておくと、名前が出てくる前の段階で候補が絞られます。一文のなかで二言語を行き来する音源では、私は固定するのをやめました。自信のある誤りを返されるより、少し揺れる結果のほうが扱いやすかったからです。

ひとつ、含みを持たせずに書いておきたいことがあります。語数と精度の関係は、私は測っておりません。測っていない数字を書くわけにはまいりません。言えるのはログが見せてくれたことだけで、私が積んでいた語の多くは、そもそも一度も落ちていなかったということです。働いていない語に枠を渡していたわけです。

枠を渡さない語を、三つに分けます

除外された語は、同じ理由で除外されたわけではありません。ここを区別しておくと、次に見直すときに迷わなくなります。

分類状態扱い
そのまま通る語ref にも hyp にも同じ回数だけ出ている枠を渡しません。「葛飾北斎」のように広く知られた語は、指定しなくても通ることが多いのです
音源に出ていない語ref に一度も現れない今回は判断材料がありません。捨てずに候補として置いておき、その語が出た音源で測り直します
崩れ方が毎回違う語誤認しているが、hyp 側の形が揃わない語彙で押すより、録り方の側を見直すほうが早いことがあります

三つめは表だけでは分かりにくいので、補足します。「歌川広重」は「歌川ひろしげ」と「歌川ひろ重」の二通りに崩れておりました。前半の「歌川」は毎回残っています。つまり、モデルが迷っているのは後半の読みだけです。

こういう語は、語彙に入れる価値がはっきりしています。一方で、語全体が毎回違う形に散る語もあります。以前の私は、この種の語をリストに足しては消し、あと少しで揃うはずだと思い込んでおりました。そのときは、話す速さや語と語のあいだの取り方が原因であることが少なくありません。私は一度、早口で続けて読み上げていた部分をゆっくり言い直すだけで、語彙を触らずに揃ったことがありました。

残った枠は、次の録りのために空けておきます

百という予算に対して、最初の一本から採れる語はほんの少しです。埋まらないことを不安に感じるかもしれませんが、空いていて構わないのです。

私は、音源を録るたびに ref と hyp の対を数行だけ足して、同じスクリプトを回し直しています。落ちる語は録るたびに少しずつ入れ替わりますし、新しく扱い始めた絵師の名前は、当然ながら最初の一本で落ちます。語彙リストは作品ではなく、台帳です——完成させるものではなく、更新し続けるものだと考えるようにしました。

もうひとつ、運用で決めていることがあります。一度採用した語も、三本続けて誤認が出なくなったら外します。モデルの側が更新されて通るようになることがあるからです。枠を占めたままにすると、そのぶん新しい語に回せなくなります。外した記録は足した記録と同じ場所に残しておきます。あとでその語がまた落ち始めたときに、一度は積んでいたこと、そしてなぜ外したのかを辿れるからです。

長いものから始めなくて構いません。手元にある10分ほどの音源を一本選んで、固有名詞を含む区間を五行だけ書き起こし、上のスクリプトに通してみてください。

それだけで、いま自分が枠を渡すべき語が何語あるのかが見えます。私の場合は二語でした。思いついた語を全部並べていたころより、はるかに手応えがあります。

お読みいただきありがとうございました。同じように音声メモから作業を組み立てている方に、少しでも重なるところがあれば幸いです。

シェア

お読みいただきありがとうございます

Gemini Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥2,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

API / SDK2026-08-29
gemini-3.5-transcribe の smart モードでは、話者ラベルも単語タイムスタンプも返ってきません
gemini-3.5-transcribe の smart モードは diarization_mode とも timestamp_granularities とも併用できません。mode が文字列とオブジェクトの両方を取る点と、機能を足すと音声の上限が半分になる点を整理します。
API / SDK2026-09-08
値上げの日をまたぐ費用の見積もりを、はじめから二段で書く
Gemini Flash の導入価格は2026年12月31日で終わり、2027年1月1日から標準価格に切り替わります。世代を据え置いても避けられないこの日を、はじめての費用見積もりでどう扱うか。動くコードとともに書き残します。
API / SDK2026-09-07
Lyria 3.5 が来た日に、音源フォルダの置き方を変えました
Lyria 3.5 のフルレングス生成が来たとき、生成した音と配信する音が同じフォルダに並んでいました。名前ではなくハッシュで線を引く、四十数行の台帳ゲートの作り方をお伝えします。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます