如何禁用TOKENIZERS_PARALLELISM=(true | false)警告?

47
我使用pytorch来训练huggingface-transformers模型,但每个时期总是会输出警告:

The current process just got forked. Disabling parallelism to avoid deadlocks... To disable this warning, please explicitly set TOKENIZERS_PARALLELISM=(true | false)

如何禁用此警告?

5个回答

68

将环境变量设置为字符串 "false"

可以通过以下两种方式之一来实现:

TOKENIZERS_PARALLELISM=false

在你的命令行界面中

或通过:

import os
os.environ["TOKENIZERS_PARALLELISM"] = "false"

在 Python 脚本中


1
对我有用。不过,还是值得一看这个答案,指出使用快速分词器可能是此问题的来源,并且您可能需要注意使用它们的任何后果。 - alelom
还请注意下面我的说明 - https://dev59.com/nlIG5IYBdhLWcg3w2VMZ#72926996 - Allohvk
我必须执行导出TOKENIZERS_PARALLELISM=false。 - alvitawa
如果将其设置为“true”,警告也会消失。重要的是将其设置为某个值。 - Crouching Kitten

22
我会把这个评论留在这里,以帮助任何想知道是否可能保持并行性的人。也因为这是直接在Google上搜索错误时第一个出现的stackoverflow页面。
根据github上的评论FastTokenizers似乎是问题所在。还有根据gitmemory上的另一条评论在分叉进程之前不应该使用令牌化器。(基本上意味着在遍历数据集加载器之前)
因此,解决方案是在训练/微调之前不要使用FastTokenizers,或者使用正常的Tokenizers。
查看huggingface文档以确定是否真的需要FastTokenizer。

这个警告信息是不是意味着训练/微调没有以并行方式进行? - Ritwik
不符合我的经验。我进行了两个实验:(a)一个带有此警告消息,(b)另一个没有。我只保存了来自(a)的数据加载器,并使用torch.save()torch.load()简单地加载它。两个实验在大约相同的时间内完成(每个时代1小时,共3个时代)。 - Ritwik
2
如何在训练后使用 FastTokenizers 的示例以及使用“普通” Tokenizer 的示例? - Alaa M.
为什么你想在训练后使用FastTokenizers呢?你应该在训练/推理期间使用它们。文档告诉你如何使用“正常”的Tokenizers。 - flo
不,它不会影响其他任何东西。请看我下面的说明。 - Allohvk

19
如果您明确选择了快速(Rust代码)的标记化程序,那么您可能有自己的理由。在处理大型数据集时,基于Rust的标记化程序可以更快地处理数据,并且可以通过在标记化程序创建期间设置"use_fast"选项来明确调用它们。现在几乎所有的HF模型都带有此选项。尽管从警告消息中不太明显,TOKENIZERS_PARALLELISM是一个环境变量而不是标记化程序超参数。将其设置为False就可以解决问题,但正如上面的一些评论所示,人们对此变化的影响存在困惑。例如,这是否会影响模型级别的并行性?让我们看看Rust代码,以了解默认行为以及如果关闭它会发生什么来解决问题。

https://docs.rs/tokenizers/latest/src/tokenizers/utils/parallelism.rs.html 在大多数情况下,我们(终端用户)不会显式地将TOKENIZERS_PARALLELISM设置为True或False。对于所有这些情况,分词器代码都会假定它为TRUE。我们可以通过将其设置为False来显式禁用它。但是,您可以看到,在这种情况下,代码使迭代器序列化。即使您不将此环境变量设置为False,执行代码本身也会在遇到Python forks时这样做(这就是导致首先显示警告的原因)。我们能避免这种情况吗?

让我们退一步看看警告本身。

"当前进程在使用并行处理后被分叉。禁用并行处理以避免死锁...要禁用此警告,您可以:-如果可能,请避免在分叉之前使用tokenizers - 显式设置环境变量TOKENIZERS_PARALLELISM =(true | false)"

只有使用HF的FastTokenizers时才会出现这种情况,因为它们在Rust中进行并行处理。在这种情况下,当我们通过Python中的多进程派生进程时会发生冲突。派生发生是因为我们将开始在train()方法中循环数据加载器(num_workers>0)。这种组合被认为是不安全的,如果遇到这种情况,标记器会关闭自身的并行性以避免死锁。当我们在这里谈论并行性时,我们严格指的是标记器代码,而不是其他任何东西。换句话说,我们仅影响将输入文本数据转换为标记的代码部分(例如使用tokenizer.encode_plus或任何其他函数)。因此,这不应影响使用利用多个GPU核心的num_workers并行线程的数据加载器函数等。我们如何知道这一点呢?好吧,我们可以尝试在dataset get_item函数中添加一个5秒的延迟和一个打印语句,然后通过循环不同值的num_workers来查看自己。当num_workers = 0时,主进程完成繁重的工作,获取之间有5秒的间隔。当num_workers = 1时,会发生分叉,我们会得到关于并行性的上述警告,并且由于主进程不参与数据提取,因此我们仍然在获取之间获得5秒的间隔。从num_workers>2开始,在5秒的间隔内根据num_workers获取多个提取。
事实上,解决上述警告的简单方法可能是在数据加载器定义中将num_workers = 0。如果num_workers为0,则没有Python fork,主进程本身执行所有数据提取操作。这种方法有效,我们能够充分利用快速分词器的优势,但牺牲了Python端的并行处理。考虑到数据加载器通过在主机(CPU)上并行预取批次以供GPU执行而以并行模式最佳工作,这通常不是一个好的选择。
如果我们设置TOKENIZERS_PARALLELISM = true会发生什么?在最新版本的Pytorch,transformers,tokenisers等中,如果您这样做,然后尝试使用num_workers> 0在数据加载器中进行训练,您的训练将冻结,没有任何错误或警告消息。事实上,这个问题激励我发布这个答案,因为我无法找到解决那个训练冻结问题的方法。根本原因实际上是数据加载器在这种情况下失败,由于上述冲突(它拒绝“fork”因为害怕死锁)。
所以回到我们的核心问题,似乎基于RUST的并行性与我们在Python中进行的fork存在冲突。然而,这可以通过简单地在训练调用之前(即在数据加载器被使用之前)删除所有对分词器的使用来轻松解决。很多时候,我们可能会使用分词器来查看令牌化输出等,只需删除所有这样的分词器调用,让train()函数循环成为分词器第一次被访问的时间。这个简单的修复使得RUST并行化发生在Python fork之后,这应该可以工作。
另一种选择是预先将您的数据转换为令牌,并将它们存储在字典中。然后,您的数据集不应该使用分词器,但在运行时只需调用dict(key),其中key是索引。这样就避免了冲突。警告仍然存在,但您不再在训练过程中使用分词器(注意,在这种情况下为了节省空间,避免在令牌化期间进行填充,并稍后使用collate_fn添加)。
话虽如此,Rust分词器实现速度非常快,即使在分词器内部调用序列化选项(即自动禁用分词器的并行性)时也通常不重要。它仍然击败了传统的分词器。
在大多数情况下,可以忽略警告并在执行期间禁用标记化程序并行处理... 或从一开始就明确将TOKENIZERS_PARALLELISM设置为False。在极少数情况下,速度至关重要,可以探索上述建议的选项之一。

1
确认。忽略警告信息就可以了。 - runzhi xiao
1
我会对像"只是忽略警告"这样简单的建议持怀疑态度并感到不满。我需要理解其中的原因。谢谢您详细的解释。 - Luke Kurlandski

2
我通过将Huggingface的transformers库版本从3.0.0降级到2.11.0,以及将tokenizers库版本从0.8.0rc4降级到0.7.0,解决了这个问题。
这似乎是Huggingface的tokenizer库版本“0.8.0rc4”的问题。目前,似乎没有解决方案来设置TOKENIZERS_PARALLELISM=(true|false),就像错误信息所说的那样。
参考:https://github.com/ThilinaRajapakse/simpletransformers/issues/515

1
将环境变量设置为字符串"false":
1. 在Bash中
export TOKENIZERS_PARALLELISM=false

关于Python
import os
os.environ["TOKENIZERS_PARALLELISM"] = "false"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接