transformer的基础结构
预训练
预先在大量数据上训练模型以学习通用特征。预训练可以让模型再见到特定任务数据之前,先通过学习大量通用数据,来获取广泛有用的特征,提高模型在目标任务上的表现和泛化能力。
由于在实际生活中,收集并标注大量数据往往是一项既耗时又昂贵的任务。特别是在某些专业领域。预训练技术使得模型能够从未标记的大规模数据中学习通用特征和先验知识,比如语言的语法规则、视觉的底层特征,从而减少对标记数据的依赖,使得在有限的数据集上也能训练出性能良好的模型。
预训练通常是无监督学习过程,基于transformer架构,利用大量无标签或弱标签的数据进行训练,得到初步具备通用知识或能力的模型。
a.掩码语言预测 基于transformer编码器的双向模型,代表如bert,在预训练时采用掩码语言建模(MLM)和下一句预测(NSP)任务。输入一个序列对(pair of sequences),并在原始输入中添加特殊标记 [CLS] 和 [SEP],[SEP]加载句子结尾,用于分割两个文本序列。部分 token 被随机替换为 [MASK] token。MLM 损失的目标是最大化 [MASK] 位置处标签 token被准确预测的条件概率,而 [CLS] 的最终表示用于预测两句话是否连贯。
b.自回归语言建模 基于transformer解码器的语言模型。将一个给定序列的概率,分解为当前token在已知前序列token条件下的概率进行建模。预训练时,模型每个位置的最终输出都会被传入softmax层,用于预测下一个token,只能利用过去的token,适用于生成任务。
c.序列到序列建模 基于完整的transformer结构,采用跨度级扰动作为主要的预训练任务,即随机选择一段任意长度的文本,用单个掩码token替换,并要求模型填充原始token。目标是最大化模型在给定损坏序列的情况下生成目标序列的概率。
大模型应用的挑战
大模型通过预训练可以掌握广泛的语言知识和模式,但预训练数据通常具有通用性,缺乏对特定领域或任务的深度理解,不完全适用于特定的目标任务。微调通过使用特定领域的数据,对已完成预训练的大模型进行针对性优化,能显著提升模型在特定任务上的性能,使得模型能更好地适应和完成特定领域的任务。
微调可以赋予大模型更定制化的功能,并且通过引入特定领域的数据集,学习该领域的知识和语言模式,在特定任务上取得更好的性能,可以更准确地理解和生成相关领域的内容。
transformer模块架构包括众多模块,微调通常是对模块中特定部分进行优化,来提升模型在特定任务上的表现,同时保持其通用能力的稳定性。
微调和RAG等其他大模型技术有何优势,什么情况下适合选择微调大模型
大模型落地主要有四种实施方案:提示词工程、检索增强生成、微调、预训练。
提示词工程,适合资源有限且需要快速验证的场景。技术难度低,投入少,处理复杂任务的性能提升有限,对提示词敏感性较高。
检索增强生成,不改变现有大模型结构,在其基础上增加外部知识库。用户提出问题时检索相关知识,增强回答准确性。
微调可以实现更加精细化的功能,如整合本地知识库进行搜索、针对特定领域问题构建问答系统等,更好地适应当前数据集。结合特定领域知识对模型再训练,生成全新的微调模型,可以经过多轮微调,对模型能力进行优化,但需要投入大量的训练资源和时间。
预训练,用于构建全新大模型或进行全面的模型调整,最为复杂和昂贵。
RAG和微调是两种主流技术路径。
RAG不改变基础模型、增加了外部知识,无需模型训练。适用于动态业务数据、模型幻觉控制、可解释性需求、成本敏感、依赖通用能力的情况。
微调改变基础模型,融合特定知识,需要模型训练。适用于模型能力定制、低延迟要求和智能设备部署。
在需要深度定制、对性能要求高、要求特定场景专精的情况下,通常会考虑微调。
微调的常见技术和原理
在选定相关数据集和预训练模型的基础上,设置合适的超参数并对模型进行必要调整,使用特定任务的数据对模型进行训练以优化其性能。包括以下四个核心步骤:
- 数据准备:选择与任务相关数据集,对其进行预处理,包括清洗、分词、编码
- 选择基础模型:选择与训练好的大语言模型,如BERT、GPT-3
- 设置微调参数:设定学习率、训练轮数、批处理大小。根据需要设置其他超参数如权重衰减、梯度剪切。
- 微调流程:加载预训练模型与权重;根据需要对模型进行修改,如更改输入层;选择合适的损失函数和优化器;使用选定的数据集进行微调训练,包括前向传播、损失计算、反向传播和权重更新。
微调流程RLHF
根据人类输入提示生成的多样化文本,其生成结果评估只能基于主观和上下文,难以用现有的基于规则的文本生成指标来衡量。并且现有的模型通常以预测下一个单词的方式和简单的损失函数 (如交叉熵) 来建模,没有显式地引入人的偏好和主观意见。RLHF(Reinforcement Learning from Human Feedback)的思想是,用生成文本的人工反馈作为性能衡量标准,或者更进一步用该反馈作为损失来优化模型,即利用人类反馈作为奖励信号来训练强化学习模型的方法,提升模型生成内容的质量,更符合人类偏好。
流程:
- 使用监督数据微调。使用标记过的数据来调整预训练的模型参数,与传统微调类似。
- 训练奖励模型。奖励模型来评估文本序列的质量,输入文本,输出数值,表示该文本符合人类偏好的程度。训练数据由多个语言模型生成的文本序列组成,经过人工评估或其他模型来打分。在模型选择方面,可以是另一个经过微调的模型,也可以是根据偏好数据从头开始训练的模型。
- 训练强化学习模型。在强化学习框架中,定义状态空间、动作空间、策略函数和价值函数。用策略梯度强化学习 (Policy Gradient RL) 算法、近端策略优化 (Proximal Policy Optimization,PPO) 微调初始 LM 的部分或全部参数。
状态空间State-输入序列的分布,环境中所有可能状态的集合。
动作空间Action-所有可能的token(词汇表中的词),智能体所有可能动作的集合。
价值函数-结合奖励模型的输出和策略约束,评估给定状态下采取特定动作的价值。
策略函数-经过微调的大语言模型,根据当前状态选择下一个动作,以量化积累奖励。
奖励Reward-智能体在环境某一状态下获得的奖励。
智能体交互过程为:在t时刻,环境状态St,达到这一状态获得奖励Rt。智能体观测到St和Rt,采取相应动作At。采取动作后,环境状态变为St+1,得到相应奖励Rt+1。智能体在这个过程中学习,最终目标是找到一个策略,根据当前观测到的环境状态和奖励反馈,选择最佳的动作。Vt时的总收益,考虑了即时和未来的收益。Vt=Rt+rVt+1。
模型微调常见技术(不同技术的计算资源要求)
按参数规模来说,可分为全参数微调和高效参数微调。全量微调(Full Fine-Tuning)利用特定任务数据集,调整预训练模型所有参数,充分适应新任务,依赖大规模计算资源,但有效利用预训练模型的通用特征。
高效参数微调(Parameter-Efficient Fine-Tuning,PEFT)通过最小化微调参数量和计算复杂度,实现高效迁移学习。仅更新部分参数,显著降低训练时间和成本。
高效参数微调可粗略分为三大类:增加额外参数(additive)、选一部分参数更新(selective)和引入重参数化(reparametrization)。其中增加额外参数可分为两小类,包括类适配器(adapters)和软提示(soft prompts)。
- LoRA
在矩阵相乘模块引入低秩矩阵模拟全量微调。不显著增加额外计算负担前提下,有效微调模型,保留了模型的原有性能水准。低秩矩阵仅放大对下游任务有用的特征,不改变预训练模型中的主要特征。具体流程:
- 确定模型目标权重矩阵。识别出需要微调的权重矩阵,通常位于多头自注意力与前馈神经网络部分。平均分配到Wq和Wk效果最好。
- 引入两个低秩矩阵。引入维度小的低秩矩阵,降维矩阵A和升维矩阵B,原始权重矩阵为d*d的话,A和B的尺寸为d*r,r*d,r<<d。随机高斯分布初始化A,0矩阵初始化B,使得训练开始此旁路矩阵是0矩阵。
- 计算低秩更新,固定原始模型参数,只训练A和B,通过AB的乘积生成新矩阵,秩远小于原始权重矩阵的秩。
- 结合原始权重,新权重=原始权重+α/r*AB。(α是缩放因子,r是秩)
LoRA常用于自注意力的查询(q_proj)、键(k_proj)、值(v_proj)和输出(o_proj)投影矩阵;也会用于MLP层的控制门投影(gate_proj)、上升投影(up_proj)、下降投影(down_proj);如果词嵌入需要微调,也可以应用于嵌入矩阵(embed_tokens),尤其是词汇量大时。
调整的超参数有低秩矩阵维度r、控制低秩矩阵的缩放因子alpha和丢弃率dropout。
较小的r可以减少参数数量,从而提高训练效率,但可能牺牲一定的模型表现;较大的r可以适用于大规模模型。通常选择8-128
调整alpha,可以平衡低秩矩阵的影响,使模型能够在微调过程中保持足够的表达能力,常选用16-32 ,较大的alpha值可以增加模型的表达能力,但也可能会训练难度。
dropout是正则化技术,在训练过程中随机丢弃神经网络中的部分神经元来防止过拟合,较高的dropout率有助于减少模型的复杂度,提升在新数据上的泛化能力。常用0.2-0.3 ,较低的dropout值(如 0.1)适合于较小的模型,较高的dropout值(如 0.4-0.5)适合于较大的网络,尤其是在防止过拟合时。
- QLoRA
结合Lora和深度量化。通过冻结的4位量化预训练语言模型,将梯度反向传播到LoRA中,训练时反量化到bf16,减少训练需要的显存。
-
4位标准浮点数量化:将预训练模型量化为4位,包含低精度存储数据类型(4-bit)和计算数据类型(16-bit ),减少模型存储需求,保持模型精度的最小损失。每个权重由4个bit表示,选择最重要的值映射到16个可能的值之一。
import torch from scipy.stats import norm offset = 0.99 num_bins = 16 #linespace,从0.01到0.99平均分成17份。norm.ppf是计算正态分布的百分位点函数,给定概率值,返回对应的分位数。如给0.5,返回0. #生成17个分位数,在正态分布的[0.01,0.99]区间内均匀分布。 quantile = norm.ppf(torch.linspace(1 - offset, offset, num_bins + 1)).tolist() # 将[1-offset,offset]区间等分为16份 #quantile[1:]选出切片,第二个到最后一个,[-1]是第一个到倒数第二个,idx=0时,原列表第一和第二个数字取平均。该函数用于计算相邻分位数的平均值,生成新列表tmp。 tmp = [(quantile[1:][idx] + val) / 2 for idx, val in enumerate(quantile[:-1])] # 计算分位数 r_max, r_min = tmp[-1], tmp[0] S = (r_max - r_min)/(1 - (-1)) Z = 1 - r_max / S Q = [x/S + Z for x in tmp] # 分位数量化到[-1,1] print (Q)
这样的处理会导致,0的映射值不一定是0。如果0点映射到0,则通过估计正反两个范围中位数,构建非对称数据类型。负数部分映射其中7位,正数部分映射8位,0占据1位;或者正数和负数均使用7位,0占用2个位。论文中正数部分取9个值,负数部分取8个值,它们都会取到0,合并时再去掉一个重复的0。
from scipy.stats import norm import torch def create_normal_map(offset=0.9677083, use_extra_value=True): #offset确定分位数始末值,第二个true代表非对称量化,true代表对称量化。 if use_extra_value: # one more positive value, this is an asymmetric type v1 = norm.ppf(torch.linspace(offset, 0.5, 9)[:-1]).tolist() # 正数部分 v2 = [0]*(256-15) ## we have 15 non-zero values in this data type v3 = (-norm.ppf(torch.linspace(offset, 0.5, 8)[:-1])).tolist() #负数部分 v = v1 + v2 + v3 else: v1 = norm.ppf(torch.linspace(offset, 0.5, 8)[:-1]).tolist() v2 = [0]*(256-14) ## we have 14 non-zero values in this data type v3 = (-norm.ppf(torch.linspace(offset, 0.5, 8)[:-1])).tolist() v = v1 + v2 + v3 values = torch.Tensor(v) values = values.sort().values values /= values.max() assert values.numel() == 256 return values Q = create_normal_map()
QLoRA采取分块量化,每个块保存绝对值最大的为量化常数,数值除以该块的量化常数后,在Q中寻找最接近的值,记录值对应的索引为量化值。在反量化时,我们以量化结果作为索引,从Q中查找到它对应的分位数,再乘以为每个块保存的量化常数,可以得到最终结果。
结合Lora和深度量化。通过冻结的4位量化预训练语言模型,将梯度反向传播到LoRA中,训练时反量化到bf16,减少训练需要的显存。 -
4位标准浮点数量化:将预训练模型量化为4位,包含低精度存储数据类型(4-bit)和计算数据类型(16-bit ),减少模型存储需求,保持模型精度的最小损失。每个权重由4个bit表示,选择最重要的值映射到16个可能的值之一。
import torch from scipy.stats import norm offset = 0.99 num_bins = 16 #linespace,从0.01到0.99平均分成17份。norm.ppf是计算正态分布的百分位点函数,给定概率值,返回对应的分位数。如给0.5,返回0. #生成17个分位数,在正态分布的[0.01,0.99]区间内均匀分布。 quantile = norm.ppf(torch.linspace(1 - offset, offset, num_bins + 1)).tolist() # 将[1-offset,offset]区间等分为16份 #quantile[1:]选出切片,第二个到最后一个,[-1]是第一个到倒数第二个,idx=0时,原列表第一和第二个数字取平均。该函数用于计算相邻分位数的平均值,生成新列表tmp。 tmp = [(quantile[1:][idx] + val) / 2 for idx, val in enumerate(quantile[:-1])] # 计算分位数 r_max, r_min = tmp[-1], tmp[0] S = (r_max - r_min)/(1 - (-1)) Z = 1 - r_max / S Q = [x/S + Z for x in tmp] # 分位数量化到[-1,1] print (Q)
这样的处理会导致,0的映射值不一定是0。如果0点映射到0,则通过估计正反两个范围中位数,构建非对称数据类型。负数部分映射其中7位,正数部分映射8位,0占据1位;或者正数和负数均使用7位,0占用2个位。论文中正数部分取9个值,负数部分取8个值,它们都会取到0,合并时再去掉一个重复的0。
from scipy.stats import norm import torch def create_normal_map(offset=0.9677083, use_extra_value=True): #offset确定分位数始末值,第二个true代表非对称量化,true代表对称量化。 if use_extra_value: # one more positive value, this is an asymmetric type v1 = norm.ppf(torch.linspace(offset, 0.5, 9)[:-1]).tolist() # 正数部分 v2 = [0]*(256-15) ## we have 15 non-zero values in this data type v3 = (-norm.ppf(torch.linspace(offset, 0.5, 8)[:-1])).tolist() #负数部分 v = v1 + v2 + v3 else: v1 = norm.ppf(torch.linspace(offset, 0.5, 8)[:-1]).tolist() v2 = [0]*(256-14) ## we have 14 non-zero values in this data type v3 = (-norm.ppf(torch.linspace(offset, 0.5, 8)[:-1])).tolist() v = v1 + v2 + v3 values = torch.Tensor(v) values = values.sort().values values /= values.max() assert values.numel() == 256 return values Q = create_normal_map()
QLoRA采取分块量化,每个块保存绝对值最大的为量化常数,数值除以该块的量化常数后,在Q中寻找最接近的值,记录值对应的索引为量化值。在反量化时,我们以量化结果作为索引,从Q中查找到它对应的分位数,再乘以为每个块保存的量化常数,可以得到最终结果。
主要在于设计合理的映射和量化损失,最小化精度损失对性能影响。
- 适配器微调Adapter Tuning
在保留预训练模型原始参数不变的前提下,使模型适应新的任务。在模型的每个或几个层之间插入小型神经网络,即“适配器”,参数规模小,层次少。通常是在多头注意力和前馈网络之后增加两个前馈子层来实现,分别是降维和升维的前馈网络。微调时原有预训练模型参数不变,训练适配器,针对特定任务进行微调。只调整部分参数,高效迅速适应新任务。
具有两大特性:少量参数和近似恒等初始化。适配器模块保持较小的规模,模型总体大小增长相对缓慢。为了适配模型的稳定训练,需要进行近似恒等初始化。将适配器初始化为接近恒等函数的形式,可以确保在训练开始时原始网络不受影响。随着训练进行,适配器会被激活以改变网络中激活值的分布。如果不需要,适配器模块也可以被忽略。
适配器插入在两个全连接层后面,每个适配器结构包括输入层、输出层、下投影前馈层、上投影前馈层、非线性层和跳跃连接。训练时只调整下图的绿色部分,包括适配器的下投影前馈层、上投影前馈层、非线性层以及Transformer模块中的两个归一层的参数。适配器模块的工作原理是先把输入的d维特征向量通过下投影前馈层(d×r维矩阵)投影为r维向量(r<<d),应用非线性层,再通过上投影前馈层(r×d维矩阵)投影回一个d维向量。
注:LoRA是在权重矩阵中加入低秩矩阵,作为原有权重矩阵的修改项。适配器调整是在各层间加入小型神经网络模块,独立于模型主体结构。
from transformers import AutoTokenizer, AutoConfig,TrainingArguments
from adapters import AutoAdapterModel,BnConfig,AdapterTrainer
model_path = "model_path"
tokenizer = AutoTokenizer.from_pretrained(model_path)
config = AutoConfig.from_pretrained(model_path, num_labels=3)
model = AutoAdapterModel.from_pretrained(model_path, config=config)
adapter_name = "trouble_shooting"
# 添加一个新的adapter,类型为Bn adapter,即bottleneck adapter
config = BnConfig(mh_adapter=True, output_adapter=True, reduction_factor=16, non_linearity="relu")
model.add_adapter(adapter_name, config=config)
# 添加一个分类头
model.add_classification_head(adapter_name,num_labels=3, activation_function="relu")
# 激活这个adapter
model.train_adapter(adapter_name)
training_args = TrainingArguments(
num_train_epochs=5,
per_device_train_batch_size = 16,
logging_steps=2,
save_steps = 10,
gradient_accumulation_steps = 4,
output_dir="/LLM/BERT/bert-adapter",#存储路径
)
trainer = AdapterTrainer (
model=model, tokenizer=tokenizer,
args=training_args, train_dataset=train_dataset,
optimizers=(optimizer, None)
)
trainer.train() # 开始训练
#推理时加载本地路径下的adapter,将其激活
model.load_adapter("/LLM/BERT/bert-adapter", set_active=True)
- 前缀微调
在预训练语言模型输入序列前,添加可训练、任务特定的前缀(任务特定向量序列),不同任务保存不同前缀,实现针对不同任务的微调,节省存储空间和成本。
适当的上下文可以在不改变语言模型参数的情况下引导模型。如想要生成“微调”,可以在输入中加入词语的常见搭配“前缀”,模型就会更倾向于生成这个词。我们希望找到一个可以引领语言模型解决自然语言生成任务的上下文,可以影响对输入x的编码,指导从x中提取怎样的信息;亦可以引导下一个token的分布来影响输出y的生成。
这样的上下文是否存在并不清楚,本方法选择优化指令作为连续的词嵌入,效果可以被传播到所有transformer激活层,也可以向右传播到后续标记。比离散提示有更丰富的表达指令,优化了前缀的所有层。
前缀本身是连续可微的虚拟标记,易于优化。在输入前面加一个前缀,形成新的输入序列,对于自回归LM为,z=[PREFIX;x;y],引导生成下文;对于编码器-解码器LM,encoder和decoder均前置一个前缀,z=[PREFIX;x;PREFIX’;y],encoder增加前缀为了引导输入部分编码,decoder增加前缀是引导后续token生成。所有的激活hi都是在可训练的矩阵中提取的,其余激活由transformer计算得到。
直接更新参数会导致优化不稳定和性能下降,需要通过一个较小的矩阵和一个大型前馈神经网络实现,较小的矩阵由相同的行数维度(即前缀长度),但是列数维度不同。训练完后可以丢弃重新参数化的参数,仅保留前缀。
实验验证,只调整embedding层的表现力不够,每层都要加入prompt的参数。
前缀调优是模块化的,可以根据不同用户提供单独前缀,仅根据该用户数据进行训练,避免数据交叉污染,也可以在单个批次中处理来自多个用户的示例。
对 BART(参数规模小于 1B)的模型应用了前缀微调来处理不同的生成任务,仅训练 0.1% 的参数即可达到接近完整微调的性能,其中使用的提示长度范围从 10 到 200 个 token。 - 提示调整
通过提示与模型交互,核心在于将下游任务转化为预训练任务。
在预训练模型输入中引入可学习嵌入向量作为提示,提示向量在训练过程中更新,指导模型输出更适合特定任务的响应。旨在通过改变输入提示,获得更好的建模结果。软提示调整连接输入标记的嵌入和可训练张量,该张量通过反向传播进行优化,提高目标任务的建模性能。软提示由反向传播获得,根据来自标记数据集的损失反馈进行调整。
比如对于一个可以回答通用问题的模型,希望可以更好地回答旅游相关的问题,原始输入是“夏天最好的旅游景点是哪里?”在输入句子中添加额外Token,比如[TRAVEL],改变输入。token可训练,通过训练,让模型更好的理解这是关于旅游的问题。
假设原始输入是X=[x1,x2,…,xm],在前面添加p个Prompt Token,形成新的输入X’=[x1’,x2’,…xp’;x1,x2,…,xm]。仅通过训练输入层的Prompt Token来适配下游任务,不需要加入MLP解决难训练问题。适用于任务复杂度低或者数据量少的下游任务。
对提示长度(1 到 150 个 token)和模型规模(10M 到 11B 参数)的消融研究表明,随着模型规模的增加,提示微调变得更加高效。例如,在 T5-11B 上进行提示微调时,无论使用 5 个还是 150 个软提示 token,都能实现相同的 SuperGLUE性能。随着模型规模的增加,微调效率提高得更快。T5-large 的性能在提示长度达到 20 token 或 有20K 可训练参数(0.002%)时趋于饱和,T5-XL 的性能在提示长度 5 时就已饱和,同样对应 20K 可训练参数(0.0002%)。然而,只有当模型规模达到 10B 级别时,提示微调才可以与完整微调相媲美。此外,由于 Transformer 具有二次复杂度,序列长度增加 20-100 个 token 可能会显著增加计算量。软提示在参数效率方面表现出色,但会带来推理开销,并且更适用于较大规模的模型。
实验证明,随着预训练模型参数的增加,最简单的设置也能达到极好的效果。模型参数达到一定量级时,Prompt 长度为1也能达到不错的效果,Prompt 长度为20就能达到极好效果。
注:前缀调整的可学习前缀,更多地用于提供输入数据的直接上下文信息,作为模型的一部分,影响整个模型的行为。
提示调整,模仿自然语言中的提示形式,将可学习向量设计为模型针对特定任务,生成特定类型的输出引导,常被视为指导信息的一部分。 - P-Tuning基于提示的微调
与提示调整类似,使用某种形式的“提示”或“指导”来引导模型输出,以适应特定任务。将Prompt转换为可以学习的Embedding层,用MLP+LSTM的方式来对Prompt Embedding进行一层处理。如果随机初始化virtual token,容易优化到局部最优值,通过prompt encoder生成prompt,用LSTM+MLP去编码virtual token,再输入到模型使用伪prompt和反向传播更新encoder。
新的输入序列为X’=[x1,…,xi,x1’,x2’,…,xp’,xi+1,…,xm],在输入序列的关键位置插入可训练的 Token,使得模型能够更有效地捕捉任务相关信息。
在网络结构中使用embedding层加上基于双层LSTM和ReLU激活的MLP实现。使用可训练的LSTM模型,即提示编码器(prompt_encoder)动态生成虚拟标记嵌入,根据输入数据生成不同的嵌入,适用于需要精细控制和理解复杂上下文的任务,常用于自然语言理解任务(NLU)。LSTM的优势:- 更好的适应性和灵活性:捕捉输入数据的时间序列,理解和适应故咋的顺序依赖的任务,如文本生成或序列标注。
- 改进的上下文理解:LSTM有循环结构,擅长处理和理解长期依赖关系和复杂上下文信息。
- 参数共享和泛化能力:LSTM参数在多个任务中共享,提高模型泛化能力,减少针对单独任务的训练需求。在提示调整中,每个人物有独立的虚拟标记,影响跨任务的泛化能力。
该方法适用于处理复杂任务和需要细粒度控制的应用场景,特别是需要在输入序列中捕捉特定位置信息的任务,但有较高的计算复杂度和资源需求,使用时根据具体需求和资源限制权衡使用。比如有一个已经训练好的可以生成文章的模型,现在希望它能够生成关于科技的文章。原始输入句子是:“Artificial intelligence is transforming the world.”通过在输入序列中间插入一些 Token,比如 [TECH],让输入变成:“Artificial intelligence [TECH] is transforming the world.” 这些 [TECH] Token 是可训练的,通过训练这些 Token,让模型更好地理解这是一个关于科技的文章。
- P-Tuning v2
P-Tuning 的问题是在小参数量模型上表现差。对P-Tuning的改进,支持参数量330M~10B规模的多任务tuning。在P-Tuning中,连续提示插入在输入序列的嵌入层中,除了语言模型的输入层,其他层的提示嵌入来自上一层。这种设计限制了优化参数的数量,而且当模型层数很深的时候,微调时会影响模型稳定性。
对此的改进是在多层插入连续提示Prompts tokens,且互相独立,增加了可训练的参数量,加到更深层结构中的Prompt也可以给模型预测带来更直接的影响。主要区别:- 删除重参数化编码器,对于NLU任务没有使用像MLP的Reparameterization。
- 针对不同任务采用不同的提示长度。提示长度在提示优化方法的超参数搜索中起着核心作用。在实验中,我们发现不同的理解任务通常用不同的提示长度来实现其最佳性能
- 回归传统的分类标签范式,而不是标签词映射器。标签词映射器(Label Word Verbalizer)一直被视为是提示优化的核心组成部分,它将one-hot类标签变成有意义的词,以利用预训练语言模型头。它阻碍了提示调优在需要无实际意义的标签和句子嵌入的场景中的应用。因此,采用随机初始化的linear head最为分类头,增强通用性,适配到序列标注任务。
- Prompt长度对于简单分类任务小于20,对于像序列标注这样的复杂任务需要100左右。
微调后的效果如何测试
评估大模型效果,需要选择合适的任务、基线方法、模型规模和计算资源,并且使用下游任务性能、计算效率、显存效率等多种指标进行全面评估。
- 确定具体下游任务
微调后的模型需要在具体的任务上测试,常见任务包括:自然语言理解(NLU)、文本分类、机器翻译、文本摘要、问答系统、对话生成等。 - 选择比较方法
为评估微调效果,需要一个完整的基线模型baseline,为下游任务性能和效率度量在相同任务上的表现提供一个参考点。- 零样本学习(Zero-shot Learning):不进行任何微调,直接用预训练模型推理。
- 完整微调(Full Fine-tuning):更新整个模型参数。
- 参数高效微调(PEFT):如 LoRA、Prefix Tuning、Adapter、Prompt Tuning 等方法。
与未微调的预训练模型或其他基线模型对比,评估微调效果。
进行消融实验,移除某些模块或特征,评估不同模块性能的影响。
- 选择模型规模与计算资源
不同规模模型在微调后表现不同,可以对比不同模型,如小型模型(10M, 100M 参数),中型模型(1B, 3B 参数),大型模型(10B, 100B 参数)。
计算资源:使用 GPU/TPU 进行训练,记录计算时间、内存占用等。
超参数:微调的 batch size、学习率、训练步数(steps)等都会影响最终效果。 - 选择下游指标
不同任务的下游性能通常使用不同的评估指标。- 分类任务:准确率;F1分数,综合评估分类模型的准确率和召回率,适用于类别不平衡的数据集。
- 机器翻译:BLEU分数(Bilingual Evaluation Understudy),机器翻译结果越接近专业人工翻译的结果越好,判断两个句子的相似程度,比较机器翻译输出与一个或多个参考翻译之间的n-gram重叠;NIST(National Institute of standards and Technology),在BLEU方法上的改进,引入了信息量的概念。
pn表示n-gram的精确率,wn表示权重,通常为均匀分布,BP是惩罚因子。
METEOR,将词汇进行匹配,除精准匹配外,还考虑基于词干、同义词的匹配,引入惩罚机制处理单词顺序的差异和未匹配单词的数量。引入chunk块(候选译文和参考译文能够对齐的、空间排列上连续的单词形成一个 chunk)评价句子流畅性,chunk越少则每个chunk平均长度越长,语序越一致。
- 文本摘要:ROUGE分数(Recall-Oriented Understudy for Gisting Evaluation),将模型生成的文本摘要与人工编写的参考摘要进行比较,计算两个摘要之间匹配的n-gram的数量,与人工生成的参考文本总数进行比较。
ROUGE-N:基于 n-gram 匹配的召回率,匹配的n-gram数除以参考文本总n-gram数
ROUGE-L:基于最长公共子序列的匹配。找到生成文本和参考文本间最长的连续子序列(可以跳过单词,但要保持顺序)除以参考文本总长度。为了捕捉顺序和语义上的相似性。
ROUGE-S:基于跳跃n-gram匹配。考虑不连续但有顺序的单词对,除以总数。
ROUGE-W:加权最长公共子序列,与ROUGE-L类似,但是对连续匹配的子序列更高的权重,强调连续匹配的部分。 - 问答系统:EM(exact match)、F1分数
- 开放式生成:
PPL(Perplexity),衡量语言模型预测测试文本的能力,越低越准确。如果模型的 PPL 为 k ,可以理解为模型在预测每个单词时,平均有 k 个候选单词需要考虑。
W表示测试文本,wi表示文本中第i个单词,P计算模型预测第i个单词的概率,N是测试文本的总单词数。
BERTScore 利用预训练的BERT模型对候选文本和参考文本进行编码,得到每个单词的上下文表示向量。对每一对单词计算向量间的余弦相似度,得到相似度矩阵。对于生成文本中的每一个词,选择与其最相似的参考文本中的词,计算相似度最大值作为precision;对于参考文本中的每一个词,选择与其最相似的生成文本中的词,计算相似度最大值作为recall。根据precision和recall计算f1-score。
不同微调方式也有不同计算效率指标:训练时间、推理时间、显存占用、可训练参数量。
额外评估指标:泛化性能、鲁棒性、公平性。
也可以人工进行评估,对生成任务进行人工打分,评估生成质量,或者通过用户反馈评估模型实际效果。
模型的评估通常会综合考虑不同维度的指标,经研究表示,存储效率、内存效率、计算效率、推理开销和下游性能指标(例如准确性),这些指标相互关联,但沿着其中一个维度的改进并不一定转化为沿着其他维度的改进。
对结果可以利用表格进行可视化,如下图,该图考虑了可训练参数的数量、GPU内存消耗、训练和推理吞吐量。