依照官网的使用文档,pytorch下使用transformers进行fine-tuning。
我基本全部都是按照文档来的。
注意:官网的例子基本是直接将模型或者数据集通过一行代码存到本地缓存中,但是需要。
我们也可以先把模型和数据集下载到本地,之后再从本地加载。我使用的是这种方式。
我们使用huggingface里自带的数据集,加载数据的多种方式可见教程。
教程里使用的是glue下的mrpc数据集,我们也使用该数据集。
但是在huggingface里带的数据集中找到的mrpc很小(感觉是个样例),我就自己从网上下载了mrpc数据集,下载地址。
下载完毕后,我使用了后缀是train和test的txt文件作为训练集和测试集,没分验证集。
加载数据集教程可见地址。
from datasets import load_dataset
raw_datasets = load_dataset('text', data_files={'train': 'MRPC/msr_paraphrase_train.txt',
'test': 'MRPC/msr_paraphrase_test.txt'})
这样就把数据集加载完成了,MRPC/msr_paraphrase_train.txt这里写自己数据集的文件地址就行。
但是在加载时,报错:
ImportError: IProgress not found. Please update jupyter and ipywidgets.
See https://ipywidgets.readthedocs.io/en/stable/user_install.html
上网搜索,按照以下步骤解决:
conda install ipywidgets
jupyter nbextension enable --py widgetsnbextension
之后数据集成功加载完毕。数据集长这样:
DatasetDict({
train: Dataset({
features: ['text'],
num_rows: 4077
})
test: Dataset({
features: ['text'],
num_rows: 1726
})
})
但是,这数据集不是我们希望的样子。这里应该是需要写一些分割数据集之类的脚本,但是没时间搜了。
由于服务器连不上外网,我电脑能连。我就先用电脑下载下来正确的数据集,再传到服务器上。具体过程如下:
from datasets import load_dataset
old_datasets = load_dataset("glue", "mrpc")
old_datasets.save_to_disk('data')
import datasets
raw_datasets = datasets.DatasetDict()
raw_datasets = raw_datasets.load_from_disk('data')
可以看看数据集里的数据长啥样:
raw_train_dataset = raw_datasets["train"]
raw_train_dataset[0]
结果:
{'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .',
'label': 1,
'idx': 0}
可以看看数据集的特征:
print(raw_train_dataset.features)
结果:
{'sentence1': Value(dtype='string', id=None), 'sentence2': Value(dtype='string', id=None), 'label': ClassLabel(num_classes=2, names=['not_equivalent', 'equivalent'], names_file=None, id=None), 'idx': Value(dtype='int32', id=None)}
tokenizer和我们想要使用的模型需要一致,tokenizer可以理解为把我们的原始文本转换成数字,从而能够输入网络,这种转换有多种模式,理论部分可以查看教程。
我们使用的模型为bert-base-uncased,还是手动下载并加载。
下载地址,可以使用git把整个库都下载下来,也可以只下载需要的。我下载了:
下载的这些可能有部分文件用不到。下载之后将它们放入文件夹bert-base-uncased中,并传给服务器。
通过以下代码加载tokenizer,py或者ipynb文件的同级目录下要有一个bert-base-uncased的文件夹,里面放着模型。
from transformers import BertTokenizer
model_name = 'bert-base-uncased'
tokenizer = BertTokenizer.from_pretrained(model_name)
可以试一下tokenizer的效果:
inputs = tokenizer("This is the first sentence.", "This is the second one.")
print(inputs)
{
'input_ids': [101, 2023, 2003, 1996, 2034, 6251, 1012, 102, 2023, 2003, 1996, 2117, 2028, 1012, 102],
'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
}
tokenizer效果的具体讲解可见教程。
由于数据集中也是两个句子和label,我们需要把两个句子也像上面一样预处理一下。
完整的预处理代码如下:
from transformers import DataCollatorWithPadding
def tokenize_function(example):
return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
tokenized_datasets是预处理后的数据集,而DataCollatorWithPadding是将数据集处理成批的工具(应该是,具体可见链接),并且会将数据整理成统一的长度(也就是该批最长的数据的长度)。与将数据整理成整个数据集最长数据的长度相比,整理成该批数据最长数据的长度显然更好。
首先定义一个TrainingArguments类,这里可以定义一些训练用的超参数。我们可以只定义模型的保存地址。具体参数可见文档。
from transformers import TrainingArguments
training_args = TrainingArguments("test-trainer")
接下来加载模型:
from transformers import BertForSequenceClassification
model = BertForSequenceClassification.from_pretrained(model_name, num_labels=2)
这里还是从本地文件里加载的模型。
接下来就可以传递之前定义的很多参数,加载一个Trainer从而进行训练。
from transformers import Trainer
trainer = Trainer(
model,
training_args,
train_dataset = tokenized_datasets["train"],
eval_dataset = tokenized_datasets["validation"],
data_collator = data_collator,
tokenizer = tokenizer,
)
之后就可以开始fine-tuning,也就是训练:
trainer.train()
训练中只有loss的值,模型效果如何并不明显。因此我们需要为trainer引入能显示验证集准确率的东西。具体讲解可见文档,简单说就是定义一个计算验证集准确率的函数,并将其引入我们的trainer。
from datasets import load_metric
def compute_metrics(eval_preds):
metric = load_metric("glue", "mrpc", mirror="tuna")
logits, labels = eval_preds
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
training_args = TrainingArguments("test-trainer", evaluation_strategy="epoch")
model = BertForSequenceClassification.from_pretrained(model_name, num_labels=2)
trainer = Trainer(
model,
training_args,
train_dataset=tokenized_datasets["train"],
eval_dataset=tokenized_datasets["validation"],
data_collator=data_collator,
tokenizer=tokenizer,
compute_metrics=compute_metrics,
)
metric = load_metric("glue", "mrpc", mirror="tuna")
这里还是需要,用了清华的镜像。或者还可以这样解决(我没试过)。
最后,开始训练:
trainer.train()