引言1

想象你是一位厨师,已经学会了很多基本的烹饪技巧(切菜、炒菜、调味等)。这些技巧就是一个预训练模型,因为可以应用到各种菜肴中。现在你想做一道宫保鸡丁了,虽然你会基本技巧,但是为了能把宫保鸡丁做好,需要根据它的特点做一些调整,比如调整火候,加花生米,调料配比等等。这个过程就是微调。

  • 预训练模型:已经学会了很多通用知识的“全能选手”。
  • 微调:针对特定任务,让这个“全能选手”变成“专业选手”。

引言2

“微调(Fine-tuning)是自然语言处理(NLP)中的核心技术之一,它允许我们在预训练模型的基础上,针对特定任务进行优化。如何使用Hugging Face的Transformers库微调预训练模型,包括数据准备模型配置训练和评估等步骤。”

  • 如何从模型中心(hub)加载大型数据集
  • 如何使用高级的 Trainer API 微调一个模型
  • 如何使用自定义训练过程
  • 如何利用 Accelerate 库在所有分布式设备上轻松运行自定义训练过程

1.  数据集从哪来?

模型中心(hub)不仅仅包含模型,还有许多别的语言的数据集。后续使用 MRPC 数据集(由 5801 对句子组成,每个句子对带有一个标签来指示它们是否为同义)中的 GLUE 基准测试数据集 (通用语言理解评估 (GLUE) 基准是用于训练、评估和分析自然语言理解系统的资源集合)作为我们训练所使用的数据集,它是构成 MRPC 数据集的 10 个数据集之一,作为一个用于衡量机器学习模型在 10 个不同文本分类任务中性能的学术基准。

# pip install datasets
from datasets import load_dataset

raw_datasets = load_dataset("glue", "mrpc") # 该命令(load_dataset)会下载数据集并缓存到 ~/.cache/huggingface/datasets,可以通过设置 HF_HOME 环境变量来自定义缓存的文件夹
raw_datasets
# 可以看到raw_datasets是一个DatasetDict 对象,包含4列。该训练集中有 3668 对句子,验证集中有 408 对,测试集中有 1725 对。
DatasetDict({
    train: Dataset({
        features: ['sentence1', 'sentence2', 'label', 'idx'],
        num_rows: 3668
    })
    validation: Dataset({
        features: ['sentence1', 'sentence2', 'label', 'idx'],
        num_rows: 408
    })
    test: Dataset({
        features: ['sentence1', 'sentence2', 'label', 'idx'],
        num_rows: 1725
    })
})

# 访问训练数据集中的每一个对象:
raw_train_dataset = raw_dataset["train"]
raw_train_dataset[0]

# 输出
{
    "idx": 0,
    "label": 1,
    "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 .',
}

# 查看每列类型
{
    "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),
}
raw_train_dataset.features
2. 数据准备
  • 需要将文本转换为模型能够理解的数字(通过一个 Tokenizer 完成)
  • 示例代码:
    from transformers import AutoTokenizer
    
    checkpoint = load_dataset("imdb")
    print(dataset["train"][0])  # 查看第一条数据
    

定义一个对输入进行tonkenize的函数

def tokenize_function(example):
    return tokenizer(example["sentence1"] , example["sentence2"], truncation=True)

raw_datasets.map(tokenize_function, batched=True) # batched=True ,因此该函数会一次性处理数据集的多个元素
# 输出如下
#attention_mask, input_ids,token_type_ids 这三个字段被添加到数据集的三个集合里(训练集、验证集和测试集)

DatasetDict({
    train: Dataset({
        features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
        num_rows: 3668
    })
    validation: Dataset({
        features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
        num_rows: 408
    })
    test: Dataset({
        features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
        num_rows: 1725
    })
})
  • 动态填充,DataCollatorWithPadding
# 用法
from transformers import DataCollatorWithPadding
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

# 例子
samples = tokenized_datasets["train"][:8]
samples = {k:v for k,v in samples.items() if k not in ["idx", "sentence1", "sentence2"]}
[len(x) for x in samples["imputs_idf"]]
# [50, 59, 47, 67, 59, 50, 62, 32]

batch = data_collartor(samples)
{k: v.shape for k, v in batch.items()}
{
    "attention_mask": torch.Size([8, 67]),
    "input_ids": torch.Size([8, 67]),
    "token_type_ids": torch.Size([8, 67]),
    "labels": torch.Size([8]),
}

3. 使用 Trainer API 微调模型

  • Transformer 提供的 Trainer类可以在数据集上微调任何预训练模型
  • 先运行:
    from datasets import load_dataset
    from transformers import AutoTokenizer, DataCollatorWithPadding
    
    raw_datasets = load_dataset("glue", "mrpc")
    checkpoint = "bert-base-uncase"
    tokenizer = Autookenizer.from_pretrained(checkpoint)
    
    def tokenize_function(example):
        return tokenizer(example["sentence1"],example["sentence2"],truncaton=Ture)
    tokenized_datasets = raw_datasets.map(tokenize_function,batched=True)
    data_collator = DataCollatorWithpadding(tokenizer=tokenizer)

Training

# 定义Trainer 之前要定义一个TrainingArguments类,它包含Trainer在训练和评估中使用的所有超参数
# 
from transformers import TrainingArguments
training_args = TrainingArguments("test-trainer")

# 使用AutoModelForSequenceClassification定义模型
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(chenckpoint, num_labels=2)

# 有了模型之后定义一个Trainer把到目前为止构建的所有对象——model,training_args, 训练和验证数据集,data_collator 和tokenizer传递给Trainer:

from transormers import Trainer
trainer = Trainer(
    model,
    training_args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    tokenizer=tokenizer,
)

# 在此数据集上进行微调,只需要调用Trainer 的 train()方法
trainer.train()
  • 评估
  • preditions = trainer.predict(tokenized_datasets["validation"])
    print(predictions.predictions.shape, predictions.label_ids.shape)
    
    import numpy as np
    preds = np.argmax(predictions.predictions, axis=-1)
    
    import evaluate
    metric = evaluate.load("glue" , "mrpc")
    metric.compute(predictions=preds, references=predictions.label_ids)
    {'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}
    
    
    ##
    def compute_metrics(eval_preds):
        metrics = eval.load("glue","mrpc")
        logits,labels = eval_preds
        predictions = ng.argmax(logits, axis=-1)
        return metric.compute(predictions=predictions, references=labels)
    trainer = Trainer(
        model,
        ...
    
    
    
    
        compute_metrics=compute_metics,
        )

4. 一个完整的训练

总结

总结本章的核心知识点:

  • 微调是利用预训练模型针对特定任务进行优化的高效方法。
  • 数据准备、模型配置、训练和评估是微调的关键步骤。
  • Hugging Face的Trainer类简化了微调过程,提供了灵活的配置选项。

互动与反馈

鼓励读者在评论区分享他们的学习心得或提出问题。例如:
“你在微调预训练模型时遇到过哪些挑战?欢迎在评论区分享你的经验!”


参考资料

列出本章课程的相关链接和文档,方便读者进一步学习:


通过以上结构,你的学习博客将内容清晰、逻辑严谨,同时兼具实用性和互动性,能够有效帮助其他学习者理解和掌握相关知识。

Logo

更多推荐