Press "Enter" to skip to content

TaatikNet:用于希伯来语音译的序列到序列学习

一个简单的字符级seq2seq学习的演示,应用于一个复杂的任务:在希伯来文本和拉丁音转写之间进行转换

如何使用深度学习在字符串之间进行转换而不被“纠缠”?(图片由Andrew Malone, CC BY 2.0提供)

本文介绍了TaatikNet以及如何轻松实现seq2seq模型。有关代码和文档,请参阅TaatikNet GitHub存储库。有关交互式演示,请访问HF Spaces上的TaatikNet。

介绍

NLP中感兴趣的许多任务涉及在不同风格、语言或格式的文本之间进行转换:

  • 机器翻译(例如英语到德语)
  • 文本摘要改写(例如长文本到短文本)
  • 拼写纠正
  • 抽象问题回答(输入:上下文和问题,输出:答案的文本)

这些任务统称为序列到序列(Seq2seq)学习。在所有这些任务中,输入和期望的输出都是字符串,它们可能具有不同的长度,并且通常不是一一对应的。

假设你有一个配对示例的数据集(例如句子列表和它们的翻译、许多拼写错误和更正文本的示例等)。如今,只要有足够的数据,使得模型可以学习到对新输入进行泛化,就很容易在这些数据上训练神经网络。让我们看看如何使用PyTorch和Hugging Face transformers库来训练seq2seq模型。

我们将重点关注一个特别有趣的用例:学习将希伯来文本和拉丁音转写之间进行转换。我们将在下面概述这个任务,但是这里呈现的思想和代码对于除了这个特定案例之外也是有用的——对于任何想要从示例数据集执行seq2seq学习的人,本教程都应该是有用的。

我们的任务:希伯来文转写

为了演示具有有趣且相当新颖的用例的seq2seq学习,我们将其应用于转写。一般来说,转写是指在不同的书写系统之间进行转换。虽然英语是用拉丁字母表写的(“ABC…”),但世界上的语言使用了许多不同的书写系统,如下图所示:

世界上的许多书写系统。(图片由Nickshanks, CC-BY-SA-3提供)

如果我们想要使用拉丁字母表来写出原本用不同书写系统书写的语言的单词,这个挑战可以通过许多方式来写犹太节日Hanukkah的名字来解释。它的维基百科文章当前的介绍如下:

Hanukkah(/ˈhɑːnəkə/;希伯来语:חֲנֻכָּה‎,现代:Ḥanukka,泰伯利亚:Ḥănukkā)是犹太人的一个节日,纪念耶路撒冷的恢复和第二圣殿在公元前2世纪马加比起义反对塞琉古帝国的开始时重新奉献。

希伯来单词חֲנֻכָּה‎可以用拉丁字母表转写为Hanukkah、Chanukah、Chanukkah、Ḥanukka或其他许多变体。在希伯来语以及许多其他书写系统中,存在着各种约定和歧义,使得转写变得复杂,并不是简单的一对一字符映射。

对于希伯来语而言,通过使用一套复杂的规则,可以大部分将带有音符(元音符号)的文本转写为拉丁字符,尽管有一些特殊情况使这个过程变得复杂。此外,如果没有音符或者进行反向映射(例如,Chanukah → חֲנֻכָּה),则进行转写变得更加困难,因为可能会有多种有效输出。

幸运的是,通过将深度学习应用于现有数据,我们可以只使用很少的代码就可以极大地解决这个问题。让我们看看如何训练一个 seq2seq 模型 — TaatikNet — 来学习如何自动转换希伯来文本和拉丁转写。我们注意到,这是一个基于字符级别的任务,因为它涉及对希伯来文本和转写之间不同字符之间的关联进行推理。我们将在下面详细讨论这一点的重要性。

顺便说一下,你可能听说过 UNIKUD,我们的模型用于给未标点的希伯来文本添加元音点。这些任务之间存在一些相似之处,但关键的区别在于 UNIKUD 执行的是基于字符级别的分类,对于每个字符,我们学习是否在其旁边插入一个或多个元音符号。相比之下,在我们的情况下,由于转写的复杂性,输入和输出文本可能在长度或顺序上不完全对应,这就是为什么我们在这里使用 seq2seq 学习(而不仅仅是基于字符级别的分类)的原因。

数据收集

与大多数机器学习任务一样,如果我们能够收集到模型输入和期望输出的许多示例,那么我们就非常幸运,因为这样我们可以使用监督学习来训练模型。

对于许多涉及单词和短语的任务来说,一个很好的资源是维基词典及其多语言对应物 — 类似于维基百科和字典的结合。特别是,希伯来语维基词典(ויקימילון)包含了带有结构化语法信息的词条,如下所示:

来自希伯来语维基词典词条 עגבניה(番茄)的语法信息

特别是,这包括了拉丁转写( agvani ya ,斜体表示重音)。同时,还有包含音符的章节标题,这为我们提供了需要训练模型的(自由许可)数据。

为了创建数据集,我们使用 Wikimedia REST API 进行这些项目的抓取(示例见此处)。请注意,维基词典条目中的原始文本对于派生作品具有宽松的许可(CC 和 GNU 许可证,详细信息见此处),并且需要共享相同许可(TaatikNet 许可证见此处);一般来说,如果你进行数据抓取,请确保使用允许使用的数据进行抓取,并适当使用正确的许可证进行派生作品。

我们对这些数据进行了各种预处理步骤,包括:

  • 删除维基标记和元数据
  • 用重音符号替换斜体以表示重音(例如,agvani ya → agvaniyá)
  • 使用 Unicode NFC 规范化来统一外观相同的字形,例如 בּ(U+05D1 希伯来字母贝特 + U+05BC 希伯来点达格什或 Mapiq)和 בּ(U+FB31 希伯来字母贝特带有达格什)。你可以通过将它们复制粘贴到“显示 Unicode 字符工具”中进行自己的比较。我们还统一了外观类似的标点符号,例如希伯来 geresh(׳)和撇号(’)。
  • 将多个词组拆分为单独的单词。

在数据抓取和预处理之后,我们得到了近1.5万个单词-转写对(csv 文件可在此处获取)。下面是一些示例:

数据集中的一些示例。注意,带有音符的希伯来文本(元音点)是第二列,但由于从右到左的文本渲染问题,它显示在第一列。

这些音译并不一致或没有错误; 例如,重音标记不一致且经常错误,使用了各种拼写约定(例如 ח 可能对应 h、kh 或 ch)。我们不打算尝试清理这些内容,而是直接将它们输入模型,让模型自己理解。

训练

现在我们有了数据集,让我们来进行项目的“核心”——在数据上训练一个 seq2seq 模型。我们将最终的模型称为 TaatikNet,以希伯来语单词 תעתיק taatik 的意思“音译”命名。我们将在这里对 TaatikNet 的训练进行高级描述,但强烈建议您查看注释的训练笔记本。训练代码本身相当简短且易于理解。

为了在 NLP 任务上取得最先进的结果,常见的范例是采用预训练的 transformer 神经网络,然后通过在任务特定的数据集上进行微调来进行迁移学习。对于 seq2seq 任务来说,最自然的基本模型选择是编码器-解码器(enc-dec)模型。常见的编码器-解码器模型,如 T5 和 BART,非常适用于常见的文本摘要等 seq2seq 任务,但由于它们对文本进行了标记化处理(将其分割为子词标记,大致是单词或单词的片段),因此对于我们需要对个别字符进行推理的任务来说,这些模型并不适用。因此,我们使用无标记化的 ByT5 编码器-解码器模型(论文,HF 模型页面),它在个别字节(大致是字符,但请参阅 Joel Spolsky 关于 Unicode 和字符集的优秀帖子,以更好地理解 Unicode 字形如何映射到字节)上执行计算。

我们首先创建一个 PyTorch 数据集对象来封装我们的训练数据。我们可以简单地将数据集 csv 文件中的数据包装起来,不做任何修改,但我们添加一些随机增强来使模型的训练过程更有趣:

def __getitem__(self, idx):  row = self.df.iloc[idx]  out = {}  if np.random.random() < 0.5:      out['input'] = row.word if np.random.random() < 0.2 else row.nikkud      out['target'] = row.transliteration  else:      out['input'] = randomly_remove_accent(row.transliteration, 0.5)      out['target'] = row.nikkud  return out

这种增强教会了 TaatikNet 接受希伯来语或拉丁语脚本作为输入,并计算相应的匹配输出。我们还随机删除元音符号或重音符号,以训练模型对它们的缺失具有鲁棒性。通常情况下,随机增强是一个不错的技巧,当您希望网络学会处理各种类型的输入,而又不想事先计算数据集中所有可能的输入和输出时。

我们使用 Hugging Face 的 pipeline API 一行代码来加载基本模型:

pipe = pipeline("text2text-generation", model='google/byt5-small', device_map='auto')

在处理数据整理和设置超参数(epoch 数量、批量大小、学习率)之后,我们在数据集上训练模型,并在每个 epoch 结束后打印选定的结果。训练循环是标准的 PyTorch 循环,除了 evaluate(…) 函数,该函数在其他地方定义,打印出模型对各种输入的当前预测结果:

for i in trange(epochs):  pipe.model.train()  for B in tqdm(dl):    optimizer.zero_grad()    loss = pipe.model(**B).loss    losses.append(loss.item())    loss.backward()    optimizer.step()  evaluate(i + 1)

比较一些早期 epoch 和训练结束时的结果:

Epoch 0 before training: kokoro => okoroo-oroa-oroa-oroa-oroa-oroa-oroa-oroa-oroa-oroa-oroa-oroa-oroa-oroa-oroa-oroa-oroa-oroa-oroa-oEpoch 0 before training: יִשְׂרָאֵל => אלאלאלאלאלאלאלאלאלאלאלאלאלאלאלאלאלאלאלאלאלאלאלאלאEpoch 0 before training: ajiliti => ajabiliti siti siti siti siti siti siti siti siti siti siti siti siti siti siti siti siti sitEpoch 1: kokoro => מְשִׁיתEpoch 1: יִשְׂרָאֵל => maráEpoch 1: ajiliti => מְשִׁיתEpoch 2: kokoro => כּוֹקוֹרְבּוֹרוֹרEpoch 2: יִשְׂרָאֵל => yishishálEpoch 2: ajiliti => אַדִּיטִיEpoch 5: kokoro => קוֹקוֹרוֹEpoch 5: יִשְׂרָאֵל => yisraélEpoch 5: ajiliti => אֲגִילִיטִיEpoch 10 after training: kokoro => קוֹקוֹרוֹEpoch 10 after training: יִשְׂרָאֵל => yisraélEpoch 10 after training: ajiliti => אָגִ'ילִיטִי

在训练模型之前,模型输出的是无意义的内容,这是预料之中的。在训练过程中,我们可以看到模型首先学习如何构建看起来合法的希伯来语和音译,但需要更长的时间来学习它们之间的联系。对于稀有的项目,比如 ג׳(gimel + geresh)对应于 j,学习所需的时间也更长。

一个注意事项:我们并未尝试优化训练过程;超参数是相当随意选择的,并且我们没有为严格评估设置验证或测试集。这只是为了提供一个简单的 seq2seq 训练示例和学习音译的概念验证;然而,超参数调优和严格评估将是未来工作的有希望的方向,以及下面限制部分提到的点。

结果

下面展示了几个示例,演示了希伯来文本(带或不带元音)和拉丁音译之间的转换,双向都有。您可以尝试在 HF Spaces 上的交互演示中自己玩弄 TaatikNet。请注意,它使用§qs波束搜索(5个波束)进行解码,并且推断是在每个单词上分别运行的。

来自交互演示的 TaatikNet 的示例输入和输出。使用波束搜索解码(5个波束)生成多个输出。
更长文本的示例输出。推断是在每个单词上分别运行的。请注意,它成功地音译了一些具有挑战性的情况,例如 שבעיניו(最后的י德没有发音),חוכמה(kamatz gadol),כאלה(次重音)

限制和进一步方向

出于简单起见,我们将 TaatikNet 实现为一个最小的 seq2seq 模型,没有进行广泛的调优。然而,如果您有兴趣改进在希伯来文本和音译之间的转换结果,有许多有希望的未来工作方向:

  • TaatikNet只尝试根据字母或音素对应关系猜测适当的拼写(希伯来语或拉丁音译)。然而,您可能希望根据上下文将音译转换为有效的希伯来文本(例如 zot dugma → זאת דוגמא,而不是拼写错误的 *זות דוגמע)。实现这一目标的可能方式包括检索增强生成(访问字典)或训练希伯来语句子及其拉丁音译对以学习上下文线索。
  • 不规则形式的输入可能会导致 TaatikNet 的解码陷入循环,例如 drapapap → דְּרַפָּפָּאפָּאפָּאפָּאפָּאפָּאפָּאפָּאפָּאפָּאפָּאפָּאפָּאפָּאפָּאפָּאפָּאפָּאפָּאפָּאפָּאפָּאפָּ。这可以通过训练增强、更多样化的训练数据或在训练或解码过程中使用循环一致性来处理。
  • TaatikNet可能无法处理在其训练数据中非常罕见的某些惯例。例如,它通常无法正确处理 ז׳(zayin+geresh),该符号表示罕见的外来音 zh。这可能表明欠拟合,或者在训练过程中使用样本权重以强调困难的示例可能会有所帮助。
  • seq2seq 训练的简便性付出了解释性和鲁棒性的代价-我们可能希望准确了解 TaatikNet 如何做出决策,并确保它们被一致地应用。一个有趣的可能扩展是将其知识蒸馏成一组基于规则的条件(例如,如果在上下文中见到字符 X,则写入 Z)。也许最近的代码预训练语言模型可以对此有所帮助。
  • 我们没有处理“完全拼写”和“残缺拼写”(כתיב מלא / חסר),即希伯来语单词在带元音符号和不带元音符号时拼写略有不同。理想情况下,模型应该在没有元音的“完全”拼写和带元音的“残缺”拼写上进行训练。有关在训练希伯来文本的模型上处理这些拼写的方法,请参阅 UNIKUD。

如果您尝试了这些或其他想法,并发现它们能够带来改进,我会非常乐意听取您的意见,并在此给予您致谢 – 随时可以通过本文下方的联系方式与我取得联系。

结论

我们已经看到,使用有监督学习来训练一个seq2seq模型是相当简单的 – 通过教它从大量的配对示例中进行泛化。在我们的案例中,我们使用了一个字符级模型(TaatikNet,从基本的ByT5模型进行了微调),但几乎相同的步骤和代码也可以用于更标准的seq2seq任务,如机器翻译。

我希望您从这个教程中学到了与我整理它时一样多的知识!如果您有任何问题、评论或建议,请随时与我联系;我的联系方式可以在我下面链接的网站上找到。

Morris Alper,MSc是特拉维夫大学的博士生,研究多模态学习(自然语言处理、计算机视觉和其他模态)。请访问他的网页以获取更多信息和联系方式:https://morrisalp.github.io/

Leave a Reply

Your email address will not be published. Required fields are marked *