实验
LoRA
以一个微调例子学习 LoRA。
微调 Qwen3-8B ,在 Tweetteval 数据集上学会情感句子三分类。了解到 Prompt Masking 、梯度累加降低显存占用。
LoRA 实验
LoRA 公式
参数设置:尤其和显存相关
详细解释一下大模型这里面的参数
CFG = dict(model_id='Qwen/Qwen3-8B',
dataset_id='cardiffnlp/tweet_eval', dataset_config='sentiment', dataset_revision='b3a375baf0f409c77e6bc7aa35102b7b3534f8be',
seed=42, train_size=16000, validation_size=1000, test_size=3000,
max_length=512, max_review_tokens=512, eval_batch_size=8, max_new_tokens=8,
r=8, lora_alpha=16, lora_dropout=0.05, target_modules=['q_proj', 'v_proj'],
train_batch_size=1, gradient_accumulation_steps=8, epochs=3, learning_rate=1e-4,
warmup_ratio=0.05, weight_decay=0.0, scheduler='linear', optim='adamw_torch',
enable_thinking=False, do_sample=False, num_beams=1)
-
max_length=512限定死训练 Token 序列的最大长度。-
首先知道,模型在训练的时候 Token 序列有个最大长度。 这是模型的最大上下文窗口。推理时,如果遇到超出训练时模型见过的最大 Token 序列长度,模型在长位置的理解能力变差(模型没在这些位置训练过),模型能力会下降。所以衡量看一个模型强不强的指标之一,看看他支持的最大上下文长度。
-
现代大模型训练,是保证一个 batch 内所有Token 序列长度一致,不足的序列 pad 到最长的那个序列;
-
Qwen3-8B 上下文窗口32K,为啥要定死到 512?省显存。Q K V 张量都有一个维度是序列长度 S,S 越大,显存占用越多;
-
提到显存,LoRA 时占用显存的都是什么?
1 原来模型的权重参数依然全部加载进显存;8B、BF16 大概占 15GiB;(原始权重依然参与前向计算,但是参数不更新)
2 前向传播的激活值:
3 需要更新的参数梯度(LoRA 省显存的原因);LoRA 的矩阵 A B 的参数的梯度都要保存;因为参数更新的方式:
AdamW 要保存两个矩阵:一阶矩和二阶矩的平方。因为原矩阵不用更新,不用保存梯度,所以省了很大显存,
4 LoRA 本身的参数
-
-
LoRA 配置
r=8; r 越小,对显存要求越低,比较保守的选择,适合小任务;
lora_alpha=16,低秩矩阵的系数;
lora_dropout=0.05 随机让神经网失活,防止过拟合;
target_modules = [“q_proj”, “v_proj”] 对每一个 Transformer Block,在 Q V 的投影矩阵注入 LoRA。 这是 一个比较小的配置。
总体参考了 huggingface 教程的设置。
答案怎么计入 Loss:Prompt Masking 屏蔽提示词
监督微调和指令微调的通用标准。
一个大模型的训练经过两个阶段:预训练、后训练。
预训练阶段:无监督,让模型根据海量文本,学会预测下一个 Token;模型这一步只是学会了人类世界的语法,他不会聊天(答非所问);
后训练:就是我们的监督微调Supervised Fine-Tuning。我们是用 Chat Template 去微调大模型,送个大模型的有 system prompt, assiatant 的思考过程 和 真正需要回答的问题。
举一个例子:对于一个情感二分类问题,我们想要模型判断的句子是:
I saw a dog yesterday, that looked like one of the wolves from twilight but smaller, was well cool
真正送入模型的文本是:
<|im_start|>system
You are a sentiment classifier. Output exactly one label: negative, neutral, or positive.<|im_end|>
<|im_start|>user
Classify the sentiment of this tweet: "I saw a dog yesterday, that looked like one of the wolves from twilight but smaller, was well cool"<|im_end|>
<|im_start|>assistant
<think>
</think>
前面有一大段都是 prompt。只有 cool 对应的 Token 才参与计算 Loss。
哪些 Token 做前向计算?
所有 Token 全部参与前向计算;
哪些 Token 参与 Loss 计算?(前面提到,正常一个 Batch 所有句子的所有 Token 前向传播得到的算损失都会累加)
只有 要预测 Positive 的那个 Token 参与计算 Loss(句子的末尾):cool 对应的 Token ,预测的下一个词 要 和正确答案计算损失。(其实还有<|im_end|>也计算损失,教会模型什么时候停止)
为什么要Prompt Masking or 为什么前面的 Token 不参与计算 Loss?
训练时候一个 Batch 的所有句子都前向传播,预测下一个输出。
我们想看模型能不能预测出 Positive。不关注模型在 dog 这个位置有没有正确预测出 yesterday。让训练集中在预测类别上。
屏蔽 Token 是什么意思?在计算上对应什么?
不参与Loss 计算。
代码是怎么实现的?
input_ids 会记录所有 Token 的 id,模型照常读取,前向传播,但是被屏蔽的 Token 对应的 label 是-100. -100 是 Magic Number。Pytorch 的很多分类损失函数,看到 label=-100 时,不计算损失。
下标是真实训练时,Token 和对应的 Label:
| position | input_id | token | attention_mask | label | |
|---|---|---|---|---|---|
| 55 | 55 | 6521 | Wed | 1 | -100 |
| 56 | 56 | 13 | . | 1 | -100 |
| 57 | 57 | 55547 | Absolutely | 1 | -100 |
| 58 | 58 | 10245 | loved | 1 | -100 |
| 59 | 59 | 279 | the | 1 | -100 |
| 60 | 60 | 1473 | show | 1 | -100 |
| 61 | 61 | 0 | ! | 1 | -100 |
| 62 | 62 | 151645 | <|im_end|> | 1 | -100 |
| 63 | 63 | 198 | \n | 1 | -100 |
| 64 | 64 | 151644 | <|im_start|> | 1 | -100 |
| 65 | 65 | 77091 | assistant | 1 | -100 |
| 66 | 66 | 198 | \n | 1 | -100 |
| 67 | 67 | 151667 | 1 | -100 | |
| 68 | 68 | 271 | \n\n | 1 | -100 |
| 69 | 69 | 151668 | 1 | -100 | |
| 70 | 70 | 271 | \n\n | 1 | -100 |
| 71 | 71 | 30487 | positive | 1 | 30487 |
| 72 | 72 | 151645 | <|im_end|> | 1 | 151645 |
注入 LoRA
lora_config=LoraConfig(r=CFG['r'],lora_alpha=CFG['lora_alpha'],lora_dropout=CFG['lora_dropout'],
target_modules=CFG['target_modules'],bias='none',task_type='CAUSAL_LM')
model=get_peft_model(model,lora_config)
model.print_trainable_parameters() # 打印参数;
# trainable params: 3,833,856 || all params: 8,194,569,216 || trainable%: 0.0468
最终效果
| 指标 | 微调前 | 微调后 | 变化 |
|---|---|---|---|
| Accuracy | 66.37% | 73.23% | +6.87 |
| 正面 F1 | 63.20% | 80.42% | +17.21 |
| 负面 F1 | 76.65% | 78.58% | +1.93 |
| 中性 F1 | 59.48% | 59.24% | −0.24 |
| Macro-F1 | 66.44% | 72.75% | +6.30 |