
本文详解如何修正梯度下降实现中因梯度计算错误、参数更新逻辑混乱导致的 θ 值发散或不收敛问题,重点在于使用解析梯度替代错误的数值/符号推导,并重构迭代流程以确保稳定收敛。
本文详解如何修正梯度下降实现中因梯度计算错误、参数更新逻辑混乱导致的 θ 值发散或不收敛问题,重点在于使用解析梯度替代错误的数值/符号推导,并重构迭代流程以确保稳定收敛。
在机器学习实践中,梯度下降算法的正确性高度依赖于梯度的数学准确性与参数更新的逻辑一致性。原始代码中存在多个关键缺陷:首先,gradient() 函数试图基于输入 X, Y, Ysol 等动态变量计算梯度,但其推导未与实际损失函数 erreurJ(theta, sigma) 对齐,导致梯度方向错误;其次,pasfixe() 内部嵌套了冗余的 for i in range(len(theta)) 循环,在每次迭代中多次更新单个参数并同步重算 h 和 Y,破坏了梯度下降“整向量一步更新”的基本假设;最后,calculh() 和 calculY() 的设计混淆了前向传播与损失定义,使调试路径复杂化。
正确的做法是:直接对损失函数 erreurJ 关于参数 θ = [w, b] 求解析梯度。题中已给出 erreurJ(theta, sigma) = 1/4 * (σ(b)² + σ(w + b)²),其中 σ(z) = z² − 1,因此可严格推导:
- ∂J/∂w = (1/4) × 2σ(w+b) × σ′(w+b) × 1
- ∂J/∂b = (1/4) × [2σ(b) × σ′(b) + 2σ(w+b) × σ′(w+b)]
代入 σ′(z) = 2z 并展开化简后,即得题解中验证无误的 grad(theta) 函数——它是一个纯参数函数,不依赖输入数据 X/Y,这正体现了该任务为无数据依赖的参数空间优化(目标是找到使 J(w,b) 最小的 (w,b),而非拟合样本)。
以下是重构后的核心梯度下降实现(关键改进已加注):
def grad(theta):
w, b = theta[0], theta[1]
# 解析梯度:完全由 erreurJ 数学形式导出,无外部变量耦合
dw = 2*b**3 + 3*b**2*w + 3*b*w**2 - 2*b + w**3 - w
db = b**3 + 3*b**2*w + 3*b*w**2 - b + w**3 - w
return np.array([dw, db])
def pasfixe(theta, eta, epsilon, sigma, sigmaprime):
theta = np.array(theta, dtype=np.float64) # 统一数值类型,避免隐式转换误差
n = 0
while np.linalg.norm(grad(theta)) > epsilon and n 100): # 全局发散检测(非逐分量)
return [100, 100]
return theta.tolist() # 返回标准 Python list 便于后续处理
重要注意事项:
-
学习率
eta需调低:原代码eta=0.1易导致震荡,题解中改为0.01更稳健; -
移除所有中间状态重算逻辑:如
h = calculh(...)和Y = calculY(...)在pasfixe中已无必要,因其不参与梯度计算; -
初始化范围要合理:
lst中[-3, 3]的随机初值覆盖了主要吸引域,过大的初始值会直接落入梯度爆炸区; -
收敛判定应基于梯度模长:
np.linalg.norm(grad(theta)) 比依赖 <code>Y值更本质,因目标是最小化J(θ)而非匹配某个输出。
最终可视化结果将清晰呈现四个稳定收敛点([-2,1], [2,-1], [0,-1], [0,1])对应的吸引域,以及红色发散区域——这正是非凸损失函数典型多极小值结构的直观印证。掌握这种“损失函数→解析梯度→纯参数优化”的闭环思维,是构建可靠优化器的基础能力。










