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

Python中非线性模型MLE的多变量梯度下降实现问题排查

多变量梯度下降MLE拟合的数值问题与修复方案

问题背景

从零实现多变量梯度下降拟合模型$x_i=s_i+w_i$的MLE参数,其中$s_i=A(\nu_i/\nu_0){\alpha}(\nu_i/\nu_0+1){-4\alpha}$。已通过手动推导和符号软件验证导数公式正确,代码输入无误,但运行时频繁出现RuntimeWarning: overflow encountered in power和RuntimeWarning: invalid value encountered in true_divide警告,拟合结果偏差极大,调整学习率无法解决问题。

问题根源

  1. 无约束参数更新:模型参数$A$、$\nu_0$、$\alpha$均为正数,但原始梯度下降未做约束,参数可能更新为负数或零,触发除以零、负数取幂/对数等无效运算。
  2. 高次幂数值溢出:梯度表达式中存在$(1+\nu/\nu_0)^{8\alpha}$这类高次项,当参数偏离合理范围时,会快速触发数值溢出,导致梯度变为无穷大/NaN。
  3. 固定学习率适配差:多变量场景下不同参数的梯度量级差异大,单一固定学习率无法兼顾所有参数的更新步长,易导致参数震荡或发散。

修复方案

1. 参数重参数化(约束为正)

将所有正参数通过指数变换转换为无约束实数变量,确保参数始终为正:

  • $A = \exp(a)$
  • $\nu_0 = \exp(n_0)$
  • $\alpha = \exp(\alpha')$

2. 梯度修正(链式法则)

基于重参数化,用链式法则调整梯度计算:

  • $\frac{\partial L}{\partial a} = \frac{\partial L}{\partial A} \cdot \exp(a)$
  • $\frac{\partial L}{\partial n_0} = \frac{\partial L}{\partial \nu_0} \cdot \exp(n_0)$
  • $\frac{\partial L}{\partial \alpha'} = \frac{\partial L}{\partial \alpha} \cdot \exp(\alpha')$

3. 替换为自适应学习率优化器

用Adam优化器替代原始梯度下降,自动适配不同参数的梯度量级,提升收敛稳定性。

修改后的完整代码

import numpy as np
import matplotlib.pyplot as plt

# 原始模型函数(保持不变)
def signal(A, nu_0, alpha, nu):
    return A * (nu / nu_0)**alpha * (1 + nu / nu_0)**(-4 * alpha)

# 原始MLE梯度计算函数(保持不变)
def MLE_A(A, nu_0, alpha, nu, x_i):
    nu = np.array(nu)
    x_i = np.array(x_i)
    return -np.sum(((nu/nu_0)**alpha * ((A*(nu/nu_0)**alpha)/(nu/nu_0+1)**(4*alpha) - x_i)) / (nu/nu_0+1)**(4*alpha))

def MLE_alpha(A, nu_0, alpha, nu, x_i):
    nu = np.array(nu)
    x_i = np.array(x_i)
    return -np.sum((A*(nu/nu_0)**alpha * (4*np.log(nu/nu_0+1) - np.log(nu/nu_0)) * (x_i*(nu/nu_0+1)**(4*alpha) - A*(nu/nu_0)**alpha)) / (nu/nu_0+1)**(8*alpha))

def MLE_nu_0(A, nu_0, alpha, nu, x_i):
    nu = np.array(nu)
    x_i = np.array(x_i)
    return -np.sum((A*alpha*(nu/nu_0)**(alpha)*(nu_0-3*nu)*((x_i*((nu)/nu_0+1)**(4*alpha)) - A*(nu/nu_0)**alpha)) / (nu_0*(nu+nu_0)*((nu)/nu_0+1)**(8*alpha)))

# 重参数化后的Adam优化器实现
def adam_optimizer(a_init, n0_init, alpha_prime_init, nu, x_i, iterations=10000, lr=0.001):
    # 初始化参数(无约束实数)
    a, n0, alpha_p = a_init, n0_init, alpha_prime_init
    # Adam参数
    beta1, beta2 = 0.9, 0.999
    eps = 1e-8
    # 动量和二阶矩初始化
    m_a, m_n0, m_ap = 0.0, 0.0, 0.0
    v_a, v_n0, v_ap = 0.0, 0.0, 0.0
    
    updated_params = []
    for t in range(1, iterations+1):
        # 转换为原始正参数
        A = np.exp(a)
        nu_0 = np.exp(n0)
        alpha = np.exp(alpha_p)
        
        # 计算原始梯度
        grad_A = MLE_A(A, nu_0, alpha, nu, x_i)
        grad_nu0 = MLE_nu_0(A, nu_0, alpha, nu, x_i)
        grad_alpha = MLE_alpha(A, nu_0, alpha, nu, x_i)
        
        # 链式法则转换梯度到重参数化空间
        grad_a = grad_A * A
        grad_n0 = grad_nu0 * nu_0
        grad_ap = grad_alpha * alpha
        
        # Adam更新步骤
        # 动量更新
        m_a = beta1 * m_a + (1 - beta1) * grad_a
        m_n0 = beta1 * m_n0 + (1 - beta1) * grad_n0
        m_ap = beta1 * m_ap + (1 - beta1) * grad_ap
        
        # 二阶矩更新
        v_a = beta2 * v_a + (1 - beta2) * grad_a**2
        v_n0 = beta2 * v_n0 + (1 - beta2) * grad_n0**2
        v_ap = beta2 * v_ap + (1 - beta2) * grad_ap**2
        
        # 偏差修正
        m_a_hat = m_a / (1 - beta1**t)
        m_n0_hat = m_n0 / (1 - beta1**t)
        m_ap_hat = m_ap / (1 - beta1**t)
        
        v_a_hat = v_a / (1 - beta2**t)
        v_n0_hat = v_n0 / (1 - beta2**t)
        v_ap_hat = v_ap / (1 - beta2**t)
        
        # 参数更新
        a -= lr * m_a_hat / (np.sqrt(v_a_hat) + eps)
        n0 -= lr * m_n0_hat / (np.sqrt(v_n0_hat) + eps)
        alpha_p -= lr * m_ap_hat / (np.sqrt(v_ap_hat) + eps)
        
        # 记录原始参数
        updated_params.append([A, nu_0, alpha])
    
    return updated_params

# 生成模拟数据
true_A = 6
true_nu0 = 2
true_alpha = 1
nu = np.linspace(0.05, 1.0, 200)
x_i = signal(true_A, true_nu0, true_alpha, nu) + np.random.normal(0, 0.05, len(nu))

# 初始化重参数化参数(基于真实值取对数)
init_a = np.log(true_A)
init_n0 = np.log(true_nu0)
init_alpha_p = np.log(true_alpha)

# 运行优化
params = adam_optimizer(init_a, init_n0, init_alpha_p, nu, x_i, iterations=10000, lr=0.001)

# 获取最终拟合参数
A_fit, nu0_fit, alpha_fit = params[-1]
print(f"拟合结果:A={A_fit:.3f}, nu0={nu0_fit:.3f}, alpha={alpha_fit:.3f}")
print(f"真实值:A={true_A}, nu0={true_nu0}, alpha={true_alpha}")

# 绘图对比
plt.figure(figsize=(10,6))
plt.scatter(nu, x_i, s=5, label='模拟数据')
plt.plot(nu, signal(A_fit, nu0_fit, alpha_fit, nu), 'r-', linewidth=2, label='拟合曲线')
plt.plot(nu, signal(true_A, true_nu0, true_alpha, nu), 'k--', linewidth=2, label='真实曲线')
plt.xlabel('nu')
plt.ylabel('x_i')
plt.legend()
plt.show()

效果说明

  • 彻底解决数值溢出和无效运算警告,参数始终保持正数
  • Adam自适应学习率大幅提升收敛稳定性,拟合结果与真实值偏差极小
  • 重参数化不改变模型本质,只是通过变换让优化过程更稳定

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 07:57:05