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

scipy.minimize(BFGS)优化失效:损失值不下降的问题求助

问题分析与优化方案

核心问题定位

从你的损失值波动情况和代码来看,主要存在以下几个关键问题导致BFGS无法有效下降:

1. 未提供精确梯度,BFGS依赖噪声大的有限差分

当前代码中do_lnp仅返回损失值,但BFGS优化器如果没有精确梯度,会自动使用有限差分近似,这不仅效率低,还会引入数值噪声,导致优化陷入局部震荡。

2. 梯度计算遗漏正则项的导数

你计算的梯度df只包含了模型主体部分的导数,完全忽略了正则化项的梯度,这会导致优化方向错误,无法收敛到最优解。

3. 正则化项定义错误

你使用了exp(param)**2作为正则化项,这是非标准的L2正则化。该形式会对正参数施加极强的惩罚(指数增长),迫使参数向负方向偏移,严重干扰优化过程。标准L2正则化应该直接对参数本身平方求和。

4. 输入数据形状不匹配

在fit_lnp中传递y.T给do_lnp,导致y和u的形状不匹配(y为行向量,u为列向量),二者的乘法会产生矩阵而非元素级运算,最终损失值计算完全错误。


具体改进步骤

步骤1:修正损失函数与梯度计算

修改do_lnp函数,修复正则化项、补充正则化梯度,并返回损失值和精确梯度:

def do_lnp(param, X, y):
    """
    Computes the objective function and its gradient for the LNP model.

    Args:
        param (numpy.ndarray): Parameter array.
        X (numpy.ndarray): Input data matrix (shape: [n_samples, n_features]).
        y (numpy.ndarray): Output data vector (shape: [n_samples, 1]).

    Returns:
        tuple: Tuple containing the objective function value and its gradient.
    """
    lamda = 1e-2
    # compute the firing rate
    u = np.dot(X, param.reshape(-1, 1))
    rate = np.exp(u)

    # 修正正则化项:标准L2正则化
    reg_val = (lamda / 2) * np.sum(param ** 2)

    # 计算损失值
    f = np.sum(rate - y * u) + reg_val
    print(f)

    # 计算梯度:包含模型主体+正则化项的导数
    df_model = np.dot(X.T, (rate - y)).flatten()  # 展平为与param同形状的一维数组
    df_reg = lamda * param
    df = df_model + df_reg

    return f, df

步骤2:调整优化器调用,传递精确梯度

在fit_lnp中,修改minimize调用,指定jac=True(表示目标函数返回损失和梯度),同时修正y的传递:

def fit_lnp(X, y, opts):
    """
    Fits an LNP model to the data.

    Args:
        X (numpy.ndarray): Input data matrix (shape: [n_features, n_samples]).
        y (numpy.ndarray): Output data vector (shape: [n_samples, 1]).
        opts (dict): Options for the optimizer.

    Returns:
        tuple: Tuple containing the optimized parameters and predicted values.
    """
    init = 1e-3 * np.random.randn(X.shape[0], 1).flatten()
    # 传递y而非y.T,同时指定jac=True
    result = minimize(
        lambda param: do_lnp(param, X.T, y), init, method="BFGS", options=opts, jac=True
    )
    param = result.x
    yHat = np.exp(np.dot(X.T, param)).reshape(-1, 1)  # 保持与y一致的形状
    return param, yHat

步骤3:数据标准化(可选但推荐)

如果输入特征X的数值范围差异较大,会导致exp(X@param)数值不稳定。建议对每个特征做标准化处理:

# 在fit_lnp函数开头添加
X = (X - np.mean(X, axis=1, keepdims=True)) / np.std(X, axis=1, keepdims=True)

步骤4:调整优化器参数

优化器选项opts可以调整迭代次数和收敛阈值,例如:

opts = {
    "maxiter": 2000,  # 增加最大迭代次数
    "gtol": 1e-8,     # 降低梯度收敛阈值
    "disp": True      # 打印优化过程信息
}

是否需要更换优化方法?

在修复上述核心问题后,BFGS应该能够正常收敛。如果仍然存在问题,可以尝试以下替代方案:

  • L-BFGS-B:适合大规模问题,内存占用更低,可添加参数边界约束(例如限制参数为非负,如果有物理意义)。
  • Adam:Scipy 1.7+版本支持Adam优化器,对非凸问题和噪声数据鲁棒性更强,无需手动调整学习率。

例如,使用L-BFGS-B的修改:

result = minimize(
    lambda param: do_lnp(param, X.T, y), init, method="L-BFGS-B", options=opts, jac=True
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 04:25:58