互联网上的深度学习:协作训练语言模型

发布于2021年7月15日
在 GitHub 上更新
Quentin Lhoest 和 Sylvain Lesage 提供了额外帮助。

现代语言模型在预训练时通常需要大量的计算资源,如果没有数十甚至数百个GPU或TPU,几乎不可能获得它们。尽管理论上可能可以结合多个个人的资源,但实际上,这种分布式训练方法以前的成功案例有限,因为互联网连接速度远低于高性能GPU超级计算机。

在这篇博文中,我们描述了 DeDLOC — 一种新的协作分布式训练方法,它可以根据参与者的网络和硬件限制进行调整。我们展示了它可以在真实场景中成功应用,通过与40名志愿者一起预训练孟加拉语模型 sahajBERT。在孟加拉语的下游任务中,该模型取得了接近最先进的质量,结果与使用数百个高端加速器的更大模型相当。

开放协作中的分布式深度学习

我们为什么要这样做?

如今,许多最高质量的自然语言处理系统都基于大型预训练Transformer。通常,它们的质量随规模的增大而提高:通过增加参数数量并利用大量的无标签文本数据,您可以在自然语言理解和生成方面取得无与伦比的结果。

不幸的是,我们使用这些预训练模型不仅仅是因为方便。在大型数据集上训练Transformer所需的硬件资源常常超出个人甚至大多数商业或研究机构所能承担的范围。以BERT为例:其训练成本估计约为7,000美元,而对于GPT-3这样最大的模型,这个数字可能高达1200万美元!这种资源限制可能看似显而易见且不可避免,但对于更广泛的机器学习社区来说,真的没有替代预训练模型的其他方法吗?

然而,这种情况可能有一个出路:要找到解决方案,我们只需环顾四周。我们正在寻找的计算资源可能已经存在;例如,我们许多人自己家里就有配备游戏或工作站GPU的强大电脑。您可能已经猜到我们将像 Folding@homeRosetta@homeLeela Chess Zero 或各种利用志愿者计算的 BOINC 项目那样,将它们的计算能力联合起来,但这种方法更具普遍性。例如,几个实验室可以联合他们的小型集群,以利用所有可用资源,有些人可能希望使用廉价的云实例加入实验。

对于一个持怀疑态度的人来说,我们可能忽略了一个关键因素:分布式深度学习中的数据传输往往是瓶颈,因为我们需要从多个工作节点聚合梯度。确实,任何对互联网分布式训练的简单方法都注定会失败,因为大多数参与者没有千兆连接,并且可能随时从网络断开。那么,你怎么可能用家庭数据套餐训练任何东西呢?:)

作为解决这个问题的方法,我们提出了一种新的训练算法,称为开放协作中的分布式深度学习(或 **DeDLOC**),在最近发布的 预印本 中详细描述。现在,让我们来了解一下这个算法的核心思想!

与志愿者一起训练

在最常用的版本中,使用多个 GPU 进行分布式训练非常简单。回想一下,在进行深度学习时,您通常会计算损失函数在批量训练数据中许多示例上的平均梯度。在**数据并行**分布式深度学习的情况下,您只需将数据分散到多个工作节点,单独计算梯度,然后在处理完本地批次后对它们进行平均。当所有工作节点上都计算出平均梯度后,我们使用优化器调整模型权重,并继续训练我们的模型。您可以在下面看到不同任务的执行说明。

assets/24_sahajBERT/roles_tasks.png

分布式训练中对等节点执行的典型机器学习任务,可能带有角色分离

通常,为了减少同步量并稳定学习过程,我们可以在平均之前积累 N 个批次的梯度,这相当于将实际批次大小增加 N 倍。这种方法,结合对大多数最先进的语言模型使用大批次的观察,使我们产生了一个简单的想法:在每个优化器步骤之前,让我们在所有志愿者设备上积累一个**非常**大的批次!除了与常规分布式训练完全等效且易于扩展之外,这种方法还具有内置的容错优点,我们将在下面进行说明。

让我们考虑在协作实验中可能遇到的一些潜在故障情况。到目前为止,最常见的情况是,一个或几个对等节点从训练过程中断开连接:他们可能连接不稳定,或者只是想将他们的 GPU 用于其他用途。在这种情况下,我们只会遭受轻微的训练挫折:这些对等节点的贡献将从当前累积的批次大小中扣除,但其他参与者将用他们的梯度来弥补。此外,如果有更多的对等节点加入,目标批次大小将简单地更快达到,我们的训练过程将自然地加速。您可以在视频中看到这一演示

自适应平均

现在我们已经讨论了整个训练过程,还有一个问题:我们如何实际聚合参与者的梯度?大多数家用电脑无法轻易接受传入连接,下载速度也可能成为一个限制。

由于我们依靠志愿硬件进行实验,中央服务器并不是一个真正可行的选择,因为它在扩展到数十个客户端和数亿个参数时会很快面临过载。目前大多数数据并行训练运行无论如何都不使用这种策略;相反,它们依赖于 All-Reduce——一种高效的all-to-all通信原语。由于巧妙的算法优化,每个节点无需将整个局部梯度发送给每个对等节点即可计算全局平均值。

由于 All-Reduce 是去中心化的,它似乎是一个不错的选择;然而,我们仍然需要考虑硬件和网络设置的多样性。例如,一些志愿者可能通过网络较慢但 GPU 强大的计算机加入,一些可能只对其他部分对等节点有更好的连接,还有一些可能被防火墙阻止传入连接。

事实证明,我们可以通过利用这些性能信息,在运行时提出一种最优的数据传输策略!从高层次上讲,我们根据每个对等节点的互联网速度将整个梯度向量分成不同的部分:连接速度最快的对等节点聚合最大的部分。此外,如果某些节点不接受传入连接,它们只会发送数据进行聚合,但不会自己计算平均值。根据条件,这种自适应算法可以很好地恢复已知的分布式深度学习算法,并通过混合策略对其进行改进,如下所示。

Adaptative strategy

自适应算法的不同平均策略示例。

💡 分布式训练的核心技术可在 Hivemind 中获取。
查看仓库并学习如何在您自己的项目中使用此库!


sahajBERT

一如既往,拥有设计良好的算法框架并不意味着它在实践中会按预期工作,因为某些假设在实际训练运行中可能不成立。为了验证这项技术的竞争性能并展示其潜力,我们组织了一次特殊的协作活动,以预训练孟加拉语的掩码语言模型。尽管孟加拉语是世界上第五大母语,但公开可用的掩码语言模型 非常少,这强调了赋能社区的工具的重要性,为该领域带来了大量机遇。

我们与 Neuropark 社区的真实志愿者一起进行了这项实验,并使用了公开可用的数据集(OSCAR 和维基百科),因为我们希望拥有一个完全可重现的示例,可以作为其他团队的灵感。

架构

在我们的实验中,我们选择了ALBER(*一种精简版 BERT*)——一种通过掩码语言建模(MLM)和句子顺序预测(SOP)进行预训练的语言表示模型。我们选择这种架构是因为权重共享使其参数效率非常高:例如,ALBERT-large 大约有 1800 万个可训练参数,在 GLUE 基准测试中的表现与大约有 1.08 亿个权重的 BERT-base 相当。这意味着对等节点之间需要交换的数据更少,这在我们的设置中至关重要,因为它显著加快了每次训练迭代的速度。

💡 想了解更多关于ALBERT的信息吗?
论文
Transformer文档

分词器

我们模型的第一块被称为*分词器*,负责将原始文本转换为词汇表索引。因为我们训练的是孟加拉语模型,与英语不是很相似,所以我们需要在分词器中实现特定于语言的预处理。我们可以将其视为一系列操作

  1. **标准化:** 包括对原始文本数据的所有预处理操作。这是我们进行最多更改的步骤,因为删除某些细节可能会改变文本的含义,也可能保持不变,这取决于语言。例如,标准ALBERT标准化器会删除重音,而对于孟加拉语,我们需要保留它们,因为它们包含有关元音的信息。因此,我们使用以下操作:NMT标准化、NFKC标准化、去除多个空格、孟加拉语中重复Unicode字符的同质化,以及小写化。
  2. **预分词**描述了分割输入(例如,通过空格)以强制特定词元边界的规则。与原始工作一样,我们选择将空格保留在词元之外。因此,为了区分单词并避免出现多个单空格词元,每个对应单词开头的词元都以特殊字符“_”(U+2581)开头。此外,我们将所有标点符号和数字与其他字符隔离,以精简词汇表。
  3. **分词器建模:** 在此级别,文本被映射为词汇表元素的序列。有几种算法可用于此目的,例如字节对编码(BPE)或Unigram,其中大多数需要从文本语料库构建词汇表。按照ALBERT的设置,我们使用**Unigram语言模型**方法,在OSCAR数据集的去重孟加拉语部分上训练了一个包含32k个词元的词汇表。
  4. **后处理:** 分词后,我们可能需要添加架构所需的几个特殊标记,例如以特殊标记 `[CLS]` 开始序列或用特殊标记 `[SEP]` 分隔两个段。由于我们的主要架构与原始 ALBERT 相同,我们保留了相同的后处理:具体来说,我们在每个示例的开头添加一个 `[CLS]` 标记,并在两个段之间和末尾添加一个 `[SEP]` 标记。

💡 了解更多关于 分词器文档 中每个组件的信息。

您可以通过运行以下代码重复使用我们的分词器:

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("neuropark/sahajBERT")

数据集

我们需要介绍的最后一件事是训练数据集。正如您可能知道的,像 BERT 或 ALBERT 这样的预训练模型最大的优势在于您不需要带标注的数据集,而只需要大量的文本。为了训练 sahajBERT,我们使用了 2021年3月20日的孟加拉语维基百科转储 和 OSCAR 的孟加拉语子集 (600MB + 6GB 的文本)。这两个数据集都可以轻松地从 HF Hub 下载。

然而,加载整个数据集需要时间和存储空间——这是我们的对等节点不一定拥有的。为了最大限度地利用参与者提供的资源,我们实施了**数据集流式传输**,这使得他们几乎可以在加入网络后立即训练模型。具体来说,数据集中的示例在训练的同时进行并行下载和转换。我们还可以打乱数据集,这样我们的对等节点同时处理相同示例的可能性就很小。由于数据集没有提前下载和预处理,从纯文本到训练示例所需的转换(如下图所示)都是即时完成的。

Create dataset

从原始样本到训练样本

数据集流式传输模式在 🤗 数据集库的 v1.9 版本中可用,所以您现在可以按以下方式使用它

from datasets import load_dataset

oscar_dataset = load_dataset("oscar", name="unshuffled_deduplicated_bn", streaming=True)

💡 在文档中了解更多关于流式加载数据集的信息

协作活动

sahajBERT 协作训练活动于5月12日至5月21日举行。此次活动汇聚了40名参与者,其中30名是孟加拉语志愿者,10名是其中一位作者所在组织的志愿者。这40名志愿者加入了 Neuropark Discord 频道,以获取有关该活动的所有信息并参与讨论。为了加入实验,志愿者被要求:

  1. 向版主发送他们的用户名以获得白名单权限;
  2. 在本地、Google Colaboratory 或 Kaggle 上打开提供的 notebook;
  3. 运行一个代码单元格,并在提示时填写他们的 Hugging Face 凭据;
  4. 在共享仪表板上观察训练损失的下降!

出于安全考虑,我们设置了授权系统,以便只有 Neuropark 社区成员才能训练模型。撇开技术细节不谈,我们的授权协议允许我们保证每个参与者都在白名单中,并认可每个对等节点的个人贡献。

在下图中,您可以看到每个志愿者的活动。在实验过程中,志愿者登录了600个不同的会话。参与者定期并行启动多个运行,其中许多人随着时间的推移分散启动的运行。个别参与者的运行平均持续4小时,最长持续21小时。您可以在论文中阅读更多关于参与者统计数据的信息。

显示 sahajBERT 实验参与者的图表。圆圈半径与处理的批次总数成比例,如果参与者不活跃,圆圈则变灰。每个紫色方块代表一个活跃设备,颜色越深表示性能越高。

除了参与者提供的资源外,我们还使用了 16 个可抢占式(廉价但经常中断)单 GPU T4 云实例,以确保运行的稳定性。实验的累计运行时间为 234 天,在下图中您可以看到每个对等节点对损失曲线的贡献部分!

最终模型已上传至模型中心,如果您愿意,可以下载并使用它:https://huggingface.co/neuropark/sahajBERT

评估

为了评估 sahajBERT 的性能,我们将其微调到两个孟加拉语下游任务中:

  • WikiANN 的孟加拉语分支上进行命名实体识别 (NER)。此任务的目标是将输入文本中的每个标记分类为以下类别之一:人物、组织、地点或 none。
  • IndicGLUE 中的 Soham 文章数据集上进行新闻类别分类 (NCC)。此任务的目标是预测输入文本所属的类别。

我们在训练期间对 NER 任务进行了评估,以检查一切是否顺利;正如您在下面的图中看到的,情况确实如此!

来自不同预训练模型检查点的微调模型在NER任务上的评估指标。

在训练结束时,我们将 sahajBERT 与其他三个预训练语言模型进行了比较:XLM-R LargeIndicBertbnRoBERTa。在下表中,您可以看到我们的模型获得了与 HF Hub 上最佳孟加拉语模型相当的结果,尽管我们的模型只有约 1800 万个训练参数,而例如 XLM-R(一个强大的多语言基线)拥有约 5.59 亿个参数,并且在数百个 V100 GPU 上进行训练。

模型 NER F1(均值 ± 标准差) NCC 准确率(均值 ± 标准差)
sahajBERT 95.45 ± 0.53 91.97 ± 0.47
XLM-R-大 96.48 ± 0.22 90.05 ± 0.38
IndicBert 92.52 ± 0.45 74.46 ± 1.91
bnRoBERTa 82.32 ± 0.67 80.94 ± 0.45

这些模型也可以在 Hub 上找到。您可以通过在它们的模型卡上使用托管推理 API 小部件,或者直接在您的 Python 代码中加载它们来测试它们。

sahajBERT-NER

模型卡片:https://huggingface.co/neuropark/sahajBERT-NER

from transformers import (
    AlbertForTokenClassification,
    TokenClassificationPipeline,
    PreTrainedTokenizerFast,
)

# Initialize tokenizer
tokenizer = PreTrainedTokenizerFast.from_pretrained("neuropark/sahajBERT-NER")

# Initialize model
model = AlbertForTokenClassification.from_pretrained("neuropark/sahajBERT-NER")

# Initialize pipeline
pipeline = TokenClassificationPipeline(tokenizer=tokenizer, model=model)

raw_text = "এই ইউনিয়নে ৩ টি মৌজা ও ১০ টি গ্রাম আছে ।" # Change me
output = pipeline(raw_text)

sahajBERT-NCC

模型卡片:https://huggingface.co/neuropark/sahajBERT-NER

from transformers import (
    AlbertForSequenceClassification,
    TextClassificationPipeline,
    PreTrainedTokenizerFast,
)

# Initialize tokenizer
tokenizer = PreTrainedTokenizerFast.from_pretrained("neuropark/sahajBERT-NCC")

# Initialize model
model = AlbertForSequenceClassification.from_pretrained("neuropark/sahajBERT-NCC")

# Initialize pipeline
pipeline = TextClassificationPipeline(tokenizer=tokenizer, model=model)

raw_text = "এই ইউনিয়নে ৩ টি মৌজা ও ১০ টি গ্রাম আছে ।" # Change me
output = pipeline(raw_text)

结论

在这篇博文中,我们讨论了能够实现神经网络协作预训练的方法,sahajBERT 是将其应用于真实世界问题的第一个真正成功的例子。

这一切对更广泛的机器学习社区意味着什么?首先,现在可以与朋友一起进行大规模分布式预训练,我们希望看到许多以前难以获得的新颖模型。此外,我们的结果对于多语言 NLP 可能很重要,因为现在任何语言的社区都可以训练自己的模型,而无需将大量计算资源集中在一个地方。

致谢

DeDLOC 论文和 sahajBERT 训练实验由 Michael Diskin、Alexey Bukhtiyarov、Max Ryabinin、Lucile Saulnier、Quentin Lhoest、Anton Sinitsin、Dmitry Popov、Dmitry Pyrkin、Maxim Kashirin、Alexander Borzunov、Albert Villanova del Moral、Denis Mazur、Ilia Kobelev、Yacine Jernite、Thomas Wolf 和 Gennady Pekhimenko 共同完成。该项目是 Hugging FaceYandex ResearchHSE UniversityMIPTUniversity of TorontoVector Institute 合作的成果。

此外,我们还要感谢 Stas Bekman、Dmitry Abulkhanov、Roman Zhytar、Alexander Ploshkin、Vsevolod Plokhotnyuk 和 Roman Kail 在构建训练基础设施方面提供的宝贵帮助。同时,我们感谢 Abhishek Thakur 在下游评估方面提供的帮助,以及 Tanmoy Sarkar 和 Omar Sanseviero,他们帮助我们组织了协作实验,并在训练过程中定期向参与者提供状态更新。

下面是协作实验的所有参与者

参考文献

“开放协作中的分布式深度学习”,ArXiv

sahajBERT 实验的代码在 DeDLOC 仓库中。

社区

注册登录 发表评论