Python实现线性回归梯度下降拟合出水平线,问题出在哪里?
问题原因分析
- 你怀疑的误差正负抵消问题不成立:MSE损失的梯度推导本身就使用
train_y - pred_y参与计算,每个误差项会和对应特征值相乘后求和,不会出现正负误差直接抵消的问题,你的梯度计算逻辑本身是正确的。 - 第一个错误是MSE计算错误:当前代码中
mse = errors.mean()计算的是平均偏差,不是均方误差,正确的MSE应为mse = (errors ** 2).mean(),该错误不影响梯度更新,但会导致打印的收敛指标完全失效。 - 第二个也是导致你看到水平直线的核心错误是
generateLine函数逻辑错误:- 你构造x轴取值时混入了
train_y的数值范围,还强制转成了整数,而make_regression生成的train_X是浮点型小范围值,生成的直线大部分x值不在散点的展示区间内,视觉上看起来像水平直线 - 正确做法是x轴起止范围直接取
train_X的最小、最大值,生成连续的浮点型x点即可。
- 你构造x轴取值时混入了
- 可选优化:可以将学习率调整为0.1,收敛速度会更快。
修复后的完整代码
import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation from sklearn import datasets def gradientDescent(train_X,train_y,lr,epochs,init_slope=1,init_intercept=0.1): if len(train_X) != len(train_y): raise Exception("train_X and train_Y must be the same length.") # 转为一维数组避免维度问题 train_X = np.array(train_X).flatten() train_y = np.array(train_y) n = len(train_X) slope = init_slope intercept = init_intercept for e in range(epochs): pred_y = slope * train_X + intercept errors = (train_y - pred_y) # 修复MSE计算 mse = (errors ** 2).mean() slope -= lr * (-2/n) * np.sum(train_X * errors) intercept -= lr * (-2/n) * np.sum(errors) return mse, slope, intercept # 修复直线生成逻辑 def generateLine(slope,intercept,x_min,x_max,point_cnt=100): x = np.linspace(x_min, x_max, point_cnt) y = slope * x + intercept return x, y n_samples = 10 n_outliers = 2 train_X, train_y, coef = datasets.make_regression(n_samples=n_samples, n_features=1, n_informative=1, noise=20, coef=True, random_state=0) # 调大学习率加快收敛 lr = 0.1 epochs = 1000 fig, ax = plt.subplots() line, = ax.plot([0], [0]) plt.plot(train_X,train_y,'o') # 固定坐标轴范围避免显示异常 plt.xlim(np.min(train_X)-0.2, np.max(train_X)+0.2) plt.ylim(np.min(train_y)-5, np.max(train_y)+5) slope=0 intercept = 0 def animate(i): global slope, intercept if i == 1: mse, slope, intercept = gradientDescent(train_X,train_y,lr=lr, epochs = 1,init_slope= np.random.random(), init_intercept=np.random.random()) mse, slope, intercept = gradientDescent(train_X,train_y,lr=lr,epochs=1,init_slope = slope, init_intercept = intercept) # 仅使用train_X的范围生成直线 line_x, line_y = generateLine(slope, intercept, np.min(train_X), np.max(train_X)) line.set_xdata(line_x) line.set_ydata(line_y) if i % 10 == 0: print(f'Epoch: {i}, MSE = {mse:.6f}, 拟合斜率: {slope:.2f}, 真实斜率: {coef:.2f}') return line, def init(): line.set_ydata([0]) return line, ani = animation.FuncAnimation(fig, animate, frames=range(1, epochs), init_func=init, interval=100, blit=True) plt.show()
内容的提问来源于stack exchange,提问作者George T
相关产品推荐
相关产品推荐

