Huggingface Transformers库学习笔记(三):使用Transformers(下)(Using Transformers Part 2)

前言

本部分是Transformer库的基础部分的下半部分,主要包括训练和微调、模型共享和上传、分词器汇总、多语言模型。

Huggingface Transformers库学习笔记(三):使用Transformers(Using Transformers Part 2)

  • 前言
  • 使用Transformers(Using Transformers)
    • 训练和微调(Training and fine-tuning)
      • 在本地PyTorch中进行微调(Fine-tuning in native PyTorch)
        • 冻结编码器(Freezing the encoder)
      • 在本地TF-2中微调(Fine-tuning in native TensorFlow 2)
      • 训练器(Trainer)
    • 模型共享和上传(Model sharing and uploading)
    • 分词器汇总(Summary of the tokenizers)
      • 简介(Introduction)
      • Sub-word 分词(Subword tokenization)
      • 字节对编码(Byte-Pair Encoding (BPE))
        • 字节级别BPE(Byte-level BPE)
        • WordPiece
        • Unigram
        • SentencePiece
    • 多语言模型(Multi-lingual models)
      • XLM
      • BERT
      • XLM-RoBERTa

使用Transformers(Using Transformers)

训练和微调(Training and fine-tuning)

Transformers 中的模型类被设计成符合PyTorch和TensorFlow 2书写习惯的方式,可以与之无缝衔接使用。在这个快速入门中,我们将展示如何使用两种框架中可用的标准培训工具来微调(或从头开始训练)模型。我们还将展示如何使用内含的Trainer()类,它可以处理很多复杂的训练。

本指南假设您已经熟悉加载和使用我们的模型进行推理;否则,请参见任务摘要。我们还假设您熟悉在PyTorch或TF2中训练深度神经网络,并特别关注在 Transformers 中进行模型训练的细微差别和工具。

在本地PyTorch中进行微调(Fine-tuning in native PyTorch)

在Transformer库中的模型类不以TF或PyTorch模块开始,这意味着您可以使用它们就像任何在PyTorch中的模型那样进行推理和优化。

让我们考虑在序列分类数据集上微调像BERT这样的掩码语言模型的常见任务。当我们用from_pretrained()实例化模型时,将使用指定模型的模型配置和预先训练过的权值来初始化模型。该库还包括许多特定于任务的最终层或“头”,它们的权值在指定的预训练模型中不存在时被随机实例化。例如,用 BertForSequenceClassification.from_pretrained(‘bert-base-uncased’, num_labels=2) 实例化一个模型将创建一个BERT模型实例的编码器,它的权值是从 bert-base-uncased 得到,同时位于编码器顶部的随机初始化序列分类头,其输出大小为2。默认情况下,模型以eval模式初始化。我们可以调用model.train()将它置于训练模式。

from transformers import BertForSequenceClassification
model = BertForSequenceClassification.from_pretrained('bert-base-uncased')
model.train()

输出为:

BertForSequenceClassification(
  (bert): BertModel(
    (embeddings): BertEmbeddings(
      (word_embeddings): Embedding(30522, 768, padding_idx=0)
      (position_embeddings): Embedding(512, 768)
      (token_type_embeddings): Embedding(2, 768)
      (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
      (dropout): Dropout(p=0.1, inplace=False)
    )
    (encoder): BertEncoder(
      (layer): ModuleList(
        (0): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
            (dropout): Dropout(p=0.1, inplace=False)
          )
        )
#
# 省略中间(1)-(10)都是一样的结构
#
        (11): BertLayer(
          (attention): BertAttention(
            (self): BertSelfAttention(
              (query): Linear(in_features=768, out_features=768, bias=True)
              (key): Linear(in_features=768, out_features=768, bias=True)
              (value): Linear(in_features=768, out_features=768, bias=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
            (output): BertSelfOutput(
              (dense): Linear(in_features=768, out_features=768, bias=True)
              (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
              (dropout): Dropout(p=0.1, inplace=False)
            )
          )
          (intermediate): BertIntermediate(
            (dense): Linear(in_features=768, out_features=3072, bias=True)
          )
          (output): BertOutput(
            (dense): Linear(in_features=3072, out_features=768, bias=True)
            (LayerNorm): LayerNorm((768,), eps=1e-12, elementwise_affine=True)
            (dropout): Dropout(p=0.1, inplace=False)
          )
        )
      )
    )
    (pooler): BertPooler(
      (dense): Linear(in_features=768, out_features=768, bias=True)
      (activation): Tanh()
    )
  )
  (dropout): Dropout(p=0.1, inplace=False)
  (classifier): Linear(in_features=768, out_features=2, bias=True)
)

输出展示了 B E R T b a s e BERT_{base} BERTbase的内部结构,一共有12层Transformer的encoder层在其中。

微调模型是有用的,因为它允许我们使用预先训练的BERT编码器,并且很容易地在我们选择的任何序列分类数据集上训练它。我们可以使用任何PyTorch优化器,但是我们的库也提供了AdamW()优化器,它实现了梯度偏差校正和权重衰减。

from transformers import AdamW
optimizer = AdamW(model.parameters(), lr=1e-5)

优化器允许我们为特定的参数组应用不同的超参数。例如,我们可以对除偏差和层归一化项外的所有参数应用权重衰减:

no_decay = ['bias', 'LayerNorm.weight']
optimizer_grouped_parameters = [
    {'params': [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay)], 'weight_decay': 0.01},
    {'params': [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay)], 'weight_decay': 0.0}
]
optimizer = AdamW(optimizer_grouped_parameters, lr=1e-5)

这里any() 函数用于判断给定的可迭代参数 iterable 是否全部为 False,则返回 False,如果有一个为 True,则返回 True。元素除了是 0、空、FALSE 外都算 TRUE。

现在我们可以使用__call__()设置一个简单的虚拟训练批处理。这将返回一个BatchEncoding()实例,该实例准备我们可能需要传递给模型的所有东西。

from transformers import BertTokenizer

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
text_batch = ["I love Pixar.", "I don't care for Pixar."]
encoding = tokenizer(text_batch, return_tensors='pt', padding=True, truncation=True)
input_ids = encoding['input_ids']
attention_mask = encoding['attention_mask']

当我们使用labels参数调用分类模型时,第一个返回的元素是预测和传递的标签之间的交叉熵损失。在建立了优化器之后,我们可以向后传递并更新权重:

labels = torch.tensor([1,0]).unsqueeze(0)
outputs = model(input_ids, attention_mask=attention_mask, labels=labels)
loss = outputs.loss
loss.backward()
optimizer.step()

或者,你也可以直接得到logit,自己计算损失。下面的示例与前面的示例相同:

from torch.nn import functional as F

labels = torch.tensor([1,0])
outputs = model(input_ids, attention_mask=attention_mask)
loss = F.cross_entropy(outputs.logits, labels)
loss.backward()
optimizer.step()

当然,你可以像往常一样通过调用.to(‘cuda’)将模型和输入放到GPU上来训练。

我们还提供了一些学习速率调度工具。通过以下步骤,我们可以设置一个调度器,该调度器先为num_warmup_steps热身,然后在训练结束时线性衰减为0。

from transformers import get_linear_schedule_with_warmup

scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps, num_train_steps)

然后,我们要做的就是在optimizer.step()之后调用scheduler.step()。

loss.backward()
optimizer.step()
scheduler.step()

其学习率的大致图像示意图为:

Huggingface Transformers库学习笔记(三):使用Transformers(下)(Using Transformers Part 2)_第1张图片

我们强烈推荐使用以下即将讨论的Trainer(),它可以方便地处理训练模型时移动的部分,与诸如混合精度之类的功能和容易进行tensorboard日志记录。

冻结编码器(Freezing the encoder)

在某些情况下,我们可能对保持预训练编码器的权重不变和只优化头部层的权重感兴趣。要做到这一点,只需在编码器参数上将requires_grad属性设置为False,它可以通过库中任何特定于任务的模型的base_model子模块访问:

for param in model.base_model.parameters():
    param.requires_grad = False

在本地TF-2中微调(Fine-tuning in native TensorFlow 2)

暂时不太熟悉,略

训练器(Trainer)

我们还通过Trainer()和TFTrainer()提供了一个简单但功能完整的训练和评估接口。你可以训练、调整和评估任 Transformers 上的模型,使用广泛的训练选项和内置功能,如日志记录、梯度积累和混合精度。

from transformers import BertForSequenceClassification, Trainer, TrainingArguments

model = BertForSequenceClassification.from_pretrained("bert-large-uncased")

training_args = TrainingArguments(
    output_dir='./results',          # output directory
    num_train_epochs=3,              # total # of training epochs
    per_device_train_batch_size=16,  # batch size per device during training
    per_device_eval_batch_size=64,   # batch size for evaluation
    warmup_steps=500,                # number of warmup steps for learning rate scheduler
    weight_decay=0.01,               # strength of weight decay
    logging_dir='./logs',            # directory for storing logs
)

trainer = Trainer(
    model=model,                         # the instantiated  Transformers model to be trained
    args=training_args,                  # training arguments, defined above
    train_dataset=train_dataset,         # training dataset
    eval_dataset=test_dataset            # evaluation dataset
)

现在只需调用trainer.train()来进行培训,调用trainer.evaluate()来进行评估。也可以使用自己的模块,但是forward返回的第一个参数必须是希望优化的loss。

Trainer()使用一个内置的默认函数来整理batch并准备它们以供输入模型。如果需要,还可以使用data_collator参数来传递自己的collator函数,该函数以数据集提供的格式接收数据,并返回准备提供给模型的批处理。注意,TFTrainer()期望传递的数据集是来自tensorflow_datasets的数据集对象。

为了计算损失之外的额外指标,您还可以定义自己的compute_metrics函数并将其传递给训练器。

from sklearn.metrics import accuracy_score, precision_recall_fscore_support

def compute_metrics(pred):
    labels = pred.label_ids
    preds = pred.predictions.argmax(-1)
    precision, recall, f1, _ = precision_recall_fscore_support(labels, preds, average='binary')
    acc = accuracy_score(labels, preds)
    return {
        'accuracy': acc,
        'f1': f1,
        'precision': precision,
        'recall': recall
    }

Trainer()类的初始化构造方法中是可以接受compute_metrics参数的,因而可以直接传入。

最后,您可以通过在指定的logging_dir目录中启动tensorboard来查看结果,包括任何计算指标。

模型共享和上传(Model sharing and uploading)

在本页面中,我们将向您展示如何在model hub上与社区共享您培训过的模型或对新数据进行微调的模型。

此处内容暂时省略,详细请参考Model sharing and uploading

分词器汇总(Summary of the tokenizers)

在本页中,我们将更仔细地研究分词器。正如我们在预处理教程中看到的,对文本进行标记就是将其分解为word或sub-word,然后通过查找表将它们转换为id。将word或sub word转换为ids非常简单,因此在本总结中,我们将重点关注将文本拆分为word或subword(即对文本进行tokenize)。更确切的说,我们将关注于Transformer中三种主要的分词器:Byte-Pair Encoding(BPE),WordPiece和SentencePiece,并展示这些类型的分词器被哪些模型所使用。

注意:在每个模型页面上,您可以查看相关标记器的文档,以了解预先训练过的模型使用的是哪种标记器类型。例如,如果我们查看BertTokenizer,我们可以看到模型使用WordPiece。

简介(Introduction)

将文本分割成更小的块是一项比看起来更困难的任务,有多种方法可以做到这一点。例如,让我们来看看这句话“Don’t you lovetransformer ? We sure do.”标记这个文本的一个简单方法是用空格分隔它,这将给出:

["Don't", "you", "love", "", "Transformers?", "We", "sure", "do."]

这是明智的第一步,但如果我们看看令牌“Transformer ?”和“do.”,我们会注意到标点被附加到单词“Transformer”和“do”上,这不是最优的。我们应该考虑到标点符号,这样模型就不必学习单词的不同表示形式以及它后面可能出现的每一个标点符号,否则模型必须学习的表示形式的数量就会激增。考虑到标点符号,我们将得到如下结果

["Don", "'", "t", "you", "love", "", "Transformers", "?", "We", "sure", "do", "."]

更好了。然而,符号化如何处理“don’t”这个词是不太恰当的。“Don’t”代表“do not”,所以最好把它标记为[“do”,“n’t”]。这就是事情开始变得复杂的地方,这也是每个模型都有自己的标记器类型的部分原因。根据我们申请对文本进行标记的规则,将为相同的文本生成不同的标记化输出。一个预先训练过的模型只有在输入一个标记化了的规则的情况下才能正常执行,这些标记化的规则与用于标记它的训练数据的规则相同。

spaCy和Moses是两个流行的基于规则的分词器。将它们应用到我们的例子中,spaCy和Moses会输出如下内容:

["Do", "n't", "you", "love", "", "Transformers", "?", "We", "sure", "do", "."]

可以看到,这里使用了空格和标点分词,以及基于规则的分词。空间、标点和规则化都是词的词素化,词的词素化被宽泛地定义为把句子分裂成词。虽然它是将文本分割成更小块的最直观的方法,但这种标记化方法可能会导致大量文本语料库出现问题。在这种情况下,空格和标点标记化通常会生成一个非常大的词汇表(使用的所有唯一单词和标记的集合)。例如,Transformer XL使用空格和标点符号化,导致词汇大小为267,735。

如此大的词汇量迫使模型有一个巨大的嵌入矩阵作为输入和输出层,这导致了内存和时间复杂性的增加。一般来说,transformer模型的词汇量很少超过50,000个,特别是当它们仅被预先训练为一种语言时。

因此,如果简单的空格和标点符号化不能令人满意,为什么不简单地对字符进行符号化呢? 虽然字符标记化非常简单,可以大大减少内存和时间复杂性,但它使模型更难学习有意义的输入表示。例如,学习字母“t”的有意义的语境无关的表达比学习单词“today”的语境无关的表达要困难得多。因此,字符标记化常常伴随着性能的损失。因此,为了两全其美,transformer模型使用了一种介于词级和字符级的标记化,称为sub word标记化。

Sub-word 分词(Subword tokenization)

subword 分词算法的原理是:频繁使用的词不应该被拆分为更小的子词,而罕见的词应该被拆分为有意义的子词。

例如, “annoyingly” 可能被认为是一个罕见的词,它可以被分解为 “annoying” 和 “ly”。相比把"annoyingly" 单独作为一个subword来说,“annoying” 和 "ly"都是会更频繁出现的独立subword。这在黏着性语言中特别有用,比如土耳其语,你可以通过把subword串在一起来形成(几乎)任意长的复杂单词。

subword分词允许模型拥有一个合理的词汇量,同时能够学习有意义的上下文无关的表示。此外,subword 分词使该模型能够通过将它们分解为已知子词来处理它以前从未见过的词。例如, BertTokenizer将把"I have a new GPU!"分词为如下形式

from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")
tokenizer.tokenize("I have a new GPU!")

输出为:

["i", "have", "a", "new", "gp", "##u", "!"]

因为我们考虑的是无大小写模型,所以句子首先是小写的。
我们可以看到这些单词 [“i”, “have”, “a”, “new”] 出现在分词器的词汇表中,但“gpu”这个词却不是。因此,分词器将“gpu”拆分为已知的子词:[“gp”和“##u”]。“##”表示该标记的其余部分应该附加到前一个标记上,不包含空格(用于解码或反转标记)。+

作为另一个例子,XLNetTokenizer对我们之前的示例文本分词如下:

from transformers import XLNetTokenizer
tokenizer = XLNetTokenizer.from_pretrained("xlnet-base-cased")
tokenizer.tokenize("Don't you love  Transformers? We sure do.")

输出为:

["▁Don", "'", "t", "▁you", "▁love", "▁", "", "▁", "Transform", "ers", "?", "▁We", "▁sure", "▁do", "."]

我们将在介绍SentencePiece时来介绍"__"的意思,我们可以看到,“Transformer”这个罕见的词已经被分成了更常用的子词“Transform”和“ers”。

现在让我们看看不同的subword 分词算法是如何工作的。请注意,所有这些分词算法都依赖于某种形式的训练,而这种训练通常是在训练相应模型的语料库上进行的。

字节对编码(Byte-Pair Encoding (BPE))

字节对编码(BPE)是由 _ Neural Machine Translation of Rare Words with Subword Units (Sennrich et al., 2015)._ 引入。BPE依赖于将训练数据分解成单词的预标记器。预标记化可以像空间标记化一样简单,例如GPT-2、Roberta。更高级的预标记化包括基于规则的标记化,例如XLM,FlauBERT对大多数语言使用Moses,或使用Spacy和ftfy的GPT,来计算训练语料库中每个单词的频率。

在预标记化之后,创建了一组唯一的单词,并确定了每个单词在训练数据中出现的频率。接下来,BPE创建一个基础词汇表,该词汇表包含出现在唯一词汇集中的所有符号,并学习合并规则,从基础词汇表的两个符号形成一个新符号。它一直这样进行下去,直到词汇表达到所需的词汇量为止。注意,所需的词汇表大小是在训练标记器之前定义的超参数。

举个例子,假设在预标记化之后,已经确定了以下一组单词,包括它们的频率:

("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)

因此,基本词汇是“b”、“g”、“h”,“n”,“p”,“s”、“u”。将所有单词分解为基础词汇的符号,我们得到:

("h" "u" "g", 10), ("p" "u" "g", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "u" "g" "s", 5)

然后BPE计算每个可能的符号对的频率,并选择出现频率最高的符号对。在这个例子中,前面是"h",后面跟着"u"的组合出现了10+5=15次(10次在"hug"中出现,5次在"hugs"中出现)。然而,最频繁的是"u"后面跟着"g",这个组合出现了10+5+5=20次。因此,分词器学习的第一个合并规则是将所有的“u”符号和一个“g”符号组合在一起。接下来,“ug”被添加到词汇表中。这组词就变成了:

("h" "ug", 10), ("p" "ug", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "ug" "s", 5)

然后BPE标识下一个最常见的符号对。就是"un"共出现16次,然后"u"和"n"合并加入到词表中。紧随其后的是“h”和“ug”,共出现了15次。再次将这两个词合并,"hug"也添加到词汇表中。

在这个阶段,词汇表是

["b", "g", "h", "n", "p", "s", "u", "ug", "un", "hug"]

我们的一组唯一的单词被表示为

("hug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("hug" "s", 5)

假设字节对编码训练在此停止,那么学习到的合并规则将应用于新单词(只要这些新单词不包括基础词汇表中不包含的符号)。例如,当单词"bug"被分词为[“b”,“ug”],但"mug"将被分词为["", “ug”],这是因为符号’m"并不在基础词汇表中。通常来说,单个字母并不会被替换为""符号因为训练数据中每个字母都会至少出现一次,但对于一些非常特殊的字符比如表情来说就不是这样了。

如前所述,词汇量大小,即基本词汇量大小+合并次数,是一个可以选择的超参数。例如,GPT的词汇表大小为40,478,因为它们有478个基本字符,并且选择在40,000个合并后停止训练。

字节级别BPE(Byte-level BPE)

一个包含所有可能的基本字符的基本词汇表可能非常大,例如,如果所有unicode字符都被认为是基本字符。为了获得更好的基本词汇表,GPT-2使用字节作为基本词汇表,这是一种聪明的技巧,它强制基本词汇表的大小为256,同时确保每个基本字符都包含在词汇表中。通过一些处理标点符号的额外规则,GPT2的标记化器可以对每个文本进行标记,而不需要符号。GPT-2的词汇表大小为50257,它对应256字节的基本token、一个特殊的文本结束token和通过50,000个合并学习到的符号。

WordPiece

WordPiece是用于BERT、蒸馏BERT和Electra的子词标记化算法。算法概述在 Japanese and Korean Voice Search (Schuster et al., 2012),和BPE非常相似。WordPiece首先初始化词汇表,包括训练数据中出现的每个字符,并逐步学习给定数量的合并规则。与BPE不同,WordPiece并不选择最频繁的符号对,而是选择在将训练数据添加到词汇表后最大限度地提高其可能性的符号对。

那么这到底意味着什么呢?参照前面的例子,最大化训练数据的似然性等同于找到符号对,其概率除以其第一个符号之后第二个符号的概率在所有符号对中最大。如。只有当“ug”除以“u”,“g”的概率大于任何其他符号对时,“u”和“g”才会被合并。直观地说,WordPiece与BPE略有不同,因为它通过合并两个符号来评估它的损失,以确保它是有价值的。

Unigram

Unigram是一种subword 分词算法,详情在_Subword Regularization: Improving Neural Network Translation Models with Multiple Subword Candidates (Kudo, 2018)._。与BPE或WordPiece不同,Unigram将其基本词汇表初始化为大量的符号,并逐步削减每个符号以获得更小的词汇表。例如,基本词汇表可以对应所有预先标记的单词和最常见的子字符串。Unigram不是直接用于Transformer中的任何模型,但是它与SentencePiece连用。

在每个训练步骤中,Unigram算法在给定当前词汇表和Unigram语言模型的情况下定义训练数据的损失(通常定义为对数似然)。然后,对于词汇表中的每个符号,算法计算如果将该符号从词汇表中移除,总损失将增加多少。Unigram然后移除p (p通常是10%或20%)损失增加最少的符号的百分比,即那些对训练数据的整体损失影响最小的符号。重复这个过程,直到词汇表达到所需的大小。Unigram算法总是保留基本字符,以便任何单词都可以被标记。

由于Unigram不基于合并规则(与BPE和WordPiece不同),该算法在训练后有多种对新文本进行标记的方法。例如,如果一个训练好的Unigram标记器显示词汇表:

["b", "g", "h", "n", "p", "s", "u", "ug", "un", "hug"],

"hugs"可以被分词为 [“hug”, “s”], [“h”, “ug”, “s”]或者[“h”, “u”, “g”, “s”]。那么到底该选择哪一种呢?Unigram在保存词汇的基础上,保存了训练语料库中每个token的概率,这样在训练后就可以计算每个可能的token化概率。该算法只是在实践中选择最有可能的标记化,但也提供了根据其概率对可能的标记化进行抽样的可能性。

这些概率是由标记器所受训练的损失定义的。假设训练数据由以下单词组成: x 1 , x 2 , ⋯   , x N x_1, x_2, \cdots, x_N x1,x2,,xN,并且单词 x i x_i xi的所有可能的标记化的集合定义为 S ( x i ) S(x_i) S(xi),那么总的损失定义为

L = − ∑ i = 1 N log ⁡ ( ∑ x ∈ S ( x i ) p ( x ) ) \mathcal{L} = -\sum_{i=1}^{N} \log \left ( \sum_{x \in S(x_{i})} p(x) \right ) L=i=1NlogxS(xi)p(x)

SentencePiece

到目前为止所描述的所有标记化算法都有相同的问题:假设输入文本使用空格来分隔单词。然而,并不是所有的语言都使用空格来分隔单词。一个可能的解决方案是使用特定语言的预分词(Pre-Tokenizer),例如,XLM使用特定的汉语、日语和泰国语预标记器)。为了更普遍地解决这个问题,_SentencePiece: A simple and language independent subword tokenizer and detokenizer for Neural Text Processing (Kudo et al., 2018) _ 将输入视为原始输入流,因此包括要使用的字符集中的空格。然后,它使用BPE或unigram算法构造适当的词汇表。

例如,XLNetTokenizer使用了句子,这也是为什么在前面的例子中“__”字符被包含在词汇表中。使用SentencePiece进行解码非常简单,因为所有的符号都可以连接起来,而“__”被一个空格所取代。

该库中所有使用句子块的transformer模型都将它与unigram结合使用。使用SentencePiece的例子有ALBERT、XLNet、Marian和T5。

多语言模型(Multi-lingual models)

这个库中的大多数模型都是单语言模型(英语、汉语和德语)。有一些多语言模型是可用的,它们具有与单语言模型不同的机制。此页面详细介绍了这些模型的使用。

目前支持多种语言的两个模型是BERT和XLM。

XLM

不太了解,略

BERT

BERT有两个checkpoint,可以用于多语言任务:

  • bert-base-multilingual- uncastion(掩码语言建模+下一句预测,102种语言)
  • bert-base-multilingual(掩码语言建模+下一句预测,104种语言)

这些checkpoint不需要在推理时进行语言嵌入。他们应该识别上下文中使用的语言,并据此进行推断。

XLM-RoBERTa

你可能感兴趣的:(自然语言处理,Pytorch,NLP,自然语言处理)