大模型微调算法详解 —— 从零基础到全面掌握

目录


第一章:微调全景图------建立全局认知

1.1 为什么需要微调?

复制代码
预训练大模型(如GPT、Qwen、LLaMA)的问题:
  - 通用但不专业:什么都懂一点,但在特定领域不够深入
  - 格式不可控:输出格式可能不符合业务需求
  - 幻觉问题:可能生成不准确的内容
  - 成本高:直接使用大模型API成本高,微调小模型可能更经济

微调的价值:
  - 领域专业化:让模型成为特定领域的专家
  - 格式对齐:让模型按照特定格式输出
  - 知识注入:将企业内部知识注入模型
  - 降低成本:微调后的小模型可能媲美大模型在特定任务上的表现

1.2 微调方法分类全景图

复制代码
                        大模型微调方法
                            │
            ┌───────────────┼───────────────┐
            │               │               │
        全参数微调       参数高效微调(PEFT)   基于反馈的微调
        (Full FT)                         (RLHF/DPO/...)
            │               │
            │       ┌───────┼───────┬───────────┐
            │       │       │       │           │
            │     加法式   重参数化  选择式     混合式
            │       │       │       │           │
            │    Adapter   LoRA   Pruning    MAM Adapter
            │    Prefix   DoRA   BitFit
            │    Prompt   QLoRA
            │    IA³      AdaLoRA
            │             rsLoRA
            │             GaLore
            │             LoRA+
            │             VeRA
            │             PiSSA
            │             MosLoRA

1.3 核心概念解释

复制代码
在深入具体算法之前,先搞清楚几个核心概念:

1. 可训练参数比例
   全量微调:100%参数都需要训练
   LoRA:约0.1%-1%的参数
   Adapter:约1%-5%的参数
   Prefix Tuning:约0.1%的参数

2. 原始模型参数是否被修改
   全量微调:是,所有参数都被更新
   LoRA:否,原始参数冻结,只训练旁路
   Adapter:否,在层间插入新模块
   Prefix Tuning:否,在输入前添加可训练向量

3. 推理时的额外开销
   全量微调:无额外开销
   LoRA(合并后):无额外开销
   LoRA(不合并):有少量计算开销
   Adapter:有推理延迟(额外的前向传播)
   Prefix Tuning:有少量推理开销(增加序列长度)

4. 显存需求
   全量微调:极高(7B模型需要120GB+)
   LoRA:低(7B模型约16-24GB)
   QLoRA:极低(7B模型约6-10GB)
   Adapter:低
   Prefix Tuning:极低

1.4 大模型的结构基础

理解微调算法需要先了解Transformer的基本结构:

复制代码
一个Transformer层的结构:

输入 x
  │
  ├──→ [Self-Attention] ──→ 残差连接 ──→ LayerNorm
  │     ├── Q_proj (查询投影)
  │     ├── K_proj (键投影)
  │     ├── V_proj (值投影)
  │     └── O_proj (输出投影)
  │
  ├──→ [Feed-Forward Network] ──→ 残差连接 ──→ LayerNorm
  │     ├── Gate_proj (门控投影)
  │     ├── Up_proj (上投影)
  │     └── Down_proj (下投影)
  │
  └──→ 输出

一个7B模型通常有:
  - 32层Transformer
  - 每层有7个线性投影层(Q/K/V/O/Gate/Up/Down)
  - 总共 32 × 7 = 224个线性层
  - 总参数量约70亿

不同微调方法"插"在哪里:

复制代码
┌─────────────────────────────────────────────────┐
│              微调方法与模型结构的关系                │
│                                                   │
│  LoRA:     在Q/K/V/O/Gate/Up/Down旁边加旁路      │
│  Adapter:  在每层的输出后面插入小型网络             │
│  Prefix:   在注意力计算的K/V前面加可训练向量        │
│  Prompt:   在输入embedding前面加可训练向量          │
│  IA³:      对K/V/FFN的输出做可学习的缩放            │
│  BitFit:   只训练所有层的bias参数                  │
│  全量微调:  所有参数都训练                          │
└─────────────────────────────────────────────────┘

第二章:全量微调 (Full Fine-Tuning)

2.1 原理

复制代码
全量微调 = 更新模型的所有参数

数学表达:
  θ_new = θ_pretrained - η × ∇L(θ_pretrained, D_finetune)
  
  其中:
  - θ_pretrained: 预训练模型的所有参数
  - D_finetune: 微调数据集
  - L: 损失函数(通常是交叉熵)
  - η: 学习率
  - θ_new: 微调后的所有参数

2.2 优缺点

复制代码
优点:
  ✓ 效果最好(所有参数都在优化)
  ✓ 无额外推理开销(模型结构不变)
  ✓ 实现最简单(标准训练流程)
  ✓ 可以最大程度适应新任务

缺点:
  ✗ 显存需求极高(需要存储参数+梯度+优化器状态)
  ✗ 容易过拟合(小数据集上)
  ✗ 灾难性遗忘(可能丢失预训练的通用能力)
  ✗ 存储成本高(每个任务一个完整模型副本)
  ✗ 训练时间长

显存需求估算(7B模型,bf16精度):
  模型参数: 7B × 2 bytes = 14 GB
  梯度: 7B × 2 bytes = 14 GB
  优化器状态(Adam): 7B × 8 bytes = 56 GB
  激活值: 约20-40 GB(取决于batch size和序列长度)
  ─────────────────────────
  总计: 约104-124 GB(至少需要2张A100-80G)

2.3 显存优化技术

复制代码
技术1:混合精度训练 (AMP)
  - 用bf16/fp16做前向和反向传播
  - 用fp32维护主权重副本
  - 节省约50%显存

技术2:梯度检查点 (Gradient Checkpointing)
  - 不保存中间激活值,反向传播时重新计算
  - 用计算换显存
  - 节省约60%激活值显存,但训练慢约30%

技术3:梯度累积 (Gradient Accumulation)
  - 多个mini-batch的梯度累积后再更新
  - 等效于更大的batch size,但显存不增加

技术4:DeepSpeed ZeRO
  - ZeRO-1: 分片优化器状态 → 显存节省约4倍
  - ZeRO-2: 分片优化器状态+梯度 → 显存节省约8倍
  - ZeRO-3: 分片优化器状态+梯度+参数 → 显存节省约N倍(N=GPU数)

技术5:CPU Offload
  - 将优化器状态和部分参数卸载到CPU内存
  - 显存大幅节省,但训练速度下降

2.4 代码示例

python 复制代码
# 全量微调的核心代码(使用HuggingFace Trainer)
from transformers import AutoModelForCausalLM, TrainingArguments, Trainer

model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B-Instruct")

training_args = TrainingArguments(
    output_dir="./output_full_ft",
    num_train_epochs=3,
    per_device_train_batch_size=1,
    gradient_accumulation_steps=16,
    learning_rate=2e-5,            # 全量微调用较小的学习率
    bf16=True,
    gradient_checkpointing=True,   # 必须开启,否则显存不够
    deepspeed="ds_config.json",    # 使用DeepSpeed ZeRO
)

trainer = Trainer(model=model, args=training_args, train_dataset=dataset)
trainer.train()

第三章:LoRA系列算法

3.1 LoRA (Low-Rank Adaptation)

3.1.1 核心原理
复制代码
LoRA的核心思想:
  预训练权重矩阵 W 的更新量 ΔW 是低秩的
  可以用两个小矩阵的乘积来近似:ΔW = B × A

数学表达:
  原始: h = Wx
  LoRA: h = Wx + (B × A) × x = Wx + B × (Ax)
  
  其中:
  - W ∈ R^{d×k}  (原始权重,冻结不训练)
  - A ∈ R^{r×k}  (下投影矩阵,r << min(d,k))
  - B ∈ R^{d×r}  (上投影矩阵,初始化为0)
  - r: LoRA的秩(rank),通常取8, 16, 32, 64

初始化策略:
  - A: 使用高斯随机初始化(或Kaiming初始化)
  - B: 初始化为全零矩阵
  - 这样训练开始时 ΔW = B×A = 0,不影响原始模型

缩放因子:
  ΔW = (α/r) × B × A
  α通常设为r的2倍(如r=16, α=32)
  α/r = 2,相当于将LoRA的更新乘以2
3.1.2 为什么有效?
复制代码
关键洞察:预训练模型的权重更新矩阵是低秩的

直觉理解:
  - 预训练模型已经学到了丰富的知识
  - 微调只需要做"微小的调整"
  - 这种调整可以用低秩矩阵来近似
  - r=16 意味着只用16个自由度来描述调整方向

实验验证:
  - 原始论文发现 r=4 在很多任务上就够用了
  - 增大r带来的提升有限
  - 说明微调确实只需要低秩的调整
3.1.3 代码实现
python 复制代码
from peft import LoraConfig, get_peft_model, TaskType

lora_config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,
    r=16,                          # LoRA秩
    lora_alpha=32,                 # 缩放因子
    lora_dropout=0.05,             # Dropout
    target_modules=[               # 应用LoRA的目标层
        "q_proj", "k_proj", "v_proj", "o_proj",
        "gate_proj", "up_proj", "down_proj",
    ],
    bias="none",
)

model = get_peft_model(base_model, lora_config)
model.print_trainable_parameters()
# trainable params: 13,631,488 || all params: 7,615,616,000 || trainable%: 0.1790
3.1.4 超参数选择指南
复制代码
r (LoRA秩):
  - r=4-8: 简单任务(格式调整、风格迁移)
  - r=16-32: 一般任务(领域问答、指令微调)
  - r=64-128: 复杂任务(大量新知识注入)
  - 经验法则:先用r=16,效果不够再增大

lora_alpha:
  - 通常设为 r 的2倍
  - 也可以设为 r(此时缩放因子为1)
  - 更大的alpha意味着LoRA更新的影响更大

target_modules:
  - 只选注意力层: ["q_proj", "v_proj"] → 最少参数
  - 选全部注意力: ["q_proj", "k_proj", "v_proj", "o_proj"] → 推荐
  - 选全部层: 加上["gate_proj", "up_proj", "down_proj"] → 效果最好但参数多

lora_dropout:
  - 0.0-0.1(数据少时用较大的dropout)
  - 数据充足时可以设为0

3.2 QLoRA (Quantized LoRA)

3.2.1 核心原理
复制代码
QLoRA = 4bit量化基座模型 + LoRA微调

三个关键创新:
  1. 4-bit NormalFloat (NF4) 量化
     - 将模型权重从16bit压缩到4bit
     - 显存减少约4倍
     - NF4对正态分布的权重最优
  
  2. 双重量化 (Double Quantization)
     - 对量化的缩放因子也进行量化
     - 进一步节省约0.37bit/参数
  
  3. 分页优化器 (Paged Optimizers)
     - 使用NVIDIA统一内存管理
     - 在GPU显存不足时自动将优化器状态转移到CPU

显存对比(7B模型):
  LoRA (bf16): 约16-24 GB
  QLoRA (4bit): 约6-10 GB  ← 单张RTX 3090/4090就能跑
3.2.2 代码实现
python 复制代码
from transformers import BitsAndBytesConfig
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
import torch

# 4bit量化配置
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,                    # 4bit加载
    bnb_4bit_quant_type="nf4",            # NF4量化类型
    bnb_4bit_compute_dtype=torch.bfloat16, # 计算时用bf16
    bnb_4bit_use_double_quant=True,       # 开启双重量化
)

# 以4bit加载模型
model = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen2.5-7B-Instruct",
    quantization_config=bnb_config,
    device_map="auto",
)

# 准备模型进行量化训练
model = prepare_model_for_kbit_training(model)

# 配置LoRA
lora_config = LoraConfig(
    r=16, lora_alpha=32, lora_dropout=0.05,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                    "gate_proj", "up_proj", "down_proj"],
    bias="none", task_type="CAUSAL_LM",
)

model = get_peft_model(model, lora_config)

3.3 DoRA (Weight-Decomposed Low-Rank Adaptation)

3.3.1 核心原理
复制代码
DoRA的核心思想:
  将权重矩阵分解为"方向"和"大小"两个组件
  分别用不同的策略进行更新

数学表达:
  W = m × (W / ||W||)     (方向-大小分解)
  
  其中:
  - m: 大小向量(magnitude),可训练
  - W/||W||: 方向(direction),用LoRA更新
  
  更新后:
  W' = (m + Δm) × ((W + BA) / ||W + BA||)

为什么比LoRA好?
  - 分析发现:全量微调倾向于同时调整方向和大小
  - LoRA只能间接调整这两个组件
  - DoRA显式分离,每个组件用最优策略更新
3.3.2 代码实现
python 复制代码
from peft import LoraConfig, get_peft_model

# DoRA只需在LoRA配置中添加一个参数
dora_config = LoraConfig(
    r=16,
    lora_alpha=32,
    lora_dropout=0.05,
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                    "gate_proj", "up_proj", "down_proj"],
    use_dora=True,    # ← 开启DoRA
    bias="none",
    task_type="CAUSAL_LM",
)

model = get_peft_model(base_model, dora_config)

3.4 AdaLoRA (Adaptive LoRA)

3.4.1 核心原理
复制代码
AdaLoRA的核心思想:
  不同层、不同模块对微调的重要性不同
  应该给重要的模块分配更高的秩(rank)

方法:
  1. 初始化所有LoRA为相同秩
  2. 训练过程中评估每个LoRA的重要性
  3. 重要的LoRA增加秩,不重要的减少秩
  4. 总参数预算不变,但分配更合理

重要性评估:
  基于SVD分解的奇异值大小
  奇异值大的方向更重要,保留
  奇异值小的方向不太重要,裁剪
3.4.2 代码实现
python 复制代码
from peft import AdaLoraConfig, get_peft_model

adalora_config = AdaLoraConfig(
    init_r=12,           # 初始秩
    target_r=4,          # 目标秩(裁剪后)
    beta1=0.85,
    beta2=0.85,
    tinit=200,           # 开始裁剪的步数
    tfinal=1000,         # 结束裁剪的步数
    deltaT=10,           # 裁剪间隔
    lora_alpha=32,
    lora_dropout=0.05,
    target_modules=["q_proj", "v_proj"],
    task_type="CAUSAL_LM",
)

model = get_peft_model(base_model, adalora_config)

3.5 rsLoRA (Rank-Stabilized LoRA)

3.5.1 核心原理
复制代码
标准LoRA的问题:
  缩放因子 α/r 中,当r增大时,缩放因子减小
  这导致增大r时,LoRA的更新幅度反而减小
  使得增大r的收益递减

rsLoRA的解决方案:
  将缩放因子从 α/r 改为 α/√r
  
  效果:
  - 当r增大时,更新幅度不会过度缩小
  - 使得增大r能带来更一致的性能提升
  - 特别适合使用较大r值的场景
3.5.2 代码实现
python 复制代码
from peft import LoraConfig, get_peft_model

rslora_config = LoraConfig(
    r=64,
    lora_alpha=32,
    use_rslora=True,    # ← 开启rsLoRA
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
    task_type="CAUSAL_LM",
)

model = get_peft_model(base_model, rslora_config)

3.6 LoRA+

3.6.1 核心原理
复制代码
LoRA+的核心发现:
  LoRA中的A矩阵和B矩阵应该用不同的学习率

标准LoRA: lr_A = lr_B
LoRA+: lr_B = λ × lr_A,其中 λ >> 1(通常λ=16)

为什么?
  - B矩阵初始化为0,需要更大的步长来"逃离"零点
  - A矩阵已经随机初始化,需要更小的步长来精细调整
  - 实验表明 λ=16 效果最好
3.6.2 代码实现
python 复制代码
from peft import LoraConfig, get_peft_model

loraplus_config = LoraConfig(
    r=16,
    lora_alpha=32,
    loraplus_lr_ratio=16.0,    # ← B矩阵的学习率是A矩阵的16倍
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
    task_type="CAUSAL_LM",
)

model = get_peft_model(base_model, loraplus_config)

3.7 GaLore (Gradient Low-Rank Projection)

3.7.1 核心原理
复制代码
GaLore的核心思想:
  不是限制权重更新的秩,而是限制梯度的秩
  
  方法:
  1. 计算梯度 G
  2. 对梯度做低秩投影: G_lr = P × G × Q
  3. 用低秩梯度更新全量参数
  
  优势:
  - 理论上可以达到全量微调的效果
  - 显存需求和LoRA相当
  - 不需要修改模型结构

与LoRA的区别:
  LoRA: 只更新低秩的旁路参数
  GaLore: 更新全部参数,但梯度是低秩的
3.7.2 代码实现
python 复制代码
from transformers import TrainingArguments

# GaLore通过优化器配置实现
training_args = TrainingArguments(
    output_dir="./output_galore",
    optim="galore_adamw",
    optim_target_modules=["attn", "mlp"],
    galore_rank=128,                # 梯度投影的秩
    galore_update_proj_gap=200,     # 投影矩阵更新间隔
    galore_scale=0.25,
    learning_rate=2e-5,
    # ... 其他参数
)

3.8 VeRA (Vector-based Random matrix Adaptation)

3.8.1 核心原理
复制代码
VeRA的核心思想:
  比LoRA更极端的参数效率
  
  LoRA: ΔW = B × diag(d) × A
  VeRA: ΔW = B × diag(d_b) × Λ × diag(d_a) × A
  
  其中:
  - A, B: 共享的随机矩阵(所有层共享,不训练)
  - d_a, d_b: 可训练的缩放向量
  
  效果:
  - 参数量比LoRA少10倍以上
  - 但效果接近LoRA
  - 适合极端资源受限的场景

3.9 PiSSA (Principal Singular values and Singular vectors Adaptation)

3.9.1 核心原理
复制代码
PiSSA的核心改进:
  LoRA的A、B矩阵是随机初始化的
  PiSSA用SVD分解来初始化,让LoRA一开始就"有意义"

方法:
  1. 对预训练权重W做SVD: W = U × Σ × V^T
  2. 用最大的r个奇异值和向量初始化LoRA
  3. 剩余的奇异值保留在残差中

  W = (U_r × Σ_r × V_r^T) + (U_remaining × Σ_remaining × V_remaining^T)
      ↑ LoRA部分(可训练)                ↑ 残差部分(冻结)

效果:
  - 收敛更快
  - 最终效果更好
  - 特别适合小数据集微调

3.10 LoRA家族总结

复制代码
LoRA家族算法对比:

算法      核心改进              额外参数    推理开销   效果提升
────────────────────────────────────────────────────────
LoRA      基准方法              ~0.2%       无(合并后) 基准
QLoRA     4bit量化基座          ~0.2%       无(合并后) 略低于LoRA
DoRA      方向-大小分解         ~0.25%      无(合并后) +1-3%
AdaLoRA   自适应秩分配          ~0.2%       无(合并后) +1-2%
rsLoRA    稳定缩放因子          ~0.2%       无(合并后) +0.5-1%
LoRA+     不同学习率            ~0.2%       无(合并后) +0.5-1%
GaLore    低秩梯度投影          0(全参数)   无        +1-3%
VeRA      共享随机矩阵          ~0.02%      无(合并后) 略低于LoRA
PiSSA     SVD初始化             ~0.2%       无(合并后) +1-2%
MosLoRA   多尺度LoRA            ~0.3%       无(合并后) +1-2%

第四章:前缀微调系列算法

4.1 Prefix Tuning (前缀微调)

4.1.1 核心原理
复制代码
Prefix Tuning的核心思想:
  在每一层的注意力计算中,为Key和Value添加可训练的"前缀"向量
  这些前缀向量就像是"虚拟token",引导模型的注意力分布

具体做法:
  原始注意力计算:
    Q = X × W_Q,  K = X × W_K,  V = X × W_V
    Attention = softmax(QK^T/√d) × V
  
  添加前缀后:
    K = [P_K; X × W_K],  V = [P_V; X × W_V]
    其中 P_K, P_V 是可训练的前缀矩阵

  前缀长度通常为10-200个token

┌─────────────────────────────────────────────┐
│           Prefix Tuning 示意图               │
│                                              │
│  可训练前缀: [P₁][P₂]...[Pₙ]                │
│              ↓                               │
│  每一层的K/V: [P_K₁, P_K₂, ..., x₁, x₂, ...] │
│              [P_V₁, P_V₂, ..., v₁, v₂, ...] │
│                                              │
│  原始模型参数: 冻结                           │
│  前缀参数: 可训练                             │
└─────────────────────────────────────────────┘
4.1.2 参数量
复制代码
前缀参数量 = n_layers × 2 × prefix_length × hidden_dim

示例(Qwen2.5-7B):
  n_layers = 28
  hidden_dim = 3584
  prefix_length = 20
  
  参数量 = 28 × 2 × 20 × 3584 = 4,014,080(约400万,占总参数0.005%)
4.1.3 代码实现
python 复制代码
from peft import PrefixTuningConfig, get_peft_model, TaskType

prefix_config = PrefixTuningConfig(
    task_type=TaskType.CAUSAL_LM,
    num_virtual_tokens=20,          # 前缀token数量
    prefix_projection=True,         # 是否使用投影层
    encoder_hidden_size=1024,       # 投影层隐藏维度
)

model = get_peft_model(base_model, prefix_config)
4.1.4 优缺点
复制代码
优点:
  ✓ 参数量极低
  ✓ 不修改模型结构
  ✓ 不同任务可以共享同一个基座模型
  ✓ 推理时只需替换前缀

缺点:
  ✗ 增加序列长度,有推理开销
  ✗ 占用有效上下文窗口
  ✗ 在复杂任务上效果通常不如LoRA
  ✗ 训练可能不稳定

4.2 P-Tuning v1

4.2.1 核心原理
复制代码
P-Tuning的核心思想:
  在输入层插入可训练的"伪token" embedding
  这些embedding不对应真实的词表token

与Prefix Tuning的区别:
  - Prefix Tuning: 在每一层都加前缀(K和V)
  - P-Tuning v1: 只在输入层加可训练embedding

实现方式:
  使用LSTM或MLP将可训练向量编码后插入输入序列
4.2.2 代码实现
python 复制代码
from peft import PromptEncoderConfig, get_peft_model, TaskType

p_tuning_config = PromptEncoderConfig(
    task_type=TaskType.CAUSAL_LM,
    num_virtual_tokens=20,
    encoder_hidden_size=1024,
    encoder_num_layers=2,           # 编码器层数
    encoder_dropout=0.1,
)

model = get_peft_model(base_model, p_tuning_config)

4.3 P-Tuning v2

4.3.1 核心原理
复制代码
P-Tuning v2 = Prefix Tuning的改进版

核心改进:
  1. 在每一层都添加可训练前缀(而不是只在输入层)
  2. 去掉了复杂的编码器(直接用可训练参数)
  3. 对不同任务有更好的泛化性

与Prefix Tuning的关系:
  P-Tuning v2 在技术上非常接近 Prefix Tuning
  主要区别在于实现细节和默认配置
4.3.2 代码实现
python 复制代码
from peft import PrefixTuningConfig, get_peft_model, TaskType

ptuning_v2_config = PrefixTuningConfig(
    task_type=TaskType.CAUSAL_LM,
    num_virtual_tokens=100,
    prefix_projection=False,        # P-Tuning v2通常不用投影
)

model = get_peft_model(base_model, ptuning_v2_config)

4.4 Multitask Prompt Tuning (MPT)

复制代码
核心思想:
  多个相关任务共享一个基础前缀
  每个任务有自己的任务特定前缀
  
  总前缀 = 共享前缀 + 任务特定前缀
  
  优势:
  - 共享前缀学到通用知识
  - 任务前缀学到特定知识
  - 多任务联合训练效果更好

第五章:适配器系列算法 (Adapter)

5.1 Houlsby Adapter

5.1.1 核心原理
复制代码
Adapter的核心思想:
  在Transformer的每一层中插入小型的"适配器"模块
  这些适配器是可训练的,原始模型参数冻结

Houlsby Adapter的结构:
  原始输出 → LayerNorm → Down-projection → 非线性激活 → Up-projection → 残差连接

  具体地:
  - Down-projection: h → W_down × h + b_down  (d → r, 如 4096 → 64)
  - 非线性激活: ReLU 或 GELU
  - Up-projection: h' → W_up × h' + b_up      (r → d, 如 64 → 4096)
  - 残差连接: output = adapter(h) + h

┌─────────────────────────────────────┐
│         Adapter模块结构              │
│                                      │
│  输入 x (维度d)                       │
│  ├──→ [Down Projection] → (维度r)    │
│  │         ↓                         │
│  │    [激活函数 ReLU]                 │
│  │         ↓                         │
│  │    [Up Projection] → (维度d)       │
│  │         ↓                         │
│  └──→ [残差相加]                      │
│         ↓                            │
│  输出 (维度d)                         │
└─────────────────────────────────────┘

瓶颈维度r是关键参数:
  r越小:参数越少,但表达能力越弱
  r越大:参数越多,但效果越好
  通常r取64-256
5.1.2 代码实现
python 复制代码
from peft import IA3Config, get_peft_model, TaskType
# 注:PEFT库中Adapter的实现可能需要自定义
# 以下是概念性代码

class AdapterLayer(nn.Module):
    def __init__(self, hidden_dim, adapter_dim=64):
        super().__init__()
        self.down_proj = nn.Linear(hidden_dim, adapter_dim)
        self.up_proj = nn.Linear(adapter_dim, hidden_dim)
        self.act = nn.ReLU()
        nn.init.zeros_(self.up_proj.weight)
        nn.init.zeros_(self.up_proj.bias)
    
    def forward(self, x):
        residual = x
        x = self.down_proj(x)
        x = self.act(x)
        x = self.up_proj(x)
        return x + residual

5.2 AdapterFusion

复制代码
核心思想:
  训练多个任务特定的Adapter
  然后用注意力机制融合它们的输出

方法:
  1. 在每个任务上分别训练Adapter
  2. 新任务到来时,冻结所有已训练的Adapter
  3. 训练一个注意力层来融合各Adapter的输出
  
  优势:
  - 可以利用多个任务的知识
  - 不需要重新训练
  - 新任务的学习不会影响旧任务

5.3 MAM Adapter

复制代码
核心思想:
  将Prefix Tuning、Adapter和LoRA的优势结合
  
  MAM = Mix-and-Match Adapter
  
  具体做法:
  - FFN层使用Adapter
  - 注意力层使用Prefix
  - 两者结合,取长补短
  
  效果:
  - 参数量和Prefix/Adapter相当
  - 效果优于单独使用任一方法

第六章:提示微调系列算法 (Prompt Tuning)

6.1 Prompt Tuning

6.1.1 核心原理
复制代码
Prompt Tuning的核心思想:
  在输入文本前面添加一组可训练的"软提示"(soft prompt) embedding
  这些embedding不对应任何真实的词表token

  原始输入: [x₁, x₂, ..., xₙ]  (n个token embedding)
  添加软提示: [p₁, p₂, ..., pₘ, x₁, x₂, ..., xₙ]  (m个可训练 + n个原始)

  训练时:只更新 p₁...pₘ
  模型参数完全冻结
6.1.2 与Prefix Tuning的区别
复制代码
Prompt Tuning:
  - 只在输入层添加可训练embedding
  - 参数量更少
  - 实现更简单

Prefix Tuning:
  - 在每一层的K和V都添加前缀
  - 参数量稍多
  - 效果通常更好

类比:
  Prompt Tuning = 只在门口放一个"提示牌"
  Prefix Tuning = 在每个房间都放一个"提示牌"
6.1.3 代码实现
python 复制代码
from peft import PromptTuningConfig, get_peft_model, TaskType, PromptTuningInit

prompt_config = PromptTuningConfig(
    task_type=TaskType.CAUSAL_LM,
    num_virtual_tokens=100,                    # 软提示长度
    prompt_tuning_init=PromptTuningInit.TEXT,   # 用文本初始化
    prompt_tuning_init_text="请回答以下问题:",  # 初始化文本
    tokenizer_name_or_path="Qwen/Qwen2.5-7B-Instruct",
)

model = get_peft_model(base_model, prompt_config)

6.2 WARP (Word-level Adversarial ReProgramming)

复制代码
核心思想:
  在输入层添加可训练的token embedding
  同时使用对抗训练来增强鲁棒性
  
  与Prompt Tuning的区别:
  - 增加了对抗扰动
  - 对输入变化更鲁棒

6.3 提示微调方法总结

复制代码
提示微调系列对比:

方法              插入位置         参数量    效果     适用场景
──────────────────────────────────────────────────────────────
Prompt Tuning    输入层           极少      一般     简单分类任务
P-Tuning v1      输入层+编码器    少        较好     生成任务
P-Tuning v2      每一层           少        好       通用
Prefix Tuning    每一层的K/V      少        好       通用
Multitask PT     每一层(多任务)   少        最好     多任务学习

共同特点:
  - 不修改模型结构
  - 参数量极少
  - 推理时有序列长度开销
  - 适合资源极其有限的场景

第七章:IA³与其他高效微调方法

7.1 IA³ (Infused Adapter by Inhibiting and Amplifying Inner Activations)

7.1.1 核心原理
复制代码
IA³的核心思想:
  不添加新模块,而是对现有激活值做可学习的缩放
  
  具体做法:
  - 对Key的输出乘以可学习向量 l_k
  - 对Value的输出乘以可学习向量 l_v
  - 对FFN的输出乘以可学习向量 l_ff
  
  数学表达:
  K' = l_k ⊙ K    (⊙ 表示逐元素乘法)
  V' = l_v ⊙ V
  FFN_out' = l_ff ⊙ FFN_out

  参数量极低:只有3个向量 × 层数
7.1.2 代码实现
python 复制代码
from peft import IA3Config, get_peft_model, TaskType

ia3_config = IA3Config(
    task_type=TaskType.CAUSAL_LM,
    target_modules=["k_proj", "v_proj", "down_proj"],  # 对这些层做缩放
    feedforward_modules=["down_proj"],                  # FFN层
)

model = get_peft_model(base_model, ia3_config)
7.1.3 参数量
复制代码
IA3的参数量(Qwen2.5-7B示例):
  每层需要3个缩放向量:
  - l_k: hidden_dim = 3584
  - l_v: hidden_dim = 3584
  - l_ff: ffn_dim = 18944
  
  28层总计: 28 × (3584 + 3584 + 18944) = 728,448(约73万参数)
  占总参数: 73万 / 70亿 = 0.001%  ← 极少!

7.2 BitFit

7.2.1 核心原理
复制代码
BitFit的核心发现:
  只训练模型中的bias参数,也能达到不错的效果!

  一个Transformer层中的bias参数:
  - Self-Attention: Q/K/V/O的bias
  - FFN: 两层的bias
  - LayerNorm: bias
  
  参数量:约0.1%的总参数

  为什么有效?
  - bias参数虽然少,但控制着每层输出的"基线"
  - 调整基线可以显著改变模型行为
  - 类似于调整"门控"信号
7.2.2 代码实现
python 复制代码
# BitFit的实现非常简单:冻结所有权重,只训练bias
for name, param in model.named_parameters():
    if "bias" not in name:
        param.requires_grad = False
    else:
        param.requires_grad = True

7.3 Compacter

复制代码
核心思想:
  Adapter的改进版,使用:
  1. 共享的超网络(HyperNetwork)生成Adapter参数
  2. 低秩分解进一步减少参数
  3. 结构化dropout
  
  比标准Adapter参数量少很多

7.4 S4 (Structured State Spaces for Sequence Modeling)

复制代码
虽然S4不是严格的微调方法,但在长序列微调中很有价值:
  - 基于状态空间模型(SSM)
  - 替代注意力机制处理长序列
  - 推理复杂度O(1),不像注意力的O(n²)
  - 最新的Mamba架构就是基于此

第八章:基于人类反馈的微调方法 (RLHF/DPO/GRPO)

8.1 RLHF (Reinforcement Learning from Human Feedback)

8.1.1 三阶段流程
复制代码
RLHF的完整流程:

阶段1:监督微调(SFT)
  数据:人工编写的高质量问答对
  方法:标准的监督学习
  目标:让模型学会基本的指令遵循能力

阶段2:奖励模型训练(RM)
  数据:人类对模型输出的排序(A比B好)
  方法:训练一个奖励模型,输入(问题,回答)→输出分数
  目标:让奖励模型学会人类的偏好

阶段3:PPO强化学习优化
  数据:模型生成的回答 + 奖励模型的评分
  方法:用PPO算法优化语言模型,最大化奖励模型的评分
  目标:让模型生成更符合人类偏好的回答
8.1.2 奖励模型训练
python 复制代码
from transformers import AutoModelForSequenceClassification
from trl import RewardTrainer, RewardConfig

# 奖励模型:输入(问题+回答) → 输出一个标量分数
reward_model = AutoModelForSequenceClassification.from_pretrained(
    "Qwen/Qwen2.5-7B-Instruct",
    num_labels=1,  # 输出一个标量分数
)

# 训练数据格式
# {"chosen": "好的回答", "rejected": "差的回答"}

reward_config = RewardConfig(
    output_dir="./reward_model",
    num_train_epochs=1,
    per_device_train_batch_size=2,
    learning_rate=1e-5,
)

trainer = RewardTrainer(
    model=reward_model,
    args=reward_config,
    train_dataset=dataset,
    processing_class=tokenizer,
)
trainer.train()
8.1.3 PPO训练
python 复制代码
from trl import PPOTrainer, PPOConfig, AutoModelForCausalLMWithValueHead

# 给语言模型加一个Value Head(用于PPO的Critic)
model = AutoModelForCausalLMWithValueHead.from_pretrained("sft_model_path")

ppo_config = PPOConfig(
    learning_rate=1e-5,
    batch_size=16,
    mini_batch_size=4,
    ppo_epochs=4,
    kl_penalty="kl",              # KL散度惩罚
    init_kl_coef=0.2,             # KL惩罚系数
    target_kl=6.0,                # 目标KL散度
)

ppo_trainer = PPOTrainer(
    config=ppo_config,
    model=model,
    ref_model=ref_model,          # 参考模型(SFT模型的副本)
    tokenizer=tokenizer,
    dataset=dataset,
)

# 训练循环
for batch in dataloader:
    # 1. 生成回答
    response_tensors = ppo_trainer.generate(batch["input_ids"])
    
    # 2. 奖励模型评分
    rewards = reward_model(batch["input_ids"], response_tensors)
    
    # 3. PPO更新
    stats = ppo_trainer.step(batch["input_ids"], response_tensors, rewards)

8.2 DPO (Direct Preference Optimization)

8.2.1 核心原理
复制代码
DPO的核心创新:
  不需要单独训练奖励模型!
  直接从偏好数据优化策略

  RLHF流程:  SFT → 训练RM → PPO优化  (三步)
  DPO流程:   SFT → DPO优化            (两步)

  DPO的损失函数:
  L_DPO = -E[log σ(β × (log π(y_w|x)/π_ref(y_w|x) - log π(y_l|x)/π_ref(y_l|x)))]
  
  其中:
  - π: 当前策略(正在优化的模型)
  - π_ref: 参考策略(SFT模型,冻结)
  - y_w: 人类偏好的回答(chosen)
  - y_l: 人类不偏好的回答(rejected)
  - β: 温度参数(控制偏离参考策略的程度)
  - σ: sigmoid函数
8.2.2 代码实现
python 复制代码
from trl import DPOTrainer, DPOConfig

# 训练数据格式
# {"prompt": "问题", "chosen": "好的回答", "rejected": "差的回答"}

dpo_config = DPOConfig(
    output_dir="./dpo_output",
    num_train_epochs=1,
    per_device_train_batch_size=2,
    learning_rate=5e-7,            # DPO用较小的学习率
    beta=0.1,                       # 温度参数
    loss_type="sigmoid",            # 损失类型
    remove_unused_columns=False,
)

# DPO需要两个模型:当前策略和参考模型
trainer = DPOTrainer(
    model=sft_model,                # 当前策略(会被更新)
    ref_model=ref_model,            # 参考模型(冻结)
    args=dpo_config,
    train_dataset=dataset,
    processing_class=tokenizer,
)
trainer.train()

8.3 KTO (Kahneman-Tversky Optimization)

8.3.1 核心原理
复制代码
KTO的核心改进:
  DPO需要成对的偏好数据(chosen, rejected)
  KTO只需要单独的"好"或"坏"标签

  为什么更好?
  - 收集成对偏好数据很昂贵
  - 收集单独的好/坏标签更容易
  - 例如:用户点"赞"就是好,点"踩"就是坏

  KTO的损失函数基于前景理论(Prospect Theory):
  - 人类对损失比收益更敏感
  - 损失厌恶系数 λ > 1
8.3.2 代码实现
python 复制代码
from trl import KTOTrainer, KTOConfig

# 训练数据格式
# {"prompt": "问题", "completion": "回答", "label": true/false}

kto_config = KTOConfig(
    output_dir="./kto_output",
    num_train_epochs=1,
    per_device_train_batch_size=2,
    learning_rate=5e-7,
    beta=0.1,
    desirable_weight=1.0,           # 好样本的权重
    undesirable_weight=1.0,         # 坏样本的权重
)

trainer = KTOTrainer(
    model=sft_model,
    ref_model=ref_model,
    args=kto_config,
    train_dataset=dataset,
    processing_class=tokenizer,
)
trainer.train()

8.4 ORPO (Odds Ratio Preference Optimization)

8.4.1 核心原理
复制代码
ORPO的核心创新:
  不需要参考模型!(DPO和KTO都需要)
  将SFT和偏好对齐合并为一步

  优势:
  - 训练流程更简单(一步到位)
  - 不需要维护两个模型
  - 显存需求更低

  损失函数 = SFT损失 + 偏好对齐损失
8.4.2 代码实现
python 复制代码
from trl import ORPOTrainer, ORPOConfig

orpo_config = ORPOConfig(
    output_dir="./orpo_output",
    num_train_epochs=1,
    per_device_train_batch_size=2,
    learning_rate=5e-7,
    beta=0.1,                       # ORPO的β参数
)

# ORPO不需要ref_model
trainer = ORPOTrainer(
    model=base_model,               # 直接从基座模型开始
    args=orpo_config,
    train_dataset=dataset,          # {"prompt", "chosen", "rejected"}
    processing_class=tokenizer,
)
trainer.train()

8.5 GRPO (Group Relative Policy Optimization)

8.5.1 核心原理
复制代码
GRPO的核心创新(DeepSeek提出):
  不需要Critic网络(PPO需要)
  用组内相对排名来估计优势函数

  方法:
  1. 对每个问题,生成一组回答(如8个)
  2. 用奖励模型或规则给每个回答打分
  3. 组内排名归一化作为优势估计
  4. 用PPO-Clip的目标函数更新策略

  优势:
  - 不需要训练Critic网络(省显存、省时间)
  - 奖励可以是稀疏的(只需要排名,不需要绝对分数)
  - 特别适合数学推理等有明确对错的任务
8.5.2 代码实现
python 复制代码
from trl import GRPOTrainer, GRPOConfig

grpo_config = GRPOConfig(
    output_dir="./grpo_output",
    num_train_epochs=1,
    per_device_train_batch_size=2,
    learning_rate=1e-6,
    num_generations=8,              # 每个问题生成8个回答
    max_completion_length=512,
    beta=0.04,                      # KL惩罚系数
)

def reward_func(completions, **kwargs):
    """奖励函数(可以用规则或奖励模型)"""
    rewards = []
    for completion in completions:
        # 这里用简单的规则判断
        if "正确" in completion or check_answer(completion):
            rewards.append(1.0)
        else:
            rewards.append(-1.0)
    return rewards

trainer = GRPOTrainer(
    model=base_model,
    args=grpo_config,
    train_dataset=dataset,          # {"prompt": "问题"}
    reward_funcs=reward_func,
    processing_class=tokenizer,
)
trainer.train()

8.6 SimPO (Simple Preference Optimization)

复制代码
核心改进:
  - DPO需要参考模型,SimPO不需要
  - 用序列长度归一化的对数概率作为隐式奖励
  - 引入了奖励边际(reward margin)参数γ

  损失函数:
  L_SimPO = -E[log σ(β/|y_w| × log π(y_w|x) - β/|y_l| × log π(y_l|x) - γ)]

  优势:
  - 更简单(不需要参考模型)
  - 更稳定
  - 效果通常优于DPO

8.7 偏好微调方法总结

复制代码
偏好微调方法对比:

方法    需要RM  需要Ref  数据格式           复杂度  效果
────────────────────────────────────────────────────────
RLHF   是      是      (prompt, response)  高      好
DPO    否      是      (chosen, rejected)  中      好
KTO    否      是      (好/坏标签)         低      较好
ORPO   否      否      (chosen, rejected)  低      好
GRPO   否      否      (prompt)            中      很好
SimPO  否      否      (chosen, rejected)  低      好

推荐:
  - 数据充足且有GPU资源 → RLHF/GRPO
  - 有成对偏好数据 → DPO/ORPO/SimPO
  - 只有好/坏标签 → KTO
  - 追求简单 → ORPO/SimPO
  - 数学推理任务 → GRPO

第九章:微调算法对比与选型指南

9.1 综合对比表

复制代码
┌──────────────────────────────────────────────────────────────────────┐
│                     微调算法综合对比                                   │
├──────────┬──────────┬──────────┬──────────┬──────────┬──────────────┤
│ 算法      │可训练参数 │ 训练显存  │ 推理开销  │ 效果     │ 适用场景      │
│          │ (7B模型) │ (7B模型) │          │          │              │
├──────────┼──────────┼──────────┼──────────┼──────────┼──────────────┤
│全量微调   │ 100%     │ >120GB   │ 无       │ ★★★★★  │ 数据充足+大GPU│
│LoRA      │ 0.1-0.5% │ 16-24GB  │ 无(合并) │ ★★★★   │ 通用推荐      │
│QLoRA     │ 0.1-0.5% │ 6-10GB   │ 无(合并) │ ★★★☆   │ 显存受限      │
│DoRA      │ 0.1-0.5% │ 16-24GB  │ 无(合并) │ ★★★★+  │ 追求更好效果  │
│AdaLoRA   │ 0.1-0.5% │ 18-26GB  │ 无(合并) │ ★★★★   │ 不确定最佳秩  │
│Prefix    │ <0.1%    │ 8-12GB   │ 有       │ ★★★    │ 极端资源受限  │
│P-Tuning  │ <0.1%    │ 8-12GB   │ 有       │ ★★★    │ 简单任务      │
│Adapter   │ 1-5%     │ 12-20GB  │ 有       │ ★★★☆   │ 多任务学习    │
│IA³       │ <0.01%   │ 8-10GB   │ 有       │ ★★☆    │ 极端资源受限  │
│BitFit    │ ~0.1%    │ 10-14GB  │ 无       │ ★★☆    │ 简单任务      │
│GaLore    │ 100%     │ 16-24GB  │ 无       │ ★★★★   │ 接近全量效果  │
├──────────┼──────────┼──────────┼──────────┼──────────┼──────────────┤
│DPO       │ 0.1-100% │ 32-48GB  │ 无       │ ★★★★   │ 偏好对齐      │
│KTO       │ 0.1-100% │ 16-32GB  │ 无       │ ★★★    │ 简单反馈      │
│ORPO      │ 0.1-100% │ 16-32GB  │ 无       │ ★★★★   │ 简单偏好对齐  │
│GRPO      │ 0.1-100% │ 24-40GB  │ 无       │ ★★★★★  │ 数学推理      │
│RLHF      │ 0.1-100% │ 64-120GB │ 无       │ ★★★★★  │ 全面对齐      │
└──────────┴──────────┴──────────┴──────────┴──────────┴──────────────┘

9.2 决策树

复制代码
我应该用什么微调方法?

Q1: 你有多少GPU显存?
├── <10GB → QLoRA / IA³ / Prefix Tuning
├── 10-24GB → LoRA (推荐) / QLoRA / DoRA
├── 24-80GB → LoRA / DoRA / GaLore / DPO
└── >80GB (多卡) → 全量微调 / RLHF

Q2: 你有多少训练数据?
├── <100条 → LoRA (小r值) / Prompt Tuning
├── 100-1000条 → LoRA / QLoRA
├── 1000-10000条 → LoRA / DoRA / 全量微调
└── >10000条 → 全量微调 / LoRA (大r值)

Q3: 你的任务是什么?
├── 知识注入 → LoRA / 全量微调
├── 格式对齐 → LoRA (小r值)
├── 偏好对齐 → DPO / ORPO / GRPO
├── 数学推理 → GRPO
└── 多任务 → Adapter / LoRA

Q4: 你需要多快的训练速度?
├── 最快 → IA³ / Prefix Tuning
├── 较快 → LoRA / QLoRA
└── 可以等待 → 全量微调 / RLHF

通用推荐:
  不确定用什么?→ 先试LoRA (r=16, lr=2e-4)
  效果不够?→ 增大r / 加DoRA / 换全量微调
  显存不够?→ 换QLoRA
  需要对齐?→ LoRA SFT → DPO

9.3 常见场景推荐

复制代码
场景1:企业知识问答系统
  推荐:LoRA SFT → DPO
  数据:2000-5000条问答对 + 500条偏好对
  预期效果:专业度提升50%+

场景2:代码生成助手
  推荐:LoRA SFT → GRPO
  数据:5000+代码样本
  特别注意:代码任务需要较长的max_seq_length

场景3:数学推理
  推荐:LoRA SFT → GRPO
  数据:数学题+解题过程
  GRPO特别适合:答案有明确对错

场景4:对话风格调整
  推荐:LoRA (小r=8) SFT
  数据:500-1000条风格样本
  不需要RLHF:风格调整用SFT就够了

场景5:极致资源受限(单张消费级GPU)
  推荐:QLoRA (r=8-16)
  数据:不限
  可以在RTX 3090/4090上微调7B模型

第十章:工程实践与最佳实践

10.1 数据质量比数量重要

复制代码
经验法则:
  - 100条高质量数据 > 1000条低质量数据
  - 数据质量检查清单:
    □ 回答是否准确?
    □ 格式是否一致?
    □ 是否有拼写/语法错误?
    □ 长度是否合适(不过长也不过短)?
    □ 是否覆盖了目标领域的关键场景?

10.2 学习率选择

复制代码
不同微调方法的推荐学习率:

全量微调: 1e-5 ~ 5e-5
LoRA:     1e-4 ~ 3e-4  (比全量微调大一个数量级)
QLoRA:    1e-4 ~ 2e-4
DPO:      5e-7 ~ 5e-6  (比SFT小一个数量级)
GRPO:     1e-6 ~ 1e-5
Prefix:   1e-3 ~ 5e-3  (比LoRA更大)

10.3 防止过拟合

复制代码
1. 使用验证集监控
2. Early Stopping
3. 增大LoRA的dropout
4. 减小LoRA的r值
5. 增大数据量或数据增强
6. 使用更小的学习率
7. 减少训练轮数

10.4 灾难性遗忘的应对

复制代码
1. 混合训练:在微调数据中混入一部分通用数据
2. 低学习率:使用较小的学习率
3. LoRA:天然减轻遗忘(原始参数不更新)
4. 弹性权重合并(EWC):保护重要参数
5. 正则化:对与预训练模型的差异施加惩罚

10.5 多阶段微调策略

复制代码
推荐的多阶段微调流程:

阶段1:SFT(监督微调)
  目标:注入领域知识
  数据:高质量问答对
  方法:LoRA

阶段2:偏好对齐(可选)
  目标:提升回答质量
  数据:偏好对数据
  方法:DPO / ORPO

阶段3:任务特定优化(可选)
  目标:针对特定任务优化
  数据:任务特定数据
  方法:LoRA (小r值) 精调

每个阶段的模型可以作为下一阶段的基座模型

附录:术语表与参考资料

术语表

复制代码
PEFT: Parameter-Efficient Fine-Tuning,参数高效微调
LoRA: Low-Rank Adaptation,低秩自适应
QLoRA: Quantized LoRA,量化LoRA
DoRA: Weight-Decomposed Low-Rank Adaptation
AdaLoRA: Adaptive LoRA
SFT: Supervised Fine-Tuning,监督微调
RLHF: Reinforcement Learning from Human Feedback
DPO: Direct Preference Optimization
GRPO: Group Relative Policy Optimization
KTO: Kahneman-Tversky Optimization
ORPO: Odds Ratio Preference Optimization
IA³: Infused Adapter by Inhibiting and Amplifying Inner Activations
KL散度: Kullback-Leibler Divergence,衡量两个分布的差异
BF16: Brain Float 16,一种16位浮点数格式
NF4: NormalFloat 4-bit,一种4bit量化格式
ZeRO: Zero Redundancy Optimizer,DeepSpeed的显存优化技术
AMP: Automatic Mixed Precision,自动混合精度

参考论文

复制代码
1. LoRA: Hu et al., "LoRA: Low-Rank Adaptation of Large Language Models", 2021
2. QLoRA: Dettmers et al., "QLoRA: Efficient Finetuning of Quantized LLMs", 2023
3. DoRA: Liu et al., "DoRA: Weight-Decomposed Low-Rank Adaptation", 2024
4. AdaLoRA: Zhang et al., "AdaLoRA: Adaptive Budget Allocation for PEFT", 2023
5. GaLore: Zhao et al., "GaLore: Memory-Efficient LLM Training by Gradient Low-Rank Projection", 2024
6. Prefix Tuning: Li & Liang, "Prefix-Tuning: Optimizing Continuous Prompts for Generation", 2021
7. P-Tuning v2: Liu et al., "P-Tuning v2: Prompt Tuning Can Be Comparable to Fine-tuning", 2022
8. Adapter: Houlsby et al., "Parameter-Efficient Transfer Learning for NLP", 2019
9. IA³: Liu et al., "Few-Shot Parameter-Efficient Fine-Tuning is Better and Cheaper than In-Context Learning", 2022
10. DPO: Rafailov et al., "Direct Preference Optimization", 2023
11. RLHF: Ouyang et al., "Training language models to follow instructions with human feedback", 2022
12. GRPO: Shao et al., "DeepSeekMath: Pushing the Limits of Mathematical Reasoning", 2024
13. KTO: Ethayarajh et al., "KTO: Model Alignment as Prospect Theoretic Optimization", 2024
14. ORPO: Hong et al., "ORPO: Monolithic Preference Optimization without Reference Model", 2024
15. SimPO: Meng et al., "SimPO: Simple Preference Optimization with a Reference-Free Reward", 2024