大模型微调:Hugging Face Transformers全流程实战
内容精选 颁布于 2024-9-26 10:36597阅读0收藏随着大模型在人工智能畛域的兴起,如何将其运行于垂直畛域成为关键疑问。本文形容了模型微调技术,经过调整预训练模型的参数,使其顺应特定义务,从而优化模型在特定畛域的体现。文中以 Yelp 数据集为例,详细引见了如何经常使用 Hugging Face Transformers 框架对 BERT 模型启动微调,成功评论星级分类义务。文档涵盖了微调的背景、干流手腕、架构和工具,并深化解说了微调的思绪和通常步骤,包括数据预处置、模型选用、超参数设置、训练评价和结果验证等。
大模型风头正盛,微调展现威力
近年来,大型预训练模型如GPT系列、Llama系列、通义千问在各大科技巨头的推进下,成为了人工智能畛域的明星。但是,除了这些通用模型外,越来越多的垂直畛域企业也在应用这些大模型,经过模型微调,打造合乎行业需求的定制化处置打算。
以一家科技公司为例,公司须要频繁介入技术类名目的招标,每次都要依据详细名目的要求编写详细的技术打算。假设间接经常使用预训练好的通用言语模型来生成技术打算,虽然模型无了解言语和生成文本方面体现不错,但关于科技行业特有的技术术语、名目需求以及公司的业务特点,它的输入品质或许并不现实。经过模型微调,公司可以经常使用大批历史招标文件和行业相关的标志数据,对模型启动再训练,使其生成的技术打算愈加贴合公司的技术格调和名目要求。这样,微调后的模型不只能清楚提高打算生成的准确性,还能协助公司缩小手工编写期间,提高效率和招标成功率。
关于模型微调,咱们还可以举一个例子:假定你曾经学会了画画,能画出十分美丽的景色画。如今,你想画一只特定种类的小鸟,虽然你曾经有了基础的画画技巧,但为了画好这只小鸟,你还须要学习一些额外的技巧,比如观察它的羽毛色彩和翅膀状态。模型微调就像是这样一个环节:AI模型曾经把握了很多基础技艺,但要成功特定义务,还须要经过微调来“特训”,以提高它在某个畛域的体现。
从通常上看,模型微调是针对曾经经过大规模数据预训练的通用模型启动小规模的再次训练,使其在特定义务中体现更好。这种方法不只节俭了从零开局训练的资源和期间,还经过迁徙学习的方式,充沛应用了预训练模型中的丰盛常识。
在论文BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding (Devlin et al., 2018)中就指出经过预训练和微调的联合,清楚优化了模型在多项义务中的体现。在Transfer Learning for NLP: A Comprehensive Survey (Ruder, 2019) 中也提到迁徙学习与微调技术的演化,以及它们在人造言语处置中的关键作用。这些钻研标明,微调不只能让通用模型更好地顺应详细义务,还能为各行业提供高效且专业化的处置打算。这就是为什么在现代AI畛域,微调成为了一项无法或缺的技术。
干流的微调手腕
既然大模型微调处置了企业在垂直畛域的大模型经常使用疑问,那么市面上有哪些微调的手腕呢?微调(Fine-tuning)作为将大型预训练模型运行于特定义务的关键手腕,依赖于对模型参数的灵敏调整。不同义务的需求、数据量的大小以及计算资源的限度,选择了选用哪种微调方式。以下是几种干流的微调战略:
微调的架构和工具
前面咱们提到了,在不同的运行场景须要经常使用不同的微调手腕,那么有哪些大模型微调的工具和框架可以协助开发者极速上手微调呢?在业内, Hugging Face Transformers 和 DeepSpeed 是两个备受注目的工具和架构,它们的发生使得微调变得愈加高效和可裁减。
1. Hugging Face Transformers
Hugging Face 的 Transformers 框架是目前人造言语处置(NLP)畛域中最受欢迎的工具之一。它的长处在于简化了从预训练模型加载到微调整个环节的复杂性:
2. DeepSpeed
DeepSpeed 是微软推出的一款针对大规模深度学习模型的优化工具,它的关键长处在于极大地提高了训练效率,尤其适宜处置像 GPT-3 这样的超大规模模型。
参数高效的训练:经过对训练环节的优化,如梯度累积、张量并行等技术,DeepSpeed 可以在降落内存需求的同时提高训练速度。
微调思绪
虽然两个调优工具都十分低劣,但由于本文篇幅有限,所以咱们选取HuggingfaceTransformers框架来为大家启动大模型调优的通常。
这里经过一张图来引见大模型微调的思绪,如下图所示,展现了从文本预处置到模型训练、评价、验证和保管等全环节。关于本次微调,咱们宿愿处置“分类疑问”,让模型了解“评论内容”与“评分星级”之间的相关,这里咱们会提供少量的评论数据,每条数据会打上评分星级。在微调之后,给模型输入任何的评论内容,它都会前往该评论对应的星级。接上去的环节将分为几个关键步骤:
1. 确认疑问
首先,要确认本次微调要处置的疑问:了解“评论内容”与“评分星级”之间的对应相关。经过微调模型,咱们宿愿模型能够依据评论的文本内容智能预测其星级评分(1-5星)。微调后的模型能够在新的评论输入时智能给出星级评定。
2. 装载数据集
本次微调会经常使用Yelp 数据集,它是一个地下的评论数据集,蕴含了少量用户对商家的评价和评分,宽泛运行于人造言语处置义务。数据集的特点包括:
3. 分词器处置文本
对评论文本启动分词,将每条评论合成为词或词组。这里咱们经常使用 BERT 自带的分词器(Tokenizer),它能够将文本转化为模型所需的输入格局(如 token IDs、attention masks 等)。经过火词,咱们能够将每个词或词组转化为模型了解的向量示意,并保管文本的结构消息。
4. 生成小数据集
为了放慢试验和测试环节,咱们从原始的 Yelp 数据集中提取一个小样本数据集。小数据集可以协助咱们极速迭代模型的训练、调参和验证。经过火词器处置后,咱们可以将这些小样本输入到模型中启动训练和评价。
5. 选用微调模型
咱们将经常使用预训练的 BERT 模型,并依据 Yelp 数据集的需求调整模型的输入层。BERT 自身经过大规模语料库的预训练,具有弱小的言语了解才干,经过微调,可以将其顺应特定的分类义务。
6. 设置超参数
超参数包括学习率、训练轮次、batch size 等。正当的超参数设置能够确保模型的有效训练,防止过拟合或欠拟合疑问。在训练环节中,咱们将活期监控模型的性能,确保其学习到评论文本中的有效消息。
7. 训练评价
在训练阶段,咱们会活期对模型启动评价。这通常在每个训练轮次完结后启动,经过验证集上的数据来评价模型的性能。经过这样的方式,能够实时了解模型的学习效果,并依据评价结果适时调整,防止过拟合等疑问。
8. 计算准确性计算
经过计算模型的预测准确性来权衡其体现。在每次评价时,模型会对验证集中的数据启动预测,而后将这些预测结果与实践标签启动比拟。经过计算准确性,能够判别模型在该分类义务中的成功率,即模型在多大水平上能够正确预测评论的星级。
9. 口头训练
开局对模型启动微调训练。
10.保管模型
在训练和验证成功后,将微调后的模型启动保管。保管的模型可以用于后续的预测和推理义务。经过调用保管的模型,咱们可以在给定新评论的状况下极速取得预测的星级评分。
11. 验证结果
将设计对比试验:让未经过微调的模型和经过微调的模型区分对 Yelp 数据集中的评论启动星级预测,检查评分结果的差异。这样可以展现微调后的模型能否能够更好地理解评论文本与评分之间的相关,并给出愈加准确的预测。
经过这些步骤,咱们将详细讨论如何经常使用 Hugging Face 的工具对 BERT 模型启动微调,并联合 Yelp 数据集,逐渐成功从数据预处置、模型训练到结果验证的整个流程。
成功微调
引见完整个微调思绪之后,咱们会经过HuggingFace的Transformers模型框架编写微调的代码。
由于微调模型须要算力资源,普通而言都须要多张高显存的显卡允许。经过上方的微调思绪大家可以知道,为了演示微调环节,咱们将数据集启动了缩减的操作,应用更小的数据集来成功微调。在实践运行场景假设须要微调更大的数据集就须要多张高阶显卡。如下图所示,咱们租用AutoDL上的虚构主机,经常使用3080ti显卡启动微调,实践测试的效果还不错,在小数据微调的状况下10-20分钟可以成功。
接着,须要装置必要的组件库,如下:
!pip install transformers>
上述库允许人造言语处置义务,尤其是触及到模型微调、评价和数据处置的义务。上方是对每个库的简明解释:
1. transformers
这个库是由 Hugging Face 提供的,它蕴含了许多预训练的人造言语处置模型,允许包括文本分类、生成、翻译等义务。
2.>该库也是由 Hugging Face 提供的,专门用于处置和治理各种机器学习数据集。它可以加载、预处置和操作不同格局的数据集,允许间接加载许多地下数据集如 Yelp、IMDB 等。
3. evaluate
用于评价模型的性能。它提供了各种罕用的评价目的(如准确率、准确率、召回率等),繁难在模型训练和验证时计算这些目的,协助权衡模型的体现。
4. accelerate
用于减速模型训练的库,允许散布式训练和减速设施的无缝切换,比如在CPU和GPU之间转换,或许经常使用多个GPU启动训练。
5. scikit-learn
十分盛行的机器学习库,提供了各种统计学习工具、数据预处置和模型评价方法。在人造言语处置名目中,它罕用于启动数据处置和分类算法的基础操作,如分词、向量化、模型评价等。
装载数据集
这里咱们经常使用了Yelp数据集,它是从 2 年的 Yelp 数据集应战赛中提取的,蕴含少量的用户评论,关键用于情感分类和文本分类义务。经过剖析这些评论文本,可以预测用户的情感偏差,也就是评分的星级。数据集中的评论关键以英文撰写,每条评论蕴含两个**元素:评论的文本内容和对应的评分标签。评论内容中触及的文本经过不凡处置,双引号和换行符都启动了转义。评分标签示意评论的星级,范围从 1 到 5 星不等。整个数据集被随机划分为训练集和测试集,每个星级类别各蕴含 130,000 条训练样本和 10,000 条测试样本,总共蕴含 650,000 条训练数据和 50,000 条测试数据。
数据集的格局大抵如下:
{'label': 0,'text': 'I got \'new\' tires from them and within two weeks got a flat...'}
接着经过如下代码加载数据。
经过代码可以看出,load_dataset 函数可以间接加载 yelp_review_full 数据集,它来自 Hugging Face 的>当经常使用 load_dataset("yelp_review_full") 时,函数会经过数据集称号智能从 Hugging Face 的数据集库中找到对应的资源,并加载相应的数据集。这象征着你不须要手动下载或预备数据集,Hugging Face 曾经在其平台上托管了这个数据集,并定义了它的结构和格局。因此,load_dataset 能够经过提供的称号间接访问并加载。
在加载终了数据,可以经过dataset["train"][100],检查Yelp Review 数据集中提取第 100 条训练数据,确认数据曾经加载终了。
分词器文本处置
分词器的上班是将咱们看得懂的言语文字转化为模型能了解的数字。无论是机器学习模型还是深度学习模型,它们都只能处置数字,而不是间接处置文本。咱们须要一个方法来把文本“翻译”成模型能够了解的方式。分词器正是担任这一“翻译”上班的工具。经过将句子合成成“词”或“字符”,而后给每个词或字符调配一个数字(通常叫做输入ID),模型就可以开局处置这些数字了。
如下图所示,比如,“我青睐苹果”这句话,分词器就会把“我”翻译成1,“青睐”翻译成2,“苹果”翻译成3。而这些数字是依据词汇表(vocabulary)中的子词来索引的,可以了解为在模型外部有一个很大的词汇表,词汇表中保养每个词对应的ID,你输入ID模型就知道你要表白什么词(相似身份证与人之间的相关)。这个词汇表可以看作是模型用来了解言语的“字典”,其中蕴含了少量经常出现的词和子词(subwords)。每个子词在词汇表中都有一个对应的索引 ID,当咱们输入一段文本时,分词器会将它切分红多个子词,并查找每个子词在词汇表中的索引。
为什么须要词汇表?由于言语的多样性十分大,而单词的数量是有限的。间接给每个完整的单词一个惟一的 ID 会造成词汇表十分宏大,这不只会参与模型的复杂性,还容易发生很多未知的词(也就是词汇表中没有的词)。经常使用子词来替代整个单词能够缩小词汇表的大小,同时确保即使遇到生僻词也能经过已知的子词来表白。例如,英文"unhappiness" 可以被拆分为 "un-" 、"happi-" 和“ness”三个子词,即使模型没有见过完整的“unhappiness”这个词,它依然可以经过上述三个子词来了解。
如下图所示,繁难了解,分词器的上班就是讲输入的文字参照词汇表,将其转化为子词ID的方式,而后输入到大模型中启动处置。
接着来看代码:
from transformers import AutoTokenizertokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-cased")def tokenize_function(examples):return tokenizer(examples["text"], padding="max_length", truncation=True)tokenized_datasets =>
从 Hugging Face 预训练模型库中加载了google-bert/bert-base-cased版本分词器。
def tokenize_function(examples)用于处置数据集中的每一个数据点。
tokenizer(examples["text"], padding="max_length", truncation=True):这里经常使用分词器将输入的文本数据启动分词。分词器会将文本转换为标志(tokens),并将其转换为适宜模型输入的数字化方式。padding="max_length":示意对文本启动填充,使一切文本的长度相反,到达预约义的最大长度。truncation=True:假设文本长度超越最大长度,智能截断多余局部。
tokenized_datasets =>生成小数据集
在处置大型数据集时,训练模型和启动验证会消耗少量期间和计算资源。因此,咱们通常会创立一个小数据集来启动极速的试验和调试。
参与如下代码:
small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(1000))small_eval_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(1000))
tokenized_datasets["train"] 和 tokenized_datasets["test"]: 区分从已分词的数据集中失掉训练集和测试集。
指定微调模型
微调数据集是基于一个预训练模型倒退的,这里选用bert-base-cased 模型,用于一个有 5 个分类标签(1-5星的评价)的序列分类义务。
代码如下:
from transformers import AutoModelForSequenceClassificationmodel = AutoModelForSequenceClassification.from_pretrained("google-bert/bert-base-cased", num_labels=5)
AutoModelForSequenceClassification:是 Hugging Face 的 transformers 库中提供的模型,用于文本分类义务。它基于预训练的 Transformer 模型,并参与一个分类头(线性层)来处置分类义务。经过这个类中的from_pretrained("google-bert/bert-base-cased")方法加载预训练模型。
num_labels=5:定义了分类义务的标签数为 5,示意咱们要对文本启动 5 类分类义务。
设置超参数
设置超参数协助性能模型训练时的各种参数,比如训练输入的目录、批量大小、学习率、能否经常使用 GPU 等。
如下代码用于设置训练模型时的超参数,经常使用 Hugging Face transformers 库中的 TrainingArguments 类。
from transformers import TrainingArgumentstraining_args = TrainingArguments(output_dir="test_trainer")
为了简化演示环节这里只性能了output_dir="test_trainer"作为模型输入的门路。在训练环节中,模型的审核点(checkpoints)、日志文件、以及最终模型都会被保管在这个目录下。
此外,TrainingArguments 提供了许多其余参数,你可以依据详细的训练需求来设置,比如:
训练评价与计算预测的准确性
在模型的训练和评价环节中,特意是在分类义务中,权衡模型性能的一个关键目的就是准确性。训练好的模型可以在验证集上启动评价,检查模型在实践数据上的体现,以此来权衡其分类才干。咱们会创立一个compute_metrics 函数,它用来计算预测结果和实践标签的差异来评价模型的准确性。同时会经常使用evaluate 库的 accuracy 目的失掉模型在验证集上的体现。
代码如下:
import numpy as npimport evaluatemetric = evaluate.load("accuracy")def compute_metrics(eval_pred):logits, labels = eval_predpredictions = np.argmax(logits, axis=-1)return metric.compute(predictions=predictions, references=labels)
metric = evaluate.load("accuracy")加载了 Hugging Face 库中预约义的“准确率”评价目的。 compute_metrics(eval_pred) 函数是为训练/验证流程提供的评价函数,传入的参数 eval_pred 蕴含了两个局部:logits 和 labels。
logits:模型输入的原始预测结果,通常是未经过激活函数的值(在分类义务中通常是未经 softmax 的分数)。
为什么突然跳出一个logits来了?这里须要稍微解释一下,在深度学习的分类模型中,模型在最后一层输入的是各个类别的分数(logits)。这些分数用来示意模型对各个类别的预测概率。在很多状况下,模型会间接输入 logits,而不是最终的类别,由于 logits 还可以进一步处置(比如经过 softmax 转换为概率)。
大家还记得吗?在最开局处置文本的时刻,咱们须要针对输入文本启动分词(tokenization),接着每个词会被映射成嵌入(embedding)。嵌入作为输入经过模型的层层处置,最后在输入层发生 logits,用于预测文本的分类结果。因此,分词是处置输入数据的第一步,而 logits 是最终的输入分数,用来确定分类的结果。
来举个例子说说logits繁难大家了解,假定咱们有一个分类义务,模型须要把一句话分红 5 个类别之一(比如情感分类:0=负面, 1=中性, 2=侧面, 3=不懂, 4=惊讶)。在模型处置输入之后,它会输入一个蕴含 5 个数值的向量(logits),示意模型对每个类别的信念。
示例:
输入句子:"I love this product!"
假定模型的输入 logits 是:
logits = [2.5, -1.3, 5.2, 0.8, -0.5]
这个向量示意模型对每个类别的信念。这里的是 logits 中的最大值,示意模型以为这个句子最或许属于第 2 类(“侧面”情感),虽然它对每个类别都有必定的信念值。
所以,这里的logits就是在对预测结果启动“打分”,哪个分类上分数越高,模型就会以为这个文字形容属于哪类。
而labels示意的是实践标签,它用于与模型的预测结果对比。
logits, labels = eval_pred:将 eval_pred 拆分为 logits 和 labels。
predictions = np.argmax(logits, axis=-1):经常使用 np.argmax() 函数从 logits 中找到最大值的索引,代表模型的最终预测类别。axis=-1 示意沿最后一个维度启动操作,关于每个输入,输入模型以为最或许的类别。
metric.compute(predictions=predictions, references=labels):计算模型预测的准确性。predictions 是模型的预测值,references 是实在标签。这个函数前往一个蕴含准确性(accuracy)目的的字典。
创立Trainer对象口头微调义务
微调前的基本上班曾经成功了, 接上去咱们须要创立一个Trainer类,用于简化训练和评价环节,该类蕴含了模型、训练参数、训练和验证数据集,以及评价目的计算方法。而后,口头trainer.train() 用于微调训练,模型的权重会依据训练数据逐渐优化。最后,trainer.save_model() 会将训练好的模型保管,繁难后续经常使用。代码如下:
trainer = Trainer(model=model,# 定义的模型( BERT 分类模型)args=training_args,# 训练的参数,如学习率、批次大小、训练周期等train_dataset=small_train_dataset,# 用于训练的小数据集eval_dataset=small_eval_dataset,# 用于评价的小数据集compute_metrics=compute_metrics,# 之前定义的,验证的准确性等目的的函数)trainer.train() # 开局训练模型trainer.save_model() # 保管训练后的模型
口头上述代码,失掉如下运转截图:
从图中可以看出,验证集上的准确性(Accuracy)区分为:
虽然第三轮的验证损失参与了,准确率却依然优化了一点。这或许标明模型在某些类别上的预测更准确,但全体的损失依然较大,标明模型存在过拟合或不平衡的体现。
同时失掉如下运转消息:
TrainOutput(global_step=375, training_loss=0.49261214192708336, metrics={'train_runtime': 127.2273, 'train_samples_per_second': 23.58, 'train_steps_per_second': 2.947, 'total_flos': 789354427392000.0, 'train_loss': 0.49261214192708336, 'epoch': 3.0})
从消息可以看出,模型微调训练共启动了 3 个 Epoch,口头了 375 个训练步数,总运转期间为 127 秒。在训练环节中,模型每秒能够处置约 23.58 个样本,每秒口头 2.95 个训练步。最终,模型的平均训练损失为 0.49,标明模型在训练数据上的体现较为现实。此外,训练的总浮点运算量(FLOPs)为 789,354,427,392,000,展现了训练环节中触及的少量计算量。
验证微调
前面咱们经差错掉数据集针对BERT模型启动微调,口头微调命令之后成功了微调的环节,并且经过trainer.save_model()语句将微调之后的模型保管到本地。接上去,经过对比微调前后模型对评论消息的星级反应,来检查微调之后模型的才干能否优化。
先调用测试之前的模型输入评论内容,看模型对评论给出什么星级的评定。
如下图所示,测试的评论是2star(2星的星级评定)。
口头如下代码:
from transformers import AutoTokenizer, AutoModelForSequenceClassificationimport torchimport numpy as np# Yelp review 示例input_text = "My first time going to Barb's Country Junction and I really wanted to like this place, I really did...however,……."# 加载预训练模型和 tokenizertokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-cased")model_pretrained = AutoModelForSequenceClassification.from_pretrained("google-bert/bert-base-cased", num_labels=5)# 标志化输入文本inputs = tokenizer(input_text, padding=True, truncation=True, return_tensors="pt")# 经常使用预训练模型启动预测(没有经过微调)with torch.no_grad():outputs_pretrained = model_pretrained(**inputs)# 失掉未微调模型的预测标签predicted_label_pretrained = np.argmax(outputs_pretrained.logits, axis=-1).item()print(f"Prediction before fine-tuning (without knowledge of Yelp reviews): {predicted_label_pretrained}")
失掉如下结果。
Prediction before fine-tuning (without knowledge of Yelp reviews): 3
很清楚,从结果上看模型预测的星级为“3”,与数据集标注的“2”存在差异。
接着应用微调之后模型启动推理,代码如下:
model = AutoModelForSequenceClassification.from_pretrained("test_trainer")tokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-cased")# 输入文本input_text = "My first time going to Barb's Country Junction and I really wanted to like this place, I really did...however, ……"# 标志化输入文本inputs = tokenizer(input_text, padding=True, truncation=True, return_tensors="pt")# 失掉模型预测with torch.no_grad():# 封锁梯度计算outputs = model(**inputs)# 失掉预测标签predicted_label = np.argmax(outputs.logits, axis=-1).item()# 输入结果print(f"Predicted label: {predicted_label}")
上述代码经过model = AutoModelForSequenceClassification.from_pretrained("test_trainer"),失掉微调之后test_trainer目录下的模型启动推理。 口头结果如下:
Predicted label: 2
与预期的结果相反,说明微调之后的模型能够胜任评论星级分类的上班。
总结
大模型微调技术为将预训练模型运行于垂直畛域提供了有效途径。经过微调,咱们可以应用预训练模型的常识,并联合特定畛域的义务需求,打造定制化处置打算。本文以 Yelp 数据集为例,演示了如何经常使用 Hugging Face Transformers 框架对 BERT 模型启动微调,成功评论星级分类义务。文档涵盖了微调的背景、干流手腕、架构和工具,并深化解说了微调的思绪和通常步骤,包括数据预处置、模型选用、超参数设置、训练评价和结果验证等。置信经过本文的学习,读者能够更好地理解大模型微调技术,并将其运行于实践名目中。
作者引见
崔皓,社区编辑,资深架构师,领有18年的软件开发和架构阅历,10年散布式架构阅历。