Transformers 微调
基于 Transformers 实现模型微调训练的主要流程
-
数据集下载
-
数据预处理
-
训练超参数配置
-
训练评估指标设置
-
训练器基本介绍
-
实战训练
-
模型保存
一个典型的数据点包括文本和相应的标签。来自YelpReviewFull测试集的示例如下:
{
'label': 0,
'text': 'I got \'new\' tires from them and within two weeks got a flat. I took my car to a local mechanic to see if i could get the hole patched, but they said the reason I had a flat was because the previous patch had blown - WAIT, WHAT? I just got the tire and never needed to have it patched? This was supposed to be a new tire. \\nI took the tire over to Flynn\'s and they told me that someone punctured my tire, then tried to patch it. So there are resentful tire slashers? I find that very unlikely. After arguing with the guy and telling him that his logic was far fetched he said he\'d give me a new tire \\"this time\\". \\nI will never go back to Flynn\'s b/c of the way this guy treated me and the simple fact that they gave me a used tire!'
}
数据字段
‘text’: 评论文本使用双引号(“)转义,任何内部双引号都通过2个双引号(”")转义。换行符使用反斜杠后跟一个 “n” 字符转义,即 “\n”。
‘label’: 对应于评论的分数(介于1和5之间)。
数据拆分(分成训练跟测试)
Yelp评论完整星级数据集是通过随机选取每个1到5星评论的130,000个训练样本和10,000个测试样本构建的。总共有650,000个训练样本和50,000个测试样本。
下载数据集
import os
# 代理的地址,格式为 http://ip:port
http_proxy="http://proxy.sensetime.com:3128/"
https_proxy="http://proxy.sensetime.com:3128/"
# 设置代理
os.environ["HTTP_PROXY"] = http_proxy
os.environ["HTTPS_PROXY"] = https_proxy
from datasets import load_dataset
dataset = load_dataset("yelp_review_full")
#得到的dataset 其实就是一个字典(key:value格式)train 跟test就是这个下载下来的数据集的key。而dataset["train"] 通过这个可以拿到Dataset格式的训练数据集(集合)
print(dataset["train"][0])
数据集抽样
import random
import pandas as pd
import datasets
from IPython.display import display, HTML
#用于从给定的数据集 (dataset) 中随机选择一些示例并显示
def show_random_elements(dataset, num_examples=10):
assert num_examples <= len(dataset), "Can't pick more elements than there are in the dataset."
picks = []
for _ in range(num_examples):
pick = random.randint(0, len(dataset)-1)
print(pick)
while pick in picks:
pick = random.randint(0, len(dataset)-1)
picks.append(pick)
#将从数据集中随机选择的示例创建为 Pandas DataFrame
df = pd.DataFrame(dataset[picks])
for column, typ in dataset.features.items():#遍历数据集的所有特征
if isinstance(typ, datasets.ClassLabel):#检查特征是否是分类标签
#如果是分类标签,将使用 lambda 函数将标签的索引映射到实际的类别名称
df[column] = df[column].transform(lambda i: typ.names[i])
display(HTML(df.to_html()))
#可以print(show_random_elements(dataset["train"]) 查看效果
预处理数据
下载数据集到本地后,使用 Tokenizer 来处理文本,对于长度不等的输入数据,可以使用填充(padding)和截断(truncation)策略来处理。
Datasets 的 map 方法,支持一次性在整个数据集上应用预处理函数。
下面使用填充到最大长度的策略,处理整个数据集:
from transformers import AutoTokenizer
#用于加载预训练的文本处理模型(Tokenizer),以便将文本数据转换为模型可以接受的输入格式
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
def tokenize_function(examples):
return tokenizer(examples["text"], padding="max_length", truncation=True)
tokenized_datasets = dataset.map(tokenize_function, batched=True)#刚刚生成的dataset 通过map的方法,把里面的每个样本都进行tokenize_function操作,生成处理过的数据集tokenized_datasets
#可以show_random_elements(tokenized_datasets["train"], num_examples=1)查看效果
数据抽样
使用 1000 个数据样本,在 BERT 上演示小规模训练(基于 Pytorch Trainer)
shuffle()函数会随机重新排列列的值。如果您希望对用于洗牌数据集的算法有更多控制,可以在此函数中指定generator参数来使用不同的numpy.random.Generator。
small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(1000))
small_eval_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(1000))
微调训练配置
加载 BERT 模型
警告通知我们正在丢弃一些权重(vocab_transform和vocab_layer_norm 层),并随机初始化其他一些权重(pre_classifier和classifier 层)。在微调模型情况下是绝对正常的,因为我们正在删除用于预训练模型的掩码语言建模任务的头部,并用一个新的头部替换它,对于这个新头部,我们没有预训练的权重,所以库会警告我们在用它进行推理之前应该对这个模型进行微调,而这正是我们要做的事情。
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained("bert-base-cased", num_labels=5)
训练超参数(TrainingArguments)
完整配置参数与默认值:https://huggingface.co/docs/transformers/v4.36.1/en/main_classes/trainer#transformers.TrainingArguments
源代码定义:https://github.com/huggingface/transformers/blob/v4.36.1/src/transformers/training_args.py#L161
模型权重保存路径(output_dir)
from transformers import TrainingArguments
model_dir = "models/bert-base-cased"
# logging_steps 默认值为500,根据我们的训练数据和步长,将其设置为100, num_train_epochs 默认为3
training_args = TrainingArguments(output_dir=f"{model_dir}/test_trainer",
logging_dir=f"{model_dir}/test_trainer/runs",
logging_steps=100)
# 完整的超参数配置
print(training_args)
训练过程中的指标评估(Evaluate)
Hugging Face Evaluate 库 支持使用一行代码,获得数十种不同领域(自然语言处理、计算机视觉、强化学习等)的评估方法。 当前支持 完整评估指标:https://huggingface.co/evaluate-metric
训练器(Trainer)在训练过程中不会自动评估模型性能。因此,我们需要向训练器传递一个函数来计算和报告指标。
Evaluate库提供了一个简单的准确率函数,您可以使用evaluate.load函数加载
import numpy as np
import evaluate
metric = evaluate.load("accuracy")
接着,调用 compute 函数来计算预测的准确率。
在将预测传递给 compute 函数之前,我们需要将 logits 转换为预测值(所有Transformers 模型都返回 logits)。
def compute_metrics(eval_pred):
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
训练过程指标监控
通常,为了监控训练过程中的评估指标变化,我们可以在TrainingArguments指定evaluation_strategy参数,以便在 epoch 结束时报告评估指标。
from transformers import TrainingArguments, Trainer
training_args = TrainingArguments(output_dir=f"{model_dir}/test_trainer", evaluation_strategy="epoch",logging_dir=f"{model_dir}/test_trainer/runs",
logging_steps=100)
开始训练
实例化训练器(Trainer):可用nvidia-smi 查看使用率
trainer = Trainer(
model=model,
args=training_args,
train_dataset=small_train_dataset,
eval_dataset=small_eval_dataset,
compute_metrics=compute_metrics,
)
trainer.train()
small_test_dataset = tokenized_datasets["test"].shuffle(seed=64).select(range(100))
trainer.evaluate(small_test_dataset)
保存模型和训练状态
使用 trainer.save_model 方法保存模型,后续可以通过 from_pretrained() 方法重新加载
使用 trainer.save_state 方法保存训练状态
trainer.save_model(f"{model_dir}/finetuned-trainer")
trainer.save_state()
微调代码示例
import os
# 代理的地址,格式为 http://ip:port
http_proxy="http://proxy.sensetime.com:3128/"
https_proxy="http://proxy.sensetime.com:3128/"
# 设置代理
os.environ["HTTP_PROXY"] = http_proxy
os.environ["HTTPS_PROXY"] = https_proxy
## 下载数据集
from datasets import load_dataset
dataset = load_dataset("yelp_review_full")
#得到的dataset 其实就是一个字典(key:value格式)train 跟test就是这个下载下来的数据集的key。而dataset["train"] 通过这个可以拿到Dataset格式的训练数据集(集合)
#print(dataset["train"][0])可以查看数据集的大概的结构
import random
import pandas as pd
import datasets
from IPython.display import display, HTML
#用于从给定的数据集 (dataset) 中随机选择一些示例并显示
def show_random_elements(dataset, num_examples=10):
assert num_examples <= len(dataset), "Can't pick more elements than there are in the dataset."
picks = []
for _ in range(num_examples):
pick = random.randint(0, len(dataset)-1)
print(pick)
while pick in picks:
pick = random.randint(0, len(dataset)-1)
picks.append(pick)
#将从数据集中随机选择的示例创建为 Pandas DataFrame
df = pd.DataFrame(dataset[picks])
for column, typ in dataset.features.items():#遍历数据集的所有特征
if isinstance(typ, datasets.ClassLabel):#检查特征是否是分类标签
#如果是分类标签,将使用 lambda 函数将标签的索引映射到实际的类别名称
df[column] = df[column].transform(lambda i: typ.names[i])
display(HTML(df.to_html()))
#
from transformers import AutoTokenizer
#用于从Hugging Face加载预训练的文本处理模型(Tokenizer),以便将文本数据转换为模型可以接受的输入格式
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
def tokenize_function(examples):
return tokenizer(examples["text"], padding="max_length", truncation=True)
tokenized_datasets = dataset.map(tokenize_function, batched=True)
show_random_elements(tokenized_datasets["train"], num_examples=1)
# 使用 1000 个数据样本,在 BERT 上演示小规模训练(基于 Pytorch Trainer)
small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(1000))
small_eval_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(1000))
# 微调训练配置
# 从Hugging Face加载BERT 模型
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained("bert-base-cased", num_labels=5)
from transformers import TrainingArguments
model_dir = "models/bert-base-cased"
# logging_steps 默认值为500,根据我们的训练数据和步长,将其设置为100, num_train_epochs 默认为3
from transformers import TrainingArguments, Trainer
training_args = TrainingArguments(output_dir=f"{model_dir}/test_trainer", evaluation_strategy="epoch",logging_dir=f"{model_dir}/test_trainer/runs",
logging_steps=100)
# Evaluate库提供了一个简单的准确率函数,使用`evaluate.load`函数加载
import numpy as np
import evaluate
metric = evaluate.load("accuracy")
# `compute` 函数来计算预测的准确率。
def compute_metrics(eval_pred):
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
### 实例化训练器(Trainer)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=small_train_dataset,
eval_dataset=small_eval_dataset,
compute_metrics=compute_metrics)
trainer.train()
small_test_dataset = tokenized_datasets["test"].shuffle(seed=64).select(range(100))
trainer.evaluate(small_test_dataset)