《如何为遮蔽语言建模任务训练BERT》
如何训练BERT来应对语言遮蔽任务
使用Python和Transformers库从零开始构建用于MLM任务的语言模型的实践指南
介绍
近年来,大型语言模型(LLM)引起了机器学习社区的关注。在LLM出现之前,我们对各种语言建模技术进行了关键的研究阶段,包括掩码语言建模、因果语言建模和序列到序列语言建模。
从上述列表中,像BERT这样的掩码语言模型在下游自然语言处理任务(如分类和聚类)中变得更加可用。得益于Hugging Face Transformers等库,将这些模型调整为下游任务变得更加容易和可管理。同样感谢开源社区,我们有很多可供选择的语言模型,涵盖广泛使用的语言和领域。
微调还是从零开始构建?
当将现有语言模型调整为特定用例时,有时我们可以在不进一步调整的情况下使用现有模型(所谓的微调)。例如,如果您想要一个用于英语情感/意图检测的模型,您可以在HuggingFace.co上找到适合您用例的合适模型。
然而,您只能对现实世界中遇到的某些任务有此期望。这就是我们需要另一种称为微调的附加技术的地方。首先,您必须选择要进行微调的基础模型。在这里,您必须对所选模型和目标语言的词汇相似性要小心。
然而,如果找不到适用于所需语言的合适模型,考虑从零开始构建一个。在本教程中,我们将实现用于掩码语言模型的BERT模型。
BERT架构
尽管描述BERT架构超出了本教程的范围,但为了清晰起见,让我们对它进行狭义的介绍。BERT是双向编码器自变换器(Bidirectional Encoder Representations from Transformers)的缩写,属于仅编码器变换器家族。它由Google的研究人员于2018年推出。
论文摘要:
我们介绍了一种称为BERT的新的语言表示模型,它代表双向编码器自变换器(BERT)的含义。与最近的语言表示模型不同,BERT被设计为通过在所有层中联合条件地对左右上下文进行预训练,从未标记的文本中获得深度双向表示。因此,预训练的BERT模型只需一个附加输出层就可以进行微调,创建用于各种任务的最先进模型,例如问题回答和语言推理,无需进行重大的特定任务架构修改。BERT在概念上简单而实证强大。它在包括将GLUE分数推到80.5%(绝对改进7.7%),MultiNLI准确率提高到86.7%(绝对改进4.6%),SQuAD v1.1问题回答测试F1提高到93.2(绝对改进1.5点),以及SQuAD v2.0测试F1提高到83.1(绝对改进5.1点)在内的十一个自然语言处理任务中获得了最新的最佳结果。论文:https://arxiv.org/abs/1810.04805
在上面的内容中,我们可以看到一个有趣的关键字,即双向。双向性使BERT具有人类的能力。假设您必须填写一个空白,如下所示:
“战争有时可能是一种必要的邪恶。但是无论多么必要,它永远不是一种善。”
要猜测填入空白位置的词语,您应该了解一些事情:空白之前的词语,空白之后的词语以及整个句子的上下文。BERT以与此类人类本性相同的方式工作。在训练过程中,我们隐藏一些词语并要求BERT尝试预测它们。当训练完成时,BERT可以基于它们之前和之后的词语预测掩码标记。为此,模型应在输入序列中分配不同的关注,这可能会对预测掩码标记产生重大影响。

就像你在这里看到的,模型将隐藏位置的合适单词“邪恶”视为必要的,以使该预测成立。这是一个值得注意的观点,并暗示模型理解输入序列的上下文。这种上下文意识允许BERT为给定任务生成有意义的句子嵌入。此外,这些嵌入可以在聚类和分类等下游任务中使用。关于BERT的介绍就到此为止,让我们从零开始构建一个。
定义BERT模型
我们通常有BERT(基础)和BERT(大型)两个版本。每个版本都有64个维度的头部。大型变体包含24个编码器层,而基础变体只有12个。然而,我们并不限于这些配置。令人惊讶的是,我们可以完全控制使用Hugging Face Transformers库定义模型的方式。我们所要做的就是使用BertConfig类定义所需的模型配置。
我选择了6个头部和384个总模型维度,以符合原始实现。这样,每个头部都有64个维度,与原始实现类似。让我们初始化我们的BERT模型。
from transformers import BertConfig, BertForMaskedLMconfig = BertConfig( hidden_size = 384, vocab_size= tokenizer.vocab_size, num_hidden_layers = 6, num_attention_heads = 6, intermediate_size = 1024, max_position_embeddings = 256)model = BertForMaskedLM(config=config)print(model.num_parameters()) #10457864
训练分词器
在这里,我不打算描述分词的内部工作原理。相反,让我们使用Hugging Face tokenizers库从头开始训练一个分词器。请注意,原始的BERT实现中使用的是WordPiece分词器,这是另一种基于子词的分词方法。您可以使用下面这个精简的HuggingFace资源,了解更多关于这种分词的信息。
WordPiece分词 – Hugging Face NLP课程
我们通过开源和开放式科学,正在推动和普及人工智能。
huggingface.co
这里使用的数据集是Sinhala-400M数据集(基于apache-2.0协议)。您可以使用任何数据集来进行相同的操作。
正如您可能注意到的,有些僧伽罗语的单词也用英语打出来了。让我们为这些语料库训练一个分词器。
首先,让我们导入必要的模块。使用Hugging Face Tokenizers库训练分词器的好处是,我们可以使用现有的分词器,并根据我们的训练语料库替换词汇表(在适用的情况下进行合并)。这意味着分词的步骤,如预分词和后处理分词等将被保留。为此,我们可以使用方法train_new_from_iterator BertTokenizer类。
from tokenizers.implementations import ByteLevelBPETokenizerfrom tokenizers.processors import BertProcessingfrom transformers import AutoTokenizerfrom datasets import Datasetimport pandas as pd#load base tokenizer to train on datasettokenizer_base = AutoTokenizer.from_pretrained("bert-base-cased")# convert pandas dataset to HF datasetdataset = Dataset.from_pandas(df.rename(columns={"comment":'text'}))# define iteratortraining_corpus = ( dataset[i : i + 1000]["text"] for i in range(0, len(dataset), 1000))#train the new tokenizer for datasettokenizer = tokenizer_base.train_new_from_iterator(training_corpus, 5000)#test trained tokenizer for sample texttext = dataset['text'][123]print(text)
# 我们来检查词元化过程输入_id = tokenizer(text).输入标记subword_view = [tokenizer.convert_ids_to_tokens(id) for id in input_ids]np.array(subword_view)
你可以看到像 ‘cricketer’ 这样的词被分解为 cricket 和 ##er,这意味着词元化器经过了充分的训练。然而,尝试使用不同的词表大小;我使用的是5000,相对较小但对于这个演示例子来说是合适的。
最后,我们可以将训练好的词元化器保存到我们的目录中。
tokenizer.save_pretrained("tokenizer/sinhala-wordpiece-yt-comments")
定义数据合并器和词元化数据集。
让我们为MLM任务定义一个数据合并器。在这里,我们将遮盖15%的词元。无论如何,我们可以设置不同的遮盖概率。
from transformers import DataCollatorForLanguageModelingdata_collator = DataCollatorForLanguageModeling( tokenizer=tokenizer, mlm=True, mlm_probability=0.15)
使用先前创建的词元化器对数据集进行词元化。我使用自定义类替换了原始的LineByLineTextDataset并利用了Hugging Face accelerate 加速库。
import torchfrom torch.utils.data import Datasetfrom accelerate import Accelerator, DistributedTypeclass LineByLineTextDataset(Dataset): def __init__(self, tokenizer, raw_datasets, max_length: int): self.padding = "max_length" self.text_column_name = 'text' self.max_length = max_length self.accelerator = Accelerator(gradient_accumulation_steps=1) self.tokenizer = tokenizer with self.accelerator.main_process_first(): self.tokenized_datasets = raw_datasets.map( self.tokenize_function, batched=True, num_proc=4, remove_columns=[self.text_column_name], desc="Running tokenizer on dataset line_by_line", ) self.tokenized_datasets.set_format('torch',columns=['input_ids'],dtype=torch.long) def tokenize_function(self,examples): examples[self.text_column_name] = [ line for line in examples[self.text_column_name] if len(line[0]) > 0 and not line[0].isspace() ] return self.tokenizer( examples[self.text_column_name], padding=self.padding, truncation=True, max_length=self.max_length, return_special_tokens_mask=True, ) def __len__(self): return len(self.tokenized_datasets) def __getitem__(self, i): return self.tokenized_datasets[i]
让我们对数据集进行词元化。
tokenized_dataset_train = LineByLineTextDataset( tokenizer= tokenizer, raw_datasets = dataset, max_length=256,)
好的,让我们编写我们的训练循环。
from transformers import Trainer, TrainingArgumentstraining_args = TrainingArguments( output_dir="./model", overwrite_output_dir=True, push_to_hub=True, hub_model_id="Ransaka/sinhala-bert-yt-comments", num_train_epochs=2, per_device_train_batch_size=32, save_steps=5_000, logging_steps = 1000, save_total_limit=2, use_mps_device = True, # disable this if you're running non-mac env hub_private_repo = False, # please set true if you want to save model privetly save_safetensors= True, learning_rate = 1e-4, report_to='wandb')trainer = Trainer( model=model, args=training_args, data_collator=data_collator, train_dataset=tokenized_dataset_train)trainer.train()
我们可以使用 trainer 的 train() 方法来调用训练过程。
trainer.train()
经过充分的训练后,我们的模型可以用于下游任务,如零样本分类和聚类。您可以在此 Hugging Face 空间上找到使用示例以获得更多详细信息。
锁爱关键颜 \- 由Ransaka提供的Hugging Face空间
发现由社区制作的惊人机器学习应用程序
huggingface.co
结论
受限于资源,预训练模型可能只能识别特定的语言模式,但对特定用例仍然有帮助。强烈建议在可能的情况下进行微调。
本文中的所有图片(除非另有说明)均由作者提供。
参考资料
- An Explorable BERT — https://huggingface.co/spaces/exbert项目/exbert
- BERT论文 — https://arxiv.org/abs/1810.04805
- 数据集 — https://huggingface.co/datasets/Ransaka/Sinhala-400M