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

梯度下降求解非线性方程组:残差计算与算法停滞问题咨询

非线性方程组梯度下降法:残差定义与算法停滞问题

问题描述

我已实现求解非线性方程组根的梯度下降法,但对残差的确定方式存疑:残差是否就是欧几里得范数(2-范数)即norm(F,2)?尝试绘制残差随迭代的变化曲线后,仍对残差的定义与计算感到困惑。

若我的假设正确,附图的曲线显示迭代1后残差无明显下降,是否说明算法已停滞?

实现代码

import numpy as np
import array
from numpy.linalg import norm
import math  # 原代码遗漏此导入,运行会报错

def gradient_descent(fnon, jac, x0, tol, maxk, maxm, *args):
    k = 0
    x = x0

    F = eval(fnon)(x,*args)

    residuals = []
    arr = []
    arr.append(x)
    
    while (norm(F,2) > tol and k <= maxk):
        J = eval(jac)(x,2,fnon,F,*args)
        delta = -2 * np.matmul(np.transpose(J), F)
        residuals.append(norm(F,2))
        lamb = 0.01       
        x = x + lamb * delta
        F = eval(fnon)(x,*args)
        arr.append(x)
        k += 1
    if (k >= maxk):
        print('No root found')
    else:
        print('Found root: ')
        print(x)
        
    return arr, residuals  # 原代码笔误:are应为arr

def system(x):
    F = np.zeros((2,1), dtype=np.float64)
    F[0] = x[0]*x[0] + 2*x[1]*x[1] + math.sin(2*x[0])
    F[1] = x[0]*x[0] + math.cos(x[0]+5*x[1]) - 1.2
    return F

def jacob(x,n,fnon,F0,*args):
    J = np.zeros((2,2), dtype=np.float64)
    J[0,0] = 2*(x[0]+math.cos(2*x[0]))
    J[0,1] = 4*x[1]
    J[1,0] = 2*x[0]-math.sin(x[0]+5*x[1])
    J[1,1] = -5*math.sin(x[0]+5*x[1])
    return J

arr, residuals = gradient_descent('system', 'jacob', np.array([[-2],[-1]]), 1e-2, 20, 0)

残差迭代曲线

残差随迭代次数变化曲线


问题解答

1. 残差的定义

对于非线性方程组 ( F(x) = 0 ),残差本身是当前迭代点 ( x_k ) 处的函数值 ( F(x_k) ),而我们用来量化残差大小、判断收敛性的指标通常是它的2-范数(欧几里得范数),也就是你代码里的norm(F,2)。这个范数能直观反映当前点距离方程组根的“远近”——范数越小,说明越接近满足 ( F(x)=0 ) 的根,所以你的理解完全正确。

2. 算法停滞的原因

从曲线看迭代1后残差不再下降,确实说明算法陷入了停滞,核心原因如下:

  • 固定步长不合理:你使用了固定的lamb=0.01,这个步长可能过小,导致每次迭代的更新量不足以推动残差下降;也可能在当前点附近步长过大,导致迭代来回震荡无法收敛。梯度下降法的步长不能随意固定,必须通过**线搜索(比如Armijo准则)**动态选择最优步长。
  • 梯度方向的冗余缩放:你的更新方向delta = -2 * np.matmul(J.T, F),其实最小化残差平方和 ( \frac{1}{2}|F(x)|_2^2 ) 的梯度就是 ( J^T F ),所以-2只是对方向做了缩放,和固定步长叠加后可能导致更新幅度过小,无法有效迭代。
  • 初始点位置不佳:你选择的初始点[[-2],[-1]]可能处于函数的平坦区域或局部极小值附近,此时梯度幅值很小,自然无法产生有效的更新。可以尝试更换初始点(比如[[0],[0]])测试。
  • 代码中的小错误:原代码遗漏math库导入、返回值笔误(are应为arr),这些都会影响代码运行,也可能间接导致迭代异常。

3. 改进建议

  • 替换固定步长为线搜索:实现Armijo线搜索逻辑,每次迭代动态计算最优步长;
  • 简化梯度方向:将delta改为-np.matmul(J.T, F),去掉冗余的-2,通过线搜索控制步长;
  • 更换初始点测试,观察残差变化趋势;
  • 增加迭代过程的打印输出,查看每次迭代的x和F值,更直观地定位问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 02:47:06