使用 TensorFlow 和 Amazon SageMaker PlatoBlockchain 数据智能在 TB 级数据上创建、训练和部署十亿参数的语言模型。垂直搜索。人工智能。

使用 TensorFlow 和 Amazon SageMaker 在 TB 级数据上创建、训练和部署十亿参数的语言模型

近年来,语言模型的规模不断扩大一直是自然语言处理 (NLP) 的最大趋势之一。 自 2018 年以来,我们看到了前所未有的更大语言模型的开发和部署,包括 BERT 及其变体、GPT-2、T-NLG 和 GPT-3(175 亿个参数)。

这些模型推动了可能的架构创新的界限。 在训练大规模深度学习模型时,我们面临着一些挑战,尤其是新一波的生成式预训练变压器。 这些挑战包括硬件限制以及计算和效率的权衡。 为了克服模型和数据并行性的这些挑战,AWS 提供了广泛的功能。

在这篇文章中,我们介绍了两种主要方法:数据并行化和模型并行化 亚马逊SageMaker,并讨论它们的优缺点。

该模型

对于语言模型,我们使用论文中介绍的 Transformers 注意就是您所需要的. Transformer 是深度学习模型,旨在通过依靠自我注意机制来绘制输入和输出之间的全局依赖关系,从而故意避免 RNN 的缺陷。 Transformer 模型架构允许显着更好的并行化,并且可以在相对较短的训练时间内实现高性能。 建立在变形金刚的成功之上,BERT 在论文中介绍 BERT:用于语言理解的深度双向变压器的预训练,为语言表示添加了双向预训练。 受完形填空任务的启发,BERT 使用掩码语言建模 (MLM) 进行了预训练,其中模型学习恢复随机掩码标记的原始单词。 BERT 模型还在下一句预测 (NSP) 任务上进行了预训练,以预测两个句子的阅读顺序是否正确。 自 2018 年问世以来,BERT 及其变体已广泛用于语言模型。

我们首先为令牌和位置嵌入创建两个嵌入层。 输入嵌入是令牌嵌入和位置嵌入的总和。

class TokenAndPositionEmbedding(tf.keras.layers.Layer): """ Creates two separate embedding layers: one for tokens and one for token index (positions). """ def __init__(self, maxlen, vocab_size, embed_dim): super(TokenAndPositionEmbedding, self).__init__() self.token_emb = tf.keras.layers.Embedding(input_dim=vocab_size, output_dim=embed_dim) self.pos_emb = tf.keras.layers.Embedding(input_dim=maxlen, output_dim=embed_dim) def call(self, x): maxlen = tf.shape(x)[-1] # positions are represented by a token's index positions = tf.range(start=0, limit=maxlen, delta=1) positions = self.pos_emb(positions) # token embedding x = self.token_emb(x) # return sum as input return x + positions

然后我们定义一个带有两个子层的转换器解码器块:一个多头自注意力层和一个简单的全连接前馈网络,然后是层归一化和 dropout:

class TransformerBlock(tf.keras.layers.Layer): def __init__(self, embed_dim, num_heads, ff_dim, rate=0.1): # self attention layer super(TransformerBlock, self).__init__() self.att = tf.keras.layers.MultiHeadAttention( num_heads=num_heads, key_dim=embed_dim) # feed forward layer self.ffn = [tf.keras.layers.Dense(ff_dim, activation="relu"), tf.keras.layers.Dense(embed_dim)] # layer normalization self.layernorm1 = tf.keras.layers.LayerNormalization(epsilon=1e-6) self.layernorm2 = tf.keras.layers.LayerNormalization(epsilon=1e-6) # dropout self.dropout1 = tf.keras.layers.Dropout(rate) self.dropout2 = tf.keras.layers.Dropout(rate) def call(self, inputs): # getting batch size and seq len from input shape input_shape = tf.shape(inputs) batch_size = input_shape[0] seq_len = input_shape[1] # decoder casual mask casual_mask = casual_attention_mask(batch_size, seq_len, seq_len, tf.bool) # self attention forward pass attention_output = self.att(inputs, inputs, attention_mask=causal_mask) # dense layers, dropout and normalization attention_output = self.dropout1(attention_output) ffn_output = self.ffn[0](out1) ffn_output = self.ffn[1](ffn_output) out2 = self.dropout2(ffn_output) return self.layernorm2(out1 + out2)

最后,我们使用前面的嵌入层和转换器块创建我们的语言模型:

class MyModel(tf.keras.Model): def __init__(self, maxlen, vocab_size, embed_dim, num_heads, feed_forward_dim, num_layers, learning_rate): super(MyModel, self).__init__(maxlen, vocab_size, embed_dim, num_heads, feed_forward_dim, num_layers, learning_rate) # embedding layer self.embedding_layer = TokenAndPositionEmbedding(maxlen, vocab_size, embed_dim) # transformer blocks self.transformer_blocks = [ TransformerBlock(embed_dim, num_heads, feed_forward_dim) for i in range(num_layers) ] # last dense layer self.dense = tf.keras.layers.Dense(vocab_size) def call(self, inputs, training=None): x_emb = self.embedding_layer(inputs) x = x_emb for transformer_block in self.transformer_blocks: x = transformer_block(x) outputs = self.dense(x) return [outputs, x_emb] def init_train_settings(maxlen, vocab_size, embed_dim, num_heads, feed_forward_dim, num_layers, learning_rate): """ Creates model, optimizer and loss function """ model = MyModel(maxlen, vocab_size, embed_dim, num_heads, feed_forward_dim, num_layers, learning_rate) loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True) optimizer = tf.keras.optimizers.Adam(learning_rate=learning_rate) return model, optimizer, loss_fn

根据您的超参数,您可以将此模型从数千个参数扩展到数十亿个参数。 十亿参数模型的主要挑战是您不能在一个实例中托管模型,并且需要将模型分布在多个节点上以进行训练和推理。

数据集

在我们的实验中,我们使用了 桩数据集. Pile 是一个 800 GiB 的英文文本数据集,专为训练大规模语言模型而设计。 它由 22 个多样化和高质量的数据集创建,包括已建立的 NLP 数据集和新引入的数据集。

数据集是从各种数据源创建的,包括书籍; GitHub存储库; 网页; 聊天记录; 以及医学、物理、数学、计算机科学和哲学论文。 具体来说,它使用以下来源:Pile-CC、PubMed Central、ArXiv、GitHub、FreeLaw Project、Stack Exchange、美国专利商标局、PubMed、Ubuntu、IRC、HackerNews、YouTube、PhilPapers、Books3、Project Gutenberg( PG-19)、OpenSubtitles、英语维基百科、DM 数学、EuroParl、安然电子邮件语料库和 NIH ExPorter。 它还包括 OpenWebText2 和 BookCorpus2,它们分别是原始 OpenWebText 和 BookCorpus 数据集的扩展。 数据源的多样性可以提高一般的跨领域知识,从而提高下游的泛化能力。

这个数据集的主要挑战是庞大的规模。 该数据集有 825 GiB 的文本,转换为 4.2 TiB 的预处理和压缩数据点。 与我们在训练和托管模型时面临的挑战类似,在单个实例上使用此数据集训练模型将花费大量时间并且不切实际。

我们的解决方案是将数据集分解为大约 1 GiB 的数据块,加载和预处理 TensorFlow 数据集 对象,并将它们存储在 亚马逊弹性文件服务 (亚马逊 EFS)。 TensorFlow 数据集提供了易于使用的高性能数据管道,可与我们的模型很好地集成。 Amazon EFS 是一项易于使用的服务,它使我们能够构建一个共享文件系统,该系统会随着文件的添加和删除而自动扩展。 此外,Amazon EFS 能够在需要时突增到更高的吞吐量水平,这在我们的数据和模型训练管道中至关重要。

接下来,我们研究分布式训练策略来应对这些挑战。

分布式培训

在这个项目中,我们面临两个挑战:扩展模型大小和数据量。 增加模型大小和可训练参数的数量可能会提高准确性,但您可以将模型放入单个 GPU 内存甚至单个实例中的多个 GPU 中是有限度的。 此外,更大的模型尺寸需要更多的时间来训练。

您可以通过两种不同的方式应对这些挑战:数据并行性和模型并行性。 借助数据并行性,我们通过将小批量的记录分布在不同的设备上来执行随机梯度下降 (SGD),以加快训练速度。 然而,并行数据训练带来了额外的复杂性,即使用来自所有设备的梯度计算小批量梯度平均值,这一步骤称为 AllReduce,随着训练集群的增长,这变得越来越难。 在使用数据并行性时,我们必须能够在设备(CPU 或 GPU)中拟合模型和单个数据点,这是我们实验中的一个限制因素,因为如此大的模型的大小远大于单个 GPU 的内存尺寸。

另一种解决方案是使用模型并行性,将模型拆分到多个设备上。 模型并行性是在多个设备或节点(例如配备 GPU 的实例)之间拆分模型并创建有效管道以在这些设备上训练模型以最大化 GPU 利用率的过程。

数据并行化

并行化数据是多 GPU 或分布式训练最常用的方法。 您可以批量处理数据,将其发送到多个设备(每个设备都托管一个复制模型),然后汇总结果。 我们试验了两个数据并行化包:Horovod 和 SageMaker分布式数据并行库.

Horovod 是一个用于 TensorFlow、Keras、PyTorch 和 Apache MXNet 的分布式深度学习训练框架。 要使用 Horovod,我们经历了以下过程:

  1. 通过运行初始化 hvd.init().
  2. 将每个设备与单个进程相关联。 第一个进程或工作者与第一个设备相关联,第二个进程与第二个设备相关联,依此类推。
  3. 根据设备数量调整学习率。
  4. 将优化器包裹在 hvd.DistributedOptimizer.
  5. 将初始变量状态从排名为 0 的第一个工作人员广播到所有其他进程。 当训练以随机权重开始或从检查点恢复时,这对于确保所有工作人员的一致初始化是必要的。
  6. 确保只有设备 0 可以保存检查点,以防止其他工作人员破坏它们。

以下是训练脚本:

import horovod.tensorflow as hvd
# Initialize Horovod
hvd.init() # Pin GPU to be used to process local rank (one GPU per process)
gpus = tf.config.experimental.list_physical_devices('GPU')
for gpu in gpus: tf.config.experimental.set_memory_growth(gpu, True)
if gpus: tf.config.experimental.set_visible_devices(gpus[hvd.local_rank()], 'GPU') # Build model
... @tf.function
def training_step(texts, labels, first_batch): with tf.GradientTape() as tape: predictions = model(texts, training=True) loss = loss_fn(labels, predictions[0]) # Horovod: add Horovod Distributed GradientTape. tape = hvd.DistributedGradientTape(tape) grads = tape.gradient(loss, model.trainable_variables) opt.apply_gradients(zip(grads, model.trainable_variables)) # Horovod: broadcast initial variable states from rank 0 to all other processes. # This is necessary to ensure consistent initialization of all workers when # training is started with random weights or restored from a checkpoint. # # Note: broadcast should be done after the first gradient step to ensure optimizer # initialization. if first_batch: hvd.broadcast_variables(model.variables, root_rank=0) hvd.broadcast_variables(opt.variables(), root_rank=0) return loss # Horovod: adjust number of steps based on number of GPUs.
for batch, (texts, labels) in enumerate(dataset.take(10000 // hvd.size())): loss = training_step(texts, labels, batch == 0) if batch % 10 == 0 and hvd.local_rank() == 0: print('Step #%dtLoss: %.6f' % (batch, loss)) # Horovod: save checkpoints only on worker 0 to prevent other workers from
# corrupting it.
if hvd.rank() == 0: checkpoint.save(checkpoint_dir)

SageMaker 数据并行库使我们能够以接近线性的效率扩展我们的训练,以最少的代码更改加速我们的训练。 该库执行自定义 AllReduce 通过充分利用 AWS 的网络基础设施和 亚马逊弹性计算云 (Amazon EC2) 实例拓扑。 要使用 SageMaker 数据并行库,我们经历了以下过程:

  1. 导入和初始化 sdp.init().
  2. 将每个设备与单个设备关联 smdistributed.dataparallel 处理 local_rank. sdp.tensorflow.local_rank() 给我们设备的本地排名。 领导者等级为 0,工人等级为 1、2、3,依此类推。
  3. 根据设备数量调整学习率。
  4. 饺子皮 tf.GradientTape DistributedGradientTape 执行 AllReduce.
  5. 将初始模型变量从领导节点广播到所有工作节点。
  6. 确保只有设备 0 可以保存检查点。

模型并行化

我们可以调整超参数以保持模型足够小以使用单个 GPU 进行训练,或者我们可以使用模型并行性在多个实例的多个 GPU 之间拆分模型。 增加模型的可训练参数数量可以提高准确度,但在单个 GPU 内存中可以容纳的最大模型大小是有限制的。 我们使用 SageMaker 分布式模型并行库来训练我们更大的模型。 步骤如下:

  1. 导入并初始化库 smp.init().
  2. Keras 模型需要继承自 smp.DistributedModel 而不是 Keras Model 类。
  3. drop_remainder=True ,在 tf.Dataset.batch() 方法来确保批量大小始终可以被微批量的数量整除。
  4. 数据管道中的随机操作都需要使用相同的种子: smp.dp_rank()例如, shuffle(ds, seed=smp.dp_rank()). 这确保了拥有不同模型分区的设备之间数据样本的一致性。
  5. 前向和后向逻辑需要在一个阶梯函数中 smp.step 装饰。
  6. 使用 StepOutput 方法对跨微批次的输出执行后处理,例如 reduce_mean。 该 smp.step 函数必须有一个取决于输出的返回值 smp.DistributedModel.

训练脚本如下:

import smdistributed.modelparallel.tensorflow as smp # SMP: Initialize
smp.init() # SMP: Define smp.DistributedModel the same way as Keras sub-classing API
class MyModel(smp.DistributedModel): def __init__(self, maxlen, vocab_size, embed_dim, num_heads, feed_forward_dim, num_layers, learning_rate): super(MyModel, self).__init__(maxlen, vocab_size, embed_dim, num_heads, feed_forward_dim, num_layers, learning_rate) self.embedding_layer = gpt_model.TokenAndPositionEmbedding(maxlen, vocab_size, embed_dim) self.transformer_blocks = [ gpt_model.TransformerBlock(embed_dim, num_heads, feed_forward_dim) for i in range(num_layers) ] self.dense = tf.keras.layers.Dense(vocab_size) def call(self, inputs, training=None): x_emb = self.embedding_layer(inputs) x = x_emb for transformer_block in self.transformer_blocks: x = transformer_block(x) outputs = self.dense(x) return [outputs, x_emb] # SMP: Define smp.step. Return any tensors needed outside
@smp.step
def get_grads(texts, labels): predictions = model(texts, training=True) loss = loss_fn(labels, predictions[0]) grads = optimizer.get_gradients(loss, model.trainable_variables) return grads, loss, predictions[0] @tf.function
def train_step(texts, labels, first_batch): gradients, loss, predictions = get_grads(texts, labels) # SMP: Accumulate the gradients across microbatches gradients = [g.accumulate() for g in gradients] optimizer.apply_gradients(zip(gradients, model.trainable_variables)) # SMP: Average the loss across microbatches train_loss(loss.reduce_mean()) # SMP: Merge predictions across microbatches train_accuracy(labels, predictions.merge()) return loss.reduce_mean() histories = [] for _ in range(epochs): train_loss.reset_states() train_accuracy.reset_states() for texts, labels in text_ds: for i in range(128): text = tf.expand_dims(texts[0][i], axis=0) label = tf.expand_dims(labels[0][i], axis=0) train_step(text, label) 

有关为 SageMaker 分布式模型并行库启用 TensorFlow 训练脚本的详细指南,请参阅 修改 TensorFlow 训练脚本. 对于 PyTorch,请参阅 修改 PyTorch 训练脚本.

SageMaker调试器

在前面的部分中,我们讨论了如何使用模型和数据并行化技术优化训练。 和 Amazon SageMaker调试器,我们现在可以从训练运行中捕获性能分析信息,以确定训练改进了多少。 默认情况下,Debugger 以 500 毫秒的采样间隔捕获每个 SageMaker 训练作业的系统指标,例如 GPU、CPU 利用率、内存、网络和 I/O。 我们可以通过以下方式访问数据:

from smdebug.profiler.analysis.notebook_utils.training_job import TrainingJob
tj = TrainingJob('SMD-MP-demo-2022-01-21-06-43-23-841', "us-east-1")
tj.wait_for_sys_profiling_data_to_be_available()
system_metrics_reader = tj.get_systems_metrics_reader()

调试器提供实用程序以 想像 以不同的方式分析数据。 在以下示例中,我们看到了使用 Horovod 进行多 GPU 训练作业的总 GPU 和 CPU 利用率以及 I/O 等待时间。 要生成这些图表,我们运行以下代码:

from smdebug.profiler.analysis.notebook_utils.timeline_charts import TimelineCharts view_timeline_charts = TimelineCharts( system_metrics_reader, framework_metrics_reader, select_dimensions=["CPU", "GPU", "I/O"], select_events=["total"], show_workers=False )

GPU 利用率经常在 0-100% 之间波动,而高 I/O 等待时间和低 GPU 利用率是 I/O 瓶颈的指标。 此外,总 CPU 利用率从未超过 70%,这意味着我们可以通过增加工作进程的数量来改进数据预处理。

使用 TensorFlow 和 Amazon SageMaker PlatoBlockchain 数据智能在 TB 级数据上创建、训练和部署十亿参数的语言模型。垂直搜索。人工智能。

我们可以通过从 Horovod 切换到 SageMaker 分布式数据并行库来提高性能。 在下图中,我们可以看到 GPU 的使用效率更高,并且仅在短时间内降至低利用率。

使用 TensorFlow 和 Amazon SageMaker PlatoBlockchain 数据智能在 TB 级数据上创建、训练和部署十亿参数的语言模型。垂直搜索。人工智能。

培训基础设施

为了训练模型,我们使用 SageMaker 训练作业使用了 10 个 ml.p3.16xlarge 实例。 SageMaker 减少了训练和调整机器学习 (ML) 模型的时间和成本,而无需管理基础设施。 借助 SageMaker,您可以使用内置工具轻松训练和调整 ML 模型,以管理和跟踪训练实验、自动选择最佳超参数、调试训练作业以及监控 GPU、CPU 和网络带宽等系统资源的利用率。 数据托管在 Amazon EFS 中,这使我们能够在添加和删除文件时进行扩展和缩减,而无需管理或预置。 我们的主要目标是提高培训速度并降低成本。

模型可扩展性

尽管此基础架构主要用于语言生成,但通过 GPT 架构和 Pile 数据集,您可以使用这些技术来训练大规模的 Transformer 模型,这在 NLP 以外的许多领域都很有用。 在机器学习本身中,许多计算机视觉任务现在可以通过大参数(变压器)架构解决,在这些架构中,它们已被证明在表示学习等任务上优于传统的 CNN(卷积神经网络)(参见 通过自我监督的 Transformers 和 10 倍更高效的培训推进计算机视觉领域的最新技术)和图像到文本的大规模映射(例如 CLIP)。 大参数模型也在生命科学领域开辟了新天地,例如 蛋白质结构分析医学影像数据分析.

我们在这篇文章中详细介绍的分布式训练和管理大型模型的解决方案也应该适用于任何这些领域的模型。

权衡

研究界一直在讨论训练大规模语言模型的风险,以及是否已经对与开发它们相关的潜在风险以及减轻这些风险的策略进行了足够的思考,其中一些包括财务和环境成本。 根据一个 发表在 ACM 上,估计在 GPU 上训练单个 BERT 基础模型(没有超参数调整)需要与跨美洲飞行一样多的能量。 环境影响与模型大小成比例,并且能够有效地微调此类模型可能会显着减少排放。 AWS 最近推出了一个新的 客户碳足迹工具,作为亚马逊提高可持续性和减少碳排放努力的一部分,所有 AWS 客户都可以免费使用。 在 AWS 云上运行应用程序可能会减少碳足迹(与在 2019报告).

结论

这篇博文展示了一个解决方案,该解决方案有助于使用 SageMaker 在 AWS 云上对具有十亿个参数的语言模型进行微调。

有关 SageMaker 模型并行性的更多信息,请参阅 在 Amazon SageMaker 上使用模型并行添加和 Hugging Face 训练 175 多个参数 NLP 模型Latent Space如何使用Amazon SageMaker模型并行性库来推动大型变压器的前沿.

如果您需要帮助以加快在产品和流程中使用ML,请与 亚马逊机器学习解决方案实验室.


作者简介

使用 TensorFlow 和 Amazon SageMaker PlatoBlockchain 数据智能在 TB 级数据上创建、训练和部署十亿参数的语言模型。垂直搜索。人工智能。西亚古拉米 是 Amazon ML 解决方案实验室的高级数据科学家,在那里他为各个行业的客户构建 AI/ML 解决方案。 他对自然语言处理 (NLP) 和深度学习充满热情。 工作之余,Sia 喜欢在大自然中度过时光和打网球。

使用 TensorFlow 和 Amazon SageMaker PlatoBlockchain 数据智能在 TB 级数据上创建、训练和部署十亿参数的语言模型。垂直搜索。人工智能。梅迪·诺里(Mehdi Noori)是 Amazon ML Solutions Lab 的经理和高级应用科学家,在那里他与各行各业的客户合作,帮助他们加快云迁移之旅,并使用最先进的解决方案解决他们的 ML 问题和技术。

使用 TensorFlow 和 Amazon SageMaker PlatoBlockchain 数据智能在 TB 级数据上创建、训练和部署十亿参数的语言模型。垂直搜索。人工智能。金木h 是Amazon Machine Learning Solutions Lab的数据科学家。 他通过应用机器学习和深度学习解决了客户的各种业务问题,还帮助他们提高了技能。

使用 TensorFlow 和 Amazon SageMaker PlatoBlockchain 数据智能在 TB 级数据上创建、训练和部署十亿参数的语言模型。垂直搜索。人工智能。 丹尼·伯德 是 Amazon ML Solutions Lab 的应用科学家。 在实验室,他帮助客户开发先进的 ML 解决方案,涉及从计算机视觉到强化学习的 ML 专业。 他热衷于推动技术向前发展并在此过程中释放 AWS 产品的新潜力。

使用 TensorFlow 和 Amazon SageMaker PlatoBlockchain 数据智能在 TB 级数据上创建、训练和部署十亿参数的语言模型。垂直搜索。人工智能。弗朗西斯科·卡尔德隆·罗德里格斯 是 Amazon ML Solutions Lab 的数据科学家。 作为 ML Solutions Lab 的成员,他使用深度学习帮助 AWS 客户解决关键业务问题。 业余时间,弗朗西斯科喜欢弹奏音乐和吉他,和女儿们一起踢足球,享受与家人在一起的时光。

使用 TensorFlow 和 Amazon SageMaker PlatoBlockchain 数据智能在 TB 级数据上创建、训练和部署十亿参数的语言模型。垂直搜索。人工智能。 中山洋平 是 Amazon ML Solutions Lab 的深度学习架构师。 他与不同垂直领域的客户合作,加速他们使用人工智能和 AWS 云服务来解决业务挑战。 他有兴趣将 ML/AI 技术应用于航天工业。

使用 TensorFlow 和 Amazon SageMaker PlatoBlockchain 数据智能在 TB 级数据上创建、训练和部署十亿参数的语言模型。垂直搜索。人工智能。 娜塔莉(Nathalie Rauschmayr) 是 AWS 的高级应用科学家,她帮助客户开发深度学习应用程序。

时间戳记:

更多来自 AWS机器学习