重要提示
您正在查看 NeMo 2.0 文档。此版本为 API 和新库 NeMo Run 引入了重大更改。我们目前正在将所有功能从 NeMo 1.0 移植到 2.0。有关先前版本或 2.0 中尚不可用的功能的文档,请参阅 NeMo 24.07 文档。
最佳实践#
选择正确的质量模型类型#
NeMo Curator 提供了多种方法来确定一段文本的质量。以下是按计算所需量递增顺序排列的方法。
fastText 是一种基于 n-gram 的词袋分类器。它通常在高品质参考语料库和低品质语料库(通常是未经过滤的 Common Crawl 转储)上进行训练。虽然 NeMo Curator 不提供分类器的预训练版本,但训练它非常快速且容易。它仅需要 100,000 - 1,000,000 个文本样本进行训练,并且可以在短短几秒钟内完成训练。它的小尺寸还允许它在 CPU 上训练和运行推理。由于这些因素,我们建议在大型预训练数据集上使用 fastText 分类器,在这些数据集中,您没有计算预算来使用更复杂的方法。
BERT 风格分类器 - NeMo Curator 的分布式数据分类模块与许多 BERT 风格分类器一起用于领域分类、质量分类等。为了进行此比较,我们将仅关注文本质量分类器。NeMo Curator 在 HuggingFace 和 NGC 上提供了分类器的预训练版本,可以立即使用。我们建议在预训练的数据过滤管道的末尾使用这些分类器。
语言模型标记 - 语言模型可用于将文本标记为高质量或低质量。NeMo Curator 允许您连接到任意 LLM 推理端点,您可以使用这些端点来标记您的数据。此类端点的一个示例是 build.nvidia.com 上的 Nemotron-4 340B Instruct。由于它们的大小,这些模型可能需要大量计算,并且通常无法在整个预训练数据集上运行。我们建议在非常少量的数据上使用这些大型模型。微调数据集可以很好地利用它们。
奖励模型标记 - 与以前的方法不同,奖励模型标记用户和助手之间对话的质量,而不是标记文档的质量。此外,模型(如 Nemotron-4 340B Reward)可能会输出涵盖不同类别的多个分数。与 LLM 标记类似,NeMo Curator 可以连接到作为外部服务托管的任意奖励模型。由于这些差异及其庞大的规模,我们建议在过滤微调数据时使用奖励模型。特别是,合成数据过滤是对它们的良好利用。
处理 GPU 内存不足 (OOM) 错误#
NeMo Curator 旨在通过大量文本数据进行扩展,但当可用 GPU 内存不足以完成给定任务时,会发生 OOM 错误。为了帮助避免这些问题并确保高效处理,以下是一些管理内存使用情况和缓解 OOM 挑战的策略。
控制分区大小#
用户应考虑在使用 files_per_partition
或 blocksize
读取数据时。这可以通过以较小的块处理大型数据集来帮助减少内存负载。
blocksize
参数可用于jsonl
和parquet
文件。但是,对于 parquet 文件,当前仅当add_filename=False
时可用。对于
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_json
或DocumentDataset.read_parquet
读取数据时,从较小的files_per_partition
值开始,并根据需要增加。读取数据时,我们建议旨在创建不大于 2GB 的分区。例如,如果您知道每个文件约为 100MB,则设置
files_per_partition=20
将导致分区大小约为 2GB。有关使用 Dask 读取数据的最佳实践的其他建议,请参阅 Dask cuDF 最佳实践。
使用 get_client
函数#
对于 GPU 和 CPU 操作,我们分别提供 get_client
以使用 LocalCUDACluster
或 LocalCluster
初始化您的 Dask 客户端。虽然 NeMo Curator 团队已为 get_client
函数的参数建立了适用于大多数场景的默认值,但了解这些参数并熟悉它们对于确保在使用 Dask 配置和设置时获得最佳性能并遵守最佳实践非常有用。
请参阅 API 文档 Dask 集群函数,了解有关 get_client
函数参数的更多详细信息。您还可以参考 distributed_utils.py 脚本,了解实际的函数实现,包括 start_dask_gpu_local_cluster
和 start_dask_cpu_local_cluster
函数,它们由 get_client
调用。
添加更多 GPU#
如果可能,通过添加更多 GPU 来扩展您的系统。这提供了额外的 VRAM(视频随机存取内存),这对于保存数据集和中间计算至关重要。因此,添加更多 GPU 可以让您分配工作负载,从而减少每个 GPU 上的内存负载。
报告 GPU 内存和利用率#
在调试 GPU 内存错误时,捕获和了解 NeMo Curator 管道中每个步骤的 GPU 使用率可能很有用。Dask 仪表板 是查看 GPU 利用率和内存的良好起点。您还可以参考 本文,获取更深入的教程,包括如何使用仪表板监控 GPU。