【转载】预训练Transformer模型的使用与微调

原文地址:Developing an LLM: Building, Training, Finetuning,by Sebastian Raschka, on 2024-06-08

预训练Transformer模型的使用与微调

本周领域内进展不断,其中不乏令人振奋的AI前沿研究,我会在 https://magazine.sebastianraschka.com/archive 中展开讨论。

此外,我很高兴地向大家宣布我的新书已由No Starch Press出版,书籍链接:https://www.amazon.com/Machine-Learning-AI-Essential-Questions/dp/1718503768

如果你一直在寻找机器学习入门课程之后的进阶学习资料,这本书或许正合适。书中涵盖了30个在我过往的书籍与课程中未深入涉及的概念,我以简洁的问答形式(附带练习题)将它们整理成册。

我相信它也会成为机器学习面试备考的实用参考资料。

figure01

《机器学习与AI问答》可在 https://nostarch.com/machine-learning-q-and-ai、https://www.amazon.com/Machine-Learning-AI-Essential-Questions/dp/1718503768 及各大图书渠道购买。

当前,预训练大语言模型的使用与微调方法是最热门的讨论话题之一,因此我想分享书中的一段节选,希望能对你当下的项目有所帮助。

祝阅读愉快!

预训练大语言模型有哪些不同的使用与微调方式?

预训练大语言模型(Large Language Model, LLM)有哪些不同的使用与微调方式?最常见的三类方法包括:基于特征的方法、上下文提示法,以及更新模型部分参数的方法。

首先,大多数预训练大语言模型或语言Transformer模型无需进一步微调即可直接使用。例如,我们可以采用基于特征的方法,利用预训练Transformer生成的嵌入向量来训练新的下游模型(比如线性分类器)。其次,我们可以在输入中直接给出新任务的示例,无需对模型做任何更新或训练,就能让模型输出预期结果,这一方法也被称为提示工程(Prompting)。最后,我们也可以通过微调模型的全部参数或少量参数来达成目标效果。

下文将对这些方法展开更深入的讲解。

将Transformer用于分类任务

我们先从使用预训练Transformer的传统方法讲起:基于特征嵌入训练新模型、微调输出层、微调全部层。我们会结合分类任务场景展开讨论(提示工程相关内容将在后续“上下文学习、索引与提示微调”一节详细介绍)。

基于特征的方法

在基于特征的方法中,我们加载预训练模型并将其“冻结”——即不更新预训练模型的任何参数,而是将模型作为特征提取器,在新数据集上提取特征,再基于这些嵌入向量训练下游模型。下游模型可以是任意类型(随机森林、XGBoost等),但通常线性分类器的效果最佳。这是因为BERT、GPT、Llama、Mistral等预训练Transformer已经能从输入数据中提取高质量、高信息量的特征,这些特征嵌入往往捕捉到了数据中复杂的关联与模式,足以让线性分类器轻松实现数据的类别划分。

此外,逻辑回归、支持向量机等线性分类器通常具备较强的正则化能力,在处理预训练Transformer生成的高维特征空间时,能有效避免过拟合。这种基于特征的方法是效率最高的方案,因为完全不需要更新Transformer模型的参数。而且,当分类器需要多轮训练时,训练集对应的嵌入向量可以提前计算好(因为嵌入结果不会变化)。

图1展示了大语言模型的典型构建流程,以及通过微调适配下游任务的过程。图中,在通用文本语料上预训练得到的模型,经过微调后可以执行德译英等任务。

figure02

图1:大语言模型的通用微调流程

微调

预训练大语言模型的传统微调方法分为两种:仅更新输出层(我们称之为“微调I型”),以及更新全部层(我们称之为“微调II型”)。

微调I型与前文提到的基于特征的方法类似,但它是在大语言模型自身之上新增一层或多层输出层。模型的主干网络保持冻结,仅更新新增输出层的参数。由于不需要在整个网络中反向传播梯度,这种方法在计算吞吐量和内存占用上效率相对更高。而微调II型同样是加载模型并新增输出层,但与仅在最后几层反向传播不同,它会通过反向传播更新所有层的参数,因此是计算成本最高的方案。尽管计算开销高于基于特征的方法和微调I型,但它通常能带来更优的建模效果与预测性能,在处理专业性更强的领域专属数据集时优势尤为明显。

图2总结了本节介绍的三种方法。

figure03

图2:使用预训练大语言模型的三种传统方法。三种方法的代码示例可参见:https://github.com/rasbt/MachineLearning-QandAI-book/tree/main/supplementary/q18-using-llms/01_classifier-finetuning

除了对三种微调方法的概念总结,图2还给出了它们训练效率的经验参考。由于微调II型需要更新的层数和参数多于微调I型,其反向传播的计算成本也更高。同理,微调II型的成本也高于更简单的基于特征的方法。

感兴趣的读者可以通过以下链接查看代码示例,涵盖基于特征的方法、微调部分层、微调整个Transformer用于分类任务的实现:https://github.com/rasbt/MachineLearning-QandAI-book/tree/main/supplementary/q18-using-llms/01_classifier-finetuning

上下文学习、索引与提示微调

GPT-2、GPT-3等大语言模型普及了**上下文学习(In-context Learning)**的概念,在该场景下也常被称为零样本学习或少样本学习,如图3所示。

figure04

图3:通过提示让大语言模型实现上下文学习。上下文学习的代码示例可参见:https://github.com/rasbt/MachineLearning-QandAI-book/blob/main/supplementary/q18-using-llms/02_prompting

如图3所示,上下文学习的核心是在输入(提示词)中提供任务的背景或示例,让模型推断出预期的行为模式并生成对应的输出。这种方法利用了模型在预训练阶段从海量数据(涵盖各类任务与场景)中习得的能力。

注意:此处的少样本学习与上下文学习方法含义相近,其定义与第3章中讨论的传统少样本学习不同。

举个例子,假设我们想用GPT-3这类大规模预训练语言模型,通过上下文学习实现少样本德译英。我们只需要提供几个德译英的示例,帮助模型理解任务要求即可,如下所示:

将下列德语句子翻译成英语:
示例1:德语:”Ich liebe Pfannkuchen.” 英语:”I love pancakes.”
示例2:德语:”Das Wetter ist heute schoen.” 英语:”The weather is nice today.”
翻译这个句子:德语:”Wo ist die naechste Bushaltestelle?”

通常来说,在特定任务或特定数据集上,上下文学习的效果不如微调,因为它完全依赖预训练模型从训练数据中泛化的能力,不会针对当前任务调整模型参数。

但上下文学习也有自身的优势:当微调所需的标注数据稀缺甚至没有时,它的价值就格外凸显。同时,如果我们无法直接访问模型权重,只能通过界面或API与模型交互(比如ChatGPT),这种方法也能让我们快速尝试不同任务,无需微调模型参数。

与上下文学习相关的还有硬提示微调(Hard Prompt Tuning),“硬”指的是输入token是不可微的。前文介绍的微调方法是通过更新模型参数来提升任务效果,而硬提示微调则是通过优化提示词本身来提升性能。提示微调不会修改模型参数,但可能会用到小规模标注数据集,来找到针对特定任务的最优提示词格式。例如,为了优化前面德译英任务的提示词,我们可以尝试以下三种写法:


"Translate the German sentence '{german_sentence}' into English: {english_translation}"
"German: '{german_sentence}' | English: {english_translation}"
"From German to English: '{german_sentence}' -> {english_translation}"

提示微调是参数微调的一种低成本替代方案。但它的效果通常不如全量模型微调,因为它没有针对特定任务更新模型参数,可能限制了模型适配任务细节的能力。此外,提示微调可能耗费大量人力,因为需要人工对比不同提示词的效果,或是借助其他方法来完成评估。由于输入token不可微,这种方法也被称为硬提示法。除此之外,也有研究提出用另一个大语言模型来自动生成和评估提示词。

提示工程与上下文学习的代码示例可参见:https://github.com/rasbt/MachineLearning-QandAI-book/blob/main/supplementary/q18-using-llms/02_prompting

另一种纯基于上下文学习的应用方式是大语言模型索引(LLM Indexing),如图4所示。

figure05

图4:通过大语言模型索引从外部文档中检索信息。索引的代码示例可参见:https://github.com/rasbt/MachineLearning-QandAI-book/blob/main/supplementary/q18-using-llms/03_retrieval-augmented-generation

在大语言模型领域,索引可以看作是基于上下文学习的一种延伸方案,它能把大语言模型变成信息检索系统,从外部资源和网站中提取信息。如图4所示,索引模块会将文档或网站内容切分成更小的文本块,再将这些文本块转换成向量,存储在向量数据库中。当用户提交查询时,索引模块会计算查询向量与数据库中每个向量的相似度,最后召回相似度最高的前k个嵌入向量,用于生成最终回答。

大语言模型索引是一个统称,指代将大语言模型与现有数据源连接的框架或流程,其中最典型的就是检索增强生成(Retrieval-Augmented Generation, RAG)。RAG将大语言模型与检索系统相结合,提升模型生成回答的能力。

感兴趣的读者可以通过以下链接查看大语言模型索引与检索增强生成的代码示例:https://github.com/rasbt/MachineLearning-QandAI-book/blob/main/supplementary/q18-using-llms/03_retrieval-augmented-generation/retrieval-augmented-generation.ipynb

参数高效微调

近年来,研究者开发了许多方法,让预训练Transformer能更高效地适配新的目标任务,这类方法统称为参数高效微调(Parameter-Efficient Finetuning, PEFT)。目前主流的几类方法如图5所示。

figure06

图5:参数高效微调技术的主要分类及典型代表

与上一节讨论的硬提示法不同,**软提示(Soft Prompting)**策略优化的是提示的嵌入向量形式。硬提示微调修改的是离散的输入token,而软提示微调使用的是可训练的参数张量。

软提示微调

软提示微调的核心思想是:在查询的嵌入向量前拼接一个可训练的参数张量(即“软提示”),再通过梯度下降调整这个张量,提升模型在目标数据集上的表现。用类Python伪代码可以表示为:


x = EmbeddingLayer(input_ids)
x = concatenate([soft_prompt_tensor, x],
dim=seq_len)
output = model(x)

其中,soft_prompt_tensor的特征维度与嵌入层输出的输入嵌入维度一致。因此,修改后的输入矩阵会多出若干行,相当于在原输入序列中增加了额外的token,让序列变长。

前缀微调

另一种常用的提示微调方法是前缀微调(Prefix Tuning)。它与软提示微调类似,但区别在于:前缀微调是在每个Transformer块前都拼接可训练张量(软提示),而非只在输入嵌入层前拼接,这样可以让训练更稳定。前缀微调的实现伪代码如下:


def transformer_block_with_prefix(x):
# ➊
soft_prompt = FullyConnectedLayers( # Prefix
soft_prompt) # Prefix
# ➋
x = concatenate([soft_prompt, x], # Prefix
dim=seq_len) # Prefix
# ➌
x = SelfAttention(x)
x = LayerNorm(x + residual)
residual = x
x = FullyConnectedLayers(x)
x = LayerNorm(x + residual)
return x

代码清单1:改造为前缀微调的Transformer块

我们将代码清单1分为三部分:软提示的实现、软提示(前缀)与输入的拼接、Transformer块其余部分的实现。首先,张量soft_prompt会经过一组全连接层处理➊;接着,变换后的软提示与主输入x拼接➋,拼接的维度为seq_len,即序列长度维度;最后,后续的代码➌是Transformer块的标准操作,包括自注意力、层归一化和前馈神经网络层,整体采用残差连接结构。

如代码清单1所示,前缀微调通过添加可训练的软提示来改造Transformer块。图6进一步展示了普通Transformer块与前缀微调Transformer块的区别。

figure07

图6:普通Transformer与前缀微调的对比

软提示微调和前缀微调都属于参数高效方法,因为它们只需要训练拼接的参数张量,无需调整大语言模型本身的参数。

Adapter方法

Adapter方法与前缀微调的共同点是都会在Transformer层中新增参数。在原始的Adapter方法中,每个Transformer块的多头自注意力层和原有全连接层之后,都会新增额外的全连接层,如图7所示。

figure08

图7:普通Transformer块(左)与带Adapter层的Transformer块对比。Adapter层的代码示例可参见:https://github.com/rasbt/MachineLearning-QandAI-book/tree/main/supplementary/q18-using-llms/04_adapter

使用原始Adapter方法训练大语言模型时,仅更新新增的Adapter层,其余Transformer层保持冻结。Adapter层的参数量通常很小:Adapter块中的第一个全连接层将输入映射到低维空间,第二个全连接层再将其映射回原始输入维度,因此这种方法属于参数高效微调。

用伪代码表示,原始Adapter方法如下:


def transformer_block_with_adapter(x):
residual = x
x = SelfAttention(x)
x = FullyConnectedLayers(x) # Adapter
x = LayerNorm(x + residual)
residual = x
x = FullyConnectedLayers(x)
x = FullyConnectedLayers(x) # Adapter
x = LayerNorm(x + residual)
return x

低秩适配(LoRA)

**低秩适配(Low-Rank Adaptation, LoRA)**是另一种值得关注的主流参数高效微调方法,它通过低秩变换来重新参数化预训练大语言模型的权重。低秩变换是一种用低维表示近似高维矩阵或数据集的技术,通过找到更少的维度组合,有效捕捉原始数据中的大部分信息,从而实现低维近似。常用的低秩变换方法包括主成分分析(PCA)和奇异值分解(SVD)。

例如,假设ΔW是大语言模型中某个权重矩阵的参数更新量,维度为ℝ^(A×B)。我们可以将这个权重更新矩阵分解为两个更小的矩阵:ΔW = W_A W_B,其中W_A ∈ ℝ^(A×h),W_B ∈ ℝ^(h×B)。训练时,原始权重保持冻结,仅训练新的矩阵W_A和W_B。

既然引入了新的权重矩阵,为什么说它是参数高效的?因为这些新矩阵的规模可以非常小。举个例子,假设A=25,B=50,那么ΔW的参数量是25×50=1250。如果取h=5,那么W_A有125个参数,W_B有250个参数,两个矩阵加起来总共只有125+250=375个参数。

学习到权重更新矩阵后,全连接层的矩阵乘法可以写成如下伪代码形式:


def lora_forward_matmul(x):
h = x . W # Regular matrix multiplication
h += x . (W_A . W_B) * scalar
return h

代码清单2:结合LoRA的矩阵乘法。Adapter层的代码示例可参见:https://github.com/rasbt/MachineLearning-QandAI-book/tree/main/supplementary/q18-using-llms/05_lora

在代码清单2中,scalar是缩放因子,用于调整最终融合结果(原始模型输出加上低秩适配结果)的幅度,以此平衡预训练模型的原有知识与新任务的适配效果。根据LoRA方法的原始论文,在多个任务基准测试中,使用LoRA的模型效果略优于使用Adapter方法的模型,甚至往往比前文提到的微调II型(全量微调)的模型效果更好。

《Ahead of AI》是读者支持的付费专栏。如果想接收新文章并支持我的创作,欢迎免费订阅或成为付费会员。

基于人类反馈的强化学习(RLHF)

上一节我们聚焦于提升微调效率的方法。现在换个角度:我们如何通过微调来提升大语言模型的建模效果?

让大语言模型适配新领域或新任务的传统方法,是用标注好的目标数据进行有监督微调。例如,微调II型方法可以让预训练大语言模型适配情感分类等目标任务,使用的数据集中包含标注了正面、中性、负面等情感标签的文本。

有监督微调是大语言模型训练的基础步骤。而更进阶的一步是基于人类反馈的强化学习(Reinforcement Learning with Human Feedback, RLHF),它可以进一步提升模型与人类偏好的对齐程度。例如ChatGPT及其前身InstructGPT,就是预训练大语言模型(GPT-3)经过RLHF微调后的典型代表。

在RLHF中,预训练模型会结合监督学习与强化学习进行微调。该方法因初代ChatGPT而普及,而ChatGPT的技术又源于InstructGPT。具体来说,我们让人类对模型的不同输出进行排序或打分,收集人类反馈作为奖励信号;再用这些奖励标注训练一个奖励模型,引导大语言模型向人类偏好的方向适配。奖励模型通常以预训练大语言模型为基座,通过监督学习训练得到;之后再通过额外的微调,让预训练大语言模型向人类偏好对齐。这个额外微调阶段使用的强化学习算法是近端策略优化(Proximal Policy Optimization, PPO)

RLHF之所以使用奖励模型,而非直接用人类反馈训练预训练模型,是因为人类无法实时提供反馈,让人类全程参与学习过程会成为训练瓶颈。

预训练语言模型的适配总结

尽管微调整个预训练大语言模型的全部层,依然是适配新任务的“黄金标准”,但我们也有很多高效的方案来利用预训练Transformer。例如,通过基于特征的方法、上下文学习或参数高效微调技术,我们可以在最小化计算成本与资源消耗的同时,将大语言模型高效应用于新任务。

三种传统方法——基于特征的方法、微调I型、微调II型——在计算效率与性能之间提供了不同的权衡。软提示微调、前缀微调、Adapter方法等参数高效微调技术进一步优化了适配过程,减少了需要更新的参数量。而RLHF则为有监督微调提供了另一种思路,有望进一步提升建模效果。

总而言之,预训练大语言模型的通用性与效率仍在不断提升,为我们将模型适配到各类任务与领域提供了更多可能与策略。随着该领域研究的推进,未来预训练语言模型的使用方法还会有更多的进步与创新。

参考文献与延伸阅读

代码示例

https://github.com/rasbt/MachineLearning-QandAI-book/tree/main/supplementary/q18-using-llms/01_classifier-finetuning
https://github.com/rasbt/MachineLearning-QandAI-book/blob/main/supplementary/q18-using-llms/02_prompting
https://github.com/rasbt/MachineLearning-QandAI-book/blob/main/supplementary/q18-using-llms/03_retrieval-augmented-generation
https://github.com/rasbt/MachineLearning-QandAI-book/blob/main/supplementary/q18-using-llms/04_adapter
https://github.com/rasbt/MachineLearning-QandAI-book/tree/main/supplementary/q18-using-llms/05_lora

练习题

  1. 什么时候更适合用上下文学习而非微调?反过来呢?
  2. 在前缀微调、Adapter和LoRA方法中,如何保证模型保留(而不遗忘)原有的知识?

《机器学习与AI问答》

希望你喜欢这段节选!如果你对另外29个机器学习与AI相关主题感兴趣,可以在 https://nostarch.com/machine-learning-and-ai-beyond-basics、https://www.amazon.com/Machine-Learning-AI-Beyond-Basics/dp/1718503768 及各大图书渠道购买本书。

figure09

https://www.amazon.com/Machine-Learning-AI-Essential-Questions/dp/1718503768 这本书涵盖了30个机器学习与AI的核心主题。

《Ahead of AI》是我的个人兴趣项目,没有直接的商业收入。如果你愿意购买我的书来支持这些创作,我会非常感激。

如果你已经买了这本书,也非常欢迎在亚马逊上留下评价!

Leave a Reply

Your email address will not be published. Required fields are marked *

*