我们来学习一下 Hugging Face Transformers 这个很方便的NLP library。本节我们首先看一下官方的 Quick Tour: https://huggingface.co/transformers/quicktour.html#
官方的解释从pipeline开始。它把所有的东西全部打包到了一起为一个端到端的工具,对初学者非常友好。好,首先加载pipeline
from transformers import pipeline
加载完之后我们来创建第一个模型
classifier = pipeline('sentiment-analysis')
非常简单的操作,直接告诉 pipeline我们要干啥便能得到 classifier 这个model。那么这里的 task 是 sentiment-analysis, 就是给一句话分析语气是 positive 还是 negative.
注意:
下面我们可以尝试一下用 classifier 这个模型了,看看表现如何
classifier('We are very happy to show you the 珞 Transformers library.')
# output: {'label': 'POSITIVE', 'score': 0.9997795224189758}]
上例中,我们输入了一句话给classifier,然后它直接输出了这句话是pos还是nega还有对应的打分,这个打分实际上是最后softmax输出的概率值。我们也可以直接输入一个batch
results = classifier(["We are very happy to show you the 珞 Transformers
library.", "We hope you don't hate it."])
for result in results:
print(f"label: {result['label']}, with score: {round(result['score'], 4)}")
# output: label: POSITIVE, with score: 0.9998
# output: NEGATIVE, with score: 0.5309
可以看到,pipeline 实现的是一个端到端的model,使用非常方便。
如果想使用其他的模型,可以在 model hub 里面找,比如说我们想用 nlptown/bert-base-multilingual-uncased-sentiment” 这个模型, 那就直接把名字输入进pipeline
classifier2 = pipeline('sentiment-analysis', model="nlptown/bert-base-multilingual-uncased-sentiment")
pipeline也支持我们自己的模型和自定义的tokenizer,但是我们额外需要两个类 AutoTokenizer 和 AutoModelForSequenceClassification (若使用tensorflow则是TFAutoModelForSequenceClassification)
from transformers import AutoTokenizer, AutoModelForSequenceClassification
AutoTokenizer 的作用是 下载我们选取model对应的 tokenizer 并且实例化这个 tokenizer
AutoModelForSequenceClassification 作用是下载modek身,注意不同任务我们需要使用不同的class,具体对应关系在 task summary tutorial中
model_name = "nlptown/bert-base-multilingual-uncased-sentiment"
model = AutoModelForSequenceClassification.from_pretrained(model_name)
tokenizer = AutoTokenizer.from_pretrained(model_name)
classifier = pipeline('sentiment-analysis', model=model, tokenizer=tokenizer)
如果model hub上没找到匹配你的数据的模型,你将需要 fine-tune a pretrained model on your data. 官网上有例子该怎么做,训练完之后别忘了share with other people
紧接着我们挨个分析分析pipeline里面的各个模块
Tokenizer: 它的作用是预处理text,主要分为以下几步
inputs = tokenizer("We are very happy to show you the 珞 Transformers library.")
输入11个words,一个标点,开始和结束,总共输出14个数字
>>> inputs = {'input_ids': [101, 2057, 2024, 2200, 3407, 2000, 2265, 2017, 1996, 100, 19081, 3075, 1012, 102],
'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}
输入一个batch,tokenizer会自动truncate,pad,调整 attention mask
pt_batch = tokenizer(["We are very happy to show you the 珞 Transformers library.", "We hope you don't hate it."],
padding=True, truncation=True, max_length=512, return_tensors="pt") # pt = pytorch
输出
for key, value in pt_batch.items():
print(f"{key}: {value.numpy().tolist()}")
input_ids: [[101, 2057, 2024, 2200, 3407, 2000, 2265, 2017, 1996, 100, 19081, 3075, 1012, 102], [101, 2057, 3246, 2017, 2123, 1005, 1056, 5223, 2009, 1012, 102, 0, 0, 0]]
attention_mask: [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0]]
tokenize 之后的batch可以直接放进model中,注意pytorch需要加上一个额外的符号 ** before pt_batch
pt_outputs = pt_model(**pt_batch)
注意,所有的输出都是 tuples
>>> print(pt_outputs)
(tensor([[-4.0833, 4.3364], [ 0.0818, -0.0418]], grad_fn=<AddmmBackward>),)
这里我们只要求输出model的final activations,因此得到的是 a tuple with one element. in general,model反馈的可能不止 final activations
注意,输出的final activations是在final activation function (like SoftMax) 之前的!因为通常final activation function 会和 loss function 定义在一起.
因此,我们在用softmax才能得到预测分布
import torch.nn.functional as F
pt_predictions = F.softmax(pt_outputs[0], dim=-1)
print(pt_predictions)
tensor([[2.2043e-04, 9.9978e-01], [5.3086e-01, 4.6914e-01]], grad_fn=<SoftmaxBackward>)
可以看到,此时我们得到的 pt_predictions 和之前直接用pipeline得到的一致
如果我们有label,我们可以直接提供给model,它将直接输出loss和final activations
import torch
pt_outputs = pt_model(**pt_batch, labels = torch.tensor([1, 0]))
注意这些模型就是标准的 torch.nn.Module 或 tf.keras.Model,你们可以直接用常规方式训练
我们有提供一个额外的 Trainer 类 (如果用TF则是 TFTrainer) 帮助你训练 (处理 distributed training, mixed precision 等问题)。详见 the training tutorial
在你的模型 fine-tuned 之后,你可以保存它和它的tokenizer
tokenizer.save_pretrained(save_directory)
model.save_pretrained(save_directory)
如果想load back,那就用 from_pretrained() 方法输入 directory name (instead of the model name).
珞 Transformers 一个 cool 的特点是你可以随意在 PyTorch 和 TensorFlow 之间切换
任何一个存储的模型可以 loaded back either in PyTorch or TensorFlow.
比如说,我们要 如果要把一个存储的pytorch model load 称为 TF, then:
tokenizer = AutoTokenizer.from_pretrained(save_directory)
model = TFAutoModel.from_pretrained(save_directory, from_pt=True)
反过来,如果要把一个存储的TF model load 成pytorch,则
tokenizer = AutoTokenizer.from_pretrained(save_directory)
model = AutoModel.from_pretrained(save_directory, from_tf=True)
你也可以让 model返回所有的 hidden states 和 attention weights
pt_outputs = pt_model(**pt_batch, output_hidden_states=True, output_attentions=True)
all_hidden_states, all_attentions = pt_outputs[-2:]
在我们之前的例子中,model的名字是 distilbert-base-uncased-finetuned-sst-2-english, 这意味着说它使用the DistilBERT architecture.
因为我们使用了 AutoModelForSequenceClassification 类, 自动创建的model便是 DistilBertForSequenceClassification.
如果我们不想要这种自动创建的行为,可以直接load DistilBertForSequenceClassification
from transformers import DistilBertTokenizer, DistilBertForSequenceClassification
model_name = "distilbert-base-uncased-finetuned-sst-2-english"
model = DistilBertForSequenceClassification.from_pretrained(model_name)
tokenizer = DistilBertTokenizer.from_pretrained(model_name)
如果你想改变model本身的架构,那你可以自定义model (比如any of the hidden dimension, dropout rate, etc.)
实际上,每一个model都有一个自己的 configuration. 比如说DistilBERT这个model,它的configuration就是 DistilBertConfig
但是如果你要做核心的修改,比如改变hidden size,那你只能从头开始训练了。此时你可以从 configuration 中直接实例化这个model开始训练
下面我们给一个从头开始实例化的例子,即我们将从configuration中 (而非from_pretrained()中) 实例化model
多import一个DistilBertConfig class
from transformers import DistilBertConfig, DistilBertTokenizer, DistilBertForSequenceClassification
config = DistilBertConfig(n_heads=8, dim=512, hidden_dim=4*512)
tokenizer = DistilBertTokenizer.from_pretrained('distilbert-base-uncased')
model = DistilBertForSequenceClassification(config)
对于一些小修改,比如只是改变model head (比如label个数),你仍然可以使用pretrained model
一种方式是改configuration中number of labels
另一种更简单的方式是在from_pretrained()方法中直接输入修改(只要是configuration能改的都可以),他将自动修改config中的配置
from transformers import DistilBertConfig, DistilBertTokenizer, DistilBertForSequenceClassification
model_name = "distilbert-base-uncased"
model = DistilBertForSequenceClassification.from_pretrained(model_name, num_labels=10)
tokenizer = DistilBertTokenizer.from_pretrained(model_name)