重要提示

您正在查看 NeMo 2.0 文档。此版本为 API 和新库 NeMo Run 引入了重大更改。我们目前正在将所有功能从 NeMo 1.0 移植到 2.0。有关先前版本或 2.0 中尚不可用的功能的文档,请参阅 NeMo 24.07 文档

最佳实践#

选择正确的质量模型类型#

NeMo Curator 提供了多种方法来确定一段文本的质量。以下是按计算所需量递增顺序排列的方法。

  1. fastText 是一种基于 n-gram 的词袋分类器。它通常在高品质参考语料库和低品质语料库(通常是未经过滤的 Common Crawl 转储)上进行训练。虽然 NeMo Curator 不提供分类器的预训练版本,但训练它非常快速且容易。它仅需要 100,000 - 1,000,000 个文本样本进行训练,并且可以在短短几秒钟内完成训练。它的小尺寸还允许它在 CPU 上训练和运行推理。由于这些因素,我们建议在大型预训练数据集上使用 fastText 分类器,在这些数据集中,您没有计算预算来使用更复杂的方法。

  2. BERT 风格分类器 - NeMo Curator 的分布式数据分类模块与许多 BERT 风格分类器一起用于领域分类质量分类等。为了进行此比较,我们将仅关注文本质量分类器。NeMo Curator 在 HuggingFace 和 NGC 上提供了分类器的预训练版本,可以立即使用。我们建议在预训练的数据过滤管道的末尾使用这些分类器。

  3. 语言模型标记 - 语言模型可用于将文本标记为高质量或低质量。NeMo Curator 允许您连接到任意 LLM 推理端点,您可以使用这些端点来标记您的数据。此类端点的一个示例是 build.nvidia.com 上的 Nemotron-4 340B Instruct。由于它们的大小,这些模型可能需要大量计算,并且通常无法在整个预训练数据集上运行。我们建议在非常少量的数据上使用这些大型模型。微调数据集可以很好地利用它们。

  4. 奖励模型标记 - 与以前的方法不同,奖励模型标记用户和助手之间对话的质量,而不是标记文档的质量。此外,模型(如 Nemotron-4 340B Reward)可能会输出涵盖不同类别的多个分数。与 LLM 标记类似,NeMo Curator 可以连接到作为外部服务托管的任意奖励模型。由于这些差异及其庞大的规模,我们建议在过滤微调数据时使用奖励模型。特别是,合成数据过滤是对它们的良好利用。

处理 GPU 内存不足 (OOM) 错误#

NeMo Curator 旨在通过大量文本数据进行扩展,但当可用 GPU 内存不足以完成给定任务时,会发生 OOM 错误。为了帮助避免这些问题并确保高效处理,以下是一些管理内存使用情况和缓解 OOM 挑战的策略。

控制分区大小#

用户应考虑在使用 files_per_partitionblocksize 读取数据时。这可以通过以较小的块处理大型数据集来帮助减少内存负载。

  1. blocksize 参数可用于 jsonlparquet 文件。但是,对于 parquet 文件,当前仅当 add_filename=False 时可用。

  2. 对于 blocksize 参数,建议使用总 GPU 内存的 1/32。例如,如果您有一个具有 32GB 内存的 GPU,则可以将 blocksize="1GB" 设置为 。

利用 RMM 选项#

RAPIDS 内存管理器 (RMM) 是一个软件包,使您能够以高度可配置的方式分配设备内存。NeMo Curator 团队发现它的几个功能对于模糊去重特别有用,尤其是连接组件步骤。以下是一些可以帮助优化内存使用率的功能

  • 启用异步内存分配:使用 --rmm-async 标志允许 RMM 更有效地处理内存分配,方法是异步分配和释放 GPU 内存。

  • 设置内存释放阈值:例如,--rmm-release-threshold 50GB 可以帮助防止占用过多内存,并在达到特定限制时释放未使用的内存。请记住,使用此标志可能会稍微降低性能,因为 RMM 正忙于释放未使用的内存。

您可以将这些参数直接传递到 NeMo Curator 的 get_client 函数中,该函数会为您初始化 Dask 客户端

from nemo_curator.utils.distributed_utils import get_client

client = get_client(
  cluster_type="gpu",
  rmm_async=True,
  rmm_release_threshold="50GB",
)

或者,您可以在初始化自己的 Dask 客户端时设置这些标志,例如

from dask_cuda import LocalCUDACluster
from dask.distributed import Client

cluster = LocalCUDACluster(
    rmm_async=True,
    rmm_release_threshold="50GB",
)

client = Client(cluster)

模糊去重指南#

模糊去重是 NeMo Curator 管道中最计算密集型的算法之一。以下是一些关于管理模糊去重期间内存使用的建议

  • 减少存储桶计数:在去重期间,数据被分组到存储桶中以比较和识别近似重复文档。增加存储桶的数量会增加给定 Jaccard 相似度评分内的两个文档被标记为重复项的概率。但是,增加存储桶的数量也会因需要存储的哈希数增加而增加内存需求。因此,找到内存使用率和去重精度之间的最佳平衡非常重要。您可以通过在使用 FuzzyDuplicatesConfig 初始化 num_buckets 参数时进行实验。

    • 用户可能还需要更改 hashes_per_bucket 参数以匹配旨在达到的相同 Jaccard 阈值。这样想:在高 num_buckets 和低 hashes_per_bucket 的情况下,字符串的哈希将分散在许多存储桶中,这减少了不相似的字符串被哈希到同一存储桶中的机会,但增加了相似的字符串被哈希到不同存储桶中的机会。另一方面,在低 num_buckets 和高 hashes_per_bucket 的情况下,哈希将更密集地打包到更少数量的存储桶中,这不仅增加了相似字符串共享存储桶的可能性,而且还增加了不相似字符串被哈希到同一存储桶中的机会。

  • 减少每次洗牌的存储桶数:由于重复项仍然按存储桶逐个考虑,因此减少 FuzzyDuplicatesConfig 中的 buckets_per_shuffle 参数不会影响准确性。相反,减少每次洗牌的存储桶数有助于减少 GPU 之间传输的数据量。但是,使用较低的 buckets_per_shuffle 将增加处理数据所需的时间。

  • 调整每个分区的的文件数:以较小的块处理大型数据集可以帮助减少内存负载。当使用 DocumentDataset.read_jsonDocumentDataset.read_parquet 读取数据时,从较小的 files_per_partition 值开始,并根据需要增加。

    • 读取数据时,我们建议旨在创建不大于 2GB 的分区。例如,如果您知道每个文件约为 100MB,则设置 files_per_partition=20 将导致分区大小约为 2GB。

    • 有关使用 Dask 读取数据的最佳实践的其他建议,请参阅 Dask cuDF 最佳实践

使用 get_client 函数#

对于 GPU 和 CPU 操作,我们分别提供 get_client 以使用 LocalCUDAClusterLocalCluster 初始化您的 Dask 客户端。虽然 NeMo Curator 团队已为 get_client 函数的参数建立了适用于大多数场景的默认值,但了解这些参数并熟悉它们对于确保在使用 Dask 配置和设置时获得最佳性能并遵守最佳实践非常有用。

请参阅 API 文档 Dask 集群函数,了解有关 get_client 函数参数的更多详细信息。您还可以参考 distributed_utils.py 脚本,了解实际的函数实现,包括 start_dask_gpu_local_clusterstart_dask_cpu_local_cluster 函数,它们由 get_client 调用。

添加更多 GPU#

如果可能,通过添加更多 GPU 来扩展您的系统。这提供了额外的 VRAM(视频随机存取内存),这对于保存数据集和中间计算至关重要。因此,添加更多 GPU 可以让您分配工作负载,从而减少每个 GPU 上的内存负载。

报告 GPU 内存和利用率#

在调试 GPU 内存错误时,捕获和了解 NeMo Curator 管道中每个步骤的 GPU 使用率可能很有用。Dask 仪表板 是查看 GPU 利用率和内存的良好起点。您还可以参考 本文,获取更深入的教程,包括如何使用仪表板监控 GPU。