写点什么

落地领域大模型应知必会 (1) :主要微调方法总览

作者:Baihai IDP
  • 2023-07-03
    湖南
  • 本文字数:6572 字

    阅读完需:约 22 分钟

落地领域大模型应知必会 (1) :主要微调方法总览

编者按:随着大规模预训练模型的发展和应用,大模型微调技术已经在很多领域都有了突破性的进展,并推动了人工智能技术的发展与应用。

本文会简要介绍上下文学习(in-context learning)的含义,并介绍对 LLMs 进行微调的各种可行方式。还能够帮助我们了解如何选择大语言模型的微调方法。

快快阅读此文,开启一趟大模型微调学习之旅吧!

以下是译文,Enjoy!


本文经原作者授权,由 Baihai IDP 编译。如需转载译文,请联系获取授权。


原文链接:https://magazine.sebastianraschka.com/p/finetuning-large-language-models


作者 | SEBASTIAN RASCHKA, PHD


编译 | 岳扬


在如今高速发展的人工智能领域,高效地利用大语言模型(LLMs)已经变得越来越重要。但是,利用大语言模型的方式太多了,如果你才刚刚开始接触它,可能会感到不知所措。


实质上,我们可以通过两种主要方式将预训练大语言模型用于新任务:上下文学习(in-context learning)以及微调(finetuning)


本文我们将简要介绍上下文学习(in-context learning)的含义,并介绍对 LLMs 进行微调的各种可行方式。

01 In-Context Learning 和 Indexing

自从 GPT-2(Radford 等人[1])和 GPT-3(Brown 等人[2])问世以来,我们发现,在通用文本语料库上进行预训练的生成式大语言模型(LLMs)可以进行上下文学习。这意味着,如果我们想要执行新的特定任务,我们无需对预训练的 LLM 进行进一步的训练或微调,可以通过输入 Prompt 直接提供一些目标任务的示例,如下面的例子所示。



上下文学习的例子


如果我们不能直接访问模型(例如如果我们通过 API 使用模型),那么上下文学习就用处非常大。


与上下文学习相关的是 hard prompt tuning,即试图通过修改输入改善输出,如下图所示。



An illustration of (hard) prompt tuning 顺便说一下,我们称这种方法为 hard prompt tuning,因为在这种方法中,我们直接修改了输入的单词或 tokens。稍后我们将讨论另外一种版本的方法,称为 soft prompt tuning(或通常称为 prompt tuning)。


刚刚提到的 prompt tuning 方法为参数微调(parameter finetuning)提供了一种替代方案(且这种方案更节省资源)。然而,它的性能通常不及微调(finetuning),因为这种方法不会针对特定任务更新模型的参数,这可能会限制它对不同特定任务间细微差别的适应能力。此外,prompt tuning 可能需要进行大量工作,耗费大量人力财力,因为这种方法需要人工参与比较不同 prompt 的质量。


在我们更进一步地讨论微调之前,先来了解另一种利用纯粹的上下文学习的方法——indexing。在大语言模型领域中,indexing 可以被视为一种上下文学习的变通方法,其使得 LLMs 能够转变为信息检索系统(information retrieval systems),从外部资源和网站中提取数据。在这个过程中,indexing 模块将文档或网站分解成较小的片段,并将它们转换为可以存储在向量数据库中的向量(vectors) 。然后,当用户提交查询(query)时,indexing 模块计算嵌入查询(embedded query)与数据库中每个向量之间的向量相似度(vector similarity)。最终,indexing 模块检索出与查询最相似的前 k 个嵌入(embeddings),生成响应。



An illustration of indexing.

02 常规的基于特征的方法和微调方法

如果我们无法直接访问大型语言模型(LLM),例如通过 API 或使用用户界面与 LLM 进行交互,那么上下文学习是一种有价值且用户友好的方法。


然而,如果我们可以直接接触到 LLM,使用目标领域的数据对其进行调整(adapting)和微调(finetuning)通常会获得更好的结果。那么,如何使一个模型适应目标任务呢?下图概述了三种常见的方法:



3 种常规的基于特征的方法和微调的方法


下文中,我们将以对编码器类型(encoder-style)的大型语言模型(例如 BERT)微调为例,解析具体的微调方式原理和示例代码。下述示例微调任务为分类器任务,在这个任务中,我们试图预测电影评论是正面情感还是负面情感。需要注意的是,除了对编码器类型(encoder-style)的 LLM 进行微调外,同样的方法也适用于类似 GPT 的解码器类型(decoder-style)的 LLM。在后续的文章中,我将会举例说明这一点。


此外,我们还可以对解码器类型(decoder-style)的 LLM 进行微调,以生成对特定指令的多句子回答(multiple-sentence answers),而不仅仅是进行文本分类。关于这个问题,我会在未来的文章中提供实战案例,敬请期待。

2.1 基于特征的方法

在基于特征的方法(feature-based approach)中,我们需要加载一个预训练 LLM,并将其应用于目标数据集。在这种方法中,我对生成训练集的输出嵌入(output embeddings)特别感兴趣,这些嵌入可以作为输入特征(input features)用于训练分类模型(classification model)。虽然这种方法在像 BERT 这样以嵌入为重点的模型中非常常见,但我们也可以从生成式的 GPT-style 模型中提取嵌入。


接下来,我们可以使用逻辑回归模型(logistic regression model)、随机森林(random forest)或 XGBoost 等任何我们想要使用的模型作为分类模型。(然而,根据我的经验,在这种情况下,像逻辑回归(logistic regression)这样的线性分类器(linear classifiers)表现最佳


理论上,我们可以用下面这段代码来解释基于特征的方法:


model = AutoModel.from_pretrained("distilbert-base-uncased")
# ...# tokenize dataset# ...
# generate embeddings@torch.inference_mode()def get_output_embeddings(batch):output = model(batch["input_ids"],attention_mask=batch["attention_mask"]).last_hidden_state[:, 0]return {"features": output}
dataset_features = dataset_tokenized.map(get_output_embeddings, batched=True, batch_size=10)
X_train = np.array(imdb_features["train"]["features"])y_train = np.array(imdb_features["train"]["label"])
X_val = np.array(imdb_features["validation"]["features"])y_val = np.array(imdb_features["validation"]["label"])
X_test = np.array(imdb_features["test"]["features"])y_test = np.array(imdb_features["test"]["label"])
# train classifierfrom sklearn.linear_model import LogisticRegression
clf = LogisticRegression()clf.fit(X_train, y_train)
print("Training accuracy", clf.score(X_train, y_train))print("Validation accuracy", clf.score(X_val, y_val))print("test accuracy", clf.score(X_test, y_test))
复制代码


(对此感兴趣的读者可以在此处[4]找到完整的代码)

2.2 微调方法 I —— 更新输出层(Output Layers)

有一种方法与上述基于特征的方法比较相关,叫做微调输出层(本文称之为微调方法 I)。与基于特征的方法类似,这种方法不改变预训练 LLM 的参数,只会训练新增加的输出层,类似于在嵌入特征(embedded features)上训练逻辑回归分类器(logistic regression classifier)或小型多层感知器(small multilayer perceptron)。


相关代码如下:


model = AutoModelForSequenceClassification.from_pretrained(    "distilbert-base-uncased",     num_labels=2) 
# freeze all layersfor param in model.parameters(): param.requires_grad = False # then unfreeze the two last layers (output layers)for param in model.pre_classifier.parameters(): param.requires_grad = True
for param in model.classifier.parameters(): param.requires_grad = True # finetune modellightning_model = CustomLightningModule(model)
trainer = L.Trainer( max_epochs=3, ...)
trainer.fit( model=lightning_model, train_dataloaders=train_loader, val_dataloaders=val_loader)
# evaluate modeltrainer.test(lightning_model, dataloaders=test_loader)
复制代码


(对此感兴趣的读者可以在此处[4]找到完整的代码)


从理论上讲,这种方法的建模性能和速度(modeling performance and speed)应该与基于特征的方法差不多,因为都会去冻结骨干模型(frozen backbone model)(译者注:通过冻结骨干模型,我们可以保持其模型权重不变,仅对任务模块进行训练。这样可以减少需要更新的参数数量,提高训练效率,并在一定程度上保留原始骨干模型的特征提取能力)。然而,由于基于特征的方法使得预计算(pre-compute)和存储训练数据集的嵌入特征(embedded features)稍微更容易一些,所以在具体场景中,基于特征的方法可能更加方便

2.3 微调方法 II - 更新所有层(All Layers)

虽然 BERT 那篇论文(Devlin 等人)说过,仅微调输出层(output layer)可以实现与微调所有层相当的模型性能,但由于后者涉及的参数更多,所以成本要高得多。 例如,BERT 基础模型约有 1.1 亿个参数。然而,用于二元分类的 BERT 基础模型最后一层仅包含 1500 个参数。此外,BERT 基础模型的最后两层共有 6 万个参数,这仅占总模型大小的 0.6%左右。


我们选择走哪条路径会根据目标任务和目标领域与模型预训练的数据集的相似程度而有所不同。但是在具体实践中,微调所有层几乎总是能获得更优的模型性能。


因此,在优化模型性能时,使用预训练 LLM 的黄金标准(the gold standard)是更新所有层(本文称之为微调方法 II)。从理论上讲,微调方法 II 与微调方法 I 非常相似。唯一的区别是方法 II 不会冻结预训练 LLM 的参数,而是对其进行微调


model = AutoModelForSequenceClassification.from_pretrained(    "distilbert-base-uncased",     num_labels=2) 
# freeze layers (which we don't do here)# for param in model.parameters():# param.requires_grad = False
# finetune modellightning_model = LightningModel(model)
trainer = L.Trainer( max_epochs=3, ...)
trainer.fit( model=lightning_model, train_dataloaders=train_loader, val_dataloaders=val_loader)
# evaluate modeltrainer.test(lightning_model, dataloaders=test_loader)
复制代码


(对此感兴趣的读者可以在此处[4]找到完整的代码)


上面的这段代码被用来训练一个基于 DistilBERT 基础模型的电影评论分类器(你可以去 the code notebooks 看一看[4])


1)基于特征的方法与逻辑回归:83%的测试准确率(accuracy);


2)微调方法 I,更新最后两层:87%准确率(accuracy);


3)微调方法 II,更新所有层:92%准确率(accuracy)。


这些准确率结果符合预期,即微调更多层通常会获得更好的性能,但也需要花费更高的成本。



本文 3 种方法的训练效率和模型性能权衡经验之谈


上述场景强调了微调的三种最不同或最极端的情况:即仅训练最后一层(或几层)与训练所有层。当然,具体效果可能会因模型和数据集而异,但在探索两者之间的各种情况也是值得的。 例如,有时候我们只需训练一半的模型,就能获得与训练完成的模型相同的性能(更多关于轻量化微调(parameter-efficient finetuning)的内容将在下一节介绍)。如果你是一个好奇宝宝,下图描述了在 IMDB 电影评论数据集(IMDB movie review dataset)的 2 万个训练实例上进行微调的 DistilBERT 模型的预测性能(predictive performances)和训练时间。



在 IMDB 电影评论数据集上微调的预训练 DistilBERT 模型的性能。相关代码可以在 GitHub 上找到[5]


正如我们可以看到的,仅训练最后一层是最快速的,但模型性能也最差。 符合预期的是,训练更多层会提高模型性能,但也会增加计算成本。 有一件事情非常有趣,可以看到当训练两个全连接的输出层(fully connected output layers)和最后两个 transformer blocks(从左边数起的第三个 block)时,预测性能(predictive performance)就已经趋于饱和。因此,在这种特定情况下(即对于这个特定的模型和数据集组合),训练超过这些层(layers)似乎是一种计算资源的浪费。

03 轻量化微调(Parameter-Efficient Fine-Tuning)

轻量化微调让我们能够重复使用预训练的模型,同时最大限度地减少算力和资源的占用。总体来说,轻量化微调具有以下五个优点:


1. 能够降低计算成本(需要更少的 GPU 和 GPU 运行时间);


2. 拥有更快的训练时间(更快地完成训练);


3. 具备更低的硬件要求(适用于较小显存的 GPU 和较小的内存);


4. 具有更好的模型性能(降低过拟合);


5. 需要更少的存储空间(大部分 weights 可以在不同任务(tasks)之间共享)。


在之前的章节中,我们了解到微调更多的层通常会带来更好的结果。上面的实验是基于一个相对较小的 DistilBERT 模型。但是,如果我们想微调那些几乎无法容纳在 GPU 显存中的较大模型(例如最新的生成式 LLM 模型),我们该怎么办呢?当然,我们可以使用上述基于特征的方法或者微调方法 I。但假设我们想要获得与微调方法 II 类似的模型质量呢?


这些年来,研究人员们开发了几种技术(Lialin 等人[6])来微调 LLM,使其只需要训练少量的参数,也能具有较高的模型性能。这些方法通常被称为参数高效微调技术(parameter-efficient finetuning techniques,PEFT,本文亦译作轻量化微调)。


一些目前最受欢迎的 PEFT 技术在下图中可见。



最受欢迎的轻量化微调(PEFT)技术可选择项


那么,这些技术是如何工作的呢?简单来说,都涉及到引入少量额外的参数进行微调(而不是像我们在上面的微调方法 II 中那样对所有层进行微调)。从某种意义上说,微调方法 I(只微调最后一层)也可以被认为是一种轻量化微调(PEFT)技术。然而,像前缀微调(prefix tuning)、adapters 和 Low-Rank Adaptation (LoRA,低秩自适应))等技术,它们都“修改”了多个层(layers),以极低的成本实现了更好的预测性能(predictive performance)。


由于本文的篇幅目前已经很长了,而且这些都是超级有趣的技术,我将在未来单独介绍这些技术。

04 基于人类反馈的强化学习(Reinforcement Learning with Human Feedback)

在基于人类反馈的强化学习(Reinforcement Learning with Human Feedback,RLHF)中,使用一种结合了监督学习(supervised learning)和强化学习(reinforcement learning)的方法对预训练模型进行微调——这一方法被 ChatGPT 使用而得到大力推广,而 ChatGPT 又是基于 InstructGPT(Ouyang 等人[7])的。


在 RLHF 中,通过让人类对不同的模型输出进行排序或评分来收集人类反馈,从而提供奖励信号(reward signal) 。收集到的奖励标签(reward labels) 可以用来训练奖励模型(reward model) ,进而反过来指导 LLM(Language Model)适应人类的喜好。


奖励模型本身是通过监督学习(supervised learning)来学习的(通常使用预训练的 LLM 作为基础模型)。接下来,使用奖励模型来更新预训练的 LLM,使其适应人类偏好——训练过程使用一种被称为近端策略优化(proximal policy optimization,Schulman 等人[8])的强化学习方法。



InstructGPT 相关论文的截图,概述了 RLHF 的过程


为什么要使用奖励模型而不是直接在人类反馈的基础上训练预训练模型?这是因为让人类参与模型的学习过程会产生瓶颈(bottleneck),因为我们无法实时获取反馈。


这篇文章的篇幅已经很长了,所以我把更详细的解释推迟到以后的文章中,敬请期待!

05 总结

对预训练 LLM 的所有层(layers)进行微调仍然是适应新目标任务(new target tasks)的黄金标准(the gold standard),但是对于预训练的 Transformer 模型,有几种有效的替代方法。诸如基于特征的方法、上下文学习和轻量化微调等这些方法能够有效地将 LLM 应用于新的任务,同时还能最大限度地减少计算成本和计算资源


此外,基于人类反馈的强化学习(RLHF)作为监督微调(supervised finetuning,SFT)的一种替代方法,可以提升模型性能。


END


参考资料


1.https://d4mucfpksywv.cloudfront.net/better-language-models/language_models_are_unsupervised_multitask_learners.pdf


2.https://arxiv.org/abs/2005.14165


3.https://arxiv.org/abs/1810.04805


4.https://github.com/rasbt/LLM-finetuning-scripts/tree/main/conventional/distilbert-movie-review


5.https://github.com/rasbt/LLM-finetuning-scripts/tree/main/conventional/distilbert-movie-review/layerwise-experiment


6.https://arxiv.org/abs/2303.15647


7.https://arxiv.org/abs/2203.02155


8.https://arxiv.org/abs/1707.06347


本文经原作者授权,由 Baihai IDP 编译。如需转载译文,请联系获取授权。


原文链接


https://magazine.sebastianraschka.com/p/finetuning-large-language-models

发布于: 刚刚阅读数: 2
用户头像

Baihai IDP

关注

还未添加个人签名 2021-08-31 加入

IDP(Intelligent Development Platform)是面向数据科学家和算法工程师的新一代AI开发生产平台,便捷、高效数据科学家对数据接入与探索、模型开发、调试、训练和模型发布的需求。

评论

发布
暂无评论
落地领域大模型应知必会 (1) :主要微调方法总览_人工智能_Baihai IDP_InfoQ写作社区