如何为Scipy实现的逻辑回归设置合适初始值与边界?
我来帮你梳理下这个问题,你遇到的核心问题其实是截距项缺失、初始猜测值维度/取值不合理以及逻辑回归MLE优化的数值稳定性这几个点,咱们一步步解决:
1. 先补上缺失的截距项
逻辑回归的公式是 ( y = \text{sigmoid}(\beta_0 + \beta_1x_1 + \beta_2x_2) ),其中(\beta_0)就是截距项。你当前的特征矩阵x是(100,2),没有对应截距的全1列,所以优化出来的参数自然没有截距。
解决方法很简单,给特征矩阵拼接一列全1的向量:
# 给x添加截距项(第一列全1) x_with_intercept = np.hstack([np.ones((x.shape[0], 1)), x])
现在x_with_intercept的形状是(100,3),对应3个参数:截距(\beta_0)、两个特征的系数(\beta_1)、(\beta_2)。
2. 合理设置初始猜测值x0
你之前用的x0 = np.array([-.1])维度只有1,和现在3个参数的需求不匹配,这会直接导致优化出错。另外,初始值如果太极端,会让sigmoid函数饱和(比如(x@\beta)太大时,sigmoid趋近于1,计算log(1-sigmoid)就会得到log(0),引发无穷大损失)。
推荐几种初始值设置方式:
- 最简单的全0初始值:
x0 = np.zeros(x_with_intercept.shape[1]) - 小范围随机值:
x0 = np.random.normal(0, 0.1, x_with_intercept.shape[1])(避免参数一开始就进入饱和区) - 如果你之前有sklearn的beta结果,可以直接用它作为初始值,能加快收敛速度
3. 优化数值稳定性与边界设置
你的原始损失函数在sigmoid趋近于0或1时,容易出现数值溢出或log(0)的问题,导致损失值变成无穷大。可以用scipy.special里的工具函数来优化:
稳定版损失函数
from scipy.special import expit, logaddexp def mle_stable(beta, x, y): z = np.dot(x, beta) # 用logaddexp避免计算log(1-sigmoid)时的数值溢出 return np.sum(logaddexp(0, -z) + (1 - y) * z)
这个函数和你原来的损失函数数学上等价,但能避免极端值带来的无穷大问题。
边界设置(可选)
如果担心参数优化过程中变得过大,可以给每个参数设置合理的边界,比如限制在-10到10之间:
bounds = [(-10, 10)] * x_with_intercept.shape[1]
然后在minimize里指定method='L-BFGS-B'(这个求解器支持边界约束)。
4. 完整修正后的代码
import scipy.optimize as opt import numpy as np from scipy.special import expit, logaddexp # 假设你的原始x是(100,2),y是(100,)的0/1标签 # 步骤1:添加截距项 x_with_intercept = np.hstack([np.ones((x.shape[0], 1)), x]) # 步骤2:稳定的MLE损失函数 def mle_stable(beta, x, y): z = np.dot(x, beta) return np.sum(logaddexp(0, -z) + (1 - y) * z) # 步骤3:设置初始值和边界 x0 = np.zeros(x_with_intercept.shape[1]) # 全0初始值 bounds = [(-10, 10)] * x_with_intercept.shape[1] # 可选边界 # 步骤4:执行优化 result = opt.minimize( mle_stable, x0=x0, args=(x_with_intercept, y), method='L-BFGS-B', bounds=bounds ) # 查看结果 print("优化后的beta(含截距):", result.x) print("最终损失值:", result.fun) print("优化是否成功:", result.success)
5. 其他实现逻辑回归的方法
除了用scipy.optimize手动实现,还有更简便的工具:
- Statsmodels:专门做统计建模的库,直接支持逻辑回归,还会输出统计指标:
import statsmodels.api as sm logit_model = sm.Logit(y, x_with_intercept) result = logit_model.fit() print(result.summary()) # 包含截距、系数、p值等 - 手动梯度下降:自己实现梯度更新,更直观理解优化过程:
def gradient_descent(x, y, lr=0.01, epochs=1000): beta = np.zeros(x.shape[1]) for _ in range(epochs): y_pred = expit(np.dot(x, beta)) grad = np.dot(x.T, (y_pred - y)) beta -= lr * grad # 可选:监控损失值变化,提前停止 return beta beta_gd = gradient_descent(x_with_intercept, y)
内容的提问来源于stack exchange,提问作者python_interest

