梯度下降函数输出极大负值问题排查求助
我正在用Python编写梯度下降算法,第一段代码用于绘制2D(wx+b)和1D(wx)场景下的误差函数图,第二段是单独保存的梯度下降函数。从绘制的图像来看,min_w和min_b应为正值,但梯度下降函数却输出极大的负值。从w=0、b=0开始迭代时,参数立刻变为负值(对应误差曲线的上升方向),正确的下降方向应该是正值方向。我试过调整步长、迭代次数和初始值,都没解决问题,求排查错误原因。
主代码
import numpy as np import matplotlib.pyplot as plt x_train = np.array([1.0, 1.7, 2.0, 2.5, 3.0, 3.2]) x_mean = np.mean(x_train) x_std = np.std(x_train) x_train_normalized = (x_train - x_mean) / x_std y_train = np.array([250, 300, 480, 430, 630, 730,]) w=np.random.rand(200) * 2 - 1 b=np.random.rand(200) * 2 - 1 W, B = np.meshgrid(w, b) J=np.zeros((len(w),len(b))) J1=np.zeros((len(w))) y_bar = np.zeros((len(w), len(x_train))) m = len(x_train) x_new=x_train_normalized[:,np.newaxis,np.newaxis] y_new=y_train[:,np.newaxis,np.newaxis] y_bar=W*x_new+B sum_squared_errors=np.sum((y_bar-y_new)**2,axis=0) J=(1/(2*m))*sum_squared_errors from GD import gd2D min_w,min_b=gd2D(y_new,x_new,m,np) y_bar1=W*x_new sum_squared_errors1=np.sum((y_bar1-y_new)**2,axis=0) J1=(1/(2*m))*sum_squared_errors1 fig = plt.figure() ax = fig.add_subplot(111, projection='3d') surf = ax.plot_surface(W, B, J, cmap='viridis', edgecolor='none') ax.set_xlabel('W-axis') ax.set_ylabel('B-axis') ax.set_zlabel('J-axis') ax.view_init(elev=20, azim=-60) # 调整视角以便更好可视化 fig.colorbar(surf) # 添加侧边颜色条 plt.show() plt.scatter(W,J1,marker='X',c='r',s=10) plt.title("ABS") plt.ylabel("J") plt.xlabel("w") plt.show()
梯度下降函数代码
def gd2D(y_new,x_new,m,np): itr = 5000 alpha = 0.01 w=0 b=0 for i in range(itr): y_bar=w*x_new+b dw=(np.sum((y_new-y_bar)*x_new))/m db=(np.sum(y_new-y_bar))/m w=w-alpha*dw b=b-alpha*db return w,b
错误原因及修正方案
问题核心是梯度计算的符号错误,导致参数更新方向完全相反:
均方误差损失函数定义为:
$$J(w,b) = \frac{1}{2m}\sum_{i=1}^m (y_i - (wx_i + b))^2$$
对w和b求偏导的正确结果是:
$$\frac{\partial J}{\partial w} = -\frac{1}{m}\sum_{i=1}^m (y_i - \hat{y}i)x_i$$
$$\frac{\partial J}{\partial b} = -\frac{1}{m}\sum{i=1}^m (y_i - \hat{y}_i)$$
你的代码中,dw和db的计算没有添加负号,相当于得到了损失梯度的相反数。而梯度下降的更新规则是参数 = 参数 - 学习率 × 损失函数的梯度,这就导致实际更新时变成了沿着损失上升的方向移动,自然会跑到错误的极值点。
修正后的梯度下降函数
def gd2D(y_new,x_new,m,np): itr = 5000 alpha = 0.01 w=0 b=0 for i in range(itr): y_bar=w*x_new+b # 添加负号,得到正确的损失梯度 dw = -np.sum((y_new - y_bar) * x_new) / m db = -np.sum(y_new - y_bar) / m w = w - alpha * dw b = b - alpha * db return w,b
或者也可以保持dw和db的计算不变,调整更新公式的符号:
w = w + alpha * dw b = b + alpha * db
两种方式效果完全一致。
额外提示:x_new和y_new被处理成了三维数组,虽然求和后不影响梯度计算,但直接使用一维数组会让代码更简洁。
内容的提问来源于stack exchange,提问作者Tariq Islam

