huggingface transformers使用指南(更新and待续)

  • 2021 年 3 月 23 日
  • AI

ntransformers的前身是pytorch-transformers和pytorch-pretrained-bert,主要提供了自然语言理解(NLU)和自然语言生成(NLG)的通用体系结构(BERT,GPT-2,RoBERTa,XLM,DistilBert,XLNet等) )包含超过32种以100多种语言编写的预训练模型,以及TensorFlow 2.0和PyTorch之间的深度互操作性。

不过就上手而言,torch还是更顺滑一些(因为很多非官方例子都是用torch来撸的),顺便让我熟悉一下torch的使用。

整体上调研了github上的多个相关的项目,包括huggingface transformer,谷歌开源的bert,bert4keras,tensorflow hub,以及其它的一些个人的keras-bert之类的实现,总的来说,huggingface的优点在于:

1、企业级维护,可靠性高,生产上用起来放心;

2、star多,issues多,网上能够找到的各种各样对应不同需求的demo代码多;

3、适配tf.keras和torch,一次性可以撸两个框架;

4、官方的tutorial是真的太特么全了

5、在PyTorch和TensorFlow 2.0之间轻松切换,从而允许使用一种框架进行训练,而使用另一种框架进行推理。非常灵活,当然其实torch和tf之间框架互相转换的功能的library挺多的;

整体架构:

  • 使用每个模型只需要三个标准类:configuration,models 和tokenizer 。model用于指定使用哪一种模型,例如model为bert,则相应的网络结构为bert的网络结构,configuration是模型具体的结构配置,例如可以配置多头的数量等,这里配置需要注意的地方就是,如果自定义配置不改变核心网络结构的则仍旧可以使用预训练模型权重,如果配置涉及到核心结构的修改,例如前馈网络的隐层神经元的个数,则无法使用预训练模型权重,这个时候transformers会默认你要重新自己预训练一个模型从而随机初始化整个模型的权重,这是是一种半灵活性的设计,
  • 所有这些类都可以使用通用的from_pretrained()实例化方法,以简单统一的方式从受过训练的实例中初始化,该方法将负责下载(如果需要),缓存和加载相关的类实例以及相关的数据(config的的超参数,再 Hugging Face Hub 上提供的预先训练的检查点或您自己保存的检查点的tokenizer生成器的词汇表和模型的权重)。
  • 在这三个基本类的基础上,该库提供了两个API:pipeline()用于在给定任务上快速使用模型(及其关联的tokenizer和configuration)和 Trainer或者TFtrainer 快速训练或微调给定模型。
  • 因此,该库不是神经网络构建模块的模块化工具箱。如果要扩展/构建库,只需使用常规的Python / PyTorch / TensorFlow / Keras模块并从库的基类继承即可重用模型加载/保存之类的功能。

其他一些目标:

  • 尽可能一致地公开模型的内部:
  • 我们使用单个API授予全部隐藏状态和注意力权重的访问权限。
  • token生成器和基本模型的API已标准化,可轻松在模型之间切换。
  • 纳入主观选择的有前途的工具,以对这些模型进行微调/研究:
  • 一种向词汇表和嵌入物中添加新标记以进行微调的简单/一致的方法。
  • 遮盖和修剪transformer的任务曾的简单方法。
  • 模型类例如Bert odel是30多个PyTorch模型(torch.nn.Module)或tf.keras模型(tf.keras.Model),它们可以使用库中提供的预训练权重。
  • 配置类(例如 Bert config,用于存储构建模型所需的所有参数。您不必总是自己实例化这些。特别是,如果您使用未经任何修改的预训练模型,则创建模型将自动实例化配置(这是模型的一部分)。
  • 诸如的Tokenizer类例如BertTokenizer,用于存储每个模型的词汇表,并提供用于编码/解码要馈送到模型的令牌嵌入索引列表中的字符串的方法。

所有这些类都可以从经过预训练的实例中实例化,并使用两种方法在本地保存:

  • from_pretrained()可以实例化一个型号/配置/标记者从库本身要么提供(在列表中提供支持的型号预训练版本在这里)或本地存储(或服务器)用户,
  • save_pretrained()使您可以在本地保存模型/配置/令牌,以便可以使用来重新加载模型/配置/令牌 from_pretrained()。

另外关于bert上模型的分类,huggingface的官方文档也做了很详细的归类:

Summary of the modelshuggingface.co图标

现有的预训练模型整体上都属于下面的5个类别之一:

1、自回归模型:自回归模型在经典语言建模任务上进行了预训练:猜测下一个已读完所有先前token的token。它们对应于transformer模型的解码器部分,并且在整个句子的顶部使用了一个掩码,以便注意头只能看到文本中的之前内容,而不能看到其后的内容。尽管可以对这些模型进行微调并在许多任务上取得出色的结果,但其最自然的应用是文本生成。此类模型的典型例子是GPT;

2、自编码模型:通过以某种方式破坏输入token并尝试重建原始句子来对自编码模型进行预训练。从某种意义上说,它们与transformer中的的编码器相对应,因为它们无需任何掩码即可访问完整的输入。这些模型通常建立整个句子的双向表示。可以对它们进行微调并在许多任务(例如文本生成)上取得出色的结果,但是它们最自然的应用是文本分类或token分类(比如词性标注)。此类模型的典型例子是BERT。

请注意,自动回归模型和自动编码模型之间的唯一区别在于模型的预训练方式。因此,相同的体系结构既可以用于自动回归模型,也可以用于自动编码模型。当给定模型同时用于两种类型的预训练时,我们将其放在与首次引入该模型的文章相对应的类别中。

3、序列到序列模型:序列到序列模型将transformers的编码器和解码器同时用于翻译任务或通过将其他任务转换为序列到序列问题来训练得到的。可以将它们微调来适应许多任务(这里应该是说把sequence to sequence的预训练模型的encoder或者decoder单独抽取出来,然后用法就和上面两种模型的用法一致),但最自然的应用是翻译,摘要和问题解答。T5是一个典型的例子;

4、多模态模型将文本输入与其他类型的输入(例如图像)混合在一起,并且更特定于给定任务。

这种模型没有提供预训练权重,只是定义了模型的结构;

5、基于检索的模型

这个我实在不知道干啥用的wtf。。。

针对于不同的使用目的,bert有不同的抽象等级的api实现:

应用目标1、直接调用预训练模型不fine tune完成下游任务:

这种最简单,不进行finetune,直接完成任务,bert提供了pipeline的功能:

from transformers import pipeline
classifier = pipeline('sentiment-analysis', model="nlptown/bert-base-multilingual-uncased-sentiment")
classifier('We are very happy to show you the   Transformers library.')

我们来看一下pipeline的参数:

pipeline(task: str, model: Optional = None, \
config: Union[str, transformers.configuration_utils.PretrainedConfig, NoneType] = None, \
 tokenizer: Union[str, transformers.tokenization_utils.PreTrainedTokenizer, NoneType] = None, \
 framework: Union[str, NoneType] = None, revision: Union[str, NoneType] = None,  \
use_fast: bool = True, model_kwargs: Dict[str, Any] = {}, **kwargs) \
 -> transformers.pipelines.base.Pipeline

task参数对应下游任务,根据下游要完成的任务来设置。

model参数对应使用的预训练模型,比如可以是这样:

classifier = pipeline('sentiment-analysis', model="nlptown/bert-base-multilingual-uncased-sentiment")

如果是本地已经下载好的模型,则model后面可以直接跟模型文件夹的绝对路径。

config对应模型的具体配置,这个比较重要下面会详细描述。

tokenizer 指定分词器,这个也比较重要后面详细描述。

framework:pt或者tf用于指定模型使用torch还是tensorflow版的

use_fast:是否使用优化后的分词器,这个后面和分词器一起讲好了

个人不是很喜欢这里的设计,封装的过头了,很多细节的地方把控起来不是很方便,所以pipeline功能就不详细介绍了。

下面介绍一下第二种,通过autoXXX的功能来使用

当然pipeline本身封装的代码部分就是主要用autoXXX来实现的,这种方式可以较好的和现有的torch以及tf.keras的框架结合起来,本质上就是把这些预训练模型当作一个大型的model,相对于pipeline来说,封装的程度小一点,看一个例子:

基于huggingface/transforms(pytorch)框架实现Bert文本分类_Jay2coomzz的博客-CSDN博客_huggingface 文本分类blog.csdn.net图标

可以看到,automodelXXX的功能实际上是默认直接帮你配完一个model,对于tf.keras来说就是直接得到一个compilr之后可以直接fit的model类,对于torch来说,就是一个已经用nn.module+class的方式构建完可以训练的model。

例如上面的

self.model = BertForSequenceClassification.from_pretrained(pretrain_Model_path,config=config)

可以用

self.model = AutoModelForSequenceClassification.from_pretrained(pretrain_Model_path,config=config)

(这里补充一下,automodelforXXX可以直接代替各类的bertforXXX,albertforXXX的用法,auto能够根据你的from pretrained的model的字符串名字自动给你选择对应的类,比如from_pretrained(‘bert-large-base’),那么底层实际上就自动推断给你返回一个bertforXXX的类。

看上述的代码我们可以知道,automodelforXXX实际上默认是帮你把下游对应的任务层搭建好了,比如上面的文本分类任务,默认是帮你配了一个分类层在里面。用torch或者keras的同学就不必再构建一个新对象或者add一个分类层了。

下面看一下官方给的几个例子,可以比较好的对比pipeline和automodelXXX的使用上的差异:

Summary of the taskshuggingface.co

这里介绍了文本分类、QA、语言建模任务、文本生成、命名识别和文本摘要等多个任务,为了方便这里就介绍文本分类任务的例子:

from transformers import pipeline
nlp = pipeline("sentiment-analysis")
result = nlp("I hate you")[0]
print(f"label: {result['label']}, with score: {round(result['score'], 4)}")
result = nlp("I love you")[0]
print(f"label: {result['label']}, with score: {round(result['score'], 4)}")

上述的过程是这样的:

  1. 从检查点名称实例化标记器和模型。该模型被识别为BERT模型,并使用存储在检查点中的权重加载该模型(从huggingface hub上下载和加载模型权重)。
  2. 使用正确的特定于模型的分隔符,标记类型ID和注意掩码,从两个句子构建一个序列(encode()__call__()注意这一点)。(自动使用下载的模型对应的分词器对原始的输入句子进行模型的适配性的转换,讲输入的句子转化为模型可以input的形式)
  3. 模型对输入的tokenizer处理后的数据进行预测得到原始预测结果。
  4. 计算结果的softmax值以获得不同类的概率。
  5. 输出预测结果。

使用pipeline完成推断非常的简单,分词以及分词之后的张量转换,模型的输入和输出的处理等等都根据你设置的task(上面是”sentiment-analysis”)直接完成了,如果要针对下游任务进行finetune,huggingface提供了trainer的功能,例子在这里:

//github.com/huggingface/transformers/blob/master/examples/text-classification/run_glue.pygithub.com

比较麻烦,语法上和torch或者keras的训练方式有一定差异。当然,如果纯做推断的话,会比较方便,但是如果tokenizer之类的需要自定义,或者模型的输出层要做一些其它的特定的处理,则会比较麻烦。

相对而言,autoXXX的api用法会复杂一些,例如对于上面的任务,这个api要这么用:

from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased-finetuned-mrpc")
model = AutoModelForSequenceClassification.from_pretrained("bert-base-cased-finetuned-mrpc")
classes = ["not paraphrase", "is paraphrase"]
sequence_0 = "The company HuggingFace is based in New York City"
sequence_1 = "Apples are especially bad for your health"
sequence_2 = "HuggingFace's headquarters are situated in Manhattan"
paraphrase = tokenizer(sequence_0, sequence_2, return_tensors="pt")
not_paraphrase = tokenizer(sequence_0, sequence_1, return_tensors="pt")
paraphrase_classification_logits = model(**paraphrase).logits
not_paraphrase_classification_logits = model(**not_paraphrase).logits
paraphrase_results = torch.softmax(paraphrase_classification_logits, dim=1).tolist()[0]
not_paraphrase_results = torch.softmax(not_paraphrase_classification_logits, dim=1).tolist()[0]
# Should be paraphrase
for i in range(len(classes)):
    print(f"{classes[i]}: {int(round(paraphrase_results[i] * 100))}%")
# Should not be paraphrase
for i in range(len(classes)):
    print(f"{classes[i]}: {int(round(not_paraphrase_results[i] * 100))}%")

相对来说会更加灵活一些,写起来也更麻烦一点,分词器部分我们可以直接用python语言来自定义处理,只要最终能够转化为模型能处理的input就可以了,然后要做finetune的话,其过程和torch的模型训练过程是一样的,通过

for i in range(epochs)的循环的写法或者是skorch封装之后直接fit都可以,这样的话,显存重计算,早停等功能都可以直接沿用torch的语法(虽然huggingface transformers其实也内置了这些功能,但是我真的懒得再去学一套api了。。。)

其它任务的差异基本上差不多。

应用目标2、直接调用预训练模型不fine tune完成下游任务:

其实前面基本上已经把pipeline和autoXXX这两个api的finetune的方法讲完了。

Training and fine-tuninghuggingface.co

官方的文档里这里提供了更加详细丰富的案例,这些例子使用automodelforXXX或者是特定的bertmodelforXXX都是可以跑的。


应用目标3、更加灵活的自定义以及torch的练手

涉及到自定义的话基本上就不会是直接predict的任务了,你接了分类层之后分类层的权重也是需要去训练的

但是这种方法也不是很好,尤其是我这样的初学菜鸟,因为我不知道bert的哪些输出是直接接分类层的,练手的机会都没有(逃)。除此之外,像torch lighting,skortch,torchkeras这类很好用的torch周边不是很方便集成进来,如果搭建的时候尽量贴近原生的torch语法,那么很多torch的周边工具都会比较方便的集成进来一起使用。

(我始终觉得框架封装的太过于完成不是很好的事情,比如lightgbm这类框架都写死了,要做一些训练层面的魔改无从下手,比如lgb的优化算法如果能结合tf中的optimizers一起用就好了)

所以我打算先接着这个框架把torch用熟悉,因此automodelXXX这部分和pipeline一样跳过,等我撸熟了torch再专门总结一下这两个部分


休息

Models部分:

transformers目前提供了非常海量的预训练模型,这些预训练模型使用的模型结构、语言任务、语料等等可能都不尽相同,实在太多了压根研究不过来,先把常用的bert这类的弄清楚就好。

这些模型都可以通过相似的api来进行统一调用,常见的需求是直接使用预训练模型来完成下游任务或者在下游任务数据上做finetune。

每个标记器的工作方式不同,但基本机制保持不变。这是一个使用BERT标记器的示例,该标记器是一个WordPiece标记器:

from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
sequence = "A Titan RTX has 24GB of VRAM"
 

这个和wordpiece的工作原理有关

然后可以将这些令牌转换为模型可以理解的ID。这可以通过直接将句子提供给令牌生成器来完成,该令牌生成器利用Rust的拥抱面/令牌生成器实现最佳性能。

inputs = tokenizer(sequence)
encoded_sequence = inputs["input_ids"]
print(encoded_sequence)

请注意,令牌生成器会自动添加“特殊令牌”(如果关联的模型依赖于它们),这些“特殊令牌”是模型有时使用的特殊ID。

如果我们解码先前的ID序列,

decoded_sequence = tokenizer.decode(encoded_sequence)
>>> print(decoded_sequence)
[CLS] A Titan RTX has 24GB of VRAM [SEP]

注意面膜

注意掩码是将序列批处理在一起时使用的可选参数。此参数向模型指示应注意哪些令牌,不应该注意哪些令牌。

例如,考虑以下两个序列:

from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
sequence_a = "This is a short sequence."
sequence_b = "This is a rather long sequence. It is at least longer than the sequence A."
encoded_sequence_a = tokenizer(sequence_a)["input_ids"]
encoded_sequence_b = tokenizer(sequence_b)["input_ids"]

编码版本具有不同的长度:

len(encoded_sequence_a), len(encoded_sequence_b)
 

因此,我们不能将它们按原样放在相同的张量中。需要将第一个序列填充到第二个序列的长度,或者将第二个序列截短到第一个序列的长度。

在第一种情况下,ID列表将通过填充索引进行扩展。我们可以将列表传递给令牌生成器,并要求其像这样填充:

padded_sequences = tokenizer([sequence_a, sequence_b], padding=True)

我们可以看到,在第一句话的右边添加了0,使其与第二句话的长度相同:

padded_sequences["input_ids"]
 [[101, 1188, 1110, 170, 1603, 4954, 119, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [101, 1188, 1110, 170, 1897, 1263, 4954, 119, 1135, 1110, 1120, 1655, 2039, 1190, 1103, 4954, 138, 119, 102]]

然后可以将其转换为PyTorch或TensorFlow中的张量。注意掩码是一个二进制张量,指示填充索引的位置,以便模型不会注意它们。对于BertTokenizer1表示应注意的值,而0表示已填充的值。该注意掩码在令牌生成器返回的字典中,在键“ attention_mask”下

padded_sequences["attention_mask"]
[[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]

令牌类型ID

一些模型的目的是进行序列分类或问题解答。这些要求将两个不同的序列连接到单个“ input_ids”条目中,这通常是借助特殊令牌(例如分类[CLS]符([SEP])和分隔符()令牌)执行的。例如,BERT模型按如下方式构建其两个序列输入:

>>> # [CLS] SEQUENCE_A [SEP] SEQUENCE_B [SEP]

我们可以使用令牌生成器通过将两个序列tokenizer作为两个参数(而不是像以前那样的列表)传递给这样的语句来自动生成这样的句子:

from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
sequence_a = "HuggingFace is based in NYC"
sequence_b = "Where is HuggingFace based?"
encoded_dict = tokenizer(sequence_a, sequence_b)
decoded = tokenizer.decode(encoded_dict["input_ids"])

对于某些模型而言,这足以了解一个序列在何处结束以及另一序列在何处开始。但是,其他模型(例如BERT)也部署令牌类型ID(也称为段ID)。它们被表示为二进制掩码,标识了模型中的两种序列。

令牌生成器将此掩码作为“ token_type_ids”条目返回:

第一个序列,即用于问题的“上下文”,其所有标记均以a表示0,而第二个序列,对应于“ question”,其所有标记均以a表示1

某些模型(例如)XLNetModel使用以表示的附加令牌2

职位编号

与在其中嵌入每个令牌位置的RNN相反,转换器不知道每个令牌的位置。因此,position_ids模型使用位置ID()来识别令牌列表中每个令牌的位置。

它们是可选参数。如果没有position_ids传递给模型,则ID将自动创建为绝对位置嵌入。

在范围内选择绝对位置嵌入。一些模型使用其他类型的位置嵌入,例如正弦位置嵌入或相对位置嵌入。[0, config.max_position_embeddings - 1]

标签

标签是一个可选参数,可以传递该参数以便模型计算损耗本身。这些标签应该是模型的预期预测:它将使用标准损失,以便计算其预测与预期值(标签)之间的损失。

这些标签根据模型头而有所不同,例如:

  • 对于序列分类模型(例如BertForSequenceClassification),模型期望维度张量(batch_size),其中批次的每个值都对应于整个序列的预期标签。
  • 对于令牌分类模型(例如BertForTokenClassification),模型期望维数张量,其中每个值对应于每个单独令牌的预期标签。(batch_size, seq_length)
  • 对于屏蔽语言建模(例如BertForMaskedLM),模型需要一个维数张量, 其中每个值对应于每个单独令牌的预期标签:标签是屏蔽令牌的令牌ID,其余值将被忽略(通常是-100)。(batch_size, seq_length)
  • 对于序列到序列的任务(例如,BartForConditionalGenerationMBartForConditionalGeneration),该模型预计尺寸的张量与对应于与每个输入序列相关联的靶序列的每个值。在训练期间,BART和T5都将在内部制作适当的解码器输入ID和解码器注意掩码。通常不需要提供它们。这不适用于利用Encoder-Decoder框架的模型。有关每个特定模型标签的更多信息,请参见每个模型的文档。(batch_size, tgt_seq_length)

基本模型(例如BertModel不接受标签,因为它们是基本变压器模型,仅输出要素features。

解码器输入ID
此输入特定于编码器-解码器模型,并且包含将输入到解码器的输入ID。这些输入应用于按顺序排序的任务,例如翻译或摘要,并且通常以特定于每种模型的方式构建。
大多数编码器/解码器模型(BART,T5)都可以decoder_input_ids通过自己创建labels。在这样的模型中,通过labels是处理培训的首选方法。
请检查每个模型的文档,以了解它们如何处理这些输入ID,以进行从序列到序列的训练。

前馈分块
在变压器的每个剩余注意力块中,自我注意力层通常后面是2个前馈层。前馈层的中间嵌入大小通常大于模型的隐藏大小(例如 bert-base-uncased)。
对于size的输入,存储中间前馈嵌入所需的内存可能占内存使用的很大一部分。《Reformer:Efficient Transformer》的作者注意到,由于计算与尺寸无关,因此在数学上等效于分别计算两个前馈层的输出嵌入, 然后将它们嵌入到中,从而以增加的计算时间与减少的内存使用量进行了交换。 ,但得出数学上 相等的结果。[batch_size, sequence_length][batch_size, sequence_length, config.intermediate_size]sequence_length[batch_size, config.hidden_size]_0, ..., [batch_size, config.hidden_size]_n[batch_size, sequence_length, config.hidden_size]n = sequence_length
对于使用该函数的模型apply_chunking_to_forward()chunk_size定义并行计算的输出嵌入的数量,从而定义内存和时间复杂度之间的权衡。如果chunk_size设置为0,则不执行前馈分块。

这块介绍了输入输出,还是非常不错的。

任务摘要

此页面显示了使用库时最常见的用例。可用的模型允许许多不同的配置,并在用例中具有极大的多功能性。这里介绍了最简单的方法,展示了诸如问题回答,序列分类,命名实体识别等任务的用法。

这些示例利用了自动模型,这些模型是将根据给定的检查点实例化模型,自动选择正确的模型体系结构的类。请检查AutoModel文档以获取更多信息。随意修改代码使其更具体,并使其适应您的特定用例。

为了使模型在任务上表现良好,必须从与该任务相对应的检查点加载模型。这些检查点通常在大量数据集上进行预训练,并在特定任务上进行微调。这意味着:

  • 并非所有模型都针对所有任务进行了微调。如果要针对特定​​任务微调模型,则可以利用示例目录中的run_ $ TASK.py脚本之一。
  • 微调的模型在特定的数据集上微调。该数据集可能与您的用例和域重叠,也可能不重叠。如前所述,您可以利用示例脚本来微调模型,也可以创建自己的训练脚本。

为了推断任务,该库提供了几种机制:

  • 管道:非常易于使用的抽象,只需要两行代码即可。
  • 直接使用模型:通过直接访问令牌生成器(PyTorch / TensorFlow)和完整的推理能力,可以减少抽象次数,但具有更大的灵活性和功能。

这两种方法都在这里展示。

此处介绍的所有任务都利用针对特定任务进行了微调的预训练检查点。加载未在特定任务上微调的检查点将仅加载基础变压器层,而不加载用于该任务的附加磁头,从而随机初始化该磁头的权重。

这将产生随机输出。

序列分类

序列分类是根据给定类数对序列进行分类的任务。序列分类的一个示例是GLUE数据集,它完全基于该任务。如果要微调GLUE序列分类任务上的模型,则可以利用run_glue.pyrun_tf_glue.pyrun_tf_text_classification.pyrun_xnli.py脚本。

这是使用管道进行情感分析的示例:识别序列是正数还是负数。它在sst2上利用了经过微调的模型,这是一个GLUE任务。

这将返回一个带有分数的标签(“正”或“负”),如下所示:

from transformers import pipeline
nlp = pipeline("sentiment-analysis")
result = nlp("I hate you")[0]
print(f"label: {result['label']}, with score: {round(result['score'], 4)}")
result = nlp("I love you")[0]
print(f"label: {result['label']}, with score: {round(result['score'], 4)}")
  

这是使用模型进行序列分类以确定两个序列是否相互解释的示例。该过程如下:

  1. 从检查点名称实例化标记器和模型。该模型被识别为BERT模型,并使用存储在检查点中的权重加载该模型。
  2. 使用正确的特定于模型的分隔符,标记类型ID和注意掩码,从两个句子构建一个序列(encode()__call__()注意这一点)。
  3. 将此序列传递给模型,以便将其分类为两个可用类别之一:0(不是复述)和1(是复述)。
  4. 计算结果的softmax以获得各类的概率。
  5. 打印结果。
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased-finetuned-mrpc")
model = AutoModelForSequenceClassification.from_pretrained("bert-base-cased-finetuned-mrpc")
classes = ["not paraphrase", "is paraphrase"]
sequence_0 = "The company HuggingFace is based in New York City"
sequence_1 = "Apples are especially bad for your health"
sequence_2 = "HuggingFace's headquarters are situated in Manhattan"
paraphrase = tokenizer(sequence_0, sequence_2, return_tensors="pt")
not_paraphrase = tokenizer(sequence_0, sequence_1, return_tensors="pt")
paraphrase_classification_logits = model(**paraphrase).logits
not_paraphrase_classification_logits = model(**not_paraphrase).logits
paraphrase_results = torch.softmax(paraphrase_classification_logits, dim=1).tolist()[0]
not_paraphrase_results = torch.softmax(not_paraphrase_classification_logits, dim=1).tolist()[0]
# Should be paraphrase
for i in range(len(classes)):
    print(f"{classes[i]}: {int(round(paraphrase_results[i] * 100))}%")
# Should not be paraphrase
for i in range(len(classes)):
    print(f"{classes[i]}: {int(round(not_paraphrase_results[i] * 100))}%")

得到的就是一个nn module那个自定义的torch的网络结构啊。。那使用的话和torch用起来不是差不多吗。。。

提取式问答

提取问题答案是从给定问题的文本中提取答案的任务。SQuAD数据集是一个完全基于该任务的问题回答数据集。如果要微调SQuAD任务上的模型,则可以利用run_qa.pyrun_tf_squad.py脚本。

这是使用管道进行问题解答的示例:从给定问题的文本中提取答案。它在SQuAD上利用了经过微调的模型。

>>> from transformers import pipeline >>> nlp = pipeline(“question-answering”) >>> context = r””” Extractive Question Answering is the task of extracting an answer from a text given a question. An example of a question answering dataset is the SQuAD dataset, which is entirely based on that task. If you would like to fine-tune a model on a SQuAD task, you may leverage the examples/question-answering/run_squad.py script. “””

这将返回从文本中提取的答案,置信度得分以及“开始”和“结束”值,这些值是提取的答案在文本中的位置。

>>> result = nlp(question=”What is extractive question answering?”, context=context) >>> print(f”Answer: ‘{result[‘answer’]}‘, score: {round(result[‘score’], 4)}, start: {result[‘start’]}, end: {result[‘end’]}“) Answer: ‘the task of extracting an answer from a text given a question.’, score: 0.6226, start: 34, end: 96 >>> result = nlp(question=”What is a good example of a question answering dataset?”, context=context) >>> print(f”Answer: ‘{result[‘answer’]}‘, score: {round(result[‘score’], 4)}, start: {result[‘start’]}, end: {result[‘end’]}“) Answer: ‘SQuAD dataset,’, score: 0.5053, start: 147, end: 161

这是一个使用模型和标记器回答问题的示例。该过程如下:

  1. 从检查点名称实例化标记器和模型。该模型被识别为BERT模型,并使用存储在检查点中的权重加载该模型。
  2. 定义文本和一些问题。
  3. 遍历问题,并根据文本和当前问题构建序列,并使用正确的特定于模型的分隔符标记类型ID和注意掩码。
  4. 将此序列传递给模型。对于起点和终点位置,这将在整个序列标记(问题和文本)中输出一定范围的分数。
  5. 计算结果的softmax以获得令牌上的概率。
  6. 从标识的起始值和终止值中获取令牌,然后将这些令牌转换为字符串。
  7. 打印结果。
>>> from transformers import AutoTokenizer, AutoModelForQuestionAnswering
>>> import torch

>>> tokenizer = AutoTokenizer.from_pretrained("bert-large-uncased-whole-word-masking-finetuned-squad")
>>> model = AutoModelForQuestionAnswering.from_pretrained("bert-large-uncased-whole-word-masking-finetuned-squad")

>>> text = r"""
...   Transformers (formerly known as pytorch-transformers and pytorch-pretrained-bert) provides general-purpose
... architectures (BERT, GPT-2, RoBERTa, XLM, DistilBert, XLNet…) for Natural Language Understanding (NLU) and Natural
... Language Generation (NLG) with over 32+ pretrained models in 100+ languages and deep interoperability between
... TensorFlow 2.0 and PyTorch.
... """

>>> questions = [
...     "How many pretrained models are available in   Transformers?",
...     "What does   Transformers provide?",
...     "  Transformers provides interoperability between which frameworks?",
... ]

>>> for question in questions:
...     inputs = tokenizer(question, text, add_special_tokens=True, return_tensors="pt")
...     input_ids = inputs["input_ids"].tolist()[0]
...
...     outputs = model(**inputs)
...     answer_start_scores = outputs.start_logits
...     answer_end_scores = outputs.end_logits
...
...     answer_start = torch.argmax(
...         answer_start_scores
...     )  # Get the most likely beginning of answer with the argmax of the score
...     answer_end = torch.argmax(answer_end_scores) + 1  # Get the most likely end of answer with the argmax of the score
...
...     answer = tokenizer.convert_tokens_to_string(tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end]))
...
...     print(f"Question: {question}")
...     print(f"Answer: {answer}")
Question: How many pretrained models are available in   Transformers?
Answer: over 32 +
Question: What does   Transformers provide?
Answer: general - purpose architectures
Question:   Transformers provides interoperability between which frameworks?
Answer: tensorflow 2 . 0 and pytorch

语言建模

语言建模是使模型适合语料库的任务,语料库可以是特定于领域的。所有流行的基于变压器的模型都使用语言建模的变体进行了训练,例如具有掩盖语言建模的BERT,具有因果语言建模的GPT-2。

语言建模在预训练之外也很有用,例如,将模型分布更改为特定领域:使用在非常大的语料库上训练的语言模型,然后将其微调到新闻数据集或科学论文上,例如LysandreJik / arxiv-nlp

掩盖语言建模

掩码语言建模的任务是用掩码令牌对序列中的令牌进行掩码,并提示模型用适当的令牌填充该掩码。这允许模型同时关注右侧上下文(蒙版右侧的标记)和左侧上下文(蒙版左侧的标记)。这样的培训为需要双向上下文的下游任务(例如SQuAD)提供了坚实的基础(问题解答,请参见Lewis,Lui,Goyal等人,第4.2部分)。如果您想对掩盖的语言建模任务进行模型微调,则可以利用run_mlm.py脚本。

这是使用管道替换序列中的掩码的示例:

from transformers import pipeline

nlp = pipeline(“fill-mask”)

from pprint import pprint
pprint(nlp(f"HuggingFace is creating a {nlp.tokenizer.mask_token} that the community uses to solve NLP tasks."))

这是一个使用模型和标记器进行掩盖语言建模的示例。该过程如下:

  1. 从检查点名称实例化标记器和模型。该模型被标识为DistilBERT模型,并使用存储在检查点中的权重加载该模型。
  2. 用带掩码的标记定义一个序列,放置tokenizer.mask_token而不是单词。
  3. 将该序列编码为ID列表,然后在该列表中找到被屏蔽令牌的位置。
  4. 在掩码标记的索引处检索预测:此张量与词汇表具有相同的大小,并且值是归属于每个标记的分数。该模型为在这种情况下可能认为的代币提供了更高的分数。
  5. 使用PyTorchtopk或TensorFlowtop_k方法检索前5个令牌。
  6. 用标记替换掩码标记并打印结果

Summary of the tasks 这里全都是直接调用预训练模型的用法,finetune的都是脚本感觉不好用。

基于Huggingface使用BERT进行文本分类的fine-tuning | 程序员灯塔www.wangt.cc

这里是finetune的比较好的基于torch的例子,和之前写的from scratch那个脚本差不多。


模型总结

这是 变压器中可用模型的摘要。假定您熟悉原始的变压器模型。为便于介绍,请检查带注释的变压器。在这里,我们关注模型之间的高级差异。您可以在各自的文档中更详细地检查它们。另外,请查看经过预训练的模型页面,以查看适用于每种类型的模型和所有社区模型的检查点。

库中的每个模型都属于以下类别之一:

自回归模型在经典语言建模任务上进行了预训练:猜测下一个已读完所有先前标记的标记。它们对应于原始转换器模型的解码器,并且在整个句子的顶部使用了一个掩码,以便注意头只能看到文本中的之前内容,而不能看到其后的内容。尽管可以对这些模型进行微调并在许多任务上取得很好的效果,但是最自然的应用是文本生成。此类模型的典型示例是GPT。

通过以某种方式破坏输入令牌并尝试重建原始句子来对自动编码模型进行预训练。从某种意义上说,它们与原始变压器模型的编码器相对应,因为它们无需任何掩码即可访问完整的输入。这些模型通常建立整个句子的双向表示。可以对它们进行微调并在许多任务(例如文本生成)上取得出色的结果,但是它们最自然的应用是句子分类或标记分类。此类模型的典型示例是BERT。

请注意,自动回归模型和自动编码模型之间的唯一区别在于模型的预训练方式。因此,相同的体系结构既可以用于自动回归模型,也可以用于自动编码模型。当给定模型同时用于两种类型的预训练时,我们将其放在与首次引入该模型的文章相对应的类别中。

序列到序列模型将原始转换器的编码器和解码器同时用于翻译任务或通过将其他任务转换为序列到序列问题。可以将它们微调成许多任务,但最自然的应用是翻译,摘要和问题解答。原始的转换器模型是此类模型的一个示例(仅用于翻译),T5是可以在其他任务上进行微调的示例。

多模式模型将文本输入与其他类型的输入(例如图像)混合在一起,并且更特定于给定任务。

Summary of the modelshuggingface.co图标

都是各类模型的介绍

Preprocessing datahuggingface.co

这部分是关于tokenizer的问题,分词器的各种设定,涉及到wordpiece和bpe算法wtf。

//huggingface.co/transformers/training.htmlhuggingface.co

训练和微调

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

这很有用,因为它使我们能够使用预训练的BERT编码器,并轻松地在我们选择的任何序列分类数据集上对其进行训练。我们可以使用任何PyTorch优化器,但我们的库也提供了AdamW()实现梯度偏差校正和权重衰减的 优化器。

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

还有默认的优化器啊,wtf

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)

这部分就是torch高级一点的用法了

这个和torch的用法一样,实际上就是用torch

torch的权重冻结方法

这几个脚本应该不错,都是用trainer来训练啊,但是我想学torch啊啊啊啊啊

Training and fine-tuninghuggingface.co

Examples – transformers 4.4.2 documentationhuggingface.co

//huggingface.co/transformers/model_sharing.htmlhuggingface.co

模型的共享和

这几个example应该也不错

//huggingface.co/transformers/tokenizer_summary.html#huggingface.co

tokenizer的算法原理介绍

Multi-lingual modelshuggingface.co

多语种训练;

Pretrained modelshuggingface.co

预训练模型介绍


自定义数据集上做微调:

Fine-tuning with custom datasetshuggingface.co

这个上面的例子也都不错可以Fine-tuning with custom datasets这个上面的例子也都不错可以

Transformers Notebooks

Transformers Notebookshuggingface.co

这里还有更多笔记本,很好。

Community – transformers 4.4.2 documentationhuggingface.co

这里也是一堆的笔记本非常好:

Migrating from previous packageshuggingface.co

版本的变化和一些安装的细节;

Exporting transformers modelshuggingface.co

模型的序列化和部署功能。

Exporting transformers modelshuggingface.co

模型的序列化和部署功能。

//huggingface.co/transformers/perplexity.htmlhuggingface.co

困惑度计算。

Callbacks – transformers 4.4.2 documentationhuggingface.co

trainer的callback功能,Callbacks – transformers 4.4.2 documentationtrainer的callback功能,

各种函数的用法。

Callbacks – transformers 4.4.2 documentationhuggingface.co

其它的一些使用小设计,不一定用上可能自己写也行