跳转至

拼多多大模型面经

自我介绍

微调采用的什么模型?

微调时采用的模型通常是预训练好的基座模型,如 LLaMA 系列、Qwen 系列、DeepSeek 系列、GPT 系列等。具体选择取决于任务需求、资源预算和性能要求。例如:

  • 若任务偏重中文和多语言,可能选 Qwen;

  • 若追求开源生态和长文本能力,可能选 LLaMA 或 Mistral;

  • 若对推理效率要求极高,可能选 DeepSeek(MLA 等优化);

  • 若需处理代码或数学,可选用专门强化过这些能力的版本(如 CodeLLaMA、DeepSeek-Coder 等)。

在项目实践中,微调模型可以是开源基座 + 领域数据 SFT + 偏好对齐(DPO/RLHF)的组合。


LoRA 的 rank 怎么设置的?

LoRA 的秩(rank)是一个关键超参数,通常通过以下原则和经验设定:

  • 典型范围:对于大语言模型微调,rank 一般选在 4 到 32 之间,常用 8 或 16。

  • 任务复杂度:

  • 简单任务(如情感分类、短文本生成):rank=4 或 8 足够。
  • 复杂任务(如多轮对话、代码生成、领域知识密集):rank=16 或 32 更合适。

  • 模型规模:对于 7B-13B 的模型,rank=8~16 通常效果很好;对于超大模型,rank 甚至可以更低(如 4),因为模型本身已有很强的表达能力。

  • 数据量:数据量少时,rank 应适当减小以防止过拟合;数据量大时可适当增大。

  • alpha 的配合:LoRA 中 alpha 常设为 rank 的 2 倍(如 rank=8, alpha=16),起到缩放作用,实际有效更新幅度与 rank/alpha 比例相关。通常保持 alpha=2*rank 即可,调整学习率来补偿。

  • 实验确定:可通过小规模搜索,观察验证损失和下游指标,选取性能趋于饱和且不显著增加参数的 rank。


DPO 和 GRPO 的区别

DPO(Direct Preference Optimization):

  • 核心思想:直接利用人类偏好数据,通过重新参数化奖励函数,将 RLHF 中的奖励模型训练和策略优化两个阶段合并为一个监督学习阶段。

  • 目标函数:最大化偏好对中“优选回复”相对于“劣选回复”的对数概率比,无需显式训练奖励模型,也无需强化学习。

  • 优点:实现简单,训练稳定,计算成本低,不需要维护额外的奖励模型和 critic 网络。

  • 缺点:对偏好数据的质量敏感;缺乏显式的探索机制,可能陷入局部最优。

GRPO(Group Relative Policy Optimization,可能是 DeepSeek 提出的变体):

  • 核心思想:属于在线策略优化方法,通过为每个 prompt 采样一组回复,并利用组内相对比较来构造优势函数,无需外部奖励模型或价值函数。

  • 特点:无需单独训练 critic,直接使用同一 prompt 下多个回复的平均奖励作为基线,计算相对优势,然后优化策略。这类似于一种基于“组内对比”的 REINFORCE 风格算法。

  • 优点:简单,避免了价值函数估计的 bias 和方差问题;利用组内比较减少了绝对奖励尺度的影响。

  • 与 DPO 的关键区别:

  • DPO 是离线方法,依赖预先收集的偏好对;GRPO 是在线方法,通过实时采样和组内比较进行优化。
  • DPO 直接拟合偏好概率;GRPO 使用策略梯度,本质是强化学习,但裁剪掉了价值网络。
  • GRPO 具有探索性,能在训练中动态改进;DPO 完全受限于给定的偏好数据分布。

你做过全量微调吗,怎么估计全量微调的显存?

全量微调(Full Fine-tuning)需要更新模型的所有参数,显存占用主要包括:模型参数、梯度、优化器状态、激活值和临时缓冲区。估算方法如下:

以 FP16 混合精度训练、AdamW 优化器为例:

  • 模型参数:每参数 2 字节(FP16)。

  • 梯度:每参数 2 字节(FP16)。

  • 优化器状态:AdamW 保存一阶动量(m)和二阶动量(v),均为 FP32,每参数 4+4=8 字节。

  • 总基础占用:对于 P 个参数的模型,基础显存 ≈ P × (2 + 2 + 8) = 12P 字节。

额外部分:

  • 激活值:取决于序列长度、隐藏维度、层数、batch size。通常近似为 2-4 倍基础显存。可用公式估算:激活值 ≈ batch_size × seq_len × hidden_dim × 层数 × 常数因子。对于长序列和大 batch,激活值可能成为主要瓶颈。

  • 临时缓冲区:如 FlashAttention 的中间结果,通常较小。

实际经验值(以 LLaMA-7B 为例,P≈7B):

  • 基础 12P ≈ 84 GB(FP16 参数 14 GB + 梯度 14 GB + 优化器状态 56 GB)。

  • 加上激活值和临时开销,一张 80GB A100 通常无法用 FP16 全量微调 7B 模型(batch=1 勉强,但 batch>1 极易 OOM)。常用策略:

  • 使用 BF16 + FP32 优化器状态,显存与 FP16 类似。
  • 梯度检查点(Activation Checkpointing):以时间换空间,大幅降低激活显存。
  • ZeRO 优化:将优化器状态和梯度分片到多个 GPU。
  • 减少 batch size 或使用梯度累积。

  • 粗略估算全量微调 7B 模型所需总显存 ≈ 100-150 GB(取决于序列长度和 batch size),因此通常需要多卡或使用 LoRA 等参数高效方法。


BN 和 LN

Batch Normalization (BN):

  • 归一化维度:对 mini-batch 内每个特征维度,跨样本计算均值和方差。

image.png

  • 依赖:依赖于 batch size,batch 过小时统计量不稳定。

  • 适用场景:计算机视觉中的卷积网络,因为特征图的空间维度共享统计量。

  • 在 NLP/Transformer 中:RNN 和 Transformer 通常不用 BN,因为序列长度不一,padding 会导致统计量不准确。

Layer Normalization (LN):

  • 归一化维度:对每个样本独立地,跨特征维度计算均值和方差。

image.png

  • 依赖:完全不依赖 batch size,每个样本独立归一化。

  • 适用场景:Transformer 架构的标准选择,因为其自然适应变长序列和小 batch。

核心区别:BN 是“跨样本”归一化,LN 是“跨特征”归一化。Transformer 中 LN 几乎是唯一选择。

变体 RMSNorm:LN 的简化版,仅使用均方根进行缩放,不减去均值,计算更快,已成为 LLM 标配(如 LLaMA、Qwen 等)。


训练 batch size、learning rate 等超参设置

这些超参相互影响,典型设置如下:

  • Batch size:
  • 微调时 global batch size 通常设为 32 到 256。受限于显存,实际 micro batch size 可能为 1-4,通过梯度累积达到目标 global batch size。
  • 较小 batch 带来更多噪声,可能有助于泛化;较大 batch 训练更稳定,但需相应调整学习率。

  • Learning rate:

  • 全量微调:1e-5 到 5e-5(如 LLaMA 官方推荐 2e-5)。
  • LoRA 微调:1e-4 到 5e-4(因为仅更新少量参数,可承受更大学习率)。
  • 通常配合余弦退火(Cosine Annealing) 调度,并带有 warmup(占总步数 5%-10%)。

  • Weight decay:0.01 到 0.1,对偏置和 LayerNorm 参数通常不施加衰减。

  • Warmup:从 0 线性增加到峰值学习率,有助于训练初期稳定。

  • Gradient clipping:范数阈值通常设为 1.0。

  • 优化器:AdamW,β1=0.9, β2=0.95 或 0.999。

经验公式:学习率与 batch size 近似满足线性缩放规律(例如,batch 增大 2 倍,学习率可增大 √2 倍或保持),但通常通过小规模实验确定。


旋转位置编码(RoPE)

旋转位置编码是一种为 Transformer 注入位置信息的方法,广泛应用于 LLaMA、Qwen、Mistral 等模型。

核心原理:

  • 通过对 Query 和 Key 向量在每对维度上施加旋转变换,使得两个 token 的点积结果仅依赖于它们的相对位置,而非绝对位置。

  • 对于位置 m 和 n,其旋转后的 Q、K 的点积为:

image.png

  • 其中 是一个二维旋转矩阵,角度与频率相关。

image.png

优势:

  • 天然编码相对位置,具有平移不变性。

  • 易于通过插值或 NTK 缩放扩展到更长的序列。

  • 可与线性注意力兼容。

外推:通过调整基频(如动态 NTK)或 YaRN 等技术,可使 RoPE 支持远超训练长度的上下文。


手撕:

搜索二维矩阵(未明确具体问题,但通常指“搜索有序二维矩阵”)

假设问题是:在一个每行递增、每列递增的二维矩阵中搜索目标值。典型解法有两种:

解法一:从右上角或左下角开始线性扫描

  • 从矩阵的右上角开始(row=0, col=n-1)。

  • 若当前元素等于 target,返回 true。

  • 若当前元素 > target,则列索引左移(排除当前列)。

  • 若当前元素 < target,则行索引下移(排除当前行)。

  • 时间复杂度 O(m+n),空间 O(1)。

解法二:二分查找

  • 对每一行(或列)执行二分查找,O(m log n) 或 O(n log m)。更优的可以直接定位可能行然后二分,但不如上述线性方法简洁。

代码示例(右上角法):

def searchMatrix(matrix, target):
    if not matrix or not matrix[0]:
        return False
    rows, cols = len(matrix), len(matrix[0])
    r, c = 0, cols - 1
    while r < rows and c >= 0:
        if matrix[r][c] == target:
            return True
        elif matrix[r][c] > target:
            c -= 1
        else:
            r += 1
    return False