如何使用Transformers和Tokenizers从头开始训练新的语言模型

文章目录

  • 前言
  • 1. 下载数据集
  • 2.训练一个分词器(tokenizer)
  • 3. 从零开始训练语言模型
    • 定义这个模型的配置文件
    • 建立训练数据集
  • 检查LM是否受过训练
  • 总结

huggingface教程翻译,原文博客地址,cloab地址

前言

在过去的几个月,我们对transformers库和 tokenizers库进行了一些改进,目标是使得从头开始训练新的语言模型变得容易。

在这个demo里,展示了怎么在Esperanto训练一个"small"模型((84 M parameters = 6 layers, 768 hidden size, 12 attention heads)) ,这是和DistilBERT有相同的层和头的数量。然后在下游的词性标注任务进行微调。

1. 下载数据集

首先,在Esperanto上找到数据集文本。这里,我们使用来自INRIA.OSCAR的OSCAR语料库的Esperanto部分,它是一个庞大的多语言语料库,它是通过对网络上Common Crawl转储进行语言分类和过滤得到的。
如何使用Transformers和Tokenizers从头开始训练新的语言模型_第1张图片

在数据集的Esperanto部分仅有299M,因此我们将会拼接Leipzig Corpora Collection的Esperanto子语料库,它来自不同的文本,包括(新闻, 文学,维基百科)组成。

最后训练的语料库有3GB,对于你的模型,它仍然是小的,获得的语料越多,预训练效果越好

# in this notebook we'll only get one of the files (the Oscar one) for the sake of simplicity and performance
!wget -c https://cdn-datasets.huggingface.co/EsperBERTo/data/oscar.eo.txt

2.训练一个分词器(tokenizer)

我们选择来训练一个byte-level的字节对编码分词器(像GPT-2),和RoBERTa一样的特殊tokens。我们任意取它的大小为52000。

我们推荐训练byte-level水平的BPE(而不是像BERT的WordPiece tokenizer),因为它将从单个字节的字母表开始构建词汇表,因此,所有的单词都会被分解(不再有标记!)

# We won't need TensorFlow here
!pip uninstall -y tensorflow
# Install `transformers` from master
!pip install git+https://github.com/huggingface/transformers
!pip list | grep -E 'transformers|tokenizers'
# transformers version at notebook update --- 2.11.0
# tokenizers version at notebook update --- 0.8.0rc1
%%time 
from pathlib import Path

from tokenizers import ByteLevelBPETokenizer

paths = [str(x) for x in Path(".").glob("**/*.txt")]

# Initialize a tokenizer
tokenizer = ByteLevelBPETokenizer()

# Customize training
tokenizer.train(files=paths, vocab_size=52_000, min_frequency=2, special_tokens=[
    "",
    "",
    "",
    "",
    "",
])

保存文件到磁盘

!mkdir EsperBERTo
tokenizer.save_model("EsperBERTo")

现在有一个vocab.json文件,它是一个按照字符频率排序的列表,还有一个merges.txt的合并文件

{
     
    "": 0,
    "": 1,
    "": 2,
    "": 3,
    "": 4,
    "!": 5,
    "\"": 6,
    "#": 7,
    "$": 8,
    "%": 9,
    "&": 10,
    "'": 11,
    "(": 12,
    ")": 13,
    # ...
}
 
# merges.txt
l a
Ġ k
o n
Ġ la
t a
Ġ e
Ġ d
Ġ p
# ...

我们的tokenizer是根据Esperanto优化的。相比与普通的基于英语的tokenizer,拥有更多的本地单词由一个单独的、未拆分的token表示。例如:在Esperanto中的单词——ĉ、ĝ、ĥ、ĵ、ŝ和ŭ——都是本地编码的。我们还用一种跟有效的方式表示序列。在这个语料库中,编码序列的平均长度比使用预训练的GPT-2标记器时小约30%。

下面是如何在tokenizers中使用它,包括处理RoBERTa中的特殊tokens–当然,你也可以在transformer中使用它。

from tokenizers.implementations import ByteLevelBPETokenizer
from tokenizers.processors import BertProcessing


tokenizer = ByteLevelBPETokenizer(
    "./EsperBERTo/vocab.json",
    "./EsperBERTo/merges.txt",
)
tokenizer._tokenizer.post_processor = BertProcessing(
    ("", tokenizer.token_to_id("")),
    ("", tokenizer.token_to_id("")),
)
tokenizer.enable_truncation(max_length=512)
tokenizer.encode("Mi estas Julien.")
tokenizer.encode("Mi estas Julien.").tokens

3. 从零开始训练语言模型

更新:本节遵循run_language_modeling.py脚本,使用新的Trainer。你可以任意挑选你最喜欢的方法。

我们将会训练一个类似RoBERTa的模型,它是一个类似bert的模型但是又有一些改进(详细信息可以看文档)

因为是类似bert的模型,我们训练这个任务在Masked language modeling,例如,预测如何填充我们在数据集中随机屏蔽的任意标记。这是由示例脚本处理的。

# Check that we have a GPU
!nvidia-smi
# Check that PyTorch sees it
import torch
torch.cuda.is_available()

定义这个模型的配置文件

from transformers import RobertaConfig

config = RobertaConfig(
    vocab_size=52_000,
    max_position_embeddings=514,
    num_attention_heads=12,
    num_hidden_layers=6,
    type_vocab_size=1,
)

现在在teansformers中重新创建tokenizer

from transformers import RobertaTokenizerFast

tokenizer = RobertaTokenizerFast.from_pretrained("./EsperBERTo", max_len=512)

最后,我们来初始化模型

重要的是:

当我们从头开始训练时,我们只从配置进行初始化,而不是从现有的预先训练过的模型或检查点。

from transformers import RobertaForMaskedLM

model = RobertaForMaskedLM(config=config)
model.num_parameters()
# => 84 million parameters

建立训练数据集

我们通过应用我们的tokenizer到文本文件来建立我们的数据集。

在这里,因为我们只有一个文本文件,我们甚至不需要自定义我们的Dataset。 我们将使用LineByLineDataset

%%time
from transformers import LineByLineTextDataset

dataset = LineByLineTextDataset(
    tokenizer=tokenizer,
    file_path="./oscar.eo.txt",
    block_size=128,
)

像run_language_modeling.py脚本中描述的一样,我们需要定义一个 data_collator

这是一个小助手,这将帮助我们将数据集的不同样本批处理到一个Pytorch知道如何执行的backprop中.

from transformers import DataCollatorForLanguageModeling

data_collator = DataCollatorForLanguageModeling(
    tokenizer=tokenizer, mlm=True, mlm_probability=0.15
)

最后,我们将设置Trainer的初始值

from transformers import Trainer, TrainingArguments

training_args = TrainingArguments(
    output_dir="./EsperBERTo",
    overwrite_output_dir=True,
    num_train_epochs=1,
    per_gpu_train_batch_size=64,
    save_steps=10_000,
    save_total_limit=2,
)

trainer = Trainer(
    model=model,
    args=training_args,
    data_collator=data_collator,
    train_dataset=dataset,
    prediction_loss_only=True,
)

开始训练

%%time
trainer.train()

保存最终的模型(+ tokenizer + config)到磁盘

trainer.save_model("./EsperBERTo")

检查LM是否受过训练

除了查看训练和评估的loss外,检查我们的语言模型是否学习了任何有趣的东西最简单的方法就是通过FillMaskPipeline.

管道是标记器和模型的简单包装器,而“fill-mask”可以让您输入一个包含屏蔽标记的序列(这里是),并返回最可能被填充的序列及其概率的列表。

from transformers import pipeline

fill_mask = pipeline(
    "fill-mask",
    model="./EsperBERTo",
    tokenizer="./EsperBERTo"
)
# The sun .
# =>

fill_mask("La suno .")

好的,简单的语法可以工作。 让我们尝试一个更有趣的提示:

fill_mask("Jen la komenco de bela .")

# This is the beginning of a beautiful .
# =>

总结

最后的上传模型可以直接参考原文

你可能感兴趣的:(nlp,huggingface)