sno*_*zjy 12 pytorch huggingface-transformers huggingface-tokenizers
我使用pytorch来训练huggingface-transformers模型,但是每个epoch,总是输出警告:
The current process just got forked. Disabling parallelism to avoid deadlocks... To disable this warning, please explicitly set TOKENIZERS_PARALLELISM=(true | false)
Run Code Online (Sandbox Code Playgroud)
如何禁用此警告?
flo*_*flo 25
我将在这里留下此评论,以帮助任何想知道是否可以保持并行性的人。也因为它是直接在 Google 上搜索错误时的第一个 stackoverflow 页面。
根据github 上的评论,FastTokenizers似乎是问题所在。另外,根据gitmemory 上的另一条评论, 您不应在分叉进程之前使用分词器。(这基本上意味着在迭代数据加载器之前)
因此,解决方案是在训练/微调之前不要使用 FastTokenizers或使用普通的 Tokenizers。
检查 Huggingface 文档,了解您是否真的需要 FastTokenizer。
All*_*hvk 23
如果您明确选择了快速(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 分叉时执行此操作(这就是导致首先显示警告的原因)。我们能避免这种情况吗?
让我们退一步看看警告本身。
“在使用并行性之后,当前进程刚刚被分叉。禁用并行性以避免死锁...要禁用此警告,您可以: -tokenizers如果可能,避免在分叉之前使用 - 显式设置环境变量 TOKENIZERS_PARALLELISM=(true |假)”
这种情况仅发生在 HF 的 FastTokenizers 中,因为它们在 Rust 中进行并行处理。在这种情况下,当我们在 Python 中通过多处理分叉进程时,就会发生冲突。发生分叉是因为我们在 train() 方法中开始循环数据加载器(num_workers>0)。这种组合被认为工作不安全,如果遇到这种情况,标记器会自行关闭并行性以避免死锁。当我们在这里谈论并行性时,我们严格指代标记器代码,而不是其他任何东西。换句话说,只有我们将输入文本数据转换为标记的代码部分(例如使用 tokenizer.encode_plus 或任何其他函数)受到影响。因此,这不会影响 num_workers 的并行线程的使用,因为它利用了多个 GPU 核心……就像数据加载器功能一样。我们怎么能知道这一点呢?好吧,我们可以尝试在数据集 get_item 函数中添加 5 秒的延迟以及打印语句,然后通过循环数据加载器来查看 num_workers 的差异值。当 num_workers = 0 时,主进程会执行繁重的工作,并且读取之间有 5 秒的间隔。当 num_workers = 1 发生分叉时,我们会收到上述有关并行性的警告,并且由于主进程不参与数据提升,因此我们在获取之间仍然有 5 秒的间隙。从 num_workers > 2 开始,根据 num_workers 以 5 秒的间隔进行多次提取。
事实上,这得出的结论是,修复上述警告的一个简单选项可能是在数据加载器定义中简单地设置 num_workers = 0。如果 num_workers 为 0,则不存在 Python 分支,主进程本身会执行所有数据提升。这是可行的,我们现在能够充分利用快速分词器的强大功能,但要以消除 Python 端的并行处理为代价。考虑到数据加载器在并行模式下工作得最好,通过从主机(CPU)并行预取批次到 GPU 来执行,这通常不是一个好的选择。
如果我们设置 TOKENIZERS_PARALLELISM=true 会发生什么?在最新版本的 Pytorch、变压器、分词器等上,如果您执行此操作,然后尝试在数据加载器中使用 num_workers>0 进行训练,您的训练将冻结,不会出现任何错误,甚至不会出现警告消息。事实上,这个问题促使我发布这个答案,因为我在任何地方都找不到解决训练冻结问题的解决方案。根本原因实际上是数据加载器由于上述冲突而在这种情况下失败(由于担心死锁而拒绝“分叉”)。
回到我们的核心问题,基于 RUST 的并行性似乎与我们在 Python 中所做的分叉相冲突。然而,通过在训练调用之前(即在使用数据加载器之前)简单地删除所有标记器的使用,可以很容易地解决这个问题。很多时候,我们可能会使用标记器通过执行 my_dataset_name[0] 来查看标记化输出是什么等。只需删除所有此类标记器调用,并让 train() 函数循环成为第一次访问标记器即可。这个简单的修复使得 RUST 并行化发生在 Python 分支之后,这应该可以工作。
或者预先将数据转换为标记并将其存储在字典中。那么你的数据集根本不应该使用分词器,但在运行时只需调用 dict(key) ,其中 key 是索引。这样你就可以避免冲突。警告仍然出现,但您在训练期间不再使用 tokeniser(请注意这种情况以节省空间,避免在 tokenise 期间填充并稍后使用 collate_fn 添加)
话虽如此,Rust 标记生成器的实现速度非常快,即使在标记生成器内部调用序列化选项(即如果在标记生成器中自动禁用并行性),通常也没关系。它仍然击败了传统的标记器。
因此,在大多数情况下,人们可以忽略警告并在执行期间禁用分词器并行化...或者从一开始就将 TOKENIZERS_PARALLELISM 显式设置为 False。在极少数情况下,速度至关重要,可以探索上述建议的选项之一。
小智 19
将环境变量设置为字符串 "false"
或者通过
TOKENIZERS_PARALLELISM=false
Run Code Online (Sandbox Code Playgroud)
在你的壳里
或通过:
import os
os.environ["TOKENIZERS_PARALLELISM"] = "false"
Run Code Online (Sandbox Code Playgroud)
在 Python 脚本中
| 归档时间: |
|
| 查看次数: |
6766 次 |
| 最近记录: |