Matplotlib绘图中np.meshgrid的X/Y轴校准及梯度下降点显示问题
二元函数梯度下降可视化问题修正方案
问题根源分析
- 2D图轴范围异常:
plt.imshow()默认以数组的行列索引作为坐标轴刻度,而非你定义的x = np.arange(-1.5,2,0.1)和y = np.arange(-1.5,2,0.1)真实范围,导致轴显示为0-40(数组长度)。 - 2D散点位置错误:梯度下降得到的(x,y)是真实坐标,但
imshow的坐标系统未映射到真实范围,导致散点位置偏移。 - 3D图试验点遮挡:曲面绘制在试验点之前,且无半透明设置,导致点被遮挡;未添加3D等高线。
修正后完整代码
import numpy as np import matplotlib.pyplot as plt def func(x: float, y: float) -> float: return -( x * y * (1 - x - y) ) / 8 def partial_x_der(x: float, y: float) -> float: return (-y * (-2 * x - y + 1)) / 8 def partial_y_der(x: float, y: float) -> float: return (-x * (1 - x - 2 * y) ) / 8 def gradient(x: float, y: float): return (partial_x_der(x, y), partial_y_der(x, y)) def tuple_minus(my_tuple1: tuple, my_tuple2: tuple) -> tuple: return (my_tuple1[0] - my_tuple2[0], my_tuple1[1] - my_tuple2[1]) def tuple_multi_val(my_tuple: tuple, value: float) -> tuple: return (my_tuple[0] * value, my_tuple[1] * value) def gradient_descent(start_point: tuple, learning_rate: float, epsilon: float) -> list: max_iterations = 1000 cur_point = start_point result_points = list() # 先添加初始点 result_points.append((cur_point[0], cur_point[1], func(cur_point[0], cur_point[1]))) for iter in range(max_iterations): grad = gradient(cur_point[0], cur_point[1]) next_point = tuple_multi_val(grad, learning_rate) if np.abs(next_point[0]) < epsilon and np.abs(next_point[1]) < epsilon: print(f"学习率{learning_rate},在({cur_point[0]:.4f};{cur_point[1]:.4f})找到最小值,函数值{func(cur_point[0], cur_point[1]):.4f},迭代次数{iter}") result_points.append((cur_point[0], cur_point[1], func(cur_point[0], cur_point[1]))) return result_points cur_point = tuple_minus(cur_point, next_point) if iter % 10 == 0: result_points.append((cur_point[0], cur_point[1], func(cur_point[0], cur_point[1]))) print(f"达到最大迭代次数{max_iterations},在({cur_point[0]:.4f};{cur_point[1]:.4f})找到最小值") result_points.append((cur_point[0], cur_point[1], func(cur_point[0], cur_point[1]))) return result_points def show_gradient_descend(points_to_show: list): x = np.arange(-1.5, 2, 0.1) y = np.arange(-1.5, 2, 0.1) X, Y = np.meshgrid(x, y) Z = func(X, Y) # 修正后的2D图 plt.figure(figsize=(8,6)) # 指定extent映射真实坐标范围:[x_min, x_max, y_min, y_max] im = plt.imshow(Z, 'GnBu', origin='lower', extent=[x.min(), x.max(), y.min(), y.max()]) # 基于真实坐标绘制等高线 cset = plt.contour(X, Y, Z, np.arange(-0.5, 1, 0.1), linewidths=1, cmap=plt.cm.Set2) plt.clabel(cset, inline=True, fmt='%1.1f', fontsize=8) plt.colorbar(im) # 提取试验点的x,y坐标绘制散点 point_x = [p[0] for p in points_to_show] point_y = [p[1] for p in points_to_show] plt.scatter(point_x, point_y, color='r', marker='o', zorder=5) plt.title('二元函数2D等高线与梯度下降路径') plt.xlabel("X") plt.ylabel("Y") plt.xlim(-1.5, 2) plt.ylim(-1.5, 2) # 优化后的3D图 fig = plt.figure(figsize=(10,8)) ax = fig.add_subplot(projection='3d') # 曲面设置半透明,避免遮挡试验点 surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='GnBu', linewidth=0, antialiased=False, alpha=0.7) # 添加3D等高线(投影到XY平面) ax.contour(X, Y, Z, zdir='z', offset=Z.min()-0.1, cmap='GnBu') # 绘制梯度下降试验点,设置zorder保证可见 point_z = [p[2] for p in points_to_show] ax.scatter(point_x, point_y, point_z, color='r', marker='o', s=50, zorder=10) # 连接试验点,展示路径 ax.plot(point_x, point_y, point_z, color='r', linestyle='--', zorder=8) ax.set_title('$z= -(x*y*(1-x-y)) / 8$ 3D可视化') ax.set_xlabel("X") ax.set_ylabel("Y") ax.set_zlabel("Z") ax.set_xlim(-1.5, 2) ax.set_ylim(-1.5, 2) plt.show() def main(): x0 = (0, 0) x1 = (1, 1) xm = (5/10, 9/10) epsilon = 0.0001 points = [x0, x1, xm] # 收集所有起始点的梯度下降路径 all_points = [] for point in points: all_points.extend(gradient_descent(point, 0.42, 0.0001)) # 去重(可选,避免重复点) all_points = list(set(all_points)) show_gradient_descend(all_points) if __name__ == "__main__": main()
关键修改说明
2D图修正
- 轴范围映射:给
imshow添加extent=[x.min(), x.max(), y.min(), y.max()],将数组索引映射到真实x/y坐标范围。 - 等高线与散点对齐:
contour改为基于X,Y真实坐标绘制,散点直接使用梯度下降得到的(x,y)值,同时设置zorder=5保证散点在等高线之上。 - 显式设置轴范围:添加
plt.xlim和plt.ylim确保显示范围符合预期。
3D图优化
- 试验点可见性:
- 给曲面添加
alpha=0.7设置半透明,让下方点可见; - 散点设置
zorder=10,确保在曲面上层; - 添加路径连线,更清晰展示梯度下降过程。
- 给曲面添加
- 添加3D等高线:使用
ax.contour并指定zdir='z'和offset,将等高线投影到XY平面下方,避免遮挡曲面。 - 代码健壮性:
main函数中收集所有起始点的路径,统一可视化;修正了梯度下降函数的拼写错误(gradiant_descent改为gradient_descent),并添加初始点记录。
内容的提问来源于stack exchange,提问作者Vytenis Kajackas
相关产品推荐
相关产品推荐

