BertTokenizerFast分词速度远慢于预期(慢3.9万-25.83万倍)求助
问题排查与优化方案
一、核心问题:分词操作在训练时重复执行(极端耗时根源)
你观测到的130-861小时极端耗时,核心原因是Hugging Face Datasets的map方法默认采用延迟执行(Lazy Mode):定义tokenized_dataset时,分词逻辑并没有立即运行,而是等到model_trainer.train()需要读取数据时才触发。如果没有正确配置缓存,甚至会在每次运行时重复执行分词,直接导致耗时爆炸。
解决方法:强制立即执行并缓存分词结果
显式指定缓存文件
在map调用中添加cache_file_name参数,确保分词结果仅计算一次:tokenized_dataset = dataset.map( lambda examples: tokenizer(examples["text"], truncation=True, padding="max_length", max_length=512), batched=True, num_proc=4, remove_columns=["text"], cache_file_name="./tokenized_cache.arrow" # 指定缓存路径 )持久化分词后数据集
分词完成后将数据集保存到磁盘,后续直接加载无需重复处理:# 保存分词结果 tokenized_dataset.save_to_disk("./tokenized_dataset") # 后续运行直接加载 from datasets import load_from_disk tokenized_dataset = load_from_disk("./tokenized_dataset")关闭延迟执行(适合中等规模数据集)
通过with_format强制数据集立即加载到内存:tokenized_dataset = dataset.map(...).with_format("torch", keep_in_memory=True)
二、分词逻辑优化(消除警告+小幅提速)
你收到的警告确实是小幅性能提示,虽非极端耗时根源,但调整代码可消除警告并提升效率:
你的lambda函数确实调用了__call__,但未显式指定截断/填充参数,导致tokenizer可能在后续步骤重复处理。修改分词逻辑如下:
tokenized_dataset = dataset.map( lambda examples: tokenizer( examples["text"], truncation=True, # 自动截断超长文本 padding="max_length", # 统一填充到指定长度 max_length=512, # 匹配BERT最大输入长度 return_special_tokens_mask=True # 可选,辅助后续文本分组 ), batched=True, num_proc=4, remove_columns=["text"], cache_file_name="./tokenized_cache.arrow" )
修改后警告会自动消失,统一文本长度也能减少后续group_texts步骤的处理时间。
三、其他耗时排查点
num_proc适配硬件:若集群/Colab的CPU核心数不足4,num_proc=4会增加进程切换开销,可改为num_proc=os.cpu_count()自动适配。- 检查语料异常:排查
mycorpus.txt是否存在超长单条文本(如几万字的连续内容),这类数据会大幅拖慢分词速度,需提前拆分长句。 - 缓存目录权限:确保集群/Colab的默认缓存目录(
~/.cache/huggingface/datasets)有写入权限,无权限会导致每次重新计算分词。
内容的提问来源于stack exchange,提问作者Syncrossus
相关产品推荐
相关产品推荐

