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

原文地址:Using and Finetuning Pretrained Transformers,by Sebastian Raschka, on 2024-04-20

使用与微调预训练Transformer模型

本周行业动态频出,其中不乏令人振奋的AI前沿研究,我会在 https://magazine.sebastianraschka.com/archive 中展开讨论。

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

如果你一直在寻找机器学习入门课程之后的进阶学习资料,这本书或许正合适。书中涵盖了30个在我过往书籍与课程中未深入展开的概念,以简洁的问答形式呈现(包含配套练习)。

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

figure01

《机器学习与AI问答》(Machine Learning Q and AI)已在 https://nostarch.com/machine-learning-q-and-aihttps://www.amazon.com/Machine-Learning-AI-Essential-Questions/dp/1718503768 及各大图书渠道发售。

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

祝阅读愉快!

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

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

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

接下来的章节将深入展开介绍这些方法。

利用Transformer完成分类任务

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

基于特征的方法

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

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

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

figure02

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

微调

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

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

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

figure03

图2:使用预训练LLM的三种传统方法。 三种方法的代码示例可参见: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:通过提示让LLM进行上下文学习。 上下文学习的代码实现可参见: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始终不可微。除此之外,也有研究提出用另一个LLM来自动生成并评估提示词。

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

还有一种纯基于上下文学习思路的方法是LLM索引(LLM Indexing),如图4所示。

figure05

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

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

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

感兴趣的读者可以通过以下链接获取LLM索引与检索增强生成的代码示例: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)。截至本文撰写时,主流的参数高效微调方法如图5所示。

figure06

图5:参数高效微调技术的主要分类及代表性方法

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

软提示调优

软提示调优的核心思路是:在查询的嵌入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(soft_prompt)
    # ➋ 将软提示(前缀)与输入拼接
    x = concatenate([soft_prompt, x],
                        dim=seq_len)
    # ➌ Transformer块的标准运算
    x = SelfAttention(x)
    x = LayerNorm(x + residual)
    residual = x
    x = FullyConnectedLayers(x) 
    x = LayerNorm(x + residual)
    return x

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

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

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

figure07

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

软提示调优和前缀调优都属于参数高效方法,因为它们只需要训练拼接的参数张量,无需更新LLM本身的参数。

适配器方法

适配器方法与前缀调优的关联在于,二者都在Transformer层中新增了额外参数。在原始的适配器方法中,每个Transformer块的多头自注意力层和原有全连接层之后,都会新增额外的全连接层,如图7所示。

figure08

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

使用原始适配器方法训练LLM时,仅更新新增的适配器层,其余Transformer层保持冻结。适配器层的参数量通常很小:适配器块的第一个全连接层会将输入映射到低维空间,第二个全连接层再将其映射回原始输入维度,因此这种方法通常被认为是参数高效的。

原始适配器方法的伪代码实现如下:

def transformer_block_with_adapter(x):
    residual = x
    x = SelfAttention(x)
    x = FullyConnectedLayers(x)
    # 适配器层
    x = LayerNorm(x + residual)
    residual = x
    x = FullyConnectedLayers(x)
    x = FullyConnectedLayers(x)
    # 适配器层
    x = LayerNorm(x + residual)
    return x

低秩适配

低秩适配(Low-Rank Adaptation,LoRA)是另一种值得关注的主流参数高效微调方法,其核心是利用低秩变换对预训练LLM的权重进行重参数化。LoRA的基础是低秩变换思想——用低维表示近似高维矩阵或数据集,通过找到更少的维度组合,有效捕捉原始数据中的大部分信息。常见的低秩变换技术包括主成分分析和奇异值分解。

例如,假设ΔW是LLM中某个权重矩阵的参数更新量,维度为ℝ^(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  # 常规矩阵乘法
    h += x . (W_A . W_B) * scalar
    return h

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

在代码清单2中,scalar是一个缩放因子,用于调整合并结果(原始模型输出+低秩适配输出)的幅度,以此平衡预训练模型的原有知识与新任务的专属适配。根据LoRA原论文的实验结果,在多个任务基准测试中,使用LoRA的模型表现略优于使用适配器方法的模型,甚至往往比前文介绍的II型微调(全量微调)的模型效果更好。

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

基于人类反馈的强化学习

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

让LLM适配新的目标领域或任务,传统方法是基于标注目标数据的监督式微调。例如,通过II型微调,我们可以让预训练LLM在情感分类这类目标任务上进行微调,使用的数据集中包含文本及其对应的情感标签(正面、中性、负面等)。

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

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

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

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

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

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

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

参考文献与延伸阅读

代码示例

练习题

  1. 什么时候使用上下文学习比微调更合适?什么时候微调更合适?
  2. 在前缀调优、适配器方法和LoRA中,我们如何保证模型保留(而不遗忘)原始知识?

《机器学习与AI问答》

希望你喜欢这段节选!如果你还想了解另外29个机器学习与AI相关的主题,可以在 https://nostarch.com/machine-learning-and-ai-beyond-basicshttps://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 *

*