微调
微调:
LoRA 与QLoRA:有何区别
LoRA(低秩自适应)和 QLoRA(量化低秩自适应)技术均可用于训练 AI 模型。更具体地说,它们都属于参数高效微调(PEFT),这种微调技术比训练大语言模型(LLM)所用的其他方法更节省资源,因此广受欢迎。
LoRA 的工作原理:
LoRA 技术使用新参数根据新数据训练 AI 模型。
并非训练整个模型和所有预训练权重,而是将它们放在一边或“冻结”,然后训练样本量较小的参数。这些样本量被称为“低秩”自适应矩阵,LoRA 就是这么来的。
它们之所以被称为低秩矩阵,是因为它们的参数和权重数量较少。训练完成后,它们会与原始参数组合,然后作为一个单独的矩阵。这样一来,就可以更高效地进行微调。
将 LoRA 矩阵看作添加到矩阵中的一行或一列会更容易理解。
将下面这个矩阵看作需要训练的所有参数:

训练参数中的所有权重需要耗费大量时间、资金和内存。训练完成后,您可能仍然需要进行更多训练,这就浪费了大量资源。
下面这列代表低秩权重:

新的低秩参数经过训练后,将单个“行”或“列”添加到原始矩阵中。这样一来,它就可以将新训练应用到所有参数。

现在,AI 模型可以与新微调的权重一起运行。
训练低秩权重需要的时间、内存和成本较少。样本量经过训练后,它就可以在更大的矩阵中应用所学知识,而不会占用任何额外内存。
LoRA 的优势:
采用 LoRA 技术时,可以用更少的时间、资源和精力对模型进行微调。其优势包括:
-
需要训练的参数较少。
-
出现过拟合的风险较低。
-
训练时间较短。
-
所用内存较少。
-
可灵活调整(可以只对模型的某些部分进行训练,而忽略其他部分)。
QLoRA 的工作原理是什么?
QLoRA 是 LoRA 的扩展。它是一种与 LoRA 类似的技术,但具有额外的优势:所需内存更少。
“QLoRA” 中的“Q”代表“量化”。在这种情况下,量化模型意味着将非常复杂、精确的参数(大量小数和大量内存)压缩成更小、更简洁的参数(较少的小数和较少的内存)。
它的目标是使用单个图形处理单元(GPU)的存储和内存对模型的一部分进行微调。它使用 4 位 NormalFloat(NF4)来实现这一点,这种新的数据类型能够量化矩阵,而且所需内存甚至比 LoRA 更少。通过将参数压缩成更小、更易于管理的数据,它可以将所需的内存占用量减少到原始大小的 4 倍。
模型经过量化后,体积会变小,因此对其进行微调就变得容易多了。
将下面这行看作原始模型的参数:

共有 12 个参数,3 个绿色,6 个蓝色,2 个黄色,还有 1 个粉色。模型经过量化后,它被压缩成之前模型的表示法。

量化后,剩下的样本量是 1 个绿色、2 个蓝色和 1 个黄色。
在量化过程中,有些数据可能会因为太小而在压缩过程中丢失。例如,丢失了 1 个粉色参数,这是因为它在参数中所占比例极小,不能在压缩版本中代表足够的数据。
在以上示例中,我们将参数从 12 个压缩到 4 个。但实际上,数十亿个参数被压缩成几个,可以在单个 GPU 上对这些参数进行可控的微调。
QLoRA 的优势
QLoRA 是一种注重低内存维护需求的技术。与 LoRA 类似,它优先考虑效率,能够实现更快、更轻松的微调训练过程。其优势包括:
-
所需内存少于 LoRA
-
有助于避免数据过拟合
-
可保持较高的准确性
-
快速、轻量级的模型微调
RLHF 与 DPO有什么本质?
RLHF 是通过“奖励模型 + 强化学习”间接优化人类偏好,而 DPO 则直接用人工偏好数据优化语言模型概率分布,跳过了强化学习。
二者的本质区别在于:RLHF 依赖显式奖励建模,DPO 通过对比损失实现隐式偏好优化。
DPO 不仅更简单、稳定、低成本,还能在大多数任务上取得与 RLHF 相当的效果,因此逐渐成为主流替代方案。
RLHF 由三步组成:
1.SFT(Supervised Fine-tuning)
-
用高质量指令数据(如“问答”“总结”“翻译”)微调模型,让它学会遵循人类指令。
-
这是“教模型说话”的第一步。

-
Reward Model 训练
-
给同一个问题生成多个回答,让人工标注者排序哪个更好。
-
用这些排序训练一个奖励模型 ( R(x, y) ),预测回答的“人类偏好分数”。
-
![image]()
-
强化学习阶段(PPO)
-
用奖励模型当“人类代理”,指导语言模型生成输出。
-
优化目标是:让模型生成的回复能最大化奖励,同时约束不要偏离原语言模型(通过 KL 散度惩罚)。

这一步的关键是:模型通过强化学习,在“奖励函数”的指引下逐步调整生成策略。
RLHF 效果强,但也很“重”:
-
流程复杂:要三步训练,特别是强化学习(PPO)部分非常难调;
-
不稳定:奖励模型噪声会导致训练不收敛;
-
成本高:每次都要采样、评估、梯度更新,计算量巨大;
-
不可控性:有时候模型会学会“讨好”奖励模型,而非真正遵循人类偏好(Reward Hacking)。
这些问题让 RLHF 成为大模型训练中最“烧 GPU”的环节之一。于是研究者开始思考:有没有办法跳过强化学习那一步,直接学到相同的偏好?
ppo详细解释:
第一阶段:Actor 开始推理 Actor 开始一个字一个字地推理: 第 1-5 步:"尊敬的王总,关于订单延迟..." Critic (评判模型) 看着前几个字,心里想:“嗯,称呼得体,直奔主题,这种开头通常能拿高分。” Critic 打分:[5.0, 5.2, 5.5, 5.8, 6.0] (分数稳步上升) 第 6-10 步:"...我们深表歉意,原因是..." Critic (评判模型):“态度诚恳,接下来只要给出一个合理的理由(比如物流积压),这题就稳了。” Critic 打分:[6.2, 6.5, 6.7, 6.8, 7.0] (7分对5分,优势在我) 第 11-12 步 (突然崩坏):"...我完全忘了这事。" Critic (评判模型) 瞬间崩溃:“什么?!这种大实话能跟客户说吗?这属于极度不专业!要扣大分!” Critic 打分:[7.0, -8.0, -8.2, ...] (📉 分数暴跌!) 第二阶段:Reference Model 默默注视 Ref Model 全程也在计算概率。 它发现 Actor 在输出“我完全忘了”这几个字时,概率和自己(SFT 模型)差别极大。因为 SFT 模型受过职业训练,在这个语境下通常会说“由于物流不可抗力”。 Ref Model 的作用:虽然它不说话,但它计算出的KL 散度瞬间飙升。这意味着 Actor 这一步不仅会被扣分,还会因为偏离职业人设被额外罚分。 第三阶段:Reward Model 秋后算账 Reward Model (奖励模型) 终于拿到整个模型回复了: “尊敬的王总,关于订单延迟我们深表歉意,原因是…我完全忘了这事儿。” RM 的动作:读完最后,判定为严重公关事故,直接打低分。 RM 打分 (Reward):-10 分 (最终标量)。 第四阶段:PPO 怎么更新?(Critic与Reward的区分) 在复盘(Backpropagation)的时候: 对于前半段(“尊敬的王总…”): Reward Model 给了 -10 分(总分很差)。 但 Critic 会站出来帮 Actor 辩护:“R老师,这学生前 10 个字写得挺好的啊!那时候我预测能得 7 分呢,是好苗子!” 结论:Actor 不需要修改前 10 个字的策略,那是好动作,需要保留。 对于后半段(“我完全忘了…”): Critic 指着第 11 步说:“就是从这个‘我’字开始,我的预测分从 7 分直接跳水到了 -8 分!” 结论:Advantage (优势) = 实际得分(-10) - 预期得分(7) = -17。 最终结果:模型会受到一个巨大的负向梯度,尽量压低在“原因是…”后面接“我完全忘了”的概率。下次它就会学乖,改成接“由于物流高峰”了。

DPO:直接偏好优化:
Anthropic 提出了 DPO(Direct Preference Optimization),它一出现就成了替代 RLHF 的轻量方案。DPO 不用训练奖励模型,也不用强化学习,而是直接在原始语言模型上优化人类偏好。

它的关键思想是我们其实已经有人工标注的“好回答”和“坏回答”对,既然知道哪一个更好,为什么还要额外学一个奖励模型再强化学习?直接优化概率分布,让模型对“好回答”的概率更高,对“坏回答”的概率更低,不就行了吗?
于是 DPO 提出了一个简洁的目标函数:

简单理解就是让模型在参考模型基础上,倾向生成被人类偏好的回答 ( y^+ ),同时降低生成不被偏好的回答 ( y^- ) 的概率。
DPO 的神奇之处在于——它等价于一个简化版的 RLHF:
-
不显式训练奖励模型;
-
不需要采样环境;
-
直接用对比损失优化语言模型。
为什么 DPO 能替代 RLHF?
DPO 的核心优势有三点:
-
端到端可训练
-
不需要单独的奖励模型,也不需要强化学习框架,直接基于语言建模损失优化即可。
-
稳定高效
-
不存在 PPO 的梯度不稳定问题,训练速度更快,显存占用更低。
-
实证表现好
-
多篇论文和开源模型(如 Zephyr、Yi、Mistral-Instruct)已经证明DPO 在人类偏好评测上几乎可以媲美 RLHF。
正因如此,现在大多数开源模型都采用 SFT + DPO 流程,既能获得接近 RLHF 的效果,又节省了大部分训练成本。
vllm的PagedAttention机制解析
https://zhuanlan.zhihu.com/p/680153425
https://zhuanlan.zhihu.com/p/681716326
在LLM推理中,KV缓存用于存储历史Token的注意力键值对,以避免重复计算。传统方法采用连续内存分配,但存在以下问题:
-
内存碎片:不同请求的序列长度动态变化,导致内存空洞。
-
预分配浪费:需预留大块连续内存,利用率低(如LLaMA-2 7B在序列长度44K时,内存占用超100GB)。
-
算术强度低:Attention计算为矩阵乘向量,Memory Bound特性显著。
PagedAttention核心机制:
受操作系统虚拟内存分页技术启发,PagedAttention通过以下创新优化内存管理:
1: 分块存储(Paged Memory)
-
将KV缓存分割为固定大小的页块(如256KB),允许非连续存储。
-
每个页块通过**页表(Lookup Table)**映射物理地址,支持动态分配与回收。
2: 动态分页管理
-
按需分配:仅为当前生成的Token分配页块,无需预分配连续内存。
-
碎片回收:请求结束后,自动释放页块并加入空闲池。
3: 跨请求共享
-
不同请求可共享相同的KV页块(如相同前缀的上下文),减少冗余存储。
-
对并行采样(如Beam Search)友好,共享基础KV数据。

VLLM异步推理架构解析:

VLLM 架构解析解读
VLLM架构旨在优化大语言模型推理服务的性能,其核心组件及交互逻辑如下:
Scheduler(调度器):
作为架构的“总控中心”,负责协调任务分配,将推理任务分发给多个 Worker 节点,实现任务的高效调度与资源分配,确保系统整体运行有序性。
KV Cache Manager(键值缓存管理器):
管理大语言模型推理关键的 KV 缓存(用于存储注意力机制中的键值对),通过 Block tables(块表) 组织缓存块,记录缓存块的使用状态。
对接 CPU Block Allocator 和 GPU Block Allocator,根据内存资源情况,灵活分配 CPU/GPU 内存块,优化缓存的存储与访问效率,减少重复计算。
Worker 节点:
包含多个 Worker(如 Worker 0、Worker 1 至 Worker N-1),每个 Worker 负责模型分片(Model Shard)的推理计算,实现模型并行。
内置 Cache Engine,用于处理 KV 缓存的读写,配合 KV Cache Manager 完成缓存管理,提升推理过程中数据访问的速度。
CPU/GPU Block Allocator(内存块分配器)
分别负责CPU和GPU内存块的分配与管理,根据 KV Cache Manager 指令,动态分配内存资源 ,确保缓存块合理存储,提升内存利用率。
架构核心优势:
通过Scheduler 的任务调度、KV Cache Manager 的缓存优化,结合 Worker 节点的模型分片并行计算,VLLM 实现了推理任务的高效调度、内存资源的精细化管理,以及模型推理的并行加速,最终提升大语言模型服务的吞吐量与性能。
吞吐量提升原理:
-
请求批处理:将多个请求合并推理(类似批处理Batching)
-
动态显存复用:利用K/V Cache共享机制减少内存占用
-
非阻塞IO:请求处理与数据传输异步进行
-
优先级调度:高优先级请求优先分配资源
解释vllm的算子融合(Operator Fusion)对推理速度的提升原理
减少GPU内核调用次数:
-
将多个独立操作(如线性变换+层归一化+Dropout)合并为一个CUDA内核,避免多次内核启动开销,降低延迟。
优化内存访问模式:
-
通过寄存器复用中间结果,减少全局内存读写次数;将分散访存转换为连续访存,提升带宽利用率。
流水线并行与延迟隐藏:
-
跨操作(如注意力计算与K/V缓存更新)实现流水线并行,利用GPU高并发特性隐藏计算延迟,典型提升40%以上推理速度。
如何通过模型并行(Model Parallel)在单节点多GPU上微调大模型
并行技术:

当数据/模型太大,涉及并行技术,将数据/模型分配到不同的 GPU 上,增加资源利用率。
数据并行:
Data Parallel(DP)
DP是一种单进程多线程的并行策略,只能在单机上进行训练,步骤如下:
-
单进程控制多GPU,即本质上是单进程多线程;
-
首先将模型加载到主 GPU 上,再复制到各个指定从 GPU;
-
将输入数据按照 Batch 维度进行拆分,各个 GPU 独立进行 forward 计算;
-
将结果同步给主 GPU 完成梯度计算和参数更新,将更新后的参数复制到各个 GPU。 由于其是单进程控制多个GPU,故会存在GPU之间负载不均衡的问题,主GPU负载较大。

在模型推理时,当数据集规模庞大时,为了充分利用多设备资源,将数据切成几部分,分别在不同的卡上进行推理。大规模的数据分到各个显卡上进行推理。
数据并行是最常见的并行形式,因为它很实现简单。
-
数据切分:在数据并行训练中,一批数据集被分割成几部分,每部分被分配到一个设备上。
-
模型完整:每个设备将持有一个完整的模型副本,并对分配的数据集部分进行推理。
张量并行:
张量切分方式分为按行进行切分和按列进行切分,分别对应行并行(Row Parallelism)与列并行(Column Parallelism)。


模型并行是不同设备负责单个计算图不同部分的计算。而将计算图中的层内的参数(张量)切分到不同设备(即层内并行),每个设备只拥有模型的一部分,以减少内存负荷,称之为张量模型并行。
流水线并行:
流水线原理是将不同的 layer 分配给指定 GPU 进行计算,流水线并行只需其之间点对点地通讯传递部分 activations。
具体步骤包括:
-
在流水线并行之中,一个模型的各层会在多个GPU上做切分。
-
一个批次(batch)被分割成较小的微批(microbatches),并在这些微批上进行流水线式执行。
-
通过流水线并行,一个模型的层被分散到多个设备上。
-
当用于具有相同transformer块重复的模型时,每个设备可以被分配相同数量的transformer层。
-
在流水线模型并行中,训练会在一个设备上执行一组操作,然后将输出传递到流水线中下一个设备,下一个设备将执行另一组不同操作。

随着模型参数量越来越大,单张卡已经无法装下完整的模型。此时将模型分成几部分,每个部分分别装载在不同的卡上进行推理,这往往是对模型不同的层进行切分。
大模型训练量化

量化:是一种模型压缩的常见方法,将模型权重从高精度(如FP16或FP32)量化为低比特位(如INT8、INT4)。常见的量化策略可以分为PTQ和QAT两大类。量化感知训练(Quantization-Aware Training):在模型训练过程中进行量化,一般效果会更好一些,但需要额外训练数据和大量计算资源。后量化(Post-Training Quantization, PTQ):在模型训练完成后,对模型进行量化,无需重新训练。
为什么要做模型量化?
模型量化既能减少资源消耗,也能提高运行速度,使大规模推理服务的性能提升。
模型量化的好处主要有:
-
存储空间减少 :显著降低模型文件的大小,便于在内存有限的设备上存储和加载模型。
-
计算效率提升 :低精度的计算可以在专用硬件(如 GPU、TPU 等)上更高效地执行,加速模型推理过程。
-
能源消耗降低 :减少了数据传输和计算过程中的能量消耗,有利于在移动设备或边缘计算场景中的应用。
bit 量化面临的挑战
-
精度损失 :在量化过程中,模型参数的信息可能会丢失,导致模型性能下降。如何在保证量化效率的同时,尽可能减少精度损失是 4 - bit 量化技术的关键问题。
-
量化方法的适配性 :不同的模型架构和任务类型可能需要特定的量化方法来获得最佳性能。开发通用且高效的 4 - bit 量化方案是一个具有挑战性的任务。

AWQ(Adaptive Weight Quantization)
AWQ 是一种基于自适应权重分块的 4 - bit 量化方法。它将模型的权重矩阵划分为多个小块,然后对每个小块单独进行量化。这种方法能够根据每个权重块的分布特性,动态调整量化参数,从而在保证量化精度的同时,提高量化的灵活性和效率。
具体来说,AWQ 首先对权重矩阵进行分块,例如将一个大小为 M×N 的权重矩阵划分为多个 k×k 的小块。然后,对于每个权重块,计算其最小值和最大值,并根据这些值确定量化范围。接着,将每个权重值映射到 4 - bit 的整数表示,同时记录量化过程中的缩放因子和零点等参数,以便在反量化时能够准确还原权重值。

GPTQ
GPTQ 是一种 4bit 模型压缩方法:
-
先把权重转成 低精度但误差很小的基础版本
-
再通过 优化 scale / 零点 + 分块量化,进一步压缩
-
虽然本质是 有损压缩,但误差被压得极小
-
最终效果:模型变小 75%,性能几乎没变

GPTQ 是一种针对大型语言模型的 4 - bit 量化方法,其核心思想是在量化过程中引入无损量化(lossless quantization)和有损量化(lossy quantization)的结合策略。GPTQ 首先通过无损量化步骤,将模型权重表示为一个低精度的基线表示,然后在此基础上应用有损量化来进一步压缩模型,同时通过优化算法最小化量化误差对模型性能的影响。
具体而言,GPTQ 的无损量化阶段利用了权重的稀疏性和冗余性,将权重表示为一个 4 - bit 的基线表示,这个过程不会丢失任何信息。然后,在有损量化阶段,GPTQ 通过调整量化参数(如缩放因子和零点),使得量化后的权重在反量化后能够尽可能接近原始权重,从而减少量化误差。此外,GPTQ 还采用了分块量化和权重重新排序等技术,进一步提高量化效率和模型性能。
微调的分类
按微调的目标和阶段划分

-
有监督微调 (SFT)
-
目标:教会模型理解并遵循人类指令,并以特定格式完成任务(如问答、摘要、翻译、代码生成等)。这是让模型从“文本补全”变为“对话助手”的关键一步。
-
数据:高质量的“指令-回答”(Instruction-Response)或“问-答”(Question-Answer)对。数据集通常形如 {"instruction": "...", "input": "...", "output": "..."}。
-
过程:使用这些标注好的数据对模型进行标准的监督学习训练。
-
例子:使用alpaca_data这样的数据集微调Llama模型,使其能够回答各种用户问题。
-
对齐微调 (Alignment Tuning)
-
目标:使模型的行为与人类的偏好和价值观对齐。目标是让模型变得更有用 (Helpful)、更诚实 (Honest)、更无害 (Harmless)。
-
数据:人类对模型多个输出的偏好排序数据。
-
主要方法:
-
a) 基于人类反馈的强化学习 (Reinforcement Learning from Human Feedback, RLHF):
-
训练奖励模型 (Reward Model, RM):用人类偏好数据训练一个模型,该模型能给模型的不同输出打分,分数高低代表符合人类偏好的程度。
-
强化学习微调:使用强化学习算法(如PPO、GRPO)来微调SFT后的模型。模型生成回答,奖励模型给出奖励,模型根据奖励调整策略,以产生能获得更高奖励(即更符合人类偏好)的回答。
-
b) 直接偏好优化 (Direct Preference Optimization, DPO):
-
一种更简单、更稳定的RLHF替代方案。它绕过了显式的奖励模型训练和复杂的强化学习过程,直接使用偏好数据,通过一个巧妙的损失函数来优化模型,使其更倾向于生成人类偏爱的回答,而不是不喜欢的回答。
微调方式
全参数微调
核心思想:直接在预训练模型的所有参数上进行梯度更新
|
优点 |
缺点 |
|
理论上效果最好,模型自由度最高 |
计算成本极高(需更新所有参数) |
|
适应能力强,可大幅改变模型行为 |
显存需求巨大(需存储完整梯度和优化器状态) |
|
每个任务需存储一份完整模型副本 |
|
|
容易过拟合,可能发生灾难性遗忘 |
参数高效微调
核心思想:冻结原始大模型(Pretrained Model)的绝大部分参数,只训练/添加少量可学习参数(通常 <1%),实现高效适配其中最实用的技术就是LoRAW_adapted = W_original + ΔW
-
W_original 是原始模型的巨大权重矩阵。
-
W_adapted 是微调后适应了新任务的权重矩阵。
-
ΔW 是整个适应过程的核心,它代表了模型为了学习新技能而产生的「权重变化量」
传统做法:全量参数微调 --> 学习整个ΔW
LoRA 低秩适应 (The Low-Rank Adaption):
首先提出一个假设:微调产生的这个“权重变化量”矩阵 ΔW,其“内在秩”是非常低的
举例说明:图像压缩
想象两张图片:
一张纯蓝色的天空图片:这张图片虽然像素很多(比如 1000x1000),但信息量极低。你不需要存储一百万个像素点的颜色值,你只需要一条指令:“创建一个 1000x1000 的画布,全部填充为蓝色 (#0000FF)”。这张图片的信息可以被高度压缩,我们就说它是低秩的。
一张充满细节的集市照片:这张图片里每个人、每个物体的颜色、位置都不同,信息高度复杂,几乎无法用简单的规律来描述。你必须老老实实地存储每个像素点的信息。我们就说它是高秩的。
“秩”可以通俗地理解为一个矩阵所包含的“本质信息”或“独立模式”的数量。 低秩意味着矩阵内部存在大量冗余和规律,可以用更简单的结构来表示。
LoRA 的核心假设就是:ΔW 更像那张蓝天图片。 尽管 ΔW 表面上是一个巨大的、包含数百万参数的矩阵,但它所代表的“知识变化”其实是非常有结构、有规律的,而不是混乱无序的。这种变化可以用少数几个“核心模式”组合而成
具体做法:
ΔW ≈ B × A
-
A 是一个“矮胖”的矩阵(例如 8 x 4096),它像一个压缩器,负责从模型的激活值中捕捉出那几个“核心模式”。它将高维信息压缩到低维空间(秩 r 的空间)。
-
B 是一个“瘦高”的矩阵(例如 4096 x 8),它像一个解压器,负责将这些捕捉到的核心模式重新“投影”回原始的高维空间,形成最终的调整量。
整个“适应”过程,就从学习一个巨大的 ΔW 矩阵(参数量 4096 * 4096),变成了只学习两个小得多的 A 和 B 矩阵(参数量 4096*8 + 8*4096)

微调流程
-
准备阶段-构建和格式化数据集
SFT微调的训练集格式如下
主流:Alpaca 格式 (Instruction, Input, Output)
这是最经典、最通用的SFT格式之一,非常灵活。
-
instruction: 描述模型需要执行的任务。
-
input: (可选) 提供任务的上下文或输入。如果任务本身在instruction中已完整描述,则此项可为空。
-
output: 模型应该生成的标准答案。
-
训练阶段
-
加载基础模型
-
操作: 使用 transformers 库加载预训练模型。
-
关键点: 为了节省显存,通常会以较低的精度加载,比如 4-bit 或 8-bit(通过 bitsandbytes 库实现)。这是LoRA能够在一张消费级显卡上运行的关键。
-
此时的模型是“冰冻”的:它的几十亿个原始参数是只读的,我们不打算直接修改它们
-
配置LoRA
|
LoRA秩 |
LoRA 分解矩阵的秩,直接决定了 LoRA 模块的参数量和表达能力 |
设置建议:
|
|
LoRA缩放系数 |
LoRA 旁路的缩放因子,可以理解为 LoRA 模块的“学习率” |
设置建议:
|
|
target_modules |
指定要在模型的哪些部分附加 LoRA 模块。通常是注意力机制中的线性层,如 q_proj (查询投影) 和 v_proj (值投影)。这相当于告诉医生:“手术只在这些关键的神经网络层上进行”。 |
-
训练循环
一些训练术语
|
批大小 (Batch Size) |
手动设置的超参数。它决定了我们一次从数据集中取出多少条数据来喂给模型 |
|
步 (Step) / 迭代 (Iteration) |
|
|
轮次 (Epoch) |
一个 Epoch 指的是模型已经完整地、不重不漏地看过了整个数据集一遍 |
这是微调真正发生的地方。训练框架会为我们自动处理这个循环。
对于数据集中的每一个样本(就是我们格式化好的prompt),循环执行以下操作
-
前向传播 (Inference during Training):
-
将格式化后的 prompt 输入模型。
-
数据流过模型时,在每个 target_module(如q_proj),计算过程是:输出 = 原始权重输出 + LoRA 旁路输出(ΔW)。
-
LoRA 的输出是由两个小矩阵 (A 和 B) 相乘得到的,这部分权重是可训练的。
-
模型根据输入,逐字预测输出,直到生成完整的响应。这就是训练过程中的推理(Inference),模型用它当前的(可能还不成熟的)知识来尝试回答问题。
-
计算损失 (Assessing the Mistake):
-
将模型生成的响应与我们数据集里提供的“标准答案” (output) 进行比较。
-
比较二者之间的差异,计算出一个“损失值 (Loss)”。损失值越高,说明模型错得越离谱。
-
最常用的算法是交叉熵损失
-
反向传播 (Learning from the Mistake):
-
这是学习的关键。计算机会计算出梯度(Gradients),梯度指明了应该如何调整 LoRA 的权重(矩阵A和B)才能让损失值变小。
-
LoRA 的精髓: 梯度只会作用于微小的 LoRA 权重,而几十亿参数的原始模型权重完全不参与更新(因为它们被冻结了)。这就是为什么 LoRA 既快又省显存。
-
权重更新 (Applying the Correction):
-
根据梯度,优化器(如 AdamW)会微小地更新 LoRA 矩阵 A 和 B 的值。
-
这一次“微调”就完成了。
这个循环会遍历完所有数据(一个 Epoch),然后再重复数个 Epochs,直到 LoRA 权重被调整到最优状态,使得模型能够稳定地生成我们期望的输出。
-
验证
假设我们设置了每500个训练步(steps)进行一次验证。
-
训练阶段 (Steps 1-499):
-
模型在训练集上进行正常的训练循环:前向传播 -> 计算损失 -> 反向传播 -> 更新 LoRA 权重。
-
这个过程重复499次。
-
验证阶段 (At Step 500):
-
暂停训练: 训练循环暂时停止。
-
进入“评估模式”: 程序会调用 model.eval()。这会关闭一些只在训练时使用的功能(如 Dropout)。
-
遍历验证集: 模型开始处理验证集中的每一条数据。
-
只做前向传播 (Inference Only): 对于验证集中的每条数据,模型只进行前向传播,生成一个预测结果。
-
计算评估指标: 将预测结果与验证集中的标准答案对比,计算出损失值(eval_loss)或其他指标(如准确率、ROUGE分数等)。
-
关键点: 绝不进行反向传播!绝不更新任何权重! 这一步纯粹是为了“检查”,而不是“学习”。
-
记录分数: 计算出整个验证集的平均损失后,将这个分数记录下来。例如:Step 500, eval_loss: 1.25。
-
决策与恢复阶段:
-
模型存档: 训练框架(如 Trainer)会比较这次的 eval_loss 和之前最好的 eval_loss。如果这次的分数更低(代表模型表现更好),它就会保存当前 LoRA 权重的一个副本(Checkpoint),并记下这是目前为止的“最佳模型”。
-
恢复训练: 验证结束。程序调用 model.train(),将模型切换回“训练模式”,然后从第501步开始,继续在训练集上进行学习。
这个“训练-验证-训练-验证...”的循环会一直持续下去,直到训练结束。
结果生成阶段
生成权重文件
训练完成后,我们需要保存我们的劳动成果,但通常不会使用最后一次训练得到的权重。我们会使用在整个训练过程中,验证损失最低的那个点("Sweet Spot")所保存下来的模型权重
-
产物是什么?:
-
不是一个全新的、70亿参数的模型!
-
产物是一个非常小的文件夹(通常只有几十兆 MB)。
-
里面主要包含两个文件:
-
adapter_model.bin (或 .safetensors): 这就是微调的最终产物——训练好的 LoRA 权重文件。它只包含了那些微小的矩阵 A 和 B 的参数。
-
adapter_config.json: LoRA 的配置文件,记录了你的 r, lora_alpha, target_modules 等设置,以便日后能正确加载。
即:微调的产物是一套轻量级的、专门用于修改原始模型行为的“适配器”或“插件”权重。
微调流程
-
参数控制
创建SFT训练任务时,涉及众多参数,初次训练我也没有什么经验,但有几个关键参数可以注意一下
-
epoch:像我的训练集有1w条,那epoch = 1就行,如果只有几千条,可以调大一些
-
学习率:一般基模用Qwen3-8B的话,学习率设置为1e-5就行
-
截断长度:我所用的训练集中,有部分数据input会较长,为防止阶段影响效果,我选择设置为16384
其他的就先采用默认值,后续可以结合训练效果再调整,完整的参数也给大家汇总列了出来
|
训练相关参数 |
含义 |
设置建议 |
|
学习率 |
控制每次权重更新的步长 |
设置建议: 这是一个极其重要的超参数。
|
|
批次大小 |
在单个 GPU 上,一次前向/后向传播处理的样本数量 |
设置建议: 在显存允许的范围内,越大越好。
|
|
训练轮数 |
整个训练数据集将被完整地遍历多少次 |
设置建议: 对于指令微调,通常不需要很多轮。
|
|
最大样本数 |
在每个数据集分割(如训练集、验证集)中,最多使用多少个样本 |
|
|
截断长度 |
模型能处理的单个样本的最大 token 长度。如果样本超过这个长度,就需要截断 (truncation) |
|
|
预处理工作进程数 |
在使用 datasets.map() 函数进行数据 Tokenize 等预处理时,使用多少个 CPU 进程并行处理 |
|
|
梯度累积步数 |
这是解决显存不足以支持大 batch size 的关键技巧。它会让模型计算 N 步的梯度,但不立即更新权重,而是将这 N 步的梯度累加起来,然后一次性更新 |
设置建议:
|
|
学习率调度器类型 |
学习率不是一成不变的,调度器会根据训练进程动态调整学习率 |
|
|
日志步数 |
每训练多少步,打印一次训练信息 |
设置一个较小的值,如 10, 20, 50,方便实时监控训练状态 |
|
保存步数 |
每训练多少步,保存一次模型的 checkpoint |
与验证步数保持一致或为其倍数。例如,eval_steps=100, save_steps=100。这样每次评估后如果模型有提升,就会被保存下来 |
|
预热比例 |
在训练初期,学习率从0逐渐增加到设定值的过程。这有助于模型在开始时稳定地学习,防止因初始梯度过大而破坏模型权重 |
|
|
权重衰减 |
一种正则化技术,用于防止过拟合。它会在损失函数中增加一个惩罚项,使得模型的权重倾向于更小的值 |
设置建议: 通常设置为一个较小的值,如 0.01 或 0.1。对于 LoRA,因为可训练参数很少,有时设为 0 也可以 |
|
LoRA秩 |
LoRA 分解矩阵的秩,直接决定了 LoRA 模块的参数量和表达能力 |
设置建议:
|
|
LoRA缩放系数 |
LoRA 旁路的缩放因子,可以理解为 LoRA 模块的“学习率”。最终的 LoRA 激活值会乘以 lora_alpha / r |
设置建议:
|
|
LoRA Dropout |
在 LoRA 层上应用的 Dropout 概率,用于防止 LoRA 模块自身的过拟合 |
如果你的数据集不大,或者发现模型在验证集上性能下降,可以设置一个值,如 0.05 或 0.1。否则,设为 0 即可 |
|
验证步数 |
每训练多少步,就在验证集上进行一次评估 |
设置一个合理的值,比如 100 或 500。可以帮助你监控模型的性能变化,判断是否过拟合,并保存最佳模型 |
|
最多保存Checkpoint数 |
在训练目录中最多保留多少个 checkpoint。当保存新的 checkpoint 时,会自动删除最旧的那个 |
设置建议: 设置为 2 或 3。一个用于保存最新的,一个用于保存目前为止最好的,可以防止磁盘空间被占满 |
强化学习
概述
-
强化学习微调(Reinforcement Learning Fine-Tuning)是大语言模型对齐的关键技术,通过奖励信号引导模型优化生成策略,使输出更符合预期目标。
-
与监督微调(SFT)直接模仿标注答案不同,强化学习微调让模型在"试错"中学习什么是更好的输出,而非仅学习什么是正确的输出
常用算法
PPO(Proximal Policy Optimization):PPO 是当前应用最广泛的策略优化算法,由 OpenAI 于 2017 年提出
-
SFT模型作为初始策略
-
对输入prompt生成多个候选回答
-
奖励模型对每个回答打分
-
使用PPO算法更新策略,最大化期望奖励
-
加入KL散度惩罚,防止偏离原始模型过远
典型应用:ChatGPT、GPT-4、LLaMA 2
核心比喻:PPO就像请了一位全职私教团队来培训孩子,这个团队包括:
主教练(Actor):正在被训练的学生
原版教材(Reference Model):学生最初的学习材料,防止他学偏
评分老师(Reward Model):看完完整答案后打分
实时助教(Critic):在学生写每个步骤时给出即时反馈
PPO的工作流程(以写道歉邮件为例):
学生开始写作:“尊敬的王总,关于订单延迟我们深表歉意,原因是...”
实时助教(Critic)点评:“开头得体,目前可得7分”
学生继续写:“...我完全忘了这事。”
实时助教大惊:“什么?这种大实话能跟客户说吗?”分数瞬间跌到-8分
完成后,评分老师(Reward Model)给出总分:-10分
复盘分析:
实时助教指出:“问题出在‘我完全忘了’这几个字”
主教练调整策略:降低在“原因是...”后面接“我完全忘了”的概率
同时参考原版教材:保持专业写作风格
PPO的精妙之处:通过Critic的实时反馈,模型能精确知道“哪一步出了问题”,而不只是得到一个笼统的低分。
但PPO的代价巨大:需要同时维护四个模型(Actor、Reference、Reward、Critic),对于百亿参数的大模型,这意味着需要四倍的显存。这就是为什么早期只有OpenAI等少数公司玩得起
-
PPO迭代训练:
-
Actor生成回答
-
Critic提供逐token评分
-
Reward Model提供整体评分
-
结合Reference约束,更新Actor参数
GRPO(Group Relative Policy Optimization):GRPO 是 DeepSeek 于 2024 年提出的改进算法,在推理类大模型领域正快速成为首选,尤其在中国AI社区影响力显著提升
-
对每个prompt采样生成多个回答(group)
-
使用奖励模型对组内回答打分
-
计算组内相对优势(relative advantage):高于组均值为正,低于为负
-
只需单个策略模型,无需参考模型(更省资源)
-
通过组内对比学习,训练更稳定
-
特点:
-
相比PPO更简单,无需维护价值网络和参考模型
-
相比DPO更灵活,可使用任意奖励函数
-
训练效率高,显存占用少
典型应用:DeepSeek-R1、DeepSeek-Math
一、一句话通俗理解
GRPO 就像 **“班级内部竞赛学习”**:
老师(GRPO) 不给每个学生单独打分(绝对分数)
而是让全班(同一问题)一起做同一道题,生成多个答案
然后互相比较、排个名次:谁做得好、谁差
只奖励 “比组内平均好” 的答案,让模型下次更倾向这么做
不用额外训练复杂的 “裁判模型”,省显存、训练更快
二、传统方法(PPO/RLHF)有什么麻烦?
以前训练大模型对齐人类偏好(比如 RLHF),一般是两套网络:
Actor(演员):模型自己,负责生成回答
Critic(批评家 / 价值网络):额外训练一个模型,专门给 Actor 的回答打分、估价值
问题:Critic 很占显存、训练慢、容易不稳定、容易 “刷分作弊”
三、GRPO 怎么改的?(3 个核心创新)
1. 去掉 Critic,只用 Actor 自己
不搞两套模型,就用生成模型自己
对同一个问题,一次性生成 N 个回答(比如 4~16 个),组成一个 “小组”
2. 相对奖励:不比绝对分,比 “组内排名”
给每个回答简单打分(对不对、清不清晰)
不算绝对分,而是算:
相对优势 = 单个得分 − 组内平均分
正数 = 比平均好,要鼓励;负数 = 比平均差,要抑制
3. 轻量稳定:像 PPO 但更省资源
沿用 PPO 的 “裁剪更新”,不让模型一下改太猛
加 KL 散度约束:别偏离原来的基础模型太远
结果:显存省约 50%,速度快 30%+,单卡也能训
GRPO和PPO对比
|
维度 |
PPO |
GRPO |
|
Value Model |
✅ 需要,用于估计 V(s)(计算优势) |
❌ 不需要 |
|
优势估计 |
A = R - V(s),依赖 Value 预测 |
组内相对比较,自然基线 |
|
显存占用 |
较高(需额外 Value Model) |
较低 |
|
每个 prompt 的采样数 |
通常 1 个 |
需要 G 个 (如 4-16) |
|
计算特点 |
推理次数少,但模型多 |
推理次数多,但模型少 |
|
稳定性 |
依赖 Value 训练质量 |
依赖足够的采样多样性 |
具体流程
-
加载基础模型
需要准备的模型:
|
模型 |
作用 |
是否训练 |
|
策略模型 (Policy Model) |
生成回答,是我们要优化的目标 |
✅ 训练 LoRA |
|
参考模型 (Reference Model) |
冻结的初始模型副本,计算 KL 惩罚 |
❌ 冻结 |
|
奖励模型/函数 (Reward) |
评估回答质量,提供奖励信号 |
❌ 不训练 |
-
配置GRPO算法
|
参数 |
说明 |
设置建议 |
|
group_size (G) |
每个 prompt 生成多少个候选回答 |
常见值 4, 8, 16,越大方差估计越准,但计算量越大 |
|
learning_rate |
策略更新的步长 |
1e-6 到 5e-6,比 SFT 更小 |
|
kl_coef (β) |
KL 散度惩罚系数 |
0.01 - 0.1,控制策略偏离程度 |
|
clip_range (ε) |
策略比率裁剪范围 |
标准值 0.2 |
|
temperature |
采样温度,控制生成多样性 |
0.7 - 1.0,需要足够多样性 |
|
max_new_tokens |
每个回答的最大生成长度 |
根据任务设置,如 512 或 1024 |
LoRA 配置: 同 SFT
LoRA 配置: 同 SFT
|
参数 |
设置建议 |
|
r (rank) |
16 或 32 |
|
lora_alpha |
通常设为 2 * r |
|
target_modules |
q_proj, v_proj, k_proj, o_proj 等 |
-
GRPO核心思想
为什么不需要 Value Model?
|
算法 |
优势估计方式 |
额外模型 |
|
PPO |
A = R - V(s),需要 Value Model 预测基线 |
需要 Value Model |
|
GRPO |
用同一 prompt 的多个回答的平均奖励作为基线 |
❌ 不需要 |
GRPO 的基线估计公式:
对于 prompt q,生成 G 个回答 {o₁, o₂, ..., o_G}
每个回答获得奖励 {r₁, r₂, ..., r_G}
基线 baseline = mean(r₁, r₂, ..., r_G) //算术平均值
标准差 std = std(r₁, r₂, ..., r_G)
优势 Aᵢ = (rᵢ - baseline) / std # 标准化的相对优势
直观理解:
-
如果一个回答的奖励高于组内平均,它就是"好的",应该被强化
-
如果低于平均,就是"差的",应该被抑制
-
通过组内比较,天然形成了基线,无需额外模型
-
训练循环(每个step)
-
批量采样
输入: 一批 prompts {q₁, q₂, ..., q_B} (B = batch_size)
对每个 prompt qᵢ:
-
使用策略模型 π_θ 生成 G 个回答 {oᵢ₁, oᵢ₂, ..., oᵢ_G}
-
采样时使用 temperature > 0 保证多样性
-
记录每个 token 的生成概率 π_θ(token|context)
总共生成 B × G 个回答
示例(B=2,G=4)
-
Prompt 1:"1+1等于几?"
-
回答 1.1: "1+1=2,因为..." → 记录生成概率
-
回答 1.2: "答案是2" → 记录生成概率
-
回答 1.3: "1+1=3" → 记录生成概率
-
回答 1.4: "等于二" → 记录生成概率
-
Prompt 2: "中国的首都是?"
-
回答 2.1: "北京" → 记录生成概率
-
回答 2.2: "中国首都是北京市" → 记录生成概率
-
回答 2.3: "上海" → 记录生成概率
-
回答 2.4: "Beijing" → 记录生成概率
-
计算奖励
使用Reward Model 和奖励函数对生成的每个回答进行打分
奖励函数(计算奖励值,适用于结构化输出场景)
from typing import List, Tuple, Set, Union
from swift.plugin import ORM, orms
class TestORM(ORM):
def __init__(self):
pass
def __call__(self, completions: List[str], **kwargs) -> List[float]:
# 从kwargs中直接获取两个关键字段的真实值列表
real_need_verify = kwargs.get("Whether the input needs further verification", [])
real_controversial = kwargs.get("Whether the input is controversial in tone", [])
# 如果没有真实值数据,返回0分
if not real_need_verify or not real_controversial:
return [0.0 for _ in range(len(completions))]
# 确保长度一致
if len(real_need_verify) != len(completions) or len(real_controversial) != len(completions):
return [0.0 for _ in range(len(completions))]
scores = []
for i, completion in enumerate(completions):
try:
# 解析JSON字符串
solution = json.loads(completion) if completion.strip() else {}
# 获取模型预测的字段值
model_need_verify = solution.get("Whether the input needs further verification", "").strip()
model_controversial = solution.get("Whether the input is controversial in tone", "").strip()
# 获取真实值
real_verify = str(real_need_verify[i]).strip()
real_contro = str(real_controversial[i]).strip()
# 计算奖励(字符串严格匹配)
reward1 = 1.0 if model_need_verify.lower() == real_verify.lower() else 0.0
reward2 = 1.0 if model_controversial.lower() == real_contro.lower() else 0.0
score = (reward1 + reward2) / 2.0 # 归一化到 [0, 1]
scores.append(score)
except Exception as e:
# 解析失败时返回0分
scores.append(0.0)
return scores
orms['test_reward'] = TestORM
-
计算组内相对优势
GRPO 核心: 在每个 prompt 的组内计算相对优势
对于 prompt qᵢ 的 G 个回答:
-
计算组内平均奖励: μᵢ = mean(rᵢ₁, rᵢ₂, ..., rᵢ_G)
-
计算组内标准差: σᵢ = std(rᵢ₁, rᵢ₂, ..., rᵢ_G)
-
计算标准化优势: Aᵢⱼ = (rᵢⱼ - μᵢ) / (σᵢ + ε)
其中 ε 是一个小常数 (如 1e-8),防止除零
示例:
Prompt 1: 奖励 = [1.0, 1.0, 0.0, 1.0]
μ₁ = (1.0+1.0+0.0+1.0)/4 = 0.75
σ₁ = 0.433
-
A₁₁ = (1.0-0.75)/0.433 = +0.58 → 强化 ↑
-
A₁₂ = (1.0-0.75)/0.433 = +0.58 → 强化 ↑
-
A₁₃ = (0.0-0.75)/0.433 = -1.73 → 抑制 ↓
-
A₁₄ = (1.0-0.75)/0.433 = +0.58 → 强化 ↑
解读:
• 优势 > 0: 该回答比组内平均水平好,应该增加其生成概率
• 优势 < 0: 该回答比组内平均水平差,应该降低其生成概率
• 优势 ≈ 0: 该回答表现一般,概率基本不变
如果想要在某个模型基础上做全参数微调,究竟需要多少显存?
一般 n B的模型,最低需要 16-20 n G的显存。(cpu offload基本不开的情况下)
vicuna-7B为例,官方样例配置为 4*A100 40G,测试了一下确实能占满显存。(global batch size 128,max length 2048)当然训练时用了FSDP、梯度累积、梯度检查点等方式降显存。
loss指标评估
loss是评价模型训练效果的一个重要指标。模型微调结束后会保存loss曲线,可以根据该曲线进一步调整参数。

在训练过程中,loss曲线可能会有波动,但整体是下降的趋势。一般模型微调后,最终的模型 loss 需要下降到 1 以下才会取得较好的效果。如果loss过大可能欠拟合,loss过小可能过拟合。
模型loss未达预期可以通过调整以下参数优化,简单概括“面多了加水,水多了加面”。

lora_rank:控制 LoRA 矩阵有多 “宽”
lora_target:控制 LoRA 加在哪些层上
-
损失高:rank 调小 + target 减少模块 + 学习率调小
-
损失低:rank 调大 + target 增加模块 + 学习率调大
DeepSpeed 深度学习优化库
DeepSpeed 是微软开源的、基于 PyTorch 的深度学习优化库,核心是通过 ZeRO 显存优化和 3D 并行技术,让你能用更少的 GPU 训练超大模型(如千亿参数 LLM),同时速度更快、成本更低。
一、核心定位与价值

二、核心技术组件
1. ZeRO(零冗余优化器)— 显存优化基石
ZeRO 核心思想:分片(Sharding) → 把参数、梯度、优化器状态拆分到不同卡上,只存自己那份。
分 3 个阶段(显存节省递增,通信开销也递增):
-
ZeRO-1:仅拆分优化器状态(如 Adam 的 m、v)→ 显存 ↓ 约 4 倍
-
ZeRO-2:拆分优化器状态 + 梯度 → 显存 ↓ 约 8 倍
-
ZeRO-3:拆分参数 + 梯度 + 优化器状态 → 显存近乎线性下降(支持万亿参数)
DeepSpeed 的革命性技术,通过分片策略将模型状态(优化器状态、梯度、参数)在多 GPU 间拆分,消除传统数据并行中的冗余存储:

配合 **CPU/NVMe 卸载 (O2/O3)** 技术,可将部分状态转移到 CPU 内存或硬盘,进一步释放 GPU 显存,实现单卡训练 13B + 参数模型。
2. 3D 并行策略
-
数据并行:样本拆分到不同 GPU,加速训练吞吐量
-
模型并行:将单层模型拆分到多个 GPU,突破单卡显存限制
-
流水线并行:按层划分模型并分配到不同 GPU,实现层间并行计算
DeepSpeed 支持自动混合 3D 并行,根据模型规模和硬件配置智能选择最优策略。
3. 混合精度训练
原生支持FP16/BF16低精度计算,结合动态损失缩放 (Dynamic Loss Scaling) 技术,在不损失模型精度的前提下:
-
显存占用减少 50%
-
计算速度提升 2-3 倍
-
降低通信带宽需求




浙公网安备 33010602011771号