在本教程中,您将学习如何使用基于Habana Gaudi的DL1实例在AWS上从头开始预训练BERT-base,以利用Gaudi的性价比优势。我们将使用Hugging Face Transformers、Optimum Habana和Datasets库来使用遮蔽语言建模预训练BERT-base模型,这是BERT的两个原始预训练任务之一。在开始之前,我们需要设置深度学习环境。
查看代码
您将学习如何:
- 准备数据集
- 训练一个分词器
- 预处理数据集
- 在Habana Gaudi上预训练BERT
注意:步骤1到3可以/应该在不同的实例大小上运行,因为这些是CPU密集型任务。
要求
在开始之前,请确保您满足以下要求:
- 具有DL1实例类型配额的AWS账户
- 已安装AWS CLI
- 在CLI中配置了AWS IAM用户,并拥有创建和管理EC2实例的权限
有用的资源
- 为Hugging Face Transformers与Habana Gaudi在AWS上设置深度学习环境
- 使用EC2远程运行器和Habana Gaudi进行简化的深度学习设置
- Optimum Habana文档
- 预训练脚本
- 代码:pre-training-bert.ipynb
BERT是什么?
BERT是来自Transformer的双向编码器表示的缩写,是用于自然语言处理的机器学习模型。它由Google AI Language的研究人员于2018年开发,并作为解决11种以上最常见语言任务的瑞士军刀解决方案,例如情感分析和命名实体识别。
在我们的BERT 101 🤗最先进的NLP模型解释博客中了解更多关于BERT的内容。
什么是遮蔽语言建模(MLM)?
MLM通过遮蔽(隐藏)句子中的一个单词,并强制BERT双向使用覆盖词的两侧的词来预测被隐藏的单词,从而实现/强制了从文本中进行双向学习。
遮蔽语言建模示例:
“天啊!我在钓鱼,一条巨大的鳟鱼刚刚[MASK]了我的鱼线!”
在此处阅读更多关于遮蔽语言建模。
让我们开始吧。🚀
注意:步骤1到3是在AWS c6i.12xlarge实例上运行的。
1. 准备数据集
本教程被“分割”成两个部分。第一部分(步骤1-3)是关于准备数据集和分词器。第二部分(步骤4)是关于在准备好的数据集上预训练BERT。在我们开始数据集准备之前,我们需要设置我们的开发环境。如介绍中所提到的,您不需要在DL1实例上准备数据集,可以使用您的笔记本电脑或台式机。
首先,我们将安装transformers
、datasets
和git-lfs
,以便将我们的分词器和数据集推送到Hugging Face Hub以供以后使用。
!pip install transformers datasets
!sudo apt-get install git-lfs
完成设置后,让我们登录到Hugging Face Hub,以便在训练期间和之后将我们的数据集、分词器、模型工件、日志和指标推送到Hub。
为了能够将我们的模型推送到Hub,您需要在Hugging Face Hub上注册。
我们将使用huggingface_hub
包中的notebook_login
实用程序来登录我们的帐户。您可以在Access Tokens的设置中获取您的令牌。
from huggingface_hub import notebook_login
notebook_login()
由于我们现在已登录,让我们获取user_id
,这将用于推送工件。
from huggingface_hub import HfApi
user_id = HfApi().whoami()["name"]
print(f"user id '{user_id}' will be used during the example")
原始的BERT模型是在维基百科和BookCorpus数据集上进行预训练的。这两个数据集都可以在Hugging Face Hub上获得,并且可以使用datasets
库进行加载。
注意:对于维基百科,我们将使用20220301
的数据集,与原始的数据集不同。
作为第一步,我们正在加载数据集并将它们合并在一起,以创建一个大的数据集。
from datasets import concatenate_datasets, load_dataset
bookcorpus = load_dataset("bookcorpus", split="train")
wiki = load_dataset("wikipedia", "20220301.en", split="train")
wiki = wiki.remove_columns([col for col in wiki.column_names if col != "text"]) # 仅保留文本列
assert bookcorpus.features.type == wiki.features.type
raw_datasets = concatenate_datasets([bookcorpus, wiki])
我们不会进行一些高级的数据集准备工作,比如去重、过滤或其他预处理。如果你计划使用此notebook从头开始训练自己的BERT模型,我强烈建议将这些数据准备步骤包含在你的工作流程中。这将有助于改进你的语言模型。
2. 训练一个分词器
为了能够训练我们的模型,我们需要将文本转换为标记化格式。大多数Transformer模型都配有一个预训练的分词器,但由于我们是从头开始预训练模型,我们还需要在我们的数据上训练一个分词器。我们可以使用transformers
库和BertTokenizerFast
类来训练一个分词器。
关于训练新分词器的更多信息可以在我们的Hugging Face课程中找到。
from tqdm import tqdm
from transformers import BertTokenizerFast
# 保存分词器的仓库ID
tokenizer_id="bert-base-uncased-2022-habana"
# 创建一个Python生成器来动态加载数据
def batch_iterator(batch_size=10000):
for i in tqdm(range(0, len(raw_datasets), batch_size)):
yield raw_datasets[i : i + batch_size]["text"]
# 从现有的分词器创建一个分词器,以便重用特殊标记
tokenizer = BertTokenizerFast.from_pretrained("bert-base-uncased")
我们可以使用train_new_from_iterator()
函数开始训练分词器。
bert_tokenizer = tokenizer.train_new_from_iterator(text_iterator=batch_iterator(), vocab_size=32_000)
bert_tokenizer.save_pretrained("tokenizer")
我们将分词器推送到Hugging Face Hub,以便稍后训练我们的模型。
# 推送分词器之前,你需要登录
bert_tokenizer.push_to_hub(tokenizer_id)
3. 预处理数据集
在开始训练模型之前,最后一步是对数据集进行预处理/分词化。我们将使用我们训练过的分词器对数据集进行分词化,并将其推送到Hub,以便在训练过程中更轻松地加载它。分词化的过程也非常简单,如果文档的长度超过512
个标记,那么它们将被截断,并且不会分割为多个文档。
from transformers import AutoTokenizer
import multiprocessing
# 加载分词器
# tokenizer = AutoTokenizer.from_pretrained(f"{user_id}/{tokenizer_id}")
tokenizer = AutoTokenizer.from_pretrained("tokenizer")
num_proc = multiprocessing.cpu_count()
print(f"The max length for the tokenizer is: {tokenizer.model_max_length}")
def group_texts(examples):
tokenized_inputs = tokenizer(
examples["text"], return_special_tokens_mask=True, truncation=True, max_length=tokenizer.model_max_length
)
return tokenized_inputs
# 预处理数据集
tokenized_datasets = raw_datasets.map(group_texts, batched=True, remove_columns=["text"], num_proc=num_proc)
tokenized_datasets.features
作为数据处理函数,我们将把数据集中的所有文本连接起来,并生成tokenizer.model_max_length
(512)的块。
from itertools import chain
# 主要的数据处理函数,将数据集中的所有文本连接起来,并生成
# max_seq_length的块。
def group_texts(examples):
# 连接所有文本。
concatenated_examples = {k: list(chain(*examples[k])) for k in examples.keys()}
total_length = len(concatenated_examples[list(examples.keys())[0]])
# 我们丢弃剩余的部分,如果模型支持填充,我们可以添加填充而不是丢弃,你可以根据你的需求自定义这部分。
if total_length >= tokenizer.model_max_length:
total_length = (total_length // tokenizer.model_max_length) * tokenizer.model_max_length
# 按照max_len的大小切分为块。
result = {
k: [t[i : i + tokenizer.model_max_length] for i in range(0, total_length, tokenizer.model_max_length)]
for k, t in concatenated_examples.items()
}
return result
tokenized_datasets = tokenized_datasets.map(group_texts, batched=True, num_proc=num_proc)
# 打乱数据集
tokenized_datasets = tokenized_datasets.shuffle(seed=34)
print(f"the dataset contains in total {len(tokenized_datasets)*tokenizer.model_max_length} tokens")
# 数据集总共包含3417216000个标记
在我们开始训练之前的最后一步是将我们准备好的数据集推送到hub。
# 将数据集推送到Hugging Face
dataset_id=f"{user_id}/processed_bert_dataset"
tokenized_datasets.push_to_hub(f"{user_id}/processed_bert_dataset")
4. 在Habana Gaudi上预训练BERT
在这个例子中,我们将使用AWS上的Habana Gaudi在DL1实例上运行预训练。我们将使用远程运行工具包(Remote Runner toolkit)来轻松地从本地设置中启动我们的预训练。如果您想了解更多关于这个工作原理的知识,可以查看使用EC2远程运行器和Habana Gaudi轻松设置深度学习。
!pip install rm-runner
当使用GPU时,您将使用Trainer和TrainingArguments。由于我们将在Habana Gaudi上运行训练,我们正在利用optimum-habana库,我们可以使用GaudiTrainer和GaudiTrainingArguments代替。GaudiTrainer是Trainer的一个包装器,它允许您在Habana Gaudi实例上预训练或微调Transformer模型。
-from transformers import Trainer, TrainingArguments
+from optimum.habana import GaudiTrainer, GaudiTrainingArguments
# 定义训练参数
-training_args = TrainingArguments(
+training_args = GaudiTrainingArguments(
+ use_habana=True,
+ use_lazy_mode=True,
+ gaudi_config_name=path_to_gaudi_config,
...
)
# 初始化我们的Trainer
-trainer = Trainer(
+trainer = GaudiTrainer(
model=model,
args=training_args,
train_dataset=train_dataset
... # other arguments
)
我们使用的DL1实例有8个可用的HPU核心,这意味着我们可以利用分布式数据并行训练我们的模型。为了将我们的训练作为分布式训练运行,我们需要创建一个训练脚本,可以使用多进程在所有HPUs上运行。我们创建了一个run_mlm.py脚本,使用GaudiTrainer实现了遮蔽语言建模。为了执行我们的分布式训练,我们使用optimum-habana中的DistributedRunner运行器,并传递我们的参数。或者,您可以查看optimum-habana存储库中的gaudi_spawn.py。
在开始训练之前,我们需要定义我们想要在训练中使用的超参数。我们利用了GaudiTrainer的Hugging Face Hub集成,可以自动将我们的检查点、日志和指标推送到存储库中。
from huggingface_hub import HfFolder
# 超参数
hyperparameters = {
"model_config_id": "bert-base-uncased",
"dataset_id": "philschmid/processed_bert_dataset",
"tokenizer_id": "philschmid/bert-base-uncased-2022-habana",
"gaudi_config_id": "philschmid/bert-base-uncased-2022-habana",
"repository_id": "bert-base-uncased-2022",
"hf_hub_token": HfFolder.get_token(), # 需要使用 `huggingface-cli login` 登录
"max_steps": 100_000,
"per_device_train_batch_size": 32,
"learning_rate": 5e-5,
}
hyperparameters_string = " ".join(f"--{key} {value}" for key, value in hyperparameters.items())
我们可以通过创建EC2RemoteRunner,然后启动它来开始我们的训练。这将启动我们的AWS EC2 DL1实例,并使用huggingface/optimum-habana:latest容器在上面运行我们的run_mlm.py脚本。
from rm_runner import EC2RemoteRunner
# 创建EC2远程运行器
runner = EC2RemoteRunner(
instance_type="dl1.24xlarge",
profile="hf-sm", # 调整为您的配置文件
region="us-east-1",
container="huggingface/optimum-habana:4.21.1-pt1.11.0-synapse1.5.0"
)
# 使用gaudi_spawn进行分布式训练启动我的脚本
runner.launch(
command=f"python3 gaudi_spawn.py --use_mpi --world_size=8 run_mlm.py {hyperparameters_string}",
source_dir="scripts",
)
这个实验运行了60k个步骤。
在我们的超参数
中,我们定义了一个max_steps
属性,将预训练限制在了100_000
个步骤。以全局批量大小256
进行的100_000
个步骤大约需要12.5小时。
BERT最初是在全局批量大小为256
的情况下进行的100万个步骤的预训练:
我们以256个序列(256个序列 * 512个标记=128,000个标记/批次)的批量大小进行1,000,000个步骤的训练,大约相当于在33亿个词语语料库上进行40个时代。
这意味着如果我们要进行完整的预训练,需要大约125小时(12.5小时 * 10),并且使用Habana Gaudi在AWS上的成本约为~$1,650,非常便宜。
相比之下,速度最快的BERT预训练记录由DeepSpeed团队保持,他们报告称,在1个DGX-2上(由16个配备32GB内存的NVIDIA V100 GPU提供动力),预训练BERT需要大约33.25小时。
为了比较成本,我们可以使用p3dn.24xlarge作为参考,该实例配备了8个NVIDIA V100 32GB GPU,成本约为~31.22美元/小时。我们需要两个这样的实例才能达到DeepSpeed报告的相同“设置”,目前我们忽略了多节点设置(I/O、网络等)带来的任何开销。这将使DeepSpeed基于GPU的AWS训练成本约为~$2,075,比Habana Gaudi目前的成本高出25%。
这里需要注意的是,使用DeepSpeed通常可以将性能提升约1.5-2倍。1.5-2倍的提升意味着没有DeepSpeed的相同预训练作业可能需要两倍的时间和两倍的成本,即约为3,000-4,000美元。
我们期待在Gaudi DeepSpeed集成更广泛可用后再次进行实验。
结论
这就是本教程的全部内容。现在您知道如何使用Hugging Face Transformers和Habana Gaudi从头开始预训练BERT的基础知识。您还看到了从Trainer
迁移到GaudiTrainer
是多么简单。
我们将我们的实现与最快的BERT预训练结果进行了比较,并发现Habana Gaudi仍然能够节省25%的成本,并使我们能够以~$1,650进行BERT预训练。
这些结果令人难以置信,因为它将使公司能够根据其语言和领域调整其预训练模型,以提高准确性,相比通用的BERT模型提高10%。
如果您有兴趣从头开始训练自己的BERT或其他Transformers模型以降低成本并提高准确性,请联系我们的专家了解我们的专家加速计划。要了解有关Habana解决方案的更多信息,请阅读有关我们的合作伙伴关系以及如何联系他们的信息。
代码:pre-training-bert.ipynb
感谢阅读!如果您有任何问题,请随时通过Github或论坛与我联系。您也可以通过Twitter或LinkedIn与我联系。