单特征梯度下降线性回归发散问题:代码未收敛原因排查
梯度下降实现单特征线性回归模型发散排查
我参考教程实现了基于梯度下降的单特征线性回归,代码逻辑是按梯度更新权重,但运行后模型明显发散,Cost值持续大幅增长,参数m、c严重偏离预期的0.4和3。以下是我的代码、运行输出及数据集散点图,求帮忙排查问题:
我的代码
import numpy as np import matplotlib.pyplot as plt plt.rcParams['figure.figsize'] = (12.0, 9.0) x = np.linspace(0, 100, 100) # 100个0到100之间的数值 y = 0.4*x + 3 # 预处理输入数据 plt.scatter(x, y) plt.show() # 构建模型 m = 0 c = 0 L = 0.001 # 学习率 epochs = 10 # 梯度下降迭代次数 n = float(len(x)) # X的样本数量 # 执行梯度下降 for i in range(epochs): y_pred = m*x + c # 当前Y的预测值 cost=np.sum(np.square(y - y_pred)) D_m = (-2/n) * sum(x * (y - y_pred)) # 对m的导数 D_c = (-2/n) * sum(y - y_pred) # 对c的导数 m = m - L * D_m # 更新m c = c - L * D_c # 更新c print("Cost: {}, m: {}, c: {}".format(cost, m, c)) print (m, c)
运行输出
Cost: 66502.69360269362, m: 2.980134680134681, c: 0.04600000000000002 Cost: 2154894.319611961, m: -14.012236408983222, c: -0.20610546801346816 Cost: 70050303.28853585, m: 82.87521068285723, c: 1.241530383820881 Cost: 2277388248.5411315, m: -469.56062336187455, c: -7.002473745232483 Cost: 74039836741.10417, m: 2680.334036437331, c: 40.01359353844544 Cost: 2407098544049.198, m: -15279.827701739041, c: -228.05383729236456 Cost: 78256836757675.23, m: 87125.94814145382, c: 1300.4310405561246 Cost: 2544196836061482.0, m: -496774.302711737, c: -7414.71863567037 Cost: 8.271401974355902e+16, m: 2832525.2415301367, c: 42277.587072774666 Cost: 2.6891036751423396e+18, m: -16150572.367496913, c: -241059.4462543846 -16150572.367496913 -241059.4462543846
数据集散点图

问题排查与修复方案
核心问题
- 学习率过大:
L=0.001对于取值范围0-100的x来说步长太大,参数更新时直接跳过最优值,甚至在最小值附近来回震荡、发散。 - 数据未做归一化:
x的数值范围是0-100,较大的数值导致梯度计算结果也很大,结合大学习率进一步加剧了参数发散。
修复后的代码
import numpy as np import matplotlib.pyplot as plt plt.rcParams['figure.figsize'] = (12.0, 9.0) x = np.linspace(0, 100, 100) y = 0.4*x + 3 # 数据归一化:将x缩放到0-1区间 x_normalized = (x - np.min(x)) / (np.max(x) - np.min(x)) plt.scatter(x, y) plt.show() m = 0 c = 0 L = 0.1 # 归一化后可适当增大学习率,保证收敛速度 epochs = 1000 # 增加迭代次数,确保参数收敛到最优值 n = float(len(x_normalized)) for i in range(epochs): y_pred = m*x_normalized + c cost = np.sum(np.square(y - y_pred)) D_m = (-2/n) * sum(x_normalized * (y - y_pred)) D_c = (-2/n) * sum(y - y_pred) m = m - L * D_m c = c - L * D_c # 每100轮打印一次状态,方便观察收敛情况 if i % 100 == 0: print(f"第{i}轮 - Cost: {cost:.2f}, m: {m:.2f}, c: {c:.2f}") # 将归一化后的m转换回原始x尺度的参数 m_original = m / (np.max(x) - np.min(x)) c_original = c - m_original * np.min(x) print(f"\n原始尺度下的最优参数:m={m_original:.2f}, c={c_original:.2f}")
关键调整说明
- 数据归一化:把
x缩放到0-1区间,消除数值范围差异带来的梯度爆炸问题,让梯度下降更稳定。 - 调整学习率:归一化后可以使用更大的学习率(如0.1),既保证收敛速度,又不会出现发散。
- 增加迭代次数:归一化后收敛过程更平稳,需要足够的迭代次数让参数逼近真实值0.4和3。
内容的提问来源于stack exchange,提问作者zell
相关产品推荐
相关产品推荐

