微调教程小测验 - 答案
选择题答案
1. 答案:B
labels = input_ids.clone() 让模型预测整个序列中每个位置的下一个token。这是自回归语言模型的标准训练方式——模型在每个位置上预测下一个token,labels就是向右移动一位的input_ids。HuggingFace的CausalLM内部会自动处理这个shift操作。
2. 答案:B
r 是LoRA低秩分解中矩阵的秩。原始权重矩阵 $W \in \mathbb{R}^{d \times d}$,LoRA将变化量分解为 $\Delta W = BA$,其中 $B \in \mathbb{R}^{d \times r}$,$A \in \mathbb{R}^{r \times d}$。r=16 意味着使用秩为16的低秩矩阵来近似权重变化,新增参数量为 $2 \times d \times r$。
3. 答案:D
bnb_4bit_gradient_accumulation 不是BitsAndBytes的配置参数。正确的4-bit量化参数包括:
bnb_4bit_use_double_quant:是否对量化常数二次量化bnb_4bit_quant_type:量化类型(“nf4"或"fp4”)bnb_4bit_compute_dtype:计算时的数据类型
梯度累积(gradient accumulation)是训练参数,在 TrainingArguments 中配置。
4. 答案:B
在NEFT的噪声幅度公式 alpha / sqrt(L * d) 中:
L= 序列长度(input_embeddings.size(1))d= 嵌入维度(input_embeddings.size(2))
对应代码:
| |
这样设计保证了噪声的相对强度不随序列长度和嵌入维度的变化而变化。
5. 答案:C Loss Mask的作用是只对助手回答部分计算损失,忽略system prompt和user问题部分的损失。在标准SFT中,模型会对整个序列计算loss,但实际上我们只希望模型学会"如何回答"。Loss Mask通过将prompt部分的loss置为0来实现这一目标。
6. 答案:B
"offload_optimizer": {"device": "none"} 表示优化器状态保留在GPU显存中,不卸载到CPU。如果设置为 "cpu",优化器状态会被卸载到CPU内存,可以进一步节约GPU显存,但会因为CPU-GPU数据传输而降低训练速度。
7. 答案:C
use_gradient_checkpointing="unsloth" 使用Unsloth团队优化过的梯度检查点实现,比标准的 True(PyTorch原生实现)节约约30%的VRAM,并且能支持2倍大的batch size。这是通过更精细的计算图管理和内存复用实现的。
8. 答案:B 很多Causal LM(如Llama系列)的tokenizer没有专门定义pad_token,因为预训练时不需要padding。但在微调时,batch中不同长度的序列需要padding到相同长度,所以必须指定pad_token。将其设为eos_token是最常见的做法,不会影响训练效果,因为attention_mask会正确标识padding位置。
9. 答案:C 有效batch_size = per_device_train_batch_size * gradient_accumulation_steps * num_gpus = 1 * 8 * 1 = 8。单GPU情况下num_gpus=1。gradient_accumulation的作用是累积8个micro-batch的梯度后再执行一次参数更新,模拟batch_size=8的效果。
10. 答案:B
在使用accelerate/DeepSpeed进行分布式训练时,设备分配由accelerate框架自动管理。如果手动设置 device_map="auto",会与accelerate的设备分配策略冲突,导致错误或意外行为。代码注释中明确写道:“在使用 accelerate 进行分布式训练时,不需要手动指定 device_map,accelerate 会自动处理设备分配”。
简答题答案
11. 为什么必须添加EOS_TOKEN?
EOS_TOKEN(End of Sequence Token)是告诉模型"回答到此结束"的信号。在训练数据末尾添加EOS_TOKEN后,模型会学习到在回答完成后生成这个特殊token。
如果不添加EOS_TOKEN:
- 模型在推理时不知道何时停止生成
- 会出现"无限生成"的问题——模型不断输出重复或无意义的文本,直到达到max_tokens限制
- 生成质量严重下降
代码中的实现:
| |
12. 两种损失归一化方式的区别
方式一:loss.sum() / loss_mask.sum()(推荐)
- 只计算mask为1的token的平均loss
- 假设100个token中有30个是response部分(mask=1),loss = 这30个token的loss之和 / 30
- 梯度信号集中、准确
方式二:torch.mean(loss)(不推荐)
- 对所有token(包括被mask为0的)计算平均
- 假设100个token中有30个是response部分,loss = 这30个token的loss之和 / 100
- 梯度信号被大量零值稀释,有效梯度被缩小为原来的 30/100 = 0.3倍
- 相当于隐式降低了学习率,训练效率低
方式一更合理,因为它准确反映了模型在response部分的预测质量,梯度信号不被稀释。
13. lora_alpha与r的关系
LoRA的实际缩放公式为:output = W*x + (lora_alpha / r) * B*A*x
缩放比例 = lora_alpha / r,控制LoRA旁路对原始权重的影响强度。
当 r=16, lora_alpha=32 时:
- 缩放比例 = 32 / 16 = 2
- 这意味着LoRA旁路的输出会乘以2再加到原始输出上
- LoRA旁路的影响力是默认(缩放比例=1)时的2倍
- 模型更容易学到新的行为模式,但也更容易偏离预训练知识
常见设置:
lora_alpha = r:缩放比例=1,最平衡的设置lora_alpha = 2*r:缩放比例=2,适合需要较大适应的场景- 代码中两处实战均使用
r=16, lora_alpha=16,即缩放比例=1
14. 法律数据格式转换的映射关系
原始JSONL格式(每行一个JSON对象):
| |
转换后的LLaMA-Factory格式(整体一个JSON数组):
| |
关键字段映射:
| 原始字段 | 目标字段 | 说明 |
|---|---|---|
input | conversations[0]["value"] | 用户输入,from 设为 "human" |
output | conversations[1]["value"] | 模型输出,from 设为 "gpt" |
| (无) | system | 系统提示,此处为空字符串 |
| (无) | tools | 工具定义,此处为空字符串 |
文件格式也从逐行JSONL变为整体JSON数组,使用 json.dump 而非逐行写入。
15. DeepSpeed配置中"auto"值的协同机制
在DeepSpeed的JSON配置文件中,将参数设为 "auto" 意味着该参数的值将在运行时自动从HuggingFace的 TrainingArguments 中获取。
工作流程:
- 用户在
TrainingArguments中设置超参数(如learning_rate=1e-4, weight_decay=0.01) SFTTrainer(或Trainer)初始化时,检测到使用了DeepSpeed- Trainer会解析DeepSpeed配置文件,将所有
"auto"值替换为TrainingArguments中的对应值 - 替换关系如下:
| DeepSpeed配置 | TrainingArguments来源 |
|---|---|
optimizer.params.lr: "auto" | learning_rate |
optimizer.params.weight_decay: "auto" | weight_decay |
scheduler.params.warmup_num_steps: "auto" | 根据 warmup_ratio 计算 |
gradient_accumulation_steps: "auto" | gradient_accumulation_steps |
gradient_clipping: "auto" | max_grad_norm |
train_micro_batch_size_per_gpu: "auto" | per_device_train_batch_size |
这种设计的好处是:避免在两个配置文件中重复设定同一个参数,减少配置不一致导致的bug。用户只需在 TrainingArguments 中设置一次,DeepSpeed会自动同步。