任务与先修
本章实现监督微调,英文缩写SFT。输入是提示x与示范回答y,任务是增加示范的条件概率。读者需掌握上一章的四候选策略,以及一元指数、对数和链式法则。我们先在有限候选中把全部八个参数的梯度推完,再说明语言模型多token回答的求和与掩码。本章没有把四分类参考改名为语言模型;它只用最小可计算形式检验同一种条件似然原则。项目SFT运行二百次全批梯度下降,所有示范与参数更新都可打印重建。
对提示x,动作a的logit是 。定义概率 。只要logit有限,所有概率严格正且和为一。logit本身可负、可大于一,不是置信概率;给所有类别加同一常数不改变softmax。这个平移自由度说明参数并非每个方向都可由观测唯一确定,但不影响分布与目标的定义。数值实现先减去最大logit,再计算指数和,避免大正数溢出。
从似然到交叉熵
一条示范类别k的似然为 ,负对数为 。这是精确代数变换:将softmax分子分母取log,得到logit差与归一化项。对第j个logit求导,归一化项的导数是 ;负贡献负的指示值,即j等于k时减一。因此 。梯度各分量之和为零,正好对应共同平移不会改变损失。
再对实际参数求导。因为对导数为一,对导数为x,所以偏置梯度就是减目标指示,斜率梯度再乘x。其他类别的参数也有梯度,不能只给正确类别加分而不管分母。对N条示范取平均,逐条梯度相加后除N。项目实现直接累加这八个标量,再用学习率乘梯度从参数中减去;没有自动微分黑箱,也没有冻结某一层参数。
算例一:一个提示的一步更新
设x=2,四个logit都为零,真实示范是编号一“正数,简短”。概率各四分之一,损失log4。偏置梯度依次是 [0.25,−0.75,0.25,0.25];斜率梯度是 [0.5,−1.5,0.5,0.5]。若初始全部参数零、学习率0.1,更新后偏置为 [−0.025,0.075,−0.025,−0.025],斜率为 [−0.05,0.15,−0.05,−0.05]。同一x下新logit为 [−0.125,0.375,−0.125,−0.125]。
正确类别相对其余高0.5,故新概率为 ,约0.354661,大于更新前0.25;新损失约1.036592,低于log4约1.386294。独立检查一是四个偏置梯度与四个斜率梯度各自求和为零;独立检查二是对任一参数做中心有限差分,重算两次loss,结果应与解析导数接近。有限差分只验证该点数值,不代替前面的导数推导。
语言模型的回答条件与掩码
若回答由多个token组成,记提示为x,回答token依次是y1到yT,其中可把最后EOS也列作回答结束目标。条件概率按链式法则为各步概率乘积:第t步只读提示与此前回答token。取负log后变成T项求和。这不是把整段回答当四分类,而是把每个输出位置的词表分类接到同一条件序列上。teacher forcing给模型真实的过去回答,不给同一预测位置读取当前或未来目标的权限。
response-only指只对回答及其EOS评分,提示token用于条件而不进入损失。它不是从输入中删掉提示,否则就改变任务。也不是仅对“输入看起来属于回答”的位置评分,因为首回答token由提示末尾位置预测。正确掩码必须以目标所属片段决定,先做输入目标错位,再把非回答目标设为忽略值。序列填充也要从loss分母中排除,但真实EOS不排除。
算例二:首回答token在哪一格
玩具完整串是提示 [10,11]、分隔符 [12]、回答 [20,21]、EOS [2]。输入为 [10,11,12,20,21],目标为 [11,12,20,21,2]。response-only目标应改成 [−100,−100,20,21,2];有效mask为 [0,0,1,1,1]。第三个输入位置是分隔符12,却负责预测首回答20,所以它必须计分。若按“输入是回答token”决定mask,就漏掉20,还可能在别处额外计一个错误位置。
设三个回答目标真实概率为0.5、0.25、0.5,总NLL为四倍log2,三项负log分别为log2、二倍log2、log2,总四倍log2,再除三个目标,得到约0.924196。直接把总串五格平均会得到较低的约0.554518,却只是把两个未评分提示位置放入分母,不是模型更好。EOS构成第三项,删掉它会改变终止学习。
第二个独立检查是枚举预测对:12预测20、20预测21、21预测2,恰好覆盖回答与结束;10预测11、11预测12只作提示,不评分。第一回答token和最后结束token都在台账中。若对批次右填充,目标负一百完全不查词表;有效计数由mask或长度给出,不能根据输入编号猜测。这些原则可以直接迁移到decoder训练,但有限四候选项目每条示范只有一次完整响应决策,不含伪造的token掩码接口。
经验分布、重复与权重
本项目同一x的示范有两条正确简短、一条正确冗长。若忽略参数共享,把该x的自由分布记为q,对应经验频率为2/3与1/3。平均交叉熵为 。用约束q简短加q冗长等于一求极小:导数为负的三分之二除q简短,加三分之一除一减q简短,令零解得q简短2/3。这个最优只在排除错误类别或允许其概率趋零的理想边界成立,有限softmax训练只能接近。
这个例子解释为何示范不等于偏好。把正确简短推到0.99会让那一条正确冗长示范的负log很大,SFT可能反对进一步简化;偏好数据则明确要求简短胜过冗长。课程把两种目标分别保留,不把“后训练效果”压成同一个loss。若真实示范含互相矛盾的答案,SFT会拟合这种经验混合,而不是自动知道哪条是真理。数据质量假设必须在损失之前说明。
多token回答还带来另一种权重选择。若两个回答长度分别一和九,把所有token总NLL除总长度,会使长回答总体权重更高;若先每回答平均再对回答平均,二者等权。也可以每回答取总NLL再平均,那又给长回答更大的典型梯度尺度。项目的四候选响应长度在统计上都是一个动作,因此按十八条示范平均。不能把它的权重结果直接套到长短文本SFT后声称已处理长度偏差。
梯度检查能证明什么
项目测试对八个参数逐一加减十万分之一,重算相同固定示范集合的损失,以中心差分验证解析梯度,最大误差要求小于亿分之一。还检查全部八参数从随机base到SFT确有变化。第一项定位推导或实现错,第二项定位梯度虽算出却未更新的问题。八参数全部变化不证明全局最优,也不保证任意数据都让每个参数每步非零;本任务对称时某些梯度可能精确抵消,必须区分合理零值与断链。
SFT使用全批梯度下降、固定学习率0.15、二百更新,无动量和随机抽样,因此继续训练只依赖当前参数与固定数据,不需要恢复Adam矩或数据游标。随机性只在初始八参数的小范围均匀初始化;保存的seed与初始参数可重建起点。本章不把这种轻量状态约定推广为真实大模型训练的完整checkpoint需求,后者还有优化器、调度器、随机源与分布式状态。
验证SFT应看同一留出上下文的完整概率,而不是只看argmax。一个模型可能每次greedy都正确,却给冗长回答大量概率;偏好阶段要调整的正是分布质量。反过来,某个靠近零的新上下文可能因为共享线性斜率而不够自信,即使远离零的训练点loss很低。按上下文打印四概率,能把总体均值里的这种差异找出来。本项目测试中的正负零点五就是一个预先固定的边界观察。
本章交付是SFT的目标、解析梯度、真实全参数更新与回答mask规则。下一章不再假设“标签给出唯一示范”,而是把同一提示下的winner/loser作为新的数据对象。reference必须在SFT结束时深复制并冻结,因为后面DPO的比率将以它为固定基准;若它随策略一起更新,本章得到的模仿分布就不再承担原定参照角色。
均值的分母还决定梯度尺度。两回答长度分别三和十二,若每个token的局部梯度方向相同,按回答总NLL平均会让长回答贡献四倍总幅度;按每回答token均值再平均则二者相近。若按全体十五个token平均,每个token等权,但每回答总权重仍一比四。三种目标都可以明确实施,不能把“避免长样本多占权重”当作未写出的默认。序列项目选择回答总和再平均,使DPO的完整回答事件定义与SFT台账可直接对应。
监督梯度在单个样本上对错误类别的正项并不等于“惩罚所有未示范回答永远错误”。同一提示下一条示范可能把另一个类别作为正确项,平均后得到经验频率平衡。若把所有未示范响应都标成绝对坏回答,就引入比SFT更强的比较监督。本课通过三条示范的频率求解展示这一区别,偏好数据的winner/loser关系在下一章另行提供。
序列SFT沿真实过去回答训练,因此可以在生成时自己的错误前缀上失效。这一条件分布差异不是mask故障:正确mask仍只使用过去,但训练过去来自示范,生成过去来自策略。应保留自由生成及停止状态作为独立检查,不能把teacher-forcing每目标NLL很低直接翻译成整段回答一定正确。
import math
x=2.; label=1; lr=.1
W=[[0.,0.] for _ in range(4)]
def loss(w):
z=[b+s*x for b,s in w];a=max(z)
return a+math.log(sum(math.exp(v-a) for v in z))-z[label]
grad=[[.25-float(i==label),x*(.25-float(i==label))] for i in range(4)]
errors=[]
for i in range(4):
for j in range(2):
a=[r.copy() for r in W];b=[r.copy() for r in W];a[i][j]+=1e-5;b[i][j]-=1e-5
errors.append(abs((loss(a)-loss(b))/2e-5-grad[i][j]))
assert max(errors)<1e-9
new=[[v-lr*g for v,g in zip(row,gr)] for row,gr in zip(W,grad)]
assert loss(new)<loss(W)
target=[11,12,20,21,2]; response_target=[-100,-100,20,21,2]
assert [v for v in response_target if v!=-100]==[20,21,2]
print('before',loss(W),'after',loss(new),'max gradient error',max(errors),'response targets',response_target)
TRY IT YOURSELF
练习一:首token掩码
提示[7]、分隔符[8]、回答[9]、EOS[2],写输入、目标和response-only目标。
提示 1
提示 2
对照参考解答
TRY IT YOURSELF
练习二:监督最优分布
同一提示有三条A、两条B示范,自由两类概率的最优分布是什么?写一阶条件。
提示 1
提示 2
对照参考解答
TRY IT YOURSELF
练习三:验收全参数学习
解析梯度正确但参数没有变化,列两个可能原因与能区分它们的检查。