代码编织梦想

介绍

在面对特定的下游任务时,如果进行Full FineTuning(即对预训练模型中的所有参数都进行微调),太过低效;而如果采用固定预训练模型的某些层,只微调接近下游任务的那几层参数,又难以达到较好的效果。

PEFT(Parameter-Efficient Fine-Tuning)是一个用于在不微调所有模型参数的情况下,有效地将预先训练的语言模型(PLM)适应各种下游应用的库。PEFT方法只微调少量(额外)模型参数,显著降低了计算和存储成本,因为微调大规模PLM的成本高得令人望而却步。最近最先进的PEFT技术实现了与完全微调相当的性能。

大模型训练的微调方法,包括prompt tuning、prefix tuning、LoRA、p-tuning和AdaLoRA等。

Adapter Tuning

谷歌的研究人员首次在论文《Parameter-Efficient Transfer Learning for NLP》提出针对 BERT 的 PEFT 微调方式,拉开了 PEFT 研究的序幕。下图所示的 Adapter 结构,将其嵌入 Transformer 的结构里面,在训练时,固定住原来预训练模型的参数不变,只对新增的 Adapter 结构进行微调。
在这里插入图片描述
首先是一个 down-project 层将高维度特征映射到低维特征,然后过一个非线形层之后,再用一个 up-project 结构将低维特征映射回原来的高维特征;同时也设计了 skip-connection 结构,确保了在最差的情况下能够退化为 identity。

从实验结果来看,该方法能够在只额外对增加的 3.6% 参数规模(相比原来预训练模型的参数量)的情况下取得和 Full-finetuning 接近的效果(GLUE 指标在 0.4% 以内)。

在这里插入图片描述

Prefix Tuning

在输入 token 之前构造一段任务相关的 virtual tokens 作为 Prefix,然后训练的时候只更新 Prefix 部分的参数,而 Transformer 中的其他部分参数固定。,学习到的前缀指令文本向量可以挖掘大模型的潜力去引导模型完成特定任务。

在这里插入图片描述
Prefix Tuning 和构造 Prompt 类似,只是 Prompt 是人为构造的“显式”的提示,并且无法更新参数,而 Prefix 则是可以学习的“隐式”的提示。

同时,为了防止直接更新 Prefix 的参数导致训练不稳定的情况,在 Prefix 层前面加了 MLP 结构(相当于将 Prefix 分解为更小维度的 Input 与 MLP 的组合后输出的结果),训练完成后,只保留 Prefix 的参数。

embedding = torch.nn.Embedding(num_virtual_tokens, token_dim)
transform = torch.nn.Sequential(
    torch.nn.Linear(token_dim, encoder_hidden_size),
    torch.nn.Tanh(),
    torch.nn.Linear(encoder_hidden_size, num_layers * 2 * token_dim),
)

PREFIX_TUNING 需要设置一个参数,即 指令文本的长度 num_virtual_tokens,研究表明这个数量一般设置在10-20之间。不同的任务所需要的 Prefix 的长度有差异。

```python
model_name_or_path = "./unsup-simcse-roberta-base"
peft_type = PeftType.PREFIX_TUNING
peft_config = PrefixTuningConfig(task_type="SEQ_CLS", num_virtual_tokens=20)
lr = 1e-2
model = AutoModelForSequenceClassification.from_pretrained(model_name_or_path, return_dict=True)
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()

Prompt Tuning

论文《The Power of Scale for Parameter-Efficient Prompt Tuning》
只要模型规模够大,简单加入 Prompt tokens 进行微调,就能取得很好的效果。
在这里插入图片描述
固定预训练参数,为每一个任务额外添加一个或多个embedding,之后拼接query正常输入LLM,并只训练这些embedding。左图为单任务全参数微调,右图为prompt tuning。

实验

在这里插入图片描述

  • (a) Prompt 长度影响:模型参数达到一定量级时,Prompt 长度为 1 也能达到不错的效果,Prompt 长度为 20 就能达到极好效果。
  • (b) Prompt 初始化方式影响:Random Uniform 方式明显弱于其他两种,但是当模型参数达到一定量级,这种差异也不复存在。
  • © 预训练的方式:LM Adaptation 的方式效果好,但是当模型达到一定规模,差异又几乎没有了。
  • (d) 微调步数影响:模型参数较小时,步数越多,效果越好。同样随着模型参数达到一定规模,zero shot 也能取得不错效果。

文章表明随着模型越大,PROMPT_TUNING 的效果几乎能和模型整个微调的效果一样。形成了只是学习一个指令然后去模型中检索某种能力的范式。

from peft import PromptTuningConfig, get_peft_model

model_name_or_path = "./unsup-simcse-roberta-base"
peft_type = PeftType.PROMPT_TUNING
peft_config = PromptTuningConfig(task_type="SEQ_CLS", num_virtual_tokens=20)
lr = 1e-3
model = AutoModelForSequenceClassification.from_pretrained(model_name_or_path, return_dict=True)
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()

https://huggingface.co/docs/peft/conceptual_guides/prompting

P-Tuning V1

P-Tuning 方法的提出主要是为了解决这样一个问题:大模型的 Prompt 构造方式严重影响下游任务的效果。

在这里插入图片描述

P-Tuning 提出将 Prompt 转换为可以学习的 Embedding 层,只是考虑到直接对 Embedding 参数进行优化会存在这样两个挑战:

  • Discretenes:对输入正常语料的 Embedding 层已经经过预训练,而如果直接对输入的 prompt embedding 进行随机初始化训练,容易陷入局部最优
  • Association:没法捕捉到 prompt embedding 之间的相关关系。
    在这里插入图片描述
    手动尝试最优的提示无异于大海捞针,于是便有了自动离散提示搜索的方法(左图),但提示是离散的,神经网络是连续的,所以寻找的最优提示可能是次优的。p-tuning依然是固定LLM参数,利用多层感知机和LSTM对prompt进行编码,编码之后与其他向量进行拼接之后正常输入LLM。注意,训练之后只保留prompt编码之后的向量即可,无需保留编码器。

作者在这里提出用 MLP+LSTM 的方式来对 prompt embedding 进行一层处理

在这里插入图片描述
需要提醒的是这里的指令文本是伪文本,可能就是 unused1, unused2…等,目前源代码里面就是随机初始化的一个embeding.

self.lstm_head = torch.nn.LSTM(
                    input_size=self.input_size,
                    hidden_size=self.hidden_size,
                    num_layers=num_layers,
                    dropout=lstm_dropout,
                    bidirectional=True,
                    batch_first=True,
  )

self.mlp_head = torch.nn.Sequential(
    torch.nn.Linear(self.hidden_size * 2, self.hidden_size * 2),
    torch.nn.ReLU(),
    torch.nn.Linear(self.hidden_size * 2, self.output_size),
)
self.mlp_head(self.lstm_head(input_embeds)[0])

以上代码可清晰展示出prompt encoder的结构。

P-Tuning V1 与 Prefix-Tuning 的区别:

  • Prefix Tuning 是将额外的 embedding 加在开头,看起来更像是模仿 Instruction 指令;而 P-Tuning 的位置则不固定。
  • Prefix Tuning 通过在每个 Attention 层都加入 Prefix Embedding 来增加额外的参数,通过 MLP 来初始化;而 P-Tuning 只是在输入的时候加入 Embedding,并通过 LSTM+MLP 来初始化。
from peft import PromptTuningConfig, get_peft_model

model_name_or_path = "./unsup-simcse-roberta-base"
peft_type = PeftType.P_TUNING
lr = 1e-3
peft_config = PromptEncoderConfig(task_type="CAUSAL_LM", num_virtual_tokens=20, encoder_hidden_size=128)
model = AutoModelForCausalLM.from_pretrained(model_name_or_path, return_dict=True)
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()

P-Tuning V2

论文《P-Tuning v2: Prompt Tuning Can Be Comparable to Fine-tuning Universally Across Scales and Tasks》
P-Tuning v2 的目标就是要让 Prompt Tuning 能够在不同参数规模的预训练模型、针对不同下游任务的结果上都达到匹敌 Fine-tuning 的结果。

Prompt Tuning 方法可能在这两个方面都存在局限性:

  • 不同模型规模:Prompt Tuning 和 P-tuning 这两种方法都是在预训练模型参数规模够足够大时,才能达到和 Fine-tuning 类似的效果,而参数规模较小时效果则很差。在这里插入图片描述
  • 不同任务类型:Prompt Tuning 和 P-tuning 这两种方法在 sequence tagging 任务上表现都很差。
主要结构

相比 Prompt Tuning 和 P-tuning 的方法,P-tuning v2 方法在多层加入了 Prompts tokens 作为输入,带来两个方面的好处:

  1. 带来更多可学习的参数(从 P-tuning 和 Prompt Tuning 的 0.1% 增加到0.1%-3%),同时也足够 parameter-efficient。

  2. 加入到更深层结构中的 Prompt 能给模型预测带来更直接的影响。

在这里插入图片描述v1到v2的可视化:蓝色部分为参数冻结,橙色部分为可训练部分

几个关键设计因素
  • Reparameterization:Prefix Tuning 和 P-tuning 中都有 MLP 来构造可训练的 embedding。本文发现在自然语言理解领域,面对不同的任务以及不同的数据集,这种方法可能带来完全相反的结论
  • Prompt Length:不同的任务对应的最合适的 Prompt Length 不一样,比如简单分类任务下 length=20 最好,而复杂任务需要更长Prompt Length。
  • Multi-task Learning 多任务对于 P-Tuning v2 是可选的,但可以利用它提供更好的初始化来进一步提高性能。
  • Classification Head 使用 LM head 来预测动词是 Prompt Tuning 的核心,但在完整的数据设置中没有必要这样做,并且这样做与序列标记不兼容。P-tuning v2 采用和 BERT 一样的方式,在第一个 token 处应用随机初始化的分类头。
实验结果
  • 不同预训练模型大小下的表现,在小模型下取得与Full-finetuning相近的结果,并远远优于P-Tuning。
  • 不同任务下的 P-Tuning v2 效果都很好,而 P-Tuning 和 Prompt Learning 效果不好;同时,采用多任务学习的方式能在多数任务上取得最好的结果。
  • Verbalizer with LM head v.s. [CLS] label with linear head,两种方式没有太明显的区别
  • Prompt depth,在加入相同层数的 Prompts 前提下,往更深层网络加效果大部分优于往更浅层网络。
from peft import PromptTuningConfig, get_peft_model

model_name_or_path = "./unsup-simcse-roberta-base"
peft_type = PeftType.P_TUNING
lr = 1e-3
peft_config = PrefixTuningConfig(task_type="SEQ_CLS", num_virtual_tokens=20)
model = AutoModelForSequenceClassification.from_pretrained(model_name_or_path, return_dict=True)
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()

LoRA

现有的一些 PEFT 的方法还存在这样一些问题:

  • 由于增加了模型的深度从而额外增加了模型推理的延时,如 Adapter 方法
  • Prompt 较难训练,同时减少了模型的可用序列长度,如 Prompt Tuning、Prefix Tuning、P-Tuning 方法
  • 往往效率和质量不可兼得,效果差于 full-finetuning

Low-Rank Adaption(LoRA),设计了如下所示的结构,在涉及到矩阵相乘的模块,引入 A、B 这样两个低秩矩阵模块去模拟 Full-finetune 的过程,相当于只对语言模型中起关键作用的低秩本质维度进行更新。
在这里插入图片描述在这里插入图片描述这么做就能完美解决以上存在的 3 个问题:

  • 相比于原始的 Adapter 方法“额外”增加网络深度,必然会带来推理过程额外的延迟,该方法可以在推理阶段直接用训练好的 A、B 矩阵参数与原预训练模型的参数相加去替换原有预训练模型的参数,这样的话推理过程就相当于和 Full-finetune 一样,没有额外的计算量,从而不会带来性能的损失。
  • 由于没有使用 Prompt 方式,自然不会存在 Prompt 方法带来的一系列问题。
  • 该方法由于实际上相当于是用 LoRA 去模拟 Full-finetune 的过程,几乎不会带来任何训练效果的损失,后续的实验结果也证明了这一点。
![在这里插入图片描述](https://img-blog.csdnimg.cn/dbfdf222640b48df9879b545944e4fe1.png)

```python
from peft import PromptTuningConfig, get_peft_model

model_name_or_path = "./unsup-simcse-roberta-base"
peft_type = peft_type = PeftType.LORA
lr = 3e-4
peft_config = LoraConfig(task_type="SEQ_CLS", inference_mode=False, r=8, lora_alpha=16, lora_dropout=0.1)
model = AutoModelForCausalLM.from_pretrained(model_name_or_path, return_dict=True)
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()

https://huggingface.co/docs/peft/conceptual_guides/lora

PETL 大统一

通用形式

在这里插入图片描述通过对 Prefix Tuning 的推导,得出了和 Adapter Tuning 以及 LoRA 形式一致的形式。

更近一步地,可以将这些 Tuning 的方法统一在同一套框架下,
在这里插入图片描述
包括这几大要素:
在这里插入图片描述

典型应用:

  1. ChatGLM-Tuning :一种平价的chatgpt实现方案,基于清华的 ChatGLM-6B + LoRA 进行finetune。
  2. Alpaca-Lora:使用低秩自适应(LoRA)复现斯坦福羊驼的结果。Stanford Alpaca 是在 LLaMA 整个模型上微调,而 Alpaca-Lora 则是利用 Lora 技术,在冻结原模型 LLaMA 参数的情况下,通过往模型中加入额外的网络层,并只训练这些新增的网络层参数。由于这些新增参数数量较少,这样不仅微调的成本显著下降,还能获得和全模型微调类似的效果。
  3. BLOOM-LORA:由于LLaMA的限制,我们尝试使用Alpaca-Lora重新实现BLOOM-LoRA。

参考

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_40959890/article/details/130720273

bernhard‘s talk on towards causal nlp 笔记-爱代码爱编程

因果学习系列笔记 这是我的 GitHub 因果学习笔记仓库 https://github.com/xin007-kong/ryCausalLearning,欢迎 star🤩 讲者是 Bernhard

由浅入深了解 深度神经网络优化算法-爱代码爱编程

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 导言 优化是从一组可用的备选方案中选择最佳方案。优化无疑是深度学习的核心。基于梯度下降的方法已经成为训练深度

认识深度学习中的循环神经网络(rnn、lstm、gru)相关知识-爱代码爱编程

      大家好,我是带我去滑雪!         循环神经网络(Recurrent Neural Network, RNN)是一种用于处理序列数据的神经网络。它的主要特点在于,在处理每个序列输入时,它会维护一个状态,这个状态会被保存下来并传入到下一个输入。 RNN将输入序列分解成时间步长序列,每个时间步长都会将当前的输入和前一个时间步的隐藏状态(如果

百度q1财报发布:文心一言性能提升10倍,大语言模型带来革命性潜力-爱代码爱编程

北京时间5月16日,百度(NASDAQ:BIDU,HKEX:9888)发布了截至2023年3月31日的第一季度未经审计的财务报告。第一季度,百度实现营收311亿元,同比增长10%;归属百度的净利润(non-GAAP)达到57亿元,同比大幅增长48%,营收和利润双双超市场预期。 百度创始人、董事长兼首席执行官李彦宏表示:“我们相信生成式人工智能和大

gpt大语言模型alpaca-lora本地化部署实践【大语言模型实践一】 | 京东云技术团队-爱代码爱编程

模型介绍 Alpaca模型是斯坦福大学研发的LLM(Large Language Model,大语言)开源模型,是一个在52K指令上从LLaMA 7B(Meta公司开源的7B)模型微调而来,具有70亿的模型参数(模型参数越

pytorch lstm和lstmp的原理及其手写复现-爱代码爱编程

PyTorch LSTM和LSTMP的原理及其手写复现 0、前言全部参数的细致介绍代码实现Reference 0、前言 关于LSTM的原理以及公式其实在这篇博客一步一步详解LSTM网络【从RN

使用transformers框架导入t5-爱代码爱编程

前言 在上一篇的博客基于transformer的Seq2Seq机器翻译模型训练、预测教程讲述了怎么训练一个翻译的Seq2Seq模型,这篇博客则来说说怎么使用huggingface中训练好的模型来完成翻译的任务。 环境和模

长短期记忆介绍_长短期记忆模型是谁提出的-爱代码爱编程

长短期记忆(Long Short-Term Memory,简称LSTM)是一种用于处理时间序列数据的循环神经网络(Recurrent Neural Network,简称RNN)模型。它是由德国计算机科学家Sepp Hochreiter和他的同事于1997年提出的,旨在解决标准RNN中存在的梯度消失和梯度爆炸问题,以及长期依赖关系的建模问题。LSTM在自然语

【小沐学nlp】python实现聊天机器人(openai,模型概述笔记)_python创建对话模型-爱代码爱编程

🍺NLP开发系列相关文章编写如下🍺:1🎈【小沐学NLP】Python实现词云图🎈2🎈【小沐学NLP】Python实现图片文字识别🎈3🎈【小沐学NLP】Python实现中文、英文分词🎈4🎈【小沐学NLP】Python实现聊天机