在深度学习中,微调是用于提高预训练模型性能的重要技术。除了对ChatGPT 进行微调之外,还有许多其他预训练模型可以进行微调。

PEFT是什么

PEFT(Parameter-Efficient Fine-Tuning)是huggingface的一款开源工具,可以高效地对大型模型进行微调。它集成了4 种微调大型模型的方法。通过微调少量参数,可以达到接近微调所有参数的效果。使得即使在GPU资源不足的情况下也可以对大型模型进行微调。

微调方法

微调可分为完全微调和复用两种方法:

• Full Fine-tuning:Full Fine-tuning 是指对整个预训练模型进行微调,包括所有模型参数。在这种方法中,预训练模型的所有层和参数都会被更新和优化,以满足目标任务的需求。这种微调方法通常适用于任务与预训练模型差异较大的情况,或者任务对模型灵活性和适应性要求较高的情况。 Full Fine-tuning需要更大的计算资源和时间,但可以获得更好的性能。 • 部分微调(Repurpose):部分微调是指微调过程中仅更新模型的顶层或少数层,而预训练模型的底层参数保持不变。这种方法的目的是通过微调顶层来适应特定任务,同时保留预训练模型的一般知识。当目标任务与预训练模型有一定相似性,或者任务数据集较小时,重新利用通常适用。由于只更新了少数层,Repurpose 比Full Fine-tuning 需要更少的计算资源和时间,但在某些情况下性能可能会降低。

微调预训练模型的方法:

• 微调所有层:参与微调预训练模型的所有层以适应新任务。 • 微调顶层:仅微调预训练模型的顶层以适应新任务。 • 冻结底层:保持预训练模型的底层不变,仅对顶层进行微调。 • 逐层微调:从底层开始,对预训练模型逐层微调,直至所有层都微调完毕。 • 迁移学习:将预训练模型的知识迁移到新任务中,以提高模型性能。这种方法通常使用微调顶层或冻结底层。

Fine tuning

经典的微调方法涉及使用少量特定于任务的数据继续训练预训练模型。在此过程中,预训练模型的权重会更新以更好地适应任务。所需的微调量取决于预训练语料库和特定任务语料库之间的相似性。如果两者相似,则可能只需要少量的微调。如果两者不相似,可能需要更多的微调。

Prompt Tuning(P-tuning)

Prompt Tuning是Google在2021年的论文《The Power of Scale for Parameter-Efficient Prompt Tuning》中提出的一种微调方法。在参数高效的微调方法中,最简单的方法是Prompt Tuning(也就是我们常说的P-Tuning)。通过固定模型前馈层参数,仅更新部分嵌入参数,可以实现大型模型的低成本微调。

经典的提示调优方法不涉及对底层模型的任何参数更新。相反,它专注于制作可以指导预训练模型产生所需输出的输入线索或模板。主要结构是使用提示编码器(BiLSTM+MLP)对一些伪提示(离散标记)进行编码,然后将它们与输入嵌入进行拼接。同时利用LSTM进行Reparamerization加速训练,并引入少量用于自然语言提示的锚字符(Anchor)。如英国)进一步增强效果。然后结合(大写,英国)生成结果,然后优化生成的编码器部分。

然而,P-tuning v1 有两个显着的缺点:任务上不通用,规模上不通用。在一些复杂的自然语言理解NLU任务上效果很差。同时,预训练模型的参数数量不能太少。具体效果论文中提到了以下几点:

• Prompt 长度的影响:当模型参数达到一定程度时,Prompt 长度为1 可以取得很好的效果,Prompt 长度为20 可以取得很好的效果。 • 提示初始化方法的影响:Random Uniform 方法明显弱于其他两种方法,但当模型参数达到一定水平时,这种差异不再存在。 • 预训练方法:LM Adaptation 方法效果很好,但当模型达到一定规模时,差异几乎消失。 • 微调步数的影响:当模型参数较小时,步数越多,效果越好。同样,当模型参数达到一定规模时,零射击也能取得不错的效果。当参数达到100亿时,效果与全参数微调方法相同。代码示例:

from peft import PromptTuningConfig, get_peft_modelpeft_config=PromptTuningConfig(task_type='SEQ_CLS', num_virtual_tokens=10)model=AutoModelForCausalLM.from_pretrained(model_name_or_path, return_dict=True)model=get_peft_model(model, peft_config)

Prefix Tuning

2 202 年提出1 篇论文《Prefix-Tuning: Optimizing Continuous Prompts for Generation》前缀调整方法。与更新所有参数的Full-finetuning方法不同,该方法在输入token之前构造一个与任务相关的虚拟token作为Prefix,然后在训练时只更新Prefix部分的参数,而Transformer中的其他参数是固定的。

与微调相比,前缀调优技术仅优化模型调整过程中可以学习到的连续任务特定向量(前缀)的一小部分,而不是整个模型的参数。这种方法其实和构造Prompt类似,只不过Prompt是人为构造的“显式”提示,无法更新参数,而Prefix是一种“隐式”提示,可以学习。手动尝试寻找最优提示相当于大海捞针,所以就有了自动离散提示搜索的方法,但是提示是离散的,而神经网络是连续的,所以找到的最优提示可能是次优的。

代码示例:

peft_config=PrefixTuningConfig(task_type='CAUSAL_LM', num_virtual_tokens=20)model=AutoModelForCausalLM.from_pretrained(model_name_or_path, return_dict=True)model=get_peft_model(model, peft_config) 在P-tuning的支持下,GPT可以达到甚至超过BERT NLU 领域的表现。下图是详细对比:

P-tuning v2

V2版本主要基于P-tuning和prefix-tuning技术,并引入Deep Prompt Encoding和Multi-task Learning等策略进行优化。实验表明,仅通过微调0.1%的参数,在330M到10B不同参数尺度的LM模型上就可以实现与Fine-tuning相当的性能。

论文《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 这两种方法在序列标记任务上都表现不佳。

从v1到v2的可视化:蓝色部分是参数冻结,橙色部分是可训练部分。可以看到,在右侧的p-tuning v2中,在序列的前端添加了连续提示,并在每一层添加了可训练的提示。在左侧的v1 模型中,仅在输入嵌入中插入提示会导致可训练参数受到句子长度的限制。此外,P-Tuning v2 还包括以下改进:

• 删除了重新参数化加速训练方法; • 使用多任务学习优化:根据多任务数据集的Prompt进行预训练,然后适配下游任务。 • 放弃使用Verbalizer进行词汇映射,复用[CLS]和字符标签,像传统finetune一样使用cls或token的输出做NLU,以增强通用性并可以适应序列标注任务。 P-Tuning v2的几个关键设计因素:

• 重新参数化:Prefix Tuning 和P-tuning 都具有MLP 来构造可训练的嵌入。论文发现,在自然语言理解领域,面对不同的任务和不同的数据集,这种方法可能会带来完全相反的结论。 • 提示长度:最合适的提示长度对应不同的任务。例如,length=20 最适合简单的分类任务,而复杂的任务则需要更长的Prompt Length。 • 多任务学习多任务对于P-Tuning v2 来说是可选的,但可以利用它来提供更好的初始化,以进一步提高性能。 • 分类头使用LM 头来预测动词是Prompt Tuning 的核心,但我们发现这在完整的数据设置中不是必需的,并且这样做与序列标记不兼容。 P-tuning v2 使用与BERT 相同的方法,在第一个token 处应用随机初始化的分类标头。代码示例:

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)

AdaLoRA

预训练语言模型中不同的权重参数有不同的效果对下游任务的贡献不同。因此,需要更智能地分配参数预算,以便那些对模型性能贡献更大的参数在微调过程中能够更高效地更新。

具体来说,通过奇异值分解将权重矩阵分解为矩阵,并且根据新的重要性度量动态调整每个矩阵中奇异值的大小。这使得在微调过程中仅更新那些对模型性能有显着贡献或必要的参数,从而提高模型性能和参数效率。

代码示例:

peft_config=AdaLoraConfig(peft_type='ADALORA', task_type='SEQ_2_SEQ_LM', r=8, lora_alpha=32, target_modules=['q', 'v'], lora_dropout=0.01)model=AutoModelForCausalLM.from_pretrained(model_name_or_path, return_dict=true)model=get_peft_model(model, peft_config)

GPT4模型微调分类

总结!微调大型模型的常用方法

1. Adapter-based Methods(基于适配器的方法):

《Parameter-Efficient Transfer Learning for NLP》 提出了一种针对BERT 的PEFT 微调方法,拉开了PEFT 研究的序幕。他们指出,当面对具体的下游任务时,进行Full-Fintuning(即对预训练模型中的所有参数进行微调)效率太低;如果使用固定预训练模型的某些层,则只需要进行微调。那些接近下游任务的参数层很难取得更好的结果。

于是他们设计了如下图所示的Adapter结构,并将其嵌入到Transformer结构中。训练时,固定了原来预训练模型的参数,只对新的Adapter结构进行微调。同时,为了保证训练的效率(即引入尽可能少的参数),他们设计了这样的结构的Adapter:

首先,向下投影层将高维特征映射到低维特征;然后,经过非线性层后,使用上投影结构将低维特征映射回原始高维特征;同时,一个skip-连接结构保证了它在最坏的情况下能够退化为恒等式(类似于残差结构)。

这种方法节省了资源,因为它不需要对整个模型进行微调。示例包括AdapterDrop、并行适配器、剩余适配器等。

2. Prompt-based Methods(基于提示的方法):

该分支重点关注使用连续提示(例如嵌入向量)来调整模型的行为,而不是直接修改模型的权重。这种类型的方法通常用于生成任务,例如文本生成。提示可以被视为模型输入的一部分,它们经过训练以激励模型产生特定的输出。例子包括前缀调优、提示调优等。参见上面的介绍。

3. Low-rank Adaptation(低秩适配):

低秩自适应方法努力将模型权重的变化限制在低秩子空间内。这通常涉及分解模型的权重矩阵并仅微调其参数的一小部分。这有效地减少了计算资源的消耗,同时仍然允许模型足够灵活地学习新任务。 LoRA 及其变体,例如Q-LoRA、Delta-LoRA、LoRA-FA 等都属于这一类。

4. Sparse Methods(稀疏方法):

此分支包含仅更新模型中一小部分参数的方法。这些参数被选择为最有可能影响任务性能,而其他参数保持不变。稀疏方法的优点是它们通常更有效地使用资源。比如有Intrinsic SAID、Fish Mask、BitFit等。

5. Others(其他方法):

该分支可能包括不易分类为上述任何类别的其他方法,或结合多种技术的混合方法。这些方法可能包括具体的结构改变、算法优化等,以提高微调过程的效率或有效性。

大模型微调步骤总结

如上所述,大模型微调的方法有很多种,每种方法都会有不同的微调过程、方法、准备工作和周期。然而,大多数大型模型的微调都有以下主要步骤,并需要相关准备:

准备数据集:

收集并准备与目标任务相关的训练数据集。确保数据集质量和标注准确性,并进行必要的数据清理和预处理。

选择预训练模型/基础模型:

根据目标任务的性质和数据集的特点选择合适的预训练模型。

设定微调策略:

根据任务要求和可用资源选择适当的微调策略。考虑是进行全面微调还是部分微调,以及微调的程度和范围。

设置超参数:

确定微调过程中的超参数,如学习率、batch size、训练轮数等。这些超参数的选择对微调的性能和收敛速度有重要影响。

初始化模型参数:

根据预训练模型的权重初始化微调模型的参数。为了进行充分的微调,所有模型参数都是随机初始化的;对于部分微调,仅随机初始化顶层或少数层的参数。

进行微调训练:

使用准备好的数据集和微调策略来训练模型。训练过程中,根据设定的超参数和优化算法,逐步调整模型参数,使损失函数最小化。

模型评估和调优:

在训练过程中,定期使用验证集对模型进行评估,并根据评估结果调整超参数或微调策略。这有助于提高模型的性能和泛化能力。

用户评论

万象皆为过客

哇,这篇总结太到位了!我之前在调模型的时候一直摸不着头脑,现在一看这些方法,感觉豁然开朗。

    有12位网友表示赞同!

致命伤

这个总结太实用了,我正准备微调一个大型模型,这些方法给了我很多灵感。

    有13位网友表示赞同!

拉扯

哈哈,看到标题我就觉得这文章肯定有用,果然没失望!微调大型模型的方法总结得挺全面的。

    有9位网友表示赞同!

惯例

感觉微调大型模型是个技术活,这篇文章总结的方法挺多,但感觉还是有些复杂啊。

    有10位网友表示赞同!

伪心

看了这篇文章,我才发现原来微调大型模型有这么多方法,以前真是小看了这个领域。

    有8位网友表示赞同!

命里缺他

总结得不错,但是感觉有些方法我可能一时半会儿也用不上,有点难度。

    有6位网友表示赞同!

漫长の人生

这篇文章真不错,把微调大型模型的方法都讲清楚了,就是字数有点多,得慢慢看。

    有14位网友表示赞同!

青楼买醉

微调大型模型的常用方法总结得很好,特别是那个“渐进式微调”的方法,我一直想知道。

    有19位网友表示赞同!

ヅ她的身影若隐若现

这篇文章真的帮了我大忙,之前微调模型的时候遇到了很多问题,现在感觉思路清晰了。

    有6位网友表示赞同!

落花忆梦

感觉微调大型模型挺有挑战性的,这篇文章总结的方法挺全面的,但实际操作起来还是有点难。

    有12位网友表示赞同!

回忆未来

总结得很好,但是我觉得标题应该加上“入门级”或者“新手必看”,这样对新手来说更友好。

    有16位网友表示赞同!

无关风月

这篇文章对我来说很有启发,微调大型模型的方法真的很多样,以后可以尝试更多。

    有8位网友表示赞同!

_心抽搐到严重畸形っ°

微调大型模型的方法总结得不错,但是感觉文章里的代码部分有点少,希望作者能补充一下。

    有7位网友表示赞同!

暮染轻纱

这篇文章让我对微调大型模型有了更深的认识,感觉之前的方法都不太行。

    有14位网友表示赞同!

孤单*无名指

微调大型模型是个耗时的工作,这篇文章总结的方法挺实用的,希望对大家都有帮助。

    有17位网友表示赞同!

安陌醉生

感觉微调大型模型的方法挺多,但是每个方法都要试一试才能知道哪个更适合自己。

    有16位网友表示赞同!

未来未必来

这篇文章给了我很大的帮助,微调大型模型的方法总结得很到位,感谢作者分享。

    有20位网友表示赞同!

苍白的笑〃

微调大型模型的方法总结得真好,但是感觉有些方法可能需要一定的技术基础,对新手不太友好。

    有6位网友表示赞同!

摩天轮的依恋

这篇文章让我对微调大型模型有了新的认识,感觉之前的方法都太简单了,以后可以尝试更复杂的方法。

    有20位网友表示赞同!

゛指尖的阳光丶

微调大型模型的方法总结得挺全面的,但是感觉文章里的实例不够具体,希望能有更详细的案例。

    有10位网友表示赞同!

标签: