You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

梯度下降拟合直线:参数爆炸及收敛异常问题求助

解决梯度下降拟合直线的参数爆炸与收敛问题

首先,我们来一步步拆解你遇到的两个核心问题:初始代码的参数爆炸,以及修改后代码的收敛缓慢且结果偏离最小二乘解。

一、初始代码的参数爆炸原因

你的初始update函数犯了梯度下降的根本性错误:梯度下降的参数更新公式是当前参数 = 当前参数 - 学习率 × 梯度,但你直接把descent_rate * 梯度赋值给了m和c,完全没有保留原有参数的基础值:

# 错误的更新逻辑
def update(n,A,b,m,c,descent_rate):
    return descent_rate * der_wrt_m(n,A,b,m,c)), descent_rate * der_wrt_c(n,A,b,m,c))

这导致参数每一步都直接被梯度的倍数覆盖,而非在原有基础上微调,必然会出现参数迅速发散到无穷大、NaN的情况。

二、修改后代码的收敛问题分析

你修复了更新逻辑,但还有几个关键错误导致收敛异常:

1. 损失函数与梯度计算的特征参数混用

在调用mean_squared_error时,你传入了A(包含x和常数项的矩阵),但函数内部用m*A[i] + c计算预测值——A[i]是二维数组(比如[0,1]),这会触发数组运算错误,损失函数计算完全偏离真实值,进而干扰梯度更新的方向。

2. 偏置项c的梯度符号错误

对c的梯度计算有误:

def der_wrt_c(n,A,b,m,c):
    d = 0
    for i in range(n):
        d += (2 * (b[i] - (m*A[i] + c)))
    return d/n

正确的均方误差对c的梯度应该是:
$$\frac{\partial MSE}{\partial c} = \frac{2}{n}\sum_{i=1}^n (m x_i + c - y_i)$$
等价于:
$$\frac{\partial MSE}{\partial c} = -\frac{2}{n}\sum_{i=1}^n (y_i - (m x_i + c))$$
你的代码中d是2*(y_i - ...)的和,返回d/n等于负的真实梯度。在update函数中,c - descent_rate * der_wrt_c相当于c + 学习率 × 真实梯度,完全搞反了更新方向,这是收敛缓慢且结果偏离的核心原因。

3. 学习率设置过小

你设置的descent_rate=0.0001太小,即使方向正确,10000次迭代也不足以让参数收敛到最优值。结合你的数据范围(x∈[0,3]),可以适当调大学习率。

三、修复后的完整代码

下面是修正了所有问题的代码,同时添加了损失值跟踪,方便观察收敛过程:

import numpy as np
import matplotlib.pyplot as plt

def mean_squared_error(n, x, y, m, c):
    e = 0
    for i in range(n):
        # 使用x[i]而非A[i],确保是标量运算
        e += ((y[i] - (m * x[i] + c)) ** 2)
    return e / n

def der_wrt_m(n, x, y, m, c):
    d = 0
    for i in range(n):
        d += x[i] * (y[i] - (m * x[i] + c))
    # 正确的梯度:2/n * sum( (mx+c - y)*x ) = -2/n * sum( x*(y - mx -c) )
    return (-2 * d) / n

def der_wrt_c(n, x, y, m, c):
    d = 0
    for i in range(n):
        d += (y[i] - (m * x[i] + c))
    # 正确的梯度:2/n * sum( mx+c - y ) = -2/n * sum( y - mx -c )
    return (-2 * d) / n

def update(n, x, y, m, c, descent_rate):
    # 参数更新:当前值 - 学习率 × 梯度
    new_m = m - descent_rate * der_wrt_m(n, x, y, m, c)
    new_c = c - descent_rate * der_wrt_c(n, x, y, m, c)
    return new_m, new_c

# 数据准备
x = np.array([0, 1, 2, 3])
y = np.array([1, 2, 0, 3])

def descent(x, y):
    m = 0
    c = 0
    # 调大学习率,适配数据范围
    descent_rate = 0.1
    iterations = 1000
    n = len(x)
    plt.scatter(x, y, label='Raw Data')
    
    # 跟踪损失变化
    loss_history = []
    for itr in range(iterations):
        m, c = update(n, x, y, m, c, descent_rate)
        current_loss = mean_squared_error(n, x, y, m, c)
        loss_history.append(current_loss)
        # 每100次迭代打印状态
        if itr % 100 == 0:
            print(f"Iteration {itr}: m={m:.4f}, c={c:.4f}, Loss={current_loss:.4f}")
    
    # 绘制拟合直线
    u = np.linspace(0, 3, 100)
    plt.plot(u, u * m + c, 'r-', label=f'Fitted Line: y={m:.2f}x + {c:.2f}')
    plt.xlabel('x')
    plt.ylabel('y')
    plt.legend()
    plt.show()
    
    # 绘制损失收敛曲线
    plt.plot(range(iterations), loss_history)
    plt.xlabel('Iterations')
    plt.ylabel('MSE Loss')
    plt.title('Loss Convergence')
    plt.show()
    
    # 输出最终结果与最小二乘解对比
    print(f"\nFinal Result: m={m:.4f}, c={c:.4f}")
    print(f"Minimum Squares Solution: m=0.4, c=0.9")

descent(x, y)

四、修复后的效果

运行这段代码后,你会看到:

  • 损失值快速下降,几百次迭代后就收敛到稳定值(约1.05,和最小二乘的MSE一致)
  • 最终参数m≈0.4,c≈0.9,完全匹配最小二乘的最优解
  • 参数不会出现爆炸或NaN的情况

额外建议

  • 特征缩放:如果数据范围很大(比如x∈[0,1000]),建议先对x做标准化((x - x.mean())/x.std()),这样可以使用更大的学习率,加速收敛。
  • 自适应学习率:可以尝试使用Adam、Adagrad等自适应学习率优化器,避免手动调参的麻烦。
  • 批量/随机梯度下降:你的代码是批量梯度下降(每次用所有数据计算梯度),如果数据量很大,可以改为随机梯度下降(每次用单个样本)或小批量梯度下降,提升效率。

内容的提问来源于stack exchange,提问作者Ahmad Moussa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:04:28