SFT(三):使用TRL SFTTrainer与LoRA微调对话模型
前两篇已经完成了两层理解: SFT 原理篇:SFT 仍然是 next-token prediction,关键在于数据、Chat Template 和 Loss Mask。 Transformers 手写篇:亲手构造 input_ids / attention_mask / labels,再交给原生 Trainer。 现在可以进入更接近真实项目的组合: Hugging Face Datasets + Transformers + TRL SFTTrainer + PEFT LoRA 本篇不会把 SFTTrainer 描述成“一行代码训练大模型”。它确实减少了很多样板代码,但仍然需要对下面这些问题负责: 数据格式是否正确。 Chat Template 是否与模型匹配。 Assistant-only loss 是否真的生效。 截断是否删除了回答。 LoRA 是否插入了预期模块。 训练与验证是否存在数据泄漏。 保存的是完整模型还是 Adapter。 推理时是否加载了正确的 Base Model。 1. TRL 是什么TRL 是 Hugging Face 生态中...
SFT(二):用Hugging Face Transformers手写监督微调
上一篇 SFT 原理篇已经把监督微调还原成 next-token prediction: Prompt 与标准答案 -> 一串 Causal LM token -> Prompt 对应 labels 设为 -100 -> 模型内部进行 one-token shift -> 只在 Answer token 上计算交叉熵 这一篇不使用 TRL 的 SFTTrainer,而只部分的 Hugging Face 组件: Dataset。 AutoTokenizer。 AutoModelForCausalLM。 forward()。 Trainer 与 TrainingArguments。 GenerationConfig 与 generate()。 目标不是重复造一个完善的训练框架,而是让我能亲眼看到 SFT 中最重要的三组张量: input_ids attention_mask labels 等这些张量完全对上后,下一篇再使用 TRL SFTTrainer + LoRA,就不会把自动化误认为魔法。 1. 本篇要训练什么我们使用一个小型 Causal Lang...
SFT(一):从Next Token Prediction到指令微调
在手写 Mini-GPT中,也已经从底层走通过: Prompt -> Tokenizer -> Embedding -> Causal Transformer -> LM Head -> logits -> sampling -> next token 但是理解推理流程后,新的问题随之出现: 一个只会续写文本的 Base Model,为什么经过训练后会开始理解“用户提问”和“助手回答”,并按照指令组织答案? 这就是本文要讨论的 Supervised Fine-Tuning,监督微调,简称 SFT。 本文不会从“调用一个 Trainer”开始,而会先把 SFT 还原成我已经熟悉的 next-token prediction。等把数据、标签和损失看清楚后,下一篇再用 Hugging Face Transformers 亲手实现,第三篇再进入 TRL SFTTrainer + LoRA。 1. SFT 到底是什么SFT 的完整名称是 Supervised Fine-Tuning。 它表示: 从一个已经预训练的模型出发,使用带有目标答案的监督数据...
Hugging Face详解:从模型下载到中文文本分类微调
前面的文章已经从零实现了 Transformer、Mini-GPT 和 RoPE。理解这些底层原理之后,一个很自然的问题是: 实际项目中,难道每次都要从头实现网络、训练 Tokenizer,再用海量数据预训练模型吗? 通常不需要。 很多任务可以从一个已经预训练好的模型出发,只添加或替换任务头,再使用自己的数据进行微调。Hugging Face 正是把“寻找模型、下载权重、统一调用、处理数据、训练评估和分享结果”串起来的一套生态。 不过,初学者很容易把下面这些概念混在一起: Hugging Face 网站。 Hugging Face Hub。 transformers Python 库。 datasets Python 库。 pipeline()。 AutoTokenizer 与 AutoModel。 Trainer。 Spaces 与在线推理服务。 本文会先把它们拆开,再通过一个中文情感分类项目完整走通: 选择预训练模型 -> 加载 Tokenizer -> 文本转成模型输入 -> 加载任务模型 -> 手动完成一次前向推理 -> 构造 Dataset ...
RoPE详解:从二维旋转到PyTorch手写旋转位置编码
前面的 Transformer 和 Mini-GPT 文章分别使用了固定正弦位置编码与可学习绝对位置向量。它们都在解决同一个问题: Self-Attention 可以比较 token 内容,却不会天然知道 token 的先后顺序。 这篇文章继续研究另一种位置编码:RoPE(Rotary Position Embedding,旋转位置编码)。 RoPE 的做法很特别。它不把位置向量直接加到 token 表示上,而是在每个注意力头内部,把 Query 和 Key 按位置旋转不同角度: token hidden state -> 线性投影得到 Q、K、V -> 根据 position 旋转 Q 和 K -> 计算 QK^T -> Softmax -> 对没有旋转的 V 加权求和 它的核心价值可以浓缩为一句话: 用绝对位置决定旋转角度,让 Query 与 Key 的点积自然只显式依赖相对位置。 本文会从二维向量开始,逐步回答下面的问题: 为什么旋转能够表示位置? 为什么两个绝对位置最后会变成一个相对距离? 为什么只旋转 Q 和 K,不旋转 V? 高维向量怎...
手写Mini-GPT:从因果语言模型到PyTorch完整实现
上一篇文章从 Encoder-Decoder 结构出发,完整介绍了 Transformer 的 Self-Attention、Multi-Head Attention、位置编码、Mask、Encoder 和 Decoder。 这一次,我们不再调用 nn.Transformer,而是只使用 PyTorch 的基础层,从零手写一个能够训练和生成文本的 Mini-GPT。 它会包含 GPT 最核心的组件: 字符级 Tokenizer。 Token Embedding 和可学习 Position Embedding。 手写 Multi-Head Causal Self-Attention。 Pre-Norm Transformer Block。 GELU 前馈网络。 自回归语言模型损失。 AdamW、学习率 warmup、余弦衰减和梯度裁剪。 Temperature、Top-k 与多项式采样。 权重绑定和 KV Cache 原理。 最终模型只有约 35 万个参数,可以在 CPU 上训练。它当然不是 ChatGPT,也不会拥有通用知识,但它足以把 GPT 内部最重要的数据流完整跑通。 ...
Transformer详解:从自注意力手算到PyTorch完整实现
上一篇 Seq2Seq 文章中,我们使用 GRU 构造了 Encoder-Decoder,并通过 Attention 缓解“把整句话压缩成一个向量”的信息瓶颈。 不过,RNN、LSTM 和 GRU 还有一个很难绕开的特点: 必须按照时间顺序逐步计算,后一个位置需要等待前一个位置的隐藏状态。 假设一句话有 100 个 token,RNN 需要从第 1 个 token 一直算到第 100 个 token。即使 GPU 很擅长矩阵并行,也不能完全消除这种前后依赖。 Transformer 改变了处理序列的方式: 不再使用循环逐步传递信息,而是让每个 token 直接通过 Attention 查看序列中的其他 token。 这样一来,训练时可以并行处理整段序列,也更容易建模相距很远的词之间的关系。 1. Transformer 要解决什么问题先看一句话: 小明 把 苹果 放在 桌子 上,因为 他 刚刚 买了 它 理解这句话时,需要建立多处联系: “他”更可能指向“小明”。 “它”更可能指向“苹果”。 “放在”与“桌子上”共同表达一个位置关系。 在 RNN 中,前面的信...
AdamW Optimizer:为什么要把权重衰减与梯度更新解耦
在上一篇 Adam Optimizer 文章中,我们知道 Adam 会同时维护梯度的一阶矩和二阶矩,并根据历史梯度为不同参数调整有效步长。 当模型出现过拟合时,我们还希望限制参数避免变得过大,于是经常会为优化器设置 weight_decay: optimizer = torch.optim.AdamW( model.parameters(), lr=1e-3, weight_decay=1e-2, ) 这里为什么常用 AdamW,而不是简单地给 Adam 加一个 L2 正则项?AdamW 中的字母 W 又代表什么? 一句话概括: AdamW 把权重衰减从 Adam 的梯度与矩估计中分离出来,让“根据损失优化参数”和“按比例缩小参数”成为两件独立的事。 1. 先回顾 AdamAdam 对当前梯度 $g_t$ 维护两种状态。 一阶矩: $$m_t=\beta_1m_{t-1}+(1-\beta_1)g_t$$ 二阶矩: $$v_t=\beta_2v_{t-1}+(1-\beta_2)g_t^2$$ 经过偏差修正后: $$\hat...
Seq2Seq:从Encoder-Decoder到Attention与PyTorch实现
前面的文本处理和 Word2Vec 文章主要解决了两个问题: 如何把文本转换成 token id。 如何把 token id 转换成词向量。 但很多自然语言处理任务不是“输入一句话,输出一个类别”,而是“输入一个序列,输出另一个序列”。 例如机器翻译: 输入:我 喜欢 自然语言处理 输出:I like natural language processing 输入和输出的长度可能不同,词序也可能不同。普通 MLP 或单纯的文本分类器很难直接处理这种映射。 Seq2Seq,全称 Sequence to Sequence,就是专门学习“序列到序列”映射的一类模型框架。 一句话概括: Seq2Seq 先用 Encoder 理解整个输入序列,再让 Decoder 根据编码结果逐步生成输出序列。 1. 什么是序列到序列任务Seq2Seq 的输入和输出都是有顺序的元素序列: $$X=(x_1,x_2,\ldots,x_{T_x})$$ $$Y=(y_1,y_2,\ldots,y_{T_y})$$ 其中输入长度 $T_x$ 与输出长度 $T_y$ 不必相同。 ...
Adam Optimizer:从梯度下降到自适应学习率
训练神经网络时,我们经常会看到下面这行代码: optimizer = torch.optim.Adam(model.parameters(), lr=0.001) 它很短,却承担着一个关键任务:根据反向传播得到的梯度,不断更新模型中的权重和偏置。 Adam 的全称是 Adaptive Moment Estimation,可以翻译为“自适应矩估计”。它同时利用梯度的移动平均和梯度平方的移动平均,为不同参数自动调整更新方向与步长。 一句话概括: Adam 在动量方法的基础上记录梯度方向,又根据历史梯度大小为每个参数调整学习率,因此通常比普通 SGD 更容易上手。 1. 优化器到底负责什么神经网络训练通常包含三个核心步骤: optimizer.zero_grad() loss.backward() optimizer.step() 它们的职责并不相同: 代码 作用 optimizer.zero_grad() 清空上一轮保存在参数上的梯度 loss.backward() 根据计算图和链式法则计算梯度 optimizer.step() 根据梯度和优化算法更...
Word2Vec:从词向量训练到神经网络分类器
前面学习文本处理时,我们已经知道:自然语言不能直接送进模型,必须先变成数字。 最朴素的方法是 One-hot。比如词表里有 10000 个词,每个词就用一个长度为 10000 的向量表示,属于自己的位置为 1,其他位置为 0。 但 One-hot 有两个明显问题: 向量太稀疏,绝大多数位置都是 0。 词和词之间没有语义关系,“国王”和“王后”在 One-hot 空间里并不比“国王”和“香蕉”更接近。 word2vec 要解决的就是这个问题: 把词表示成低维、稠密、带有语义关系的向量。 例如: 国王 -> [0.32, -0.18, 0.71, ...] 王后 -> [0.29, -0.15, 0.69, ...] 香蕉 -> [-0.44, 0.83, 0.12, ...] 如果训练得好,“国王”和“王后”的向量会更接近,而“国王”和“香蕉”的向量会更远。 这篇文章就从 word2vec 出发,理解词向量是怎么训练出来的、怎么改进和评价词向量,并进一步过渡到神经网络文本分类器。 1. 词向量到底是什么词向量,也叫 word embedding,本质上就是给每个词分...
深度学习入门:从神经网络到学习路线
学习深度学习时,很多人一上来就会被一堆名词淹没:神经网络、反向传播、CNN、RNN、Transformer、Embedding、优化器、损失函数、过拟合、预训练、微调…… 这些概念当然重要,但如果一开始没有一张地图,就很容易出现一种感觉:每个词好像都看过,但不知道它们之间到底是什么关系。 所以这篇文章不急着推公式,也不急着写复杂代码,而是先回答几个更基础的问题: 深度学习到底在学什么? 神经网络为什么可以拟合复杂关系? 一个模型是如何被训练出来的? 入门之后应该按什么顺序继续学? 一句话概括: 深度学习就是用多层神经网络从数据中自动学习特征表示,再用这些表示完成分类、回归、生成、推荐、预测等任务。 1. 深度学习是什么在传统机器学习中,我们经常需要手动设计特征。 比如要判断一封邮件是不是垃圾邮件,可能会人工提取这些特征: 是否包含“免费”“中奖”“限时”等关键词。 邮件长度是多少。 是否包含很多链接。 发件人是否陌生。 这些特征再送入逻辑回归、决策树、随机森林、GBDT 等模型中进行预测。 深度学习的思路稍微不一样。它希望模型自己从原始数据中逐层学习特征。 以图...
