Press "Enter" to skip to content

使用Hugging Face Transformers和Habana Gaudi预训练BERT

在本教程中,您将学习如何使用基于Habana Gaudi的DL1实例在AWS上从头开始预训练BERT-base,以利用Gaudi的性价比优势。我们将使用Hugging Face Transformers、Optimum Habana和Datasets库来使用遮蔽语言建模预训练BERT-base模型,这是BERT的两个原始预训练任务之一。在开始之前,我们需要设置深度学习环境。

查看代码

您将学习如何:

  1. 准备数据集
  2. 训练一个分词器
  3. 预处理数据集
  4. 在Habana Gaudi上预训练BERT

注意:步骤1到3可以/应该在不同的实例大小上运行,因为这些是CPU密集型任务。

使用Hugging Face Transformers和Habana Gaudi预训练BERT 四海 第1张

要求

在开始之前,请确保您满足以下要求:

  • 具有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实例上准备数据集,可以使用您的笔记本电脑或台式机。

首先,我们将安装transformersdatasetsgit-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",
)

使用Hugging Face Transformers和Habana Gaudi预训练BERT 四海 第2张

这个实验运行了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与我联系。

Leave a Reply

Your email address will not be published. Required fields are marked *