题目重点:从半均方误差逐层求导,区分 SGD 与全批梯度下降,用有限差分核对梯度和简单拟合验证更新方向。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练合同单特征x、实数y、预测wx+b,损失为平均的(预测−y)²/2;题目未明确具体损失,这个系数选择须说明。
- 示例按固定顺序逐样本更新,实现的是单样本SGD;实际训练可打乱样本,不能把全批更新称SGD。
- 有限值与合理特征尺度由调用方保证;没有宣称任意学习率、任意数据必定收敛。
解题路线
- 写计算图z=wx+b、r=z−y、loss=r²/2。链式法则dloss/dr=r、dr/dz=1、dz/dw=x、dz/db=1,故单样本梯度dw=rx、db=r。批均值为Σrx/n与Σr/n。
- 一次样本更新必须基于同一旧w/b及同一个residual,同时执行w←w−ηrx、b←b−ηr。若更新w后重新计算residual给b,就变成不同更新算法。
- 解释SGD噪声、batch规模与学习率:小步长慢、大步长可震荡,x尺度大时w方向曲率更高。先标准化或按验证结果调步长,保留独立验证集与基线;无噪声小例只用于检查实现。
- 训练loop列epoch、样本顺序、计算梯度、更新、评估。每步只需要标量状态;若加mini-batch,先累计同一参数下的梯度再一次更新,不逐样本更新后还声称同一batch。
- 用中心差分(loss(w+h)−loss(w−h))/(2h)核对解析梯度,避开极大极小h的误差;另外用简单直线数据验证符号和同时更新。梯度检验比只看loss下降更能发现链式法则错误。
手动走一遍
训练例x=2,y=5,w=b=0,r=−5,dw=−10、db=−5。η=0.1时w=1、b=0.5,预测2.5,单样本loss从12.5降到3.125;这只证明该步方向正确,不证明步长对所有x合适。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
def loss_and_gradient(xs, ys, weight, bias):
if len(xs) != len(ys) or not xs:
raise ValueError("equal nonempty arrays required")
residuals = [weight * x + bias - y for x, y in zip(xs, ys)]
n = len(xs)
return (
sum(r * r for r in residuals) / (2 * n),
sum(r * x for r, x in zip(residuals, xs)) / n,
sum(residuals) / n,
)
def fit_sgd(xs, ys, learning_rate=0.01, epochs=1000):
if len(xs) != len(ys) or not xs or learning_rate <= 0 or epochs < 0:
raise ValueError("invalid training inputs")
weight = bias = 0.0
for _ in range(epochs):
for x, y in zip(xs, ys):
residual = weight * x + bias - y
weight, bias = weight - learning_rate * residual * x, bias - learning_rate * residual
return weight, bias
复杂度
n样本、E轮训练O(En)时间,参数状态O(1),输入存储另计。6,000个解析梯度分量与独立中心差分对照,加一组y=2x+1的SGD拟合验证;示例没有自动早停或数值稳定保证。
易错点与追问
容易失分的边界
- MSE是否带1/2没定义,梯度系数不一致。
- 梯度漏x或更新符号错误。
- w更新后重算残差再更新b。
- 把full-batch与SGD混称。
- 用训练loss代替泛化评估。
继续深挖
- 多特征时梯度的矩阵维度是什么?
- 加L2时是否惩罚bias,梯度怎么改?
- x全相同时w/b是否能分别识别?
- 为什么数值差分只用于检查而不用于大规模训练?
