拼多多大模型面经
自我介绍¶
微调采用的什么模型?¶
微调时采用的模型通常是预训练好的基座模型,如 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 内每个特征维度,跨样本计算均值和方差。

-
依赖:依赖于 batch size,batch 过小时统计量不稳定。
-
适用场景:计算机视觉中的卷积网络,因为特征图的空间维度共享统计量。
-
在 NLP/Transformer 中:RNN 和 Transformer 通常不用 BN,因为序列长度不一,padding 会导致统计量不准确。
Layer Normalization (LN):
- 归一化维度:对每个样本独立地,跨特征维度计算均值和方差。

-
依赖:完全不依赖 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 的点积为:

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

优势:
-
天然编码相对位置,具有平移不变性。
-
易于通过插值或 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