在之前获取tokenizer和model的基础上,增加以下代码:
batch = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
# This is new
batch["labels"] = torch.tensor([1, 1])
optimizer = AdamW(model.parameters())
loss = model(**batch).loss
loss.backward()
optimizer.step()
这部分比较容易懂,为了能够得到一个较好的效果,就需要一个较大的数据集
在这个部分,我们将使用MRPC数据集作为例子,这个数据集包含5801对带有标签的句子。
加载数据:
从hub上加载数据集,所有可用的数据集:
https://huggingface.co/datasets?sort=downloads
从hub上下载MRPC数据集:
from datasets import load_dataset
raw_datasets = load_dataset("glue", "mrpc")
print(raw_datasets.data)
出现的问题:无法连接到"raw.githubusercontent.com"这个网站
解决方法:在网站https://www.ipaddress.com中输入“raw.githubusercontent.com”,可以查到该网站允许访问的Ip地址,如下图:
185.199.108.133 raw.githubusercontent.com
即可正常运行代码。
得到dataset之后,该dataset是一个字典,被分为了训练集、测试集、验证集,可以使用下标的形式进行获取:
raw_trian_dataset = raw_datasets["train"]
raw_test_dataset = raw_datasets["test"]
raw_val_dataset = raw_datasets["validation"]
print(raw_trian_dataset[0])
其中训练集中的label已经被设置成了numpy,如若想知道对应什么含义,可以使用如下语句:
raw_train_dataset.features
处理数据:
直接使用Tokenizer方法处理刚刚加载在数据集中的数据(根据dataset中的feature进行处理),并且应该将两句话拼接起来,但是使用不同的sentence token:
tokenized_dataset = tokenizer(
raw_trian_dataset["sentence1"],
raw_trian_dataset["sentence2"],
padding=True,
truncation=True,
return_tensors="pt"
)
为了能保存数据集,这里先写一个函数:
def tokenize_function(example):
return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
这里将padding去除了,因为一下子对所有的数据padding相比于对一个batch的数据padding,效率低很多。map方法提供了很大的灵活性,可以一次性对所有的数据进行处理,并且速度更快,而且不需要更大的RAM。并且处理过程可以自由设计,可以不仅仅进行tokenizer操作。最终的结果存放在input_id中。
动态填充:
在每批需要时应用填充技术,避免输入过长,这能加快训练速度。但是如果是在TPU上进行训练,则可能需要额外的填充,因为TPU更喜欢固定的形状。
为了实现这一点,必须定义一个collate函数,这里直接使用库中有的:
from transformers import DataCollatorWithPadding
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
接着使用该函数对数据集进行padding(没怎么学明白,官方文档给的有点粗略了)
Transformers提供了一个Trainer的类去对任何的预训练模型进行fine-tune操作。
首先应该定义一个TrainArguments类,它包含了Trainer用到的所有超参数。我们唯一需要输入的参数是一个模型保存路径。
from transformers inport TrainingArguments
training_args = TrainingArguments("test-trainer") # 定义一个trainer所需要的超参数
定义完所有的工具之后,就可以定义Trainer类了:
trainer = Trainer(
model,
training_args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["validation"],
data_collator=data_collator,
tokenizer=tokenizer
)
要在数据集上进行微调,只需要调用trainer.train()
开始训练之后,他会每500个steps就报告一次训练损失。但是它不会报告这个模型的性能表现,主要有两个原因:
模型评估:
我们要想写一个有用的compute_metrics()方法,该方法必须接收一个EvalPrediction对象,这个对象需要是一个元组,并且有prediction和label_ids。为了从模型中得到预测,需要使用Trainer.predict()方法。
该方法返回的也是一个元组,他有三个fields,predictions,label_ids和metrices。第三个fields现在存放的是loss,一旦我们完成了compute_metrics()方法的编写并且将它加入到Trainer中,这个field也将包含compute_metrics()方法的返回值。
print(predictions.predictions.shape, predictions.label_ids.shape) # predictions是对每个类别的一个打分,我们需要获得得分值最大的标签的index
preds = np.argmax(predictions.predictions) # 获取最大值的索引值
(题外话)准确率计算方法:sklearn.metrics.accuracy_score(true, predic)
使用evaluate库中的方法构造compute_metric()方法,这样就得到了compute_metrics方法:
def compute_metrics(eval_preds):
metric = evaluate.load("glue", "mrpc")
logits, labels = eval_preds
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
# 还要在TrainingArguments中添加新的参数:
training_args = TrainingArguments("test-trainer", evaluation_strategy="epoch")