如何在等高线图中得到正确的随机梯度下降(SGD)结果?
随机梯度下降(SGD)代码错误排查
问题描述
我实现随机梯度下降(Stochastic Gradient Descent, SGD)并尝试绘制参数更新的等高线图,但代码运行结果不符合预期。
我的代码
import numpy as np from matplotlib import pyplot as plt xk = np.linspace(-1,1,100) yk= 2 * xk + 3 + np.random.rand(len(xk)) x1,x2 = np.meshgrid(xk,yk) F = (x1 - 2) ** 2 + 2 * (x2 - 3) ** 2 fig=plt.figure() surf = fig.add_subplot(1,1,1, projection='3d') surf.plot_surface(x1,x2,F) surf.contour(x1,x2,F) fig, surf=plt.subplots() plt.contour(x1, x2, F, 20) m = 0 c = 0 learning_rate=0.01
我认为问题出在以下这段代码中,但找不到具体错误:
for k in range(10): shuffel_index=np.random.permutation(len(xk)) xk = xk[shuffel_index] yk = yk[shuffel_index] for i in range(len(xk)): grad_m = - 2 * xk[i] * (yk[i] - (np.dot(m,xk[i]) + c)) grad_c = - 2 * (yk[i] - (np.dot(m,xk[i])+c)) m = m - learning_rate * grad_m c = c - learning_rate * grad_c surf.plot(np.array([xk[0], yk[0]]),np.array([xk[1], yk[1]]),'ko-') if (k != 10 or i != len(xk)): surf.plot(np.array([xk[0], yk[0]]),np.array([xk[1], yk[1]]),'ko-') plt.show()
当前结果

期望效果
期望得到类似梯度下降的参数路径图,示例如下:
请问我的代码中存在哪些错误?
错误分析与修正方案
1. 损失函数与参数空间不匹配
你定义的F = (x1 - 2) ** 2 + 2 * (x2 - 3) ** 2是针对样本特征x1,x2的函数,和线性模型的参数m,c完全无关。而我们需要的是参数空间(m,c)上的损失等高线,即基于线性回归的残差平方和损失:
$$L(m,c) = \sum_{i=1}^n (y_i - (m x_i + c))^2$$
需要生成以m为x轴、c为y轴的网格,再计算每个(m,c)对应的损失值。
2. 绘图逻辑完全错误
循环中你绘制的是样本点的前两个值xk[0],yk[0]和xk[1],yk[1]的连线,这和参数(m,c)的更新路径毫无关系。正确做法是每次更新m,c后,在参数空间(m为横坐标,c为纵坐标)上绘制当前参数点,并连接路径。
3. 循环与绘图时机错误
plt.show()放在外层循环内,会在每轮迭代后弹出新窗口,应该放在所有循环结束后一次性显示。- 条件判断
if (k != 10 or i != len(xk))逻辑错误,且重复绘制无效点,完全没必要保留。 - 变量名拼写错误:
shuffel_index应为shuffle_index,属于不规范写法。
4. 梯度计算冗余
np.dot(m,xk[i])中m是标量,直接用m * xk[i]即可,无需使用np.dot。
修正后的完整代码
import numpy as np from matplotlib import pyplot as plt # 生成样本数据 xk = np.linspace(-1, 1, 100) yk = 2 * xk + 3 + np.random.rand(len(xk)) # 真实参数m=2, c=3,添加噪声 # 1. 生成参数空间(m,c)的损失等高线 # 定义参数范围,覆盖真实值附近 m_grid = np.linspace(0, 4, 100) c_grid = np.linspace(1, 5, 100) M, C = np.meshgrid(m_grid, c_grid) # 计算每个(m,c)对应的残差平方和损失 loss = np.zeros_like(M) for i in range(len(xk)): loss += (yk[i] - (M * xk[i] + C)) ** 2 # 绘制等高线图 fig, ax = plt.subplots() contour = ax.contour(M, C, loss, 20) ax.clabel(contour, inline=True, fontsize=8) ax.set_xlabel('m (斜率)') ax.set_ylabel('c (截距)') ax.set_title('SGD参数更新路径') # 2. 执行随机梯度下降并记录参数路径 m = 0 c = 0 learning_rate = 0.01 epochs = 10 path_m = [m] path_c = [c] for k in range(epochs): shuffle_index = np.random.permutation(len(xk)) x_shuffled = xk[shuffle_index] y_shuffled = yk[shuffle_index] for i in range(len(x_shuffled)): y_pred = m * x_shuffled[i] + c # 计算梯度 grad_m = -2 * x_shuffled[i] * (y_shuffled[i] - y_pred) grad_c = -2 * (y_shuffled[i] - y_pred) # 更新参数 m -= learning_rate * grad_m c -= learning_rate * grad_c # 记录参数用于绘制路径 path_m.append(m) path_c.append(c) # 绘制参数更新路径和真实参数点 ax.plot(path_m, path_c, 'ko-', markersize=3, linewidth=1, label='SGD路径') ax.plot(2, 3, 'r*', markersize=10, label='真实参数') ax.legend() plt.show()
内容的提问来源于stack exchange,提问作者user20697471
相关产品推荐
相关产品推荐

