YouTube の字幕をアノテーションする方法
yt-dlp で字幕を取得して Potato でアノテーションする手順。自動生成字幕にできること・できないこと、動画の扱い方、他人の作った素材を再配布する前に確認すべきこと。
yt-dlp で字幕を取得し、Potato をそのファイルに向けてください。まず知っておくべきこと。自動生成字幕には句読点も話者ラベルもなく、改行位置にも意味がないため、何を無理なくアノテーションできるかが制約されます。 Potato は yt-dlp が出力するすべての字幕形式を読めます。一覧は文字起こし形式にあります。
動画プラットフォームは、自然な話し言葉の大きくて扱いやすい供給源です。字幕はその話し言葉をテキストにし、テキストは軽く、持ち運びやすく、アノテーションもしやすい。問題は、すべての字幕が同じ種類のものではないことで、同じものとして扱うところで字幕ベースのプロジェクトはたいてい破綻します。
字幕はどう取得するか
yt-dlp は素材の有無にかかわらず字幕を取得できます。
# Human-written subtitles, if the uploader provided any
yt-dlp --write-subs --sub-langs en --skip-download <URL>
# Auto-generated captions
yt-dlp --write-auto-subs --sub-langs en --sub-format vtt --skip-download <URL>Potato は vtt、srt、json3、ttml、および srv1・srv2・srv3 の各種を読めるので、どの --sub-format を選んでも解析できます。何を残したいかで選んでください。json3 は単語単位の時間を保持し、WebVTT と SubRip は人が読んで手で直すのが楽です。
人手字幕と自動生成字幕はどう違うか
テキストエディタで見ると似ていますが、アノテーション素材としてはまったく違う振る舞いをします。
| 人手字幕 | 自動生成字幕 | |
|---|---|---|
| 句読点 | あり | なし、または信頼できない |
| 文の切れ目 | 意味がある | 恣意的 |
| 話者 | ときどき、<v Name> タグで | まったくない |
| 単語単位の時間 | なし | あり(json3) |
| 逐語性 | 読みやすさのために圧縮されがち | 実際の発話に近い |
どちらが優れているという話ではありません。人手字幕は読みやすい代わりに読みやすさのために編集されており、言いよどみが削られ、繰り返しが圧縮され、ときには文ごと書き直されています。自動生成字幕は逐語に近く、人が実際にどう話すかを扱うならこちらが欲しいものですが、句読点のない流れとして届きます。
自動生成字幕には何をアノテーションできるか
自動生成字幕の上に文単位のアノテーション設計を載せてはいけません。 キューの境界は文が終わったところではなく、字幕ウィンドウが埋まったところに落ちます。自動生成字幕におけるキューは表示上の産物であって、言語単位ではありません。アノテーターに「文ごとに」評価させると、各自が黙って別々の文境界を選び、一致度の数字は測りたかったものではなくその差を測ることになります。
現実的な選択肢は二つです。
- キューをそのままアノテーションする。 各キューに当てはまるラベルを付け、単位が恣意的でも一貫してはいることを受け入れます。話題、ある現象の有無、断片で判断できるものなら問題ありません。
- 上流で切り直す。 句読点復元モデルか、句読点を打つ音声認識を通してから時間を振り直します。手間は増えますが、本物の単位が得られます。
人手字幕であれば、この問題はほぼ消えます。境界は読みながら人が置いたものなので、たいてい節の切れ目を尊重しています。
動画はどうするか
字幕は小さく、再配布も容易です。動画はたいていそうではありません。選択肢は三つあります。
# Media hosted somewhere your annotators can reach
potato transcripts ./captions --media-url-prefix https://cdn.example.org/video -o data/talks.json
# Media downloaded locally as audio only
yt-dlp -f 'ba' -x --audio-format mp3 -o './audio/%(id)s.%(ext)s' <URL>
potato transcripts ./captions --media-dir ./audio -o data/talks.json三つ目は素材をまったく使わないことです。素材のない文字起こしでも問題なくアノテーションできます。アノテーターは聴く代わりに読みます。それが方針なら指示書にそう書いてください。書かないと、プレーヤーがないのは不具合だと思われて報告されます。
Warning: 自分が作ったものでない素材を再配布する前に、権利を確認してください。自分の研究のために字幕や素材をダウンロードすることと、その素材をアノテーターの集団に配信することは別の問題であり、プラットフォームの利用規約、著作権、所属機関の規則がいずれも関わります。パイプラインを組んだ後ではなく、組む前に片づけてください。
タスクはどう組むか
変換ツールは、正規化済みの文字起こしを含むデータファイルを書き出します。
potato transcripts ./captions --media-dir ./audio -o data/talks.json{
"id": "talk_01",
"conversation": {
"audio": "audio/talk_01.mp3",
"turns": [
{"turn_id": "t0", "speaker": null, "start": 0.0, "end": 4.2,
"text": "so the thing about caption windows is"}
]
}
}自動生成字幕から得られるのは speaker: null です。こうしたターンは選択メニュー付きの Unassigned として表示されるので、アノテーターは聴きながら話者を割り当てられます。
自動生成字幕には話者情報がないため、アノテーターが選ぶまで各キューは Unassigned のまま
annotation_task_name: "Talk Annotation"
task_dir: .
data_files:
- data/talks.json
item_properties:
id_key: id
text_key: conversation
instance_display:
fields:
- key: conversation
type: audio_dialogue
label: "Captions"
span_target: true
display_options:
show_timestamps: true
allow_speaker_assignment: auto
annotation_schemes:
- annotation_type: radio
name: cue_topic
description: "What is this caption window about?"
labels: [setup, argument, example, aside]
turn_level: true
turn_binding:
field: conversationturn_level: true は質問を動画全体ではなく各キューに紐づけます。キューしか手元にないときは、これが正しい単位です。実行するには次のようにします。
python potato/flask_server.py start config.yaml -p 8000変換ツールをまったく使わず、データファイルからディスク上の字幕ファイルを直接指すこともできます。残りの流れは同じなので、Whisper の文字起こしをアノテーションする方法を参照してください。
関連文書
- 文字起こし形式、対応するすべての字幕・文字起こし形式
- Whisper の文字起こしをアノテーションする方法、音声認識出力版のこのワークフロー
- Transcript Format Ingestion、六つの形式を並べた実行可能なショーケース
- 動画アノテーション、字幕ではなく動画そのものをアノテーションする場合
- アノテーション用データ形式の設計
- Timed Text Markup Language、Potato も読める放送用字幕の標準