Skip to content
教程阅读约需 10 分钟

用 Praat TextGrid 往返完成 ToBI 韵律标注

在 Potato 2.13.1 中以点层标注 ToBI 声调和停顿指数:从你的 Praat TextGrid 开始,导出 Praat 能直接读回的 TextGrid。

Potato Team

手头有录音和 Praat TextGrid 的语音学家和韵律研究者,可以按照本教程在 Potato 2.13.1 中做 ToBI 标注,并以 TextGrid 的形式取回标签。ToBI(Tones and Break Indices)是一套转写语调和韵律短语划分的规范。Silverman et al. (1992) 针对美国英语提出了 ToBI。本页的任务显示录音的波形,波形下方有四个层(tier)。声调层和停顿指数(break index)层存放时间点,词层存放区间,杂项层标记不流利现象和噪声。点层是 Potato 2.13.1 新增的,导入器和 textgrid 导出器都能读写 Praat 点层。多名标注者可以在同一台服务器上标注相同的语句,每名标注者的结果各自导出为一个 TextGrid。

ToBI 的四个层

一份 ToBI 转写把音频、F0 记录和四个层配在一起。Beckman, Hirschberg, and Shattuck-Hufnagel (2005) 列出的最初的主流美国英语系统(MAE_ToBI)的各层如下:

  • 声调(Tones)。 音高重音、短语重音和边界调,各自位于一个时间点。
  • 词(Words)。 正字法转写,并在时间上标出每个词的结尾。
  • 停顿指数(Break indices)。 0 到 4 之间的一个数字,表示每个词之后的音渡(juncture)程度。
  • 杂项(Misc)。 不流利现象、备注和其他事件。

在 Praat TextGrid 中,声调和停顿指数通常是点层(TextTier),词和杂项是区间层。下面配置中的标签清单遵循同一章中的 MAE_ToBI 表格:

标签类型标签
音高重音H*, L*, L+H*, L*+H, H+!H*,以及降阶的 !H*, L+!H*, L*+!H
短语重音L-, H-, !H-(位于每个停顿指数 3 或 4 处)
边界调L%, H%(位于每个停顿指数 4 处),以及地位边缘的起始调 %H
不确定*?, -?, %? 表示是否出现了声调,X*?, X-?, X%? 表示出现的是哪个声调
停顿指数0, 1, 2, 3, 4,用 - 表示不确定(3-, 4-),用 p 表示犹豫(1p, 2p, 3p)

分层视图与缺失的 F0 轨迹

Potato 的 tiered_annotation 方案绘制整段录音的波形、当前窗口的放大波形,以及每个层一行。在点层上,标注者单击即以当前标签放置一个点,拖动即可移动点。要给一个点换标签,标注者先选中这个点,再点击另一个标签按钮;按 Delete 键删除选中的点。在区间层上,标注者单击并拖动。

Potato 2.13.1 的分层视图中没有 F0 轨迹,也没有语谱图。标注者要对照 F0 曲线判断音高重音。请在 Praat 中打开同一个 WAV 文件来查看音高轨迹。每个层也只提供配置中列出的标签,没有输入标签的文本框。因此词来自 TextGrid,而不是由标注者输入。

准备 TextGrid

把每个语句的一个 TextGrid 和一个 WAV 文件放进同一个文件夹,文件名彼此对应,例如 utt01.TextGrid 和 utt01.wav。把各层命名为 tones、words、breaks 和 misc,其中 tones 和 breaks 为点层。强制对齐工具可以生成词层,Montreal Forced Aligner 默认写出长格式的 TextGrid。在 Praat 中用 Insert point tier 和 Insert interval tier 命令给对齐工具的输出补上缺少的三个层。导入前删除对齐工具输出的音素层,因为下面的配置只列出四个 ToBI 层。已有标签的层会作为预标注进入 Potato。因此,另一名标注者完成的初标会以预标注的形式进来。

导入 TextGrid

导入命令把文件夹转换成一个 Potato 项目,每个 TextGrid 对应一个条目:

bash
potato import -i textgrids -o tobi_project

我们的测试文件夹里有三个语句,一个已标注,两个只有词。导入器的报告如下:

text
Imported 3 item(s), 1 question(s), 0 answer(s) from 0 annotator(s), and 3 model suggestion(s) on 3 item(s) from Praat TextGrid files (with their audio) as tiered annotations.
  wrote tobi_project/data/textgrids.jsonl
  wrote tobi_project/media/utt01.wav
  wrote tobi_project/media/utt02.wav
  wrote tobi_project/media/utt03.wav
  wrote tobi_project/config.yaml
  wrote tobi_project/README.md

每个 TextGrid 层都成为名为 tiers 的那个 tiered_annotation 方案中的一个层,每个点层仍是点层。不同取值不超过 30 个的层,会把这些取值作为该层的标签。在我们的文件夹里,声调层只得到已标注语句用到的五个声调,停顿层得到 0、1、3 和 4。不同取值更多的层只得到一个以层名命名的标签,并把每个区间的文本保存在片段的 value 字段中。任何真实语料库的词层都属于这种层。每个导入的点和区间都是预标注。每名标注者把导入的标签当作起点,Potato 只在该标注者保存时才存储这些标签。

在波形上方显示词

词层的每个区间都显示标签 words,词本身保留在 value 字段中。导入器把每个条目的文本设为 TextGrid 的文件名。在 tobi_project 中运行下面的脚本,把这段文本替换为该语句按顺序排列的词:

python
import json
 
path = "data/textgrids.jsonl"
items = [json.loads(line) for line in open(path)]
for item in items:
    words = item["predictions"]["tiers"]["annotations"].get("words", [])
    item["text"] = " ".join(w["value"] for w in sorted(words, key=lambda w: w["start_time"]))
with open(path, "w") as f:
    f.writelines(json.dumps(item) + "\n" for item in items)

之后,Potato 会在第一个语句的各层上方显示“Marianna made the marmalade before the guests arrived at noon”。

配置

下面的配置替换生成的 config.yaml。新配置保留生成的数据和预标注设置,并给每个层配上完整的标签清单:

yaml
annotation_task_name: ToBI labeling
task_dir: .
port: 8000
data_files:
  - data/textgrids.jsonl
item_properties:
  id_key: id
  text_key: text
output_annotation_dir: annotation_output/
user_config:
  allow_all_users: true
  users: []
pre_annotation:
  enabled: true
  field: predictions
  allow_modification: true
annotation_schemes:
  - annotation_type: tiered_annotation
    name: tiers
    description: Label tones and break indices; correct the word boundaries if needed.
    source_field: audio_url
    media_type: audio
    tiers:
      - name: tones
        point: true
        labels: ["H*", "!H*", "L*", "L+H*", "L+!H*", "L*+H", "L*+!H", "H+!H*",
                 "L-", "H-", "!H-", "L-L%", "L-H%", "H-L%", "H-H%", "!H-L%", "%H",
                 "*?", "X*?", "-?", "X-?", "%?", "X%?", "%r"]
      - name: words
        labels: [words]
      - name: breaks
        point: true
        labels: ["0", "1", "2", "3", "4", "3-", "4-", "1p", "2p", "3p"]
      - name: misc
        labels: [disfl, laugh, breath, cough, noise, interrupt]

这份配置做了以下选择:

  • 完整的标签列表。 导入器只列出文件中出现过的标签,而标注者无法放置配置中没有的声调。
  • 合并的边缘调。 MAE_ToBI 让停顿指数 4 处的短语重音和边界调取该停顿的时间。因此一个标为 L-L% 的点同时标记短语重音和边界调。出于同样的原因,单独的 L% 和 H% 没有列入。
  • 层名和顺序。 层名与 TextGrid 的层一致时,预标注会落在正确的行上。导出按配置中的顺序写出各层。
  • 固定的杂项列表。 Potato 的层不接受手动输入的标签。因此杂项层提供六个事件标签,自由文本的备注留在 Praat 中。
  • 声调层上的 %r。 MAE_ToBI 在声调层上标记语调重启(contour restart),在杂项层上标记不流利现象本身。
  • words 是词层唯一的标签。 这个标签与导入器写入的一致。因此词层用于移动词边界,在词层上新画的区间导出时文本为 words。

验证器对照 Potato 自己的键列表检查每个键,对这份配置和导入器生成的配置都输出了 OK — no issues found.:

bash
python -m potato.validate_cli config.yaml --strict

在浏览器中标注

启动命令在 8000 端口上提供这个任务:

bash
potato start config.yaml -p 8000

每名标注者登录后,会在波形上方看到词,并在放置点之前从层菜单中选择一个层。一种可行的顺序是先在每个词的结尾放置停顿指数。接着标音高重音,最后在每个停顿指数 3 或 4 处标边缘调。如果每个语句需要两名标注者以计算一致性,请在配置中设置 num_annotators_per_item: 2。

导出回 Praat

导出命令为每名标注者保存过的每个语句写出一个 TextGrid:

bash
python -m potato.export -c config.yaml -f textgrid -o textgrid_out

在我们的测试中,两名模拟标注者保存了第一个语句,导出写出了这些文件:

text
Files written:
  textgrid_out/utt01_annotator1_tiers.TextGrid
  textgrid_out/utt01_annotator2_tiers.TextGrid

只有一名标注者的项目,文件名中不含用户名,例如 utt01_tiers.TextGrid。Praat 6.1.38 通过 Parselmouth 0.4.6 读取了这两个文件。Praat 识别出声调层和停顿层为点层,识别出十个词,以及杂项层上的一个 breath。第二名标注者的文件与源 TextGrid 只有两处不同:该标注者把一个声调从 0.35 秒移到了 0.36 秒,以及 0 被写成了 0.0。

往返过程中 TextGrid 保留和丢失的字段

经过 Potato 往返一次,TextGrid 的以下部分会保留下来:

  • 层。 层名以及点层或区间层的类型按导入时的样子返回;配置保持源文件的顺序时,层的顺序也一致。
  • 时间。 Potato 以毫秒为单位、保留三位小数存储点的时间和区间边界。因此每个时间都保持微秒精度。
  • 标签。 声调标签、停顿标签和每个词区间的文本都会返回,标签内的引号会像 Praat 那样写成两个。
  • 网格长度。 网格结束于标注页面测得的音频长度,或源文件的 xmax。

以下部分会丢失:

  • 起始时间。 无论源文件的 xmin 是多少,导出都让网格和每个层从 0 开始。
  • 未保存的语句。 没有人保存过的语句根本不会得到 TextGrid。
  • 文件名。 导出把文件命名为 <item>_<scheme>.TextGrid,有多名标注者时为 <item>_<user>_<scheme>.TextGrid。
  • 编码。 Praat 常以 UTF-16 保存,而导出写的是 UTF-8。Praat 两种编码都能读取。
  • 重叠。 Praat 不允许同一层上有重叠的区间,导出会把每处重叠列为一条警告。

两名标注者之间的一致性

Potato 位于 /admin/iaa 的一致性报告为每个 tiered_annotation 方案给出一个平均一致性,并把四个层合并在一起计算。Potato 按时间重叠匹配两名标注者的点和区间,匹配上但标签不同的一对得一半分。两个点只有在时间完全相同时才算匹配。在我们的测试中,第一个语句的两份答案只有一个声调的位置相差 10 毫秒,得分是 0.93 而不是 1.0。另两份答案的每个声调和停顿都相差 1 毫秒,得分为 0.24,因为只有十个词区间仍然匹配。

Potato 2.13.1 不计算带时间容差的声调标签一致性,不计算分层的一致性,也不为停顿指数计算 kappa 或有序统计量。时间维度上的一致性中的容差扫描属于机器人片段(robot episode)的分界点,不覆盖 tiered_annotation。要得到 ToBI 的信度数字,请导出两名标注者的 TextGrid,再在 Praat 或你自己的脚本中,按你选定的时间窗口配对两名标注者的声调。

本页背后的测试运行

本页的 TextGrid 和 WAV 文件由一个脚本生成,WAV 文件里是合成音而不是语音。脚本按每个词的长度为其分配时间,声调和停顿的时间由我们手工选定。导入、验证器、服务器和导出都由 Potato 2.13.1 运行。两名模拟标注者通过标注页面发送的同一个 /updateinstance 请求保存结果。一致性数字来自在两份保存的答案上运行 /admin/iaa 背后的函数。

延伸阅读

参考文献

  1. Beckman, M. E., Hirschberg, J., & Shattuck-Hufnagel, S. (2005). The original ToBI system and the evolution of the ToBI framework. In S.-A. Jun (Ed.), Prosodic Typology: The Phonology of Intonation and Phrasing (pp. 9-54). Oxford University Press. https://doi.org/10.1093/acprof:oso/9780199249633.003.0002
  2. Silverman, K., Beckman, M., Pitrelli, J., Ostendorf, M., Wightman, C., Price, P., Pierrehumbert, J., & Hirschberg, J. (1992). ToBI: A standard for labeling English prosody. Proceedings of the 2nd International Conference on Spoken Language Processing (ICSLP 1992), 867-870. https://doi.org/10.21437/ICSLP.1992-260