1.Fine-tuning in native本地 PyTorch
模型类model class不是以TF
开头的都是以Pytorch来运行的。
接下来考虑微调fine-tune一个BERT模型来做句子分类任务。当我们使用from_pretrained()
实例化一个模型时,所指定模型的配置configuration 和预训练的权重会被用于初始化模型,这个库library也会包含一些指定任务(task-specific)的最后一层final layer或者头head。当final layers 和head 没有出现在指定的预训练模型中时,会随机分配权重给它们。
举例:实例化一个模型,用的是
BertForSequenceClassification.from_pretrained('bert-base-uncased', num_labels=2)
就会创建一个实例,这个实例会带有来自bert-base-uncased
模型的encoder weights,编码权重。并且随机初始化句子分类头(sequence classification head)在encoder上,output size是2。
模型是默认在eval
模式下进行初始化的。我们可以用model.train()
来将其放进训练模式中(to put it in train mode)
from transformers import BertForSequenceClassification
model = BertForSequenceClassification.from_pretrained('bert-base-uncased')
model.train()
这是有用的,因为这允许我们利用预训练的BERT编码器(encoder),简单地在任何我们选择的分类数据集上进行训练。我们可以用任何Pytorch的优化器optimizer。同时我们也提供了AdamW()
优化器,其实现梯度偏差校正以及权重衰减。
from transformers import AdamW
optimizer = AdamW(model.parameters(), lr=1e-5)
优化器允许我们为特定的参数组应用不同的超参数。 例如,我们可以将权重衰减应用于除bias和layer归一化以外的所有参数:
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)
接下来我们可以设置一个极其简单的training batch,使用__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
参数的分类模型,返回的第一个元素就是预测值和传进去的标签值之间的Cross Entropy loss交叉熵损失。设置好我们的优化器optimizer后,我们可以就可以做后向传播并更新权重了。
labels = torch.tensor([1,0]).unsqueeze(0)
outputs = model(input_ids, attention_mask=attention_mask, labels=labels)
loss = outputs.loss
loss.backward()
optimizer.step()
或者,你可以只获取logits并计算自己的损失值,下面的代码跟上面等价
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()
我们强烈推荐使用**Trainer()**会在下面讨论,很方便的训练Transformer模型,
1.1 Freezing the encoder 冻结编码器
在有些情况下,你需要保持预训练编码器的权重冻结,只对head layers的权重进行优化,要这么做的话,只需要在encoder 参数上设置requires_grad
为 False,这样就可以使库中任何task-specific model上的base_model子模块来访问它:
for param in model.base_model.parameters():
param.requires_grad = False
2.在tensorflow上微调
略
3.Trainer
我们也提供了简单,但功能完善的训练和评估接口:Trainer()
和 TFTrainer()
,你可以用于训练,微调,评估任何Transformer模型,有这非常多的训练options可供选择,还有许多内建的属性比如 logging, gradient accumulation, and mixed precision.
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
返回的参数必须是损失值,这个损失值是用来优化的
Trainer()
使用的是内建的函数来收集batches并准备好将它们传进模型中,如果需要,也可以使用data_collator
参数来传你自己的collator function(校对函数),这个校对函数传进以你提供的数据集的格式的数据,返回可以传进模型的batch。
要计算除损失之外的其他指标,您还可以定义自己的compute_metrics
函数并将其传递给Trainer。
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
}
最后,您可以通过在指定的logging_dir目录中启动tensorboard来查看结果,包括所有计算的指标。、
一个轻量化的colab demo,展示IMDb电影情感分析,使用的Trainer,链接
from transformers import BertForSequenceClassification, BertTokenizerFast, Trainer, TrainingArguments
from nlp import load_dataset
import torch
import numpy as np
from sklearn.metrics import accuracy_score, precision_recall_fscore_support
model = BertForSequenceClassification.from_pretrained('bert-base-uncased')
tokenizer = BertTokenizerFast.from_pretrained('bert-base-uncased')
def tokenize(batch):
return tokenizer(batch['text'], padding=True, truncation=True)
train_dataset, test_dataset = load_dataset('imdb', split=['train', 'test'])
train_dataset = train_dataset.map(tokenize, batched=True, batch_size=len(train_dataset))
test_dataset = test_dataset.map(tokenize, batched=True, batch_size=len(train_dataset))
train_dataset.set_format('torch', columns=['input_ids', 'attention_mask', 'label'])
test_dataset.set_format('torch', columns=['input_ids', 'attention_mask', 'label'])
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
}
training_args = TrainingArguments(
output_dir='./results',
num_train_epochs=1,
per_device_train_batch_size=16,
per_device_eval_batch_size=64,
warmup_steps=500,
weight_decay=0.01,
evaluate_during_training=True,
logging_dir='./logs',
)
trainer = Trainer(
model=model,
args=training_args,
compute_metrics=compute_metrics,
train_dataset=train_dataset,
eval_dataset=test_dataset
)
trainer.train()
trainer.evaluate()
{'epoch': 1.0,
'eval_accuracy': 0.93552,
'eval_f1': 0.9362089434111595,
'eval_loss': 0.18167165885476963,
'eval_precision': 0.926311667971809,
'eval_recall': 0.94632}