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警告,拟合结果偏差极大,调整学习率无法解决问题。
问题根源
- 无约束参数更新:模型参数$A$、$\nu_0$、$\alpha$均为正数,但原始梯度下降未做约束,参数可能更新为负数或零,触发除以零、负数取幂/对数等无效运算。
- 高次幂数值溢出:梯度表达式中存在$(1+\nu/\nu_0)^{8\alpha}$这类高次项,当参数偏离合理范围时,会快速触发数值溢出,导致梯度变为无穷大/NaN。
- 固定学习率适配差:多变量场景下不同参数的梯度量级差异大,单一固定学习率无法兼顾所有参数的更新步长,易导致参数震荡或发散。
修复方案
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
相关产品推荐
相关产品推荐

