从原理到实操:深度拆解LoRA、Adapter、Prefix三大主流微调方案
01传统大模型微调大模型的微调传统大模型如Bert、T5、Bart在进行微调的时候有三种微调的方法。第一微调所有参数第二调整部分参数冻结住部分层第三对模型尾部加一些层训练新加层。但是这三种方法针对这些传统大模型都是几百兆的大模型并不大了。 现在的模型都是几亿、几十亿参数很少用这种方法来调了。以chatglm2-6b模型为例进行微调https://www.modelscope.cn/models/ZhipuAI/chatglm2-6b/summary训练数据的格式每行都是一个问答对问题的字段是context答案字段是target目标拼成这样一个json字符串格式。通过代码tokenize_dataset_rows.py对数据进行分词首先读取输入context问题读取输出答案问题分词和答案分词把问题分词和答案分词相加作为一个整体的input。分词核心是保证问题/答案各自的分词准确保留语义边界方便格式控制加分隔符、截断避免跨单元的错误token生成合并相加核心是将独立的token序列转换成模型能处理的连续输入让模型学习问答间的语义关联完成训练样本的构建本质是“先拆后合”拆是为了精准合是为了关联最终服务于对话模型的训练效果。BPE分词把常见组合字符串拼在一起google分词工具sentencepiece它可以实现BPE会把所有的文本都转成UTF-8再来统计UTF-8的编码哪些容易在一起所以有可能几个汉字对应一个TOKEN也有可能2个TOKEN对应一个汉字。最大序列长度max_seq_length这个参数是指输入输出如果太大会给模型显存造成很大压力如果输入输出长度超过了300它会把后面那一节给截断。处理完生成的TOKEN数据就可以用来进行训练。这个数据是一个二进制数据。加载大模型前面层数都冻结住只训练最后2层或10层所有参数60亿左右符合模型6b参数训练最后二层的参数26万冻结参数5977241600可训练参数2663642400所有参数6243584000执行代码finetune_freeze_parameter.py进行训练上图是训练代码读取分词好的token数据ids是所有输入问题答案seq_len问题的长度labels预测的标签。-100表示特殊字符不预测正常来说语言模型输入A B C D E F对应的预测为-100、-100、-100d、e、 f前面三个-100表示a、b、c 这三个词它预测出来的东西不存在真正梯度下降法是从后面这些开始前面这个-100是告诉模型这里预测出来的结果当它不存在不参加任何梯度下降法只有后面的参加。输入部分也就是问题不需要任何预测只要把答案输出预测出来就行了。用了max_length以后如果输入长于max_length直接截断如果短于 max_length就把它给补0补0的地方不需要预测。训练调参对显存的要求比如要调6B的参数batchsize10需要的显存是6*6B*10360g显存。所以不可能去调6B调个0.2B就可以了batchsize也不能调太大6是个常数。训练方法一般是Adam显存里保存的数据有首先把模型存进去模型是6B就是6B个float32浮点数Adam梯度下降法存每层的前向结果还要存之前步骤的梯度结果因为训练方法依赖于前一步的结果这些是经过一些比较粗略的参数估量基本上就是n*6*6B6跟模型的结构有关系Transformer结构都是6左右这是一个比较粗算的一个经验公式。模型可训练参数因为显卡的限制max_length也不能太大平均长度*1.5模型可训练参数不能太多batch_size理论上来说越大越好但也不能太大2^n序列长度大不影响模型的参数模型参数仍然是6B因为每个序列它共用的都是这些参数但长度过大不影响模型参数会影响前向结果中间结果就要保存很多虽然能够输出长文本但会严重影响训练。首先统计平均长度去除异常值把那种特别短和特别长的先给它去掉一般平均长度*1.5作为max_length。第二步确定好需要训练的参数微调/全量微调等方法。第三步batch_size理论越大越好所以会不断的调整batch size比如先调128如果挂了就96、64这样慢慢往下减一般是2^n次幂这样可以把显存的性能榨到最干。训练参数一定要用float32如果用float16可能会出现Nan或者****inf数据精度表达问题强行用一个低精度的来表示可能会出现错误或者溢出。同一层参数一定要一个类型不同层的参数可以是不同的类型。严格来说每个参数的类型都可以不一样只不过Pytorch只能以层为单位最小细粒度去指定。这个其实就是一种量化这个用8位来量化(load_in_8bit)这是真正的量化。微调传统微调大模型使用场景第一数据量大调的参数越多需要的数据量越大TOKEN数可调参数量这样才能保证效果好。第二机器算力足显存大小决定能否训练显卡flops决定训练时间。第三个是场景要非常垂直。这是大模型的一个全量微调这种直接微调有一些不足的地方第一就是说参数多显存不容易放下。参数多需要对应更大的数据。参数多不容易收敛。参数多调参时间过长。02大模型微调三件套这就是为什么大模型很少全量微调除了微调还有调参三件套。第一是prefix tuning或prompt tuning。在问题前加一些提示词让它来回答问题只不过它加的提示词是一个抽象的TOKEN来训练这个TOKEN的参数从果倒退因。第二是Adapter-Tuning在Transformer模块里加一层适配器像是一种串联的外挂。第三是在求attention的时候加一层Lora外挂像是一种并联的外挂。这是现在大模型微调的三件套它最终的目的是节约内存。三大类方法一个是Prompt-Tuning一个是适配器adapter还有个 LoRA。这三大方法都可以理解成给模型打外挂。每个方法都是通用的但是大模型是各自搞的导致实现方法的代码不通用需要特殊开发。02-1Prompt-tuning第一个方法Prompt-tuning问大模型“我的狗叫什么名字”大模型肯定不知道在前头可以加一个 prompt“我养了只狗它叫旺财”再问大模型“我的狗叫什么名字”它就可以回答出来了就能理解语义了前头这个叫做prompt工程改变输入来改变输出。在前头加一些虚拟的TOKEN这个TOKEN没有实实在在的意义它对应的并不是一个真实的词只是个虚拟TOKEN。比如再问它“我的狗叫什么名字”这些TOKEN在模型里头都对应一个向量把它带进训练这个TOKEN是在词典之外相当于在词典之外再额外加n个虚拟TOKEN。把整个一句话输入给模型模型输出的可能是叫小明做模型训练的时候通过Loss反向调整来改变TOKEN只调这些词对应的embedding但是模型本身参数不变。相当于给模型以TOKEN的形式添加可调的参数TOKEN词表和模型是完全一一对应的。这是它的一个基本思想有多种实现方法产生了不同的套路只做硬提示叫prompt工程硬提示对应的另外一套方法叫LangChain。Prompt-Tuning是软提示靠参数调整模型的输出这两种方法都可以实现模型的效果。Prefix-Tuning叫软提示在每一层的TOKEN之前构建一个prefix这个TOKEN类似于刚这个t1、t2…tn模型去学这些词所对应的embedding每个TOKEN直接对应embedding训练起来不容易收敛比如对应128维的embedding可能不容易收敛让它对应一个64维的一个 embedding 64维的embedding会通过一个MLP的神经网络把它再转成一个128维的embedding 这个网络参数是可训练的。训练这个网络就行了这样会容易收敛。可以前面加MLP结构也可以不加。除了Prefix推理以外刚是加了MLP 在前头又可以加lstm。比如t1、t2、t3、t4等各对应一个embedding经过一个lstm层形成四个新的t1、t2、t3、t4再经过MLP 变成我们想要的相当于说在这里加了一个LSTM对应图上的prompt encoder。为什么要加LSTM基本思想就是认为这些词这些软token虽然是虚拟不见的但它们之间其实应该是有关联的希望通过一个lstm能够提取一些上下文特征。第一种方法和第二种方法都是在每一层输入都加。大模型的结构都是Transformer一层层Transformer核心是attention就是self-attentionself attention就是kqv三个共用一套向量红色表示它有三个t1 t2 t3三个软TOKEN第一层加3个软 TOKEN第二层加另外3个软TOKEN这两个不相等第三层也另加3个前入的方法只在第一层加第一层加完第二层加第三层加每一层都对应向量维数比如向量维度是128维软token的个数是3个一共有三层就是3*3*128。每一层做attention的时候这些虚拟的层只做k和v不做q就是说第二层不要求输出结果黑色的是进行attention输出的结果。就是它做q的时候把第二层黑色给生成了。红色上下相邻的两层并不是生成的是完全虚拟出来的所以只需做K V就是说在生成它的时候它参与进来了而这个跟下面一层没有任何关系而右边黑色的是跟下面的这些是有关系的这个虚拟层是在每一层中间都会随机出来一个向量红色的是外挂只训练红色的东西但黑色的要参与运算。黑色是模型本身每个红色的点都是一个完全随机初始化的向量红色的点之间没有任何关联性。黑色的点它是由下面这些点通过self-attention 算出来的而红色的点就是一个完全随机出来的向量跟下面这些没有任何关系。这个就是P-Tuning V2每一层都初始化向量当然这个向量红色的点也有一个变种每一个红色对应一个向量直接训练这个向量变种就是一个红色的点对应一个向量这个向量再经过一个MLP。一般来说大模型微调都可以使用Transformer提供的peft库专门做prefix-tuning和lora这两种方法。执行finetune_prompt.py进行微调在训练的时候除了传原生glm模型以外参数pre_seq_len每层都加上128的软TOKEN对应的向量因为传了参数框架就会认为冻结住6B的参数。训练完之后查看微调后的和原生的对比02_2适配器Adapter适配器正常在Transformer多抽头attention前向神经网络Layer Norm两个非线性就出来了。在前向神经网络和Layer Norm中间强插一层串联在原有的网络中强插进去一个Adapter叫做适应器。适应器的结构特别简单输入维度d经过前向网络把它变小经过非线性变换再投回来完成加个softmax典型的Resnet结构Resnet结构进来的是d出来的也是d 所以它不会影响整个网络的结构。在调模型的时候只调适应器其他东西全冻住。有框架的时候就用框架没有框架的时候可以自己实现一下。冻住模型的所有参数在这里实现两个模块第一个是Adapter 两个线性的全连接层w1/w2 (in_features, mid_features)代表上图输入的d和md先变成mm再经过一个非线性再变回去从下连接到上边的这条线short carty0.1yx0.1便于收敛。再搞一个CombinedModel层由两个模型传入model1是原有的Fflmodel2是适配器输入x先进入原有的model1再进入适配器model2再输出相当于把适配器和原有的全连接层绑在一起。float16的数据类型进行推理没问题但在训练的时候需要float32不适用它不报错但是它训练出来就不对所以必须在这里给这两层转成float32训练必须用float32推理float16、float32都可以。把输入x先转成32可以计算算完以后它出去切模型又变成了16所以再把它给转回来再转成16就是这个适配器。看下这个模型结构看打印出这个模型结构它核心是0-27这28层block 它是设在多抽头attention出来之后的MLP所以真正要打外挂的这一层先attention q k v转一下把做完多抽头attention经过全连接的这一层其实对应的就是Feed forward layer。transformer.encoder.layers一共有0~27一个个取每个s都对应这个模块取s.self_attention.dense对应上图红框出来的模块让这个模块先取一下它的in_features就是它的长d输出向量作为适配器的输入把适配器和原有的全连接层绑在一起来替代。正常来说是每一层都可以加适配器每一层都可以加个适配器但每层加适配器导致可训练参数特别多机器显存扛不住。所以只给第一层加个适配器适配器的参数是需要训练的。加载模型的时候把模型放在GPU里自己生成的适配器还有绑在一起的模型默认是在CPU所以要使用CUDA放进GPU。打印下改造好的模型第0个GLMBlock原生是027这里变成127把0单独抽出来了。之前微调训练完之后就直接保存模型但是这里没有配置文件真正训练的东西只有适配器所以把适配器给保存起来就可以了用的时候再把适配器重新拼一下就可以了。02_3Lora在工作中最常用的一种方法Lora。最基本思路比如hw0*xx是输入特征w0是训练好的h是输出这个是模型的一层。如果说模型参数量非常大h是1000维x是500维w是一个1000×500的矩阵就是50万的参数如果要直接微调训练50万个参数会造成非常难调一是需要很大的数据量二是对内存要求非常高。简化就是原模型不调给它加一个外挂来调这个外挂把这两个结果加起来也就是并联在一起这个是线性代数中的低秩分解。直接调W0的代价太高外挂一个ΔW只调ΔW把ΔW变成两个矩阵相乘B*A这两个矩阵分别通过r低秩分解的方式进行一个降维这样学习的参数就会变得非常少Lora就是用低秩分解的方法来做。有一个n*m的矩阵W0它等价于A*BW0(n*m)A(n*r)*B(r*m)A*B的参数是n*rr*m比如n1000、m100、r10那么原来是10万个参数这里是1万10001.1万个参数把W0通过这种方式来表示参数量瞬间只有原来的1/10能达到同样的效果这个叫做低质分解。r是类似于一个超参一般来说都会设的非常小。矩阵的秩W0是n*mW0有这么多个参数它都是具有自由度的用这种方式n*rr*m来表示它自由度就会降低了很多它带来的好处是参数量的急剧下降r是a*b W0矩阵的秩秩是指一个矩阵它实际的维度。上图矩阵它只对应1个方程这两个方程是完全等价的维度是2×2但实际上它的秩等于1。微调模型之前W0应该是等于一个全零矩阵如果W0刚开始不等于一个全零矩阵就是一个有偏的矩阵因为这个是已经训练好的在刚开始训练的时候能够保持跟原先是一样的再慢慢的去调W0W0A*B让w0可以是a0 或b0只要有一个为0那么它就为0但是不能让两个都为0那另外一个不为零的要进行随机初始化参数随机初始化参数最好的方法就是正态分布用正态分布来随机采样是最符合先验的b为0a为正态分布采样为什么两个不能同时为0。WW0UVU V就是刚说的A B求导损失函数对U V求导如果U V 同时为0就无法学习了。任务领域越垂直做一个医疗领域的做一个法律领域的做一个金融领域的领域越垂直r应该大一点可训练的参数相对来说比较多一点从外界获取的信息就会多一点如果 r1就没有可学习的。如果领域越通用是一个泛化的场景比如做个旅游写个诗做一些日常生活的问答 r就小一点。r设的越大对原来的模型破坏的越严重就会产生灾难性遗忘灾难性遗忘在前面方法中都存在这个问题比如Prefix Tuning如果TOKEN数量比较多也是在破坏原有的模型一样就会产生一种灾难性遗忘适配器的参数很多也会产生灾难性遗忘。大模型的核心TransformerTransformerself attentionFFNself attention是一个词向量经过三个矩阵Wq、Wk、Wv变成了三个不同的向量这三个不同的向量再做self attention再做attention多抽头其实是一样的多抽头只不过是多变化了几组而已。一个向量通过三个矩阵变成三个不同的向量再进行attention操作这个就是self attentionLora就用在词向量经过三个矩阵(Wqw0)这之间改变这个向量就是在训练self attention这个部分在微调self attention这个位置。有个Lora之后Q K V它其实是同一个向量变成q k v但没有必要这3个都变。attention基本思想v的加权求和加权权重由q k来决定。只要调一个就足够了Q K不需要两个都调。比如Q K最简单的模式q和k做内积最终其实要改变的是内积的结果改变两个是可以改变这个结果但其实改变一个就完全可以达到同样的效果所以就没有必要去同时改变两个prefix跟这个思想是不一样的。prefix没有q的概念它是改变k v 。v是需要改变的 q k二选一就可以了。Lora它计算量并没有减少它在推理的时候它的计算量还增加了但是好处是只更新了部分参数内存占用量变小了内存占用量有个粗略的公式6*可训练参数2*不可训练参数这是最终的内存占用量。所以说它可训练参数这一块变得比较小整个原始的模型都是不可训练参数而可训练的参数就是加的外挂加的外挂比较小因此它内存占用量比较小。真正的训练瓶颈并不在Flops并不在运算力上而在显存上。Lora这个方法是目前最为主流的真正的重点是在内存不足的情况下Lora效果最好。以量化的int 8的方式加载模型这是一个真正的模型量化load_in_8bit设成true只能在Linux下跑在Windows下跑会缺包。做Lora配置大模型有三种常用套路这个是TaskType.CAUSAL_LM。rfinetune_args.lora_rankGLM是perfix decoder这个类型跟attention结构是一样的只是连接的线不一样self attention本身是一模一样的。在调Lora的时候是一样的只是mask不一样所以它们两个是可以混用的。dropout就是有些参数随机剪枝。把原有的模型传进去再传一个设置新出来的模型打上了Lora外挂后面的训练就可以直接训练了打上Lora外挂的时候它会把原有参数都冻结住运行finetune_lora.py代码进行微调。学AI大模型的正确顺序千万不要搞错了2026年AI风口已来各行各业的AI渗透肉眼可见超多公司要么转型做AI相关产品要么高薪挖AI技术人才机遇直接摆在眼前有往AI方向发展或者本身有后端编程基础的朋友直接冲AI大模型应用开发转岗超合适就算暂时不打算转岗了解大模型、RAG、Prompt、Agent这些热门概念能上手做简单项目也绝对是求职加分王给大家整理了超全最新的AI大模型应用开发学习清单和资料手把手帮你快速入门学习路线:✅大模型基础认知—大模型核心原理、发展历程、主流模型GPT、文心一言等特点解析✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑✅开发基础能力—Python进阶、API接口调用、大模型开发框架LangChain等实操✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经以上6大模块看似清晰好上手实则每个部分都有扎实的核心内容需要吃透我把大模型的学习全流程已经整理好了抓住AI时代风口轻松解锁职业新可能希望大家都能把握机遇实现薪资/职业跃迁这份完整版的大模型 AI 学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】