【LLM模型微调】LLMs-PEFT[微调]-LoRA总结笔记v5.0

【导读】:本文是LLM模型微调第五篇,分享论文LoRA:LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS的解读。主要内容有论文解读(提出背景、关键优势,实现步骤…),实验效果,理解低秩更新,细节解析|面经,代码详解和LoRA相关优化。

LoRA-论文解读

【1】LoRA提出的背景~

NLP范式:对通用领域数据的大规模预训练,对特定任务或领域的微调。当finetune大模型时,由于训练成本太高,不太可能重新训练更新所有模型参数。

为解决上述问题,提出多种方案,比如部分finetune,adapters和prompting。以前的方法都或多或少有其它性能问题:

  • Adapters 增加模型层数,引入了额外的推理延迟(Inference Latency);

  • Prefix-tuning 比较难训练,模型性能也并非总是稳步提升,且预留给 Prompt 的序列挤占了下游任务的输入序列空间,影响模型性能,模型效果不如直接 finetune。

  • P-tuning v2 很容易导致旧知识遗忘,微调之后的模型,在之前的问题上表现明显变差。

基于上述背景,论文作者得益于Li等关于(intrinsic dimension)内在维度 的发现:模型是过参数化的,它们有更小的内在维度,模型主要依赖于这个低的内在维度(low intrinsic dimension)去做任务适配。

[Lora中的Low-rank假设]假设模型在任务适配过程中权重的改变量是低秩(low rank)的,由此提出低秩自适应(LoRA)方法,LoRA 允许我们通过优化适应过程中密集层(dense)的低秩分解矩阵,来间接训练神经网络中的一些密集层,同时保持预先训练的权重不变。

LoRA基本思想:在不改变原始预训练权重的基础上,通过引入可训练的低秩分解矩阵来调整模型参数,以适应特定任务或领域。

(Low-Rank Adaptation)LoRA方法通过冻结预训练模型的权重,并将可训练的秩分解矩阵注入到每个 Transformer 架构的层中,从而大大减少了下游任务的可训练参数数量。

We propose Low-Rank Adaptation, or LoRA, which freezes the pretrained model weights and injects trainable rank decomposition matrices into each layer of the Transformer architecture, greatly reducing the number of trainable parameters for downstream tasks.

【2】LoRA的关键优势

  • 【便于高效切换任务】预训练模型可以共享,并用于为不同任务构建多个小型 LoRA 模块。可以冻结共享模型,通过替换图 1 中的矩阵 A 和 B 来高效切换任务,从而显著减少存储需求和任务切换开销。

  • 【LoRA 提高训练效率】通过减少计算梯度或维护优化器状态的需求,将硬件门槛降低了最多 3 倍,因为我们只需优化注入的、规模更小的低秩矩阵。

  • 【简单的线性设计】允许我们在部署时将冻结的权重矩阵W与可训练矩阵ΔW合并 得到微调后的模型,与完全微调的模型相比,几乎不引入额外的推理延迟。

  • LoRA 与许多先前的方法是正交的,可以与许多方法结合使用,如前缀调优。在附录 E 中提供了一个示例。

  • 【提高模型泛化能力】相较于全模型微调,LoRA仅需更新少量参数,对原模型进行了一定程度的正则化,因此可以显著减少计算资源消耗和潜在的过拟合风险,同时保留了预训练模型的泛化能力。

  • 【低秩适应减少参数量】相较于全模型微调,内存和存储使用量大大减少。主要得益于"低秩"性质,使得模型需要学习更新的参数规模大大减少,从原本的d*d减少到d*2r。这里r << d。

【3】LoRA算法的实现步骤

基于上述背景,论文作者得益于Li等关于(intrinsic dimension)内在维度的发现:模型是过参数化的,它们有更小的内在维度,模型主要依赖于这个低的内在维度(low intrinsic dimension)去做任务适配。

[Lora中的Low-rank假设]假设模型在任务适配过程中权重的改变量是低秩(low rank)的,由此提出低秩自适应(LoRA)方法,LoRA 允许我们通过优化适应过程中密集层(dense)的低秩分解矩阵,来间接训练神经网络中的一些密集层,同时保持预先训练的权重不变。

LoRA的实现流程如下:

  • 在原始PLM旁边增加一个旁路,做一个降维A再升维B的操作,来模拟所谓的intrinsic rank。

  • 训练的时候固定PLM的参数,只训练降维矩阵A与升维矩阵B。

  • 而模型的输入输出维度不变,输出时将BA与PLM的参数叠加。h=Wx+BAx。

  • 用随机高斯分布初始化A,用零矩阵初始化B,保证训练的开始此旁路矩阵依然是零矩阵。

可训练层维度和预训练模型层维度一致为d,先将维度d 通过全连接层降维至 r,再从 r通过全连接层映射回 d维度,其中,r<<d,r是矩阵的秩,这样矩阵计算就从d*d变为d * r + r * d=d*2r,参数量减少很多。

举例来解释:假设预训练的权重矩阵W是1k×1k矩阵,那么常规微调中的权重更新矩阵ΔW也是1k×1k矩阵。在这种情况下, ΔW有100万个参数。如果我们考虑LoRA秩为2,那么A是一个1k ×2的矩阵,B是一个2× 1k的矩阵,那么只有2×2×1k=4k个参数需要更新,参数减少了100w/4k=250倍。

【4】LoRA的公式实现| LORA 算法推导

低秩参数化更新矩阵

具体来看,假设预训练的矩阵为 ,它的更新可表示为:

其中秩 r<< min (d, k) 。W0是预训练模型初始化的参数,ΔW就是需要更新的参数。

如果是全参数微调,则ΔW的参数量=W0参数量(如果是GPT3,则ΔW≈175B)。而对于 LORA 来说,只需要微调 ΔW=BA。

在 LoRA 训练过程中,W0是固定不变的,不接收梯度更新,而 A 和 B 包含可训练参数矩阵,是可变的。

在前向计算过程中,W0与ΔW都会乘以相同的输入x,最后相加,从图1可以看出:

【全微调的一般化-AGeneralization of Full Fine-tuning.】

LORA 的这种思想有点类似于残差连接,同时使用这个旁路的更新来模拟full finetuning的过程。并且,full finetuning可以被看做是LoRA的特例(当 r 等于k 时)。

LoRA 更进一步,不要求在适应过程中权重矩阵的累计梯度更新具有完整秩。这意味着当将 LoRA 应用于所有权重矩阵并训练所有偏置时,可以通过将 LoRA 秩 r 设置为预训练权重矩阵的秩,来大致恢复全微调的表达能力。

换句话说,当增加可训练参数的数量时,训练 LoRA 大致收敛于训练原始模型,而基于适配器的方法收敛于 MLP,前缀调整方法则收敛于无法处理长输入序列的模型。

【无额外推理延迟-No Additional Inference Latency】

在部署时,LoRA也几乎未引入额外的推理延迟[inference latency],只需要计算以下改变量并放回原模型即可。

如果想切换任务,只需要切换任务的过程中,减去BA,然后换上用其它任务训练好的B’A’就可以来恢复W0。

【5】将LoRA应用到Transformer

将 LoRA 应用到 Transformer

在原则上,可以将 LoRA 应用到神经网络中的任何权重矩阵子集,以减少可训练参数的数量。

  • 在 Transformer 架构中,自注意力模块中有四个权重矩阵(Wq,Wk,Wv,Wo),以及 MLP 模块中的两个权重矩阵。

  • Wq(或 Wk,Wv)视为一个dmodel****×dmodel** 的矩阵,尽管输出维度通常被切分成多个注意力头。**

  • 将研究限制为仅适应自注意力权重用于下游任务,并冻结 MLP 模块(因此它们在下游任务中不会被训练),以简化和提高参数效率。

进一步研究了在 Transformer 中适应不同类型的注意力权重矩阵的效果,具体见第7.1节。将 MLP 层、LayerNorm 层和偏置的实证研究留待未来工作。

LoRA与Transformer的结合也很简单,仅在QKV attention的计算中增加一个旁路,而不动MLP模块:

We limit our study to only adapting the attention weights for downstream tasks and freeze the MLP modules (so they are not trained in downstream tasks) both for simplicity and parameter-efficiency.

LoRA-实验效果

【1】LoRA论文实验效果

1.基准内容理解上的效果

结论:参数量较全参数微调(Fine-Tuning)显著降低,参数量和现有高效参数微调方法持平或更低。性能优于其它参数高效微调方法,和全参数微调(Fine-Tuning)基本持平甚至更高。

2.生成任务上的效果

结论:和前面内容理解的结论基本一致,LORA 参数量更少,但是性能和全参数微调相当。

3.在GPT3上的测试效果

前面 RoBertabase 和 GPT-2都是参数量比较小的模型,而在大模型 GPT-3 175B 上,LORA 也取得了不错的效果(见上图)。在WikiSQL和MutiNLI-matched任务上,当增加微调方法的可训练参数量时,其它微调方法都出现了性能下降的现象,只有 LORA 的性能保持了稳定,见下图:

结论:LORA 相比其它微调方法,增加参数量不会导致性能的下降。性能上与全参数微调持平甚至超过。

LoRA-理解低秩更新

【#】大模型微调经验文章目录

【1】如何选择适应 LoRA 的权重类型?

在有限的参数预算下,应如何选择适应 LoRA 的权重类型以在下游任务中获得最佳性能?

根据第4.2节的描述,仅考虑自注意力模块中的权重矩阵。在 GPT-3 175B 上设定了一个参数预算为 18M(如果以 FP16 存储,约为 35MB),这对应于当我们适应一种类型的注意力权重时的秩r=8,或者当适应两种类型的注意力权重时的秩r=4,适用于所有 96 层。结果如表5所示。

**需要注意的是,将所有参数都放入ΔWq 或ΔWk 中会导致性能显著降低,而适应Wq 和Wv 两种权重矩阵则能获得最佳结果。**这表明,即使r=4的矩阵已经能够捕捉到足够的信息,因此,适应更多的权重矩阵比适应单一类型的权重矩阵(即使秩更高)更为有效。

【2】LORA最优的秩r是多少?

关注秩 r 对模型性能的影响,比较以下几种情况:适应 {Wq, Wv}、{Wq, Wk, Wv, Wc} 和仅适应 Wq。

**实验结果显示,对于一般的任务,r=1,2,4,8 就足够了。而一些领域差距比较大的任务可能需要更大的 r。**同时,增加 r 值变大并不能提升微调的效果,这可能是因为参数量增加需要更多的语料。

即使在r 很小的情况下,LoRA 的性能依然很好(尤其是对于 {Wq, Wv} 而非仅 Wq)。这表明,更新矩阵ΔW 可能具有非常小的“内在秩”。为求证此假设,作者需要计算不同秩对应的子空间之间的重叠程度。

论文中的图,得出来r=8 和 r=64中的top奇异向量重叠得最多(颜色越小表示相似程度越高),也就是说top奇异向量的作用最大,其他的奇异可能会引入更多的噪声。这证明了更新参数矩阵Δ𝑊存在极小的‘内在秩’。

【3】适应矩阵 ΔW 与 W 的比较

LoRA-细节解析|面经

【1】LoRA微调经验碎碎念

LoRA是否适用于领域适应?答:LoRA通常用于引导LLM遵循指令,而不是从预训练数据集中吸收知识。在内存有限时,可以用LoRA对特定领域数据集进行进一步的预训练。

LoRA 高效微调如何避免过拟合?过拟合还是比较容易出现的。减小r或增加数据集大小可以帮助减少过拟合,还可以尝试增加优化器的权重衰减率或LoRA层的dropout值。

LoRA微调方法为啥能加速训练? 1)只更新了部分参数:比如LoRA原论文就选择只更新Self Attention的参数,实际使用时我们还可以选择只更新部分层的参数;2)减少了通信时间:由于更新的参数量变少了,所以(尤其是多卡训练时)要传输的数据量也变少了,从而减少了传输时间;3)采用了各种低精度加速技术,如FP16、FP8或者INT8量化等。LoRA的优点是它的低秩分解很直观,在不少场景下跟全量微调的效果一致,以及在预测阶段不增加推理成本。

是否可以逐层调整LoRA的最优rank? 理论上,可以为不同层选择不同的LoRA rank,类似于为不同层设定不同学习率,但由于增加了调优复杂性,实际中很少执行。

LoRA 微调参数量怎么确定? LoRA 模型中可训练参数的结果数量取决于低秩更新矩阵的大小,其主要由秩 r 和原始权重矩阵的形状确定。实际使用过程中,通过选择不同的 lora_target 决定训练的参数量。

以 LLama 为例: --lora_target q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj

如何选择最佳的LoRA的参数r? rank的取值比较常见的是8,理论上说rank在4-8之间效果最好,再高并没有效果提升。不过论文的实验是面向下游单一监督任务的,因此在指令微调上根据指令分布的广度,rank选择还是需要在8以上的取值进行测试。选择LoRA的参数r是个需要实验的超参数,太大可能导致过拟合,太小可能不足以处理数据集中的任务多样性。

alpha参数 如何选取? alpha用于缩放学习到的权重。现有文献,包括原始LoRA论文,通常建议将Alpha固定在16,而不是将其作为可调超参数进行调整。本质和learning rate相同,好的经验法则是默认让alpha=2*rank,只调整lr,这样可以简化超参。

LoRA 高效微调如何避免过拟合?过拟合还是比较容易出现的。减小r或增加数据集大小可以帮助减少过拟合,还可以尝试增加优化器的权重衰减率或LoRA层的dropout值。

LoRA权重是否可以合并? 可以将多套LoRA权重合并。训练中保持LoRA权重独立,并在前向传播时添加,训练后可以合并权重以简化操作。训练好的低秩矩阵(B*A)+原模型权重合并(相加),计算出新的权重。

如何在已有LoRA模型上继续训练?理解此问题的情形是:已有的lora模型只训练了一部分数据,要训练另一部分数据的话,是在这个lora上继续训练呢,还是跟base 模型合并后再套一层lora,或者从头开始训练一个lora?

把之前的LoRA跟base model 合并后,继续训练就可以,为了保留之前的知识和能力,训练新的LoRA时,加入一些之前的训练数据是需要的。每次都要重头训练的话成本比较高。

是否需要在所有层启用LoRA?目前的论文研究仅限于在Attention的query和key权重矩阵启用LoRA以及在所有层启用。未来的实验可以探索其他层组合的影响。如果你在使用 LoRA,确保它应用于所有层,而不仅仅是 Key 和 Value 矩阵,以最大化模型性能。

哪些因素会影响内存使用?答:内存使用受到模型大小、批量大小、LoRA参数数量以及数据集特性的影响。例如,使用较短的训练序列可以节省内存。

其他优化器如何?答:除了Adam和AdamW,其他优化器如Sophia也值得研究,它使用梯度曲率而非方差进行归一化,可能提高训练效率和模型性能。

ChatGLM-6B LoRA后的权重多大?rank=8 target_module=query,key,value条件下,大约15M。

LoRA与完全微调或RLHF比较如何?答:虽未进行RLHF实验,但全微调需要更多资源,且可能因过拟合或非理想超参数而性能不佳。

LoRA这种微调方法和全参数比起来有什么劣势吗?LoRA中参与训练的参数量较少,解空间较小,效果相比全量微调有一定的差距。

LORA应该作用于Transformer的哪个参数矩阵?

1)将所有微调参数都放到attention的某一个参数矩阵的效果并不好,将可微调参数平均分配到 Wq 和 Wk 的效果最好;2)即使是秩仅取4也能在 ∆W 中获得足够的信息。在实际操作中,应当将可微调参数分配到多种类型权重矩阵中,而不应该用更大的秩单独微调某种类型的权重矩阵。

Lora的矩阵怎么初始化?

  • 如果A用随机高斯分布初始化,B用0矩阵初始化,那么可以保证训练的开始右旁路矩阵∆W依然是0矩阵,这样模块的输出就基本上来自于左路W,也就是大模型原有参数的计算结果,这使得模型优化的初始点和原始的大模型保持一致。

  • 如果将A和B都用0矩阵初始化,那么在训练初期,模型将无法学习到有效的特征表示,很容易导致梯度消失,导致微调效果不佳。

  • 如果A用0矩阵初始化,B用随机高斯分布初始化,那么在训练初期,模型可能会受到B矩阵的干扰,导致训练过程不稳定,甚至可能损害模型的性能。

  • 如果B,A全部随机高斯分布初始化,那么在网络训练刚开始就会有概率为得到一个过大的偏移值ΔW 从而引入太多噪声,导致难以收敛。

总之,LoRA中降维矩阵A和升维矩阵B的初始化方式是为了保持模型的表达能力,同时减少对原始模型参数的影响,从而提高微调的效率和稳定性。在实际应用中,可以根据具体任务和模型需求,对初始化方式进行适当调整。

【2】补充:为什么矩阵B、A不能同时为0

请教一下,最后补充的问题,回答了AB不能同时初始化为0的原因。那为什么选择A高斯初始化,B为0初始化,而不选择A初始化为0,B选择高斯初始化呢?

结论是:如果都随机初始化,可能导致ΔW数值大,引起模型崩溃无法学习。具体可以参考这个:https://github.com/microsoft/LoRA/issues/98

【参考链接】https://zhuanlan.zhihu.com/p/623543497

【3】PEFT-lora微调矩阵旁路矩阵A和B为什么要这么初始化参数?

LoRA通过引入两个小矩阵A和B,将原始模型参数分解为两部分,从而减少了需要微调的参数数量,提高了微调的效率。

在LoRA中,降维矩阵A和升维矩阵B的初始化方式是有特定原因的。降维矩阵A使用随机高斯分布初始化,是为了保持模型的表达能力。高斯分布的随机初始化可以帮助模型在训练初期快速学习到有效的特征表示,从而提高模型的性能。而升维矩阵B使用0矩阵初始化,是为了减少对原始模型参数的影响。将B初始化为0矩阵,使得在训练初期,模型仍然保持原始预训练参数的输出,从而保证了模型在微调初期的稳定性。

如果将A和B都用0矩阵初始化,那么在训练初期,模型将无法学习到有效的特征表示,导致微调效果不佳。如果A用0矩阵初始化,B用随机高斯分布初始化,那么在训练初期,模型可能会受到B矩阵的干扰,导致训练过程不稳定,甚至可能损害模型的性能。

LoRA-代码详解

【1】LoraCofig()的常见超参数设置

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

class peft.LoraConfig  
( peft_type: Union = None,auto_mapping: Optional = None,  
base_model_name_or_path: Optional = None,  
revision: Optional = None,  
task_type: Union = None,  
inference_mode: bool = False,  
r: int = 8,  
target_modules: Optional[Union[list[str], str]] = None,  
lora_alpha: int = 8,  
lora_dropout: float = 0.0,  
fan_in_fan_out: bool = False,  
bias: Literal['none', 'all', 'lora_only'] = 'none',  
use_rslora: bool = False,  
modules_to_save: Optional[list[str]] = None,  
init_lora_weights: bool | Literal['gaussian', 'olora', 'pissa', 'pissa_niter_[number of iters]', 'loftq'] = True,  
layers_to_transform: Optional[Union[list[int], int]] = None,  
layers_pattern: Optional[Union[list[str], str]] = None,  
rank_pattern: Optional[dict] = <factory>,  
alpha_pattern: Optional[dict] = <factory>,  
megatron_config: Optional[dict] = None,  
megatron_core: Optional[str] = 'megatron.core',  
loftq_config: Union[LoftQConfig, dict] = <factory>,  
use_dora: bool = False,  
layer_replication: Optional[list[tuple[int, int]]] = None,  
runtime_config: LoraRuntimeConfig = <factory>   
)  

LoraConfig参数说明:

1.r (int) — LoRA 注意力维度(秩)。

2.target_modules (Optional[Union[List[str], str]]) — 要应用适配器的模块名称。如果指定了这个参数,只有指定名称的模块会被替换。当传入字符串时,将执行正则匹配。当传入字符串列表时,将进行精确匹配,或者检查模块名称是否以任一传入的字符串结尾。如果指定为 ‘all-linear’,则选择所有线性/Conv1D 模块,排除输出层。如果未指定,则根据模型架构选择模块。如果架构未知,将引发错误—在这种情况下,您应该手动指定目标模块。

3.lora_alpha (int) — LoRA 缩放的 alpha 参数。

4.lora_dropout (float) — LoRA 层的 dropout 概率。

5.fan_in_fan_out (bool) — 如果要替换的层存储权重形式为 (fan_in, fan_out),请将此设置为 True。例如,gpt-2 使用 Conv1D,权重以 (fan_in, fan_out) 形式存储,因此应设置为 True。

6.bias (str) — LoRA 的偏置类型。可以是 ‘none’、‘all’ 或 ‘lora_only’。如果是 ‘all’ 或 ‘lora_only’,对应的偏置将在训练期间更新。请注意,这意味着即使禁用了适配器,模型也不会产生与基础模型在没有适配的情况下相同的输出。

7.use_rslora (bool) — 设置为 True 时,使用 Rank-Stabilized LoRA,该方法将适配器缩放因子设置为 lora_alpha/math.sqrt®,因为这种方法被证明效果更好。否则,将使用原始默认值 lora_alpha/r。

8.modules_to_save (List[str]) — 除了适配器层之外,要设置为可训练并在最终检查点中保存的模块列表。

9.init_lora_weights (bool | Literal[“gaussian”, “olora”, “pissa”, “pissa_niter_[number of iters]”, “loftq”]) — 如何初始化适配器层的权重。传入 True(默认)会使用微软参考实现中的默认初始化。传入 ‘gaussian’ 会导致线性和层的高斯初始化,缩放因子为 LoRA 秩。将初始化设置为 False 会导致完全随机的初始化,不推荐使用。传入 ‘loftq’ 使用 LoftQ 初始化。传入 ‘olora’ 使用 OLoRA 初始化。传入 ‘pissa’ 进行 Principal Singular values and Singular vectors Adaptation (PiSSA) 初始化,PiSSA 比 LoRA 收敛更快,最终性能更优。此外,PiSSA 相比 QLoRA 减少了量化误差,进一步提升了效果。传入 ‘pissa_niter_[number of iters]’ 启动基于 Fast-SVD 的 PiSSA 初始化,其中 [number of iters] 表示执行 FSVD 的子空间迭代次数,必须是非负整数。当 [number of iters] 设置为 16 时,可以在几秒钟内完成 7B 模型的初始化,训练效果大致等同于使用 SVD。

10.layers_to_transform (Union[List[int], int]) — 要转换的层索引。如果传入一个整数列表,将对列表中指定的层索引应用适配器。如果传入单个整数,将对该索引处的层应用转换。

11.layers_pattern (str) — 层模式名称,仅在 layers_to_transform 不为 None 时使用。

12.rank_pattern (dict) — 从层名称或正则表达式到不同于默认秩 r 的秩的映射。

13.alpha_pattern (dict) — 从层名称或正则表达式到不同于默认 alpha lora_alpha 的 alpha 的映射。

14.megatron_config (Optional[dict]) — Megatron 的 TransformerConfig 参数。用于创建 LoRA 的并行线性层。可以通过 core_transformer_config_from_args(get_args()) 获取,这两个函数来自 Megatron。参数将用于初始化 Megatron 的 TransformerConfig。当您想将 LoRA 应用于 megatron 的 ColumnParallelLinear 和 RowParallelLinear 层时,需要指定此参数。

15.megatron_core (Optional[str]) — 要使用的 Megatron 核心模块,默认为 “megatron.core”。

16.loftq_config (Optional[LoftQConfig]) — LoftQ 的配置。如果不为 None,则使用 LoftQ 对骨干网络权重进行量化,并初始化 LoRA 层。同时传递 init_lora_weights=‘loftq’。请注意,在这种情况下,您不应传递量化模型,因为 LoftQ 将自行量化模型。

17.use_dora (bool) — 启用 ‘Weight-Decomposed Low-Rank Adaptation’ (DoRA)。这种技术将权重更新分解为两个部分,幅度和方向。方向由普通 LoRA 处理,而幅度由单独的可学习参数处理。这可以提高 LoRA 的性能,特别是在低秩时。目前,DoRA 仅支持线性和 Conv2D 层。DoRA 引入了比纯 LoRA 更大的开销,因此建议在推理时合并权重。有关更多信息,请参阅 https://arxiv.org/abs/2402.09353。

18.layer_replication (List[Tuple[int, int]]) — 通过根据指定的范围堆叠原始模型层来构建新的层堆栈。这允许扩展(或缩小)模型而无需重复基础模型权重。新层将附加有单独的 LoRA 适配器。

19.runtime_config (LoraRuntimeConfig) — 运行时配置(不会被保存或恢复)。

import torch  
import transformers  
from peft import LoraConfig, PeftModel, get_peft_model, prepare_model_for_kbit_training  
  
rank = ...  
target_modules = ["q_proj", "k_proj", "v_proj", "out_proj", "fc_in", "fc_out", "wte"]  
config = LoraConfig(  
    r=4, lora_alpha=16, target_modules=target_modules, lora_dropout=0.1, bias="none", task_type="CAUSAL_LM"  
)  
quantization_config = transformers.BitsAndBytesConfig(load_in_8bit=True)  
  
tokenizer = transformers.AutoTokenizer.from_pretrained(  
    "kakaobrain/kogpt",  
    revision="KoGPT6B-ryan1.5b-float16",  # or float32 version: revision=KoGPT6B-ryan1.5b  
    bos_token="[BOS]",  
    eos_token="[EOS]",  
    unk_token="[UNK]",  
    pad_token="[PAD]",  
    mask_token="[MASK]",  
)  
model = transformers.GPTJForCausalLM.from_pretrained(  
    "kakaobrain/kogpt",  
    revision="KoGPT6B-ryan1.5b-float16",  # or float32 version: revision=KoGPT6B-ryan1.5b  
    pad_token_id=tokenizer.eos_token_id,  
    use_cache=False,  
    device_map={"": rank},  
    torch_dtype=torch.float16,  
    quantization_config=quantization_config,  
)  
model = prepare_model_for_kbit_training(model)  
lora_model = get_peft_model(model, config)  

【2】LoraModel( )的常见超参数设置

class peft.LoraModel  
( model,config,adapter_name ) → torch.nn.Module  
  
Parameters  
  
model (torch.nn.Module) — The model to be adapted.  
config (LoraConfig) — The configuration of the Lora model.  
adapter_name (str) — The name of the adapter, defaults to "default".  

from transformers import AutoModelForSeq2SeqLM  
from peft import LoraModel, LoraConfig  
  
config = LoraConfig(  
    task_type="SEQ_2_SEQ_LM",  
    r=8,  
    lora_alpha=32,  
    target_modules=["q", "v"],  
    lora_dropout=0.01,  
)  
  
model = AutoModelForSeq2SeqLM.from_pretrained("t5-base")  
lora_model = LoraModel(model, config, "default")  

【3】使用LoraConfig( )配置LoRA参数

在PEFT(参数高效微调)中,LoRA配置通过get_peft_model()函数封装,以创建一个可训练的PeftModel。通过调整LoraConfig中的init_lora_weights参数,可以增加或减少模型权重,从而优化模型的性能和资源消耗。

from peft import LoraConfig, get_peft_model  
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer  
  
# 加载预训练的模型和分词器  
model_name = "t5-small"  
model = AutoModelForSeq2SeqLM.from_pretrained(model_name)  
tokenizer = AutoTokenizer.from_pretrained(model_name)  
  
# 配置LoRA参数  
lora_config = LoraConfig(  
    r=8,  # 低秩矩阵的秩  
    lora_alpha=16,  # LoRA的alpha参数  
    lora_dropout=0.1,  # Dropout率  
    init_lora_weights=0.02  # 初始化LoRA权重  
)  
  
# 获取PEFT模型  
peft_model = get_peft_model(model, lora_config)  
  
# 对输入进行编码  
input_text = "Translate English to French: The weather is nice today."  
input_ids = tokenizer(input_text, return_tensors="pt").input_ids  
  
# 使用PEFT模型进行推理  
outputs = peft_model.generate(input_ids)  
output_text = tokenizer.decode(outputs[0], skip_special_tokens=True)  
  
print("Translated text:", output_text)  

  • r:低秩矩阵的秩。较低的秩会减少参数数量,从而降低内存消耗。

  • lora_alpha:LoRA的alpha参数,用于调整模型的学习率。

  • lora_dropout:Dropout率,有助于防止模型过拟合。

  • init_lora_weights:初始化LoRA权重的值,可以根据需要增加或减少模型权重。

【4】使用LoRA微调roberta文本分类案例

安装依赖环境

!pip install transformers datasets evaluate accelerate peft  

加载数据集

import torch  
from transformers import RobertaModel, RobertaTokenizer, AutoModelForSequenceClassification, TrainingArguments, Trainer, DataCollatorWithPadding  
from peft import LoraConfig, get_peft_model  
from datasets import load_dataset  
  
  
  
peft_model_name = 'roberta-base-peft'  
modified_base = 'roberta-base-modified'  
base_model = 'roberta-base'  
  
dataset = load_dataset('ag_news')  
tokenizer = RobertaTokenizer.from_pretrained(base_model)  
  
def preprocess(examples):  
    tokenized = tokenizer(examples['text'], truncation=True, padding=True)  
    return tokenized  
  
tokenized_dataset = dataset.map(preprocess, batched=True,  remove_columns=["text"])  
train_dataset=tokenized_dataset['train']  
eval_dataset=tokenized_dataset['test'].shard(num_shards=2, index=0)  
test_dataset=tokenized_dataset['test'].shard(num_shards=2, index=1)  
  
  
# Extract the number of classess and their names  
num_labels = dataset['train'].features['label'].num_classes  
class_names = dataset["train"].features["label"].names  
print(f"number of labels: {num_labels}")  
print(f"the labels: {class_names}")  
  
# Create an id2label mapping  
# We will need this for our classifier.  
id2label = {i: label for i, label in enumerate(class_names)}  
  
data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="pt")  

常规微调

# use the same Training args for all models  
training_args = TrainingArguments(  
    output_dir='./results',  
    evaluation_strategy='steps',  
    learning_rate=5e-5,  
    num_train_epochs=1,  
    per_device_train_batch_size=16,  
)  
def get_trainer(model):  
      return  Trainer(  
          model=model,  
          args=training_args,  
          train_dataset=train_dataset,  
          eval_dataset=eval_dataset,  
          data_collator=data_collator,  
      )  
full_finetuning_trainer = get_trainer(  
    AutoModelForSequenceClassification.from_pretrained(base_model, id2label=id2label),  
)  
  
full_finetuning_trainer.train()  

LoRA微调

model = AutoModelForSequenceClassification.from_pretrained(base_model, id2label=id2label)  
  
peft_config = LoraConfig(task_type="SEQ_CLS", inference_mode=False, r=8, lora_alpha=16, lora_dropout=0.1)  
peft_model = get_peft_model(model, peft_config)  
  
peft_model.print_trainable_parameters()  
peft_lora_finetuning_trainer = get_trainer(peft_model)  
peft_lora_finetuning_trainer.train()

【5】LoRA中LoraConfig源码解读

但是如果需要对Lora内部进行一些修改的话,仅仅知道上述的如何使用是远远不够的。下面我们从源头开始介绍Lora是如何操作的,本文讲解基于transformer较新的4.37.2版本。

1.首先通过get_peft_model的一系列调用关系可以找到其最基本也是最重要的LoraModel类,其继承自BaseTuner类,在BaseTuner类中最重要的就是inject_adapter 方法,该方法的作用即是遍历每个Module查看哪些是需要修改的。

2.inject_adapter方法中又用到了_create_and_replace方法,于是我们继续定位到_create_and_replace方法中,如下图所示,通过_create_new_module方法进行创建我们所需要的Lora层。

3.继续深入的话可以知道其是通过dispatch_default方法进行创建所支持的Lora层,目前支持Linear层、Embedding层和Conv2d的Lora修改。

4.以Linear为例,如下图所示,其继承自nn.Module和LoraLayer类,主要是通过LoraLayer类中对A和B矩阵进行设置,最终封装成Linear类以供调用。

如何学习大模型 AI ?

由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。

但是具体到个人,只能说是:

“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。

这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

在这里插入图片描述

第一阶段(10天):初阶应用

该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。

  • 大模型 AI 能干什么?
  • 大模型是怎样获得「智能」的?
  • 用好 AI 的核心心法
  • 大模型应用业务架构
  • 大模型应用技术架构
  • 代码示例:向 GPT-3.5 灌入新知识
  • 提示工程的意义和核心思想
  • Prompt 典型构成
  • 指令调优方法论
  • 思维链和思维树
  • Prompt 攻击和防范

第二阶段(30天):高阶应用

该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。

  • 为什么要做 RAG
  • 搭建一个简单的 ChatPDF
  • 检索的基础概念
  • 什么是向量表示(Embeddings)
  • 向量数据库与向量检索
  • 基于向量检索的 RAG
  • 搭建 RAG 系统的扩展知识
  • 混合检索与 RAG-Fusion 简介
  • 向量模型本地部署

第三阶段(30天):模型训练

恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。

到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?

  • 为什么要做 RAG
  • 什么是模型
  • 什么是模型训练
  • 求解器 & 损失函数简介
  • 小实验2:手写一个简单的神经网络并训练它
  • 什么是训练/预训练/微调/轻量化微调
  • Transformer结构简介
  • 轻量化微调
  • 实验数据集的构建

第四阶段(20天):商业闭环

对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。

  • 硬件选型
  • 带你了解全球大模型
  • 使用国产大模型服务
  • 搭建 OpenAI 代理
  • 热身:基于阿里云 PAI 部署 Stable Diffusion
  • 在本地计算机运行大模型
  • 大模型的私有化部署
  • 基于 vLLM 部署大模型
  • 案例:如何优雅地在阿里云私有部署开源大模型
  • 部署一套开源 LLM 项目
  • 内容安全
  • 互联网信息服务算法备案

学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。

如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

在这里插入图片描述

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值