Embedding在深度学习推荐模型中起着关键作用。它们被用于将输入数据中的离散特征映射到向量,以便下游的神经网络进行处理。Embedding 通常构成深度学习推荐模型中的大部分参数,大小可以达到 TB 级。在训练期间,很难将它们放入单个 GPU 的内存中。因此,现代推荐系统可能需要模型并行和数据并行的分布式训练方法组合,以最佳利用GPU计算资源来实现最好的训练性能。
NVIDIA Merlin Distributed-Embeddings ,可以方便TensorFlow 2 用户用短短几行代码轻松完成大规模的推荐模型训练。
背景
在数据并行分布式训练中,整个模型被复制到每个 GPU 上。在训练过程中,一批输入数据在多个 GPU 中分割,每张卡独立处理其自己的数据分片,从而允许计算扩展到更大批量的数据。在反向传播期间,计算的梯度通过reduction算子(例如, horovod.tensorflow.allreduce ) 来同步更新多个GPU间的参数。
另一方面,模型并行分布式训练中,模型参数被分割到多个GPU上。这种方法更适合分布存储大型embedding。训练中,每个GPU 通过alltoall通信算子(例如, ) 访问不在本机中的参数。
在之前的相关文章中, 用TensorFlow 2 在DGX A100上训练100B +参数的推荐系统 , Tomasz 讨论了如何将1130 亿参数的DLRM 模型中的embedding分布到多个 NVIDIA GPU 进行训练,并相比纯CPU 的方案实现 672 倍的性能提升。这一重大突破可以将训练时间从几天缩短到几分钟!这是通过模型并行 embedding层和数据并行MLP层来实现的。和CPU方案相比,这种混合并行的方法能够有效利用GPU 的高内存带宽加速内存受限的embedding查找,并同时利用多个 GPU 的算力加速 MLP 层。作为参考, NVIDIA A100-80GB GPU 具有超过 2 TB / s 的带宽和 80 GB HBM2 存储)。
图 1.用于训练大型推荐系统的通用“混合并行”方法
embedding表可以按表为分割单位(图中表 0 和 N ),按”列“分割(图中表 2),或者按”行”分割。MLP 层跨所有 GPU 复制,而数字特征则可以直接输入 MLP 层。
然而,实现这种复杂的混合并行训练方法并不简单,需要领域内专家设计数百行底层代码来开发和优化。为了使其更普适, NVIDIA Merlin Distributed-Embeddings 提供了一些易于使用的TensorFlow 2的封装,让所有人都只需三行 Python 代码即可轻松实现模型并行。它提供了一些涵盖并拓展原生TensorFlow功能的高性能embedding查找算子。在此基础上,它提供了一个可规模化的模型并行封装函数,帮助用户自动将embedding分布于多个GPU上。下面将展示它如何实现混合并行。
分布式模型并行
NVIDIA Merlin Distributed-Embeddings 提供了 distributed_embeddings.dist_model_parallel 模块。它有助于在多个 GPU 之间分布embedding而无需任何复杂的代码来处理跨GPU间的通信(如 all2all )。下面的代码示例显示了此 API 的用法:
import dist_model_parallel as dmp
class MyEmbeddingModel(tf.keras.Model):
def __init__(self, table_sizes):
…
self.embedding_layers = [tf.keras.layers.Embedding(input_dim, output_dim) for input_dim, output_dim in table_sizes]
# 1. Add this line to wrap list of embedding layers used in the model
self.embedding_layers = dmp.DistributedEmbedding(self.embedding_layers)
def call(self, inputs):
# embedding_outputs = [e(i) for e, i in zip(self.embedding_layers, inputs)]
embedding_outputs = self.embedding_layers(inputs)
…
要使用 Horovod 以数据并行方式运行MLP层,请将 Horovod的 Distributed GradientTape 和 broadcast 方法替换成 NVIDIA Merlin Distributed-Embeddings 里同等的API。以下示例直接取自 Horovod 文档,并进行了相对应修改。
@tf.function
def training_step(inputs, labels, first_batch):
with tf.GradientTape() as tape:
probs = model(inputs)
loss_value = loss(labels, probs)
# 2. Change Horovod Gradient Tape to dmp tape
# tape = hvd.DistributedGradientTape(tape)
tape = dmp.DistributedGradientTape(tape)
grads = tape.gradient(loss_value, model.trainable_variables)
opt.apply_gradients(zip(grads, model.trainable_variables))
if first_batch:
# 3. Change Horovod broadcast_variables to dmp’s
# hvd.broadcast_variables(model.variables, root_rank=0)
dmp.broadcast_variables(model.variables, root_rank=0)
return loss_value
通过这些微小的改变,您就可以使用混合并行训练了!
我们还提供了以下完整示例: 使用 Criteo 1TB 点击日志数据训练 DLRM 模型 以及 扩展到 22.8 TiB的合成数据 模型。
性能
为了展示 NVIDIA Merlin Distributed-Embeddings 的性能,我们在 Criteo 1TB 数据集DLRM 模型和最高达到 3 TiB embedding的合成模型上进行了模型训练的基准测试。
Criteo 数据集上的 DLRM 模型基准测试
测试表明,我们使用更简单的 API 取得了近似于专家代码的性能。NVIDIA深度学习DLRM TensorFlow 2示例代码现已更新为使用 NVIDIA Merlin Distributed-Embeddings 进行分布式混合并行训练,更多信息请参阅我们之前的文章, 用TensorFlow 2 在DGX A100上训练100B +参数的推荐系统. README中的 基准测试 部分提供了对性能结果的更多详述。
我们对1130 亿个参数( 421 个 GiB 大小)的 DLRM 模型在 Criteo TB 点击日志 数据集上用三种不同的硬件设置进行了训练:
- 仅 CPU 的解决方案。
- 单 GPU 解决方案,其中 CPU 内存用于存储最大的embedding表。
- 使用 NVIDIA DGX A100-80GB 的 8 GPU 的混合并行解决方案。此方案利用了 NVIDIA Merlin Distributed-Embeddings 里提供的模型并行api和embedding API 。
Hardware |
Description |
Training Throughput (samples/second) |
Speedup over CPU |
2 x AMD EPYC 7742 |
Both MLP layers and embeddings on CPU |
17.7k |
1x |
1 x A100-80GB; 2 x AMD EPYC 7742 |
Large embeddings on CPU, everything else on GPU |
768k |
43x |
DGX A100 (8xA100-80GB) |
Hybrid parallel with NVIDIA Merlin Distributed-Embeddings, whole model on GPU |
12.1M |
683x |
表 1.各种设置的训练吞吐量和加速
我们观察到, DGX-A100 上的 NVIDIA Merlin Distributed-Embeddings 方案比仅使用 CPU 的解决方案提供了惊人的 683 倍的加速!我们还注意到与单GPU 方案相比,混合并行的性能也有显著提升。这是因为在 GPU 显存中存储所有embedding避免了通过 CPU-GPU 接口查找embedding的开销。
合成模型基准测试
为了进一步演示方案的可规模化,我们创建了不同大小的合成数据以及对应的 DLRM 模型(表 2 )。有关模型生成方法和训练脚本的更多信息,请参见 GitHub NVIDIA-Merlin/distributed-embeddings 代码库。
Model |
Total number of embedding tables |
Total embedding size (GiB) |
Tiny |
55 |
4.2 |
Small |
107 |
26.3 |
Medium |
311 |
206.2 |
Large |
612 |
773.8 |
Jumbo |
1,022 |
3,109.5 |
表 2.合成模型尺寸
每个合成模型使用一个或多个 DGX-A100-80GB 节点进行训练,全局数据batch大小为 65536 ,并使用 Adagrad 优化器。从表 3 中可以看出, NVIDIA Merlin Distributed-Embeddings 可以在数百个 GPU 上轻松训练 TB 级模型。
Model |
Training step time (ms) |
||||
1 GPU |
8 GPU |
16 GPU |
32 GPU |
128 GPU |
|
Tiny |
17.6 |
3.6 |
3.2 |
|
|
Small |
57.8 |
14.0 |
11.6 |
7.4 |
|
Medium |
|
64.4 |
44.9 |
31.1 |
17.2 |
Large |
|
|
|
65.0 |
33.4 |
Jumbo |
|
|
|
|
102.3 |
表 3.各种硬件配置下合成模型的训练步长时间( ms )
另一方面,与传统的数据并行相比,即使对于可以容纳在单个 GPU 中的模型,多GPU分布式模型并行仍然提供了显著加速。表 4 显示了上述Tiny模型在DGX A100-80GB 上的性能对比。
Solution |
Training step time (ms) |
|||
1 GPU |
2 GPU |
4 GPU |
8 GPU |
|
NVIDIA Merlin Distributed Embeddings Model Parallel |
17.7 |
11.6 |
6.4 |
4.2 |
Native TensorFlow Data Parallel |
19.9 |
20.2 |
21.2 |
22.3 |
表 4. Tiny模型( 4.2GiB )的训练步长时间( ms )比较 NVIDIA Merlin Distributed-Embeddings 模型并行和原生 TensorFlow 数据并行
本实验使用了 65536 的全局批量和 Adagrad 优化器。
结论
在这篇文章中,我们介绍了 NVIDIA Merlin Distributed-Embeddings,仅需几行代码即可在 NVIDIA GPU 上实现基于 embedding的深度学习模型,并进行可规模化,高效率地模型并行训练。欢迎尝试以下 使用合成数据的可扩展训练示例 和 基于 Criteo 数据训练 DLRM 模型 示例.