如何在scikit-learn LogisticRegression中设置LBFGS求解器的maxfun限制?
问题
使用scikit-learn的LogisticRegression模型搭配LBFGS求解器时出现提前停止,日志如下(数据已标准化):
(...) At iterate13150 f= 4.05397D+03 |proj g|= 2.41194D+04 At iterate13200 f= 4.05213D+03 |proj g|= 1.36863D+04 .venv/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:444: ConvergenceWarning: lbfgs failed to converge (status=1): STOP: TOTAL NO. of f AND g EVALUATIONS EXCEEDS LIMIT. Increase the number of iterations (max_iter) or scale the data as shown in: https://scikit-learn.org/stable/modules/preprocessing.html Please also refer to the documentation for alternative solver options: https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression n_iter_i = _check_optimize_result( [Parallel(n_jobs=-1)]: Done 1 out of 1 | elapsed: 5.5s finished * * * Tit = total number of iterations Tnf = total number of function evaluations Tnint = total number of segments explored during Cauchy searches Skip = number of BFGS updates skipped Nact = number of active bounds at final generalized Cauchy point Projg = norm of the final projected gradient F = final function value * * * N Tit Tnf Tnint Skip Nact Projg F 62 13240 15001 1 0 0 4.800D+04 4.051D+03 F = 4051.0211050375365
sklearn采用scipy实现的LBFGS求解器,scipy/optimize/_lbfgsb_py.py:_minimize_lbfgsb的提前停止条件如下:
if n_iterations >= maxiter: task[:] = 'STOP: TOTAL NO. of ITERATIONS REACHED LIMIT' elif sf.nfev > maxfun: task[:] = ('STOP: TOTAL NO. of f AND g EVALUATIONS ' 'EXCEEDS LIMIT')
实际触发了sf.nfev > maxfun的限制,但sklearn在实例化scipy求解器时,将maxfun固定为15000(见sklearn/linear_model/_logistic.py:442)。曾通过修改sklearn源码将maxfun设为100000,求解器成功收敛,但不想维护自定义版本,询问是否有其他设置maxfun参数的方法。
解决方法
1. 手动调用scipy求解器实现逻辑回归
跳过sklearn的封装,直接用scipy.optimize.minimize调用LBFGSB求解器,可自由设置maxfun参数。需要自行构造逻辑回归的损失函数和梯度:
import numpy as np from scipy.optimize import minimize def logistic_loss(w, X, y): z = X @ w # 适配标签为0/1的情况,若标签是-1/1可直接用np.log(1 + np.exp(-y*z)) loss = np.mean(-y * np.log(1/(1+np.exp(-z))) - (1-y)*np.log(np.exp(-z)/(1+np.exp(-z)))) return loss def logistic_gradient(w, X, y): z = X @ w sigmoid = 1 / (1 + np.exp(-z)) grad = X.T @ (sigmoid - y) / len(y) return grad # 假设X是标准化特征矩阵,y是0/1标签 initial_w = np.zeros(X.shape[1]) result = minimize(logistic_loss, initial_w, jac=logistic_gradient, args=(X, y), method='L-BFGS-B', options={'maxfun': 100000}) # result.x即为训练后的模型参数
2. 用猴子补丁临时修改sklearn的求解器参数
通过运行时替换sklearn内部调用的scipy求解器,修改maxfun参数,无需修改源码:
from functools import partial import sklearn.linear_model._logistic from scipy.optimize import _minimize_lbfgsb # 保存原始求解器 original_minimize = _minimize_lbfgsb def custom_minimize(*args, **kwargs): # 强制设置maxfun为目标值 kwargs['options']['maxfun'] = 100000 return original_minimize(*args, **kwargs) # 替换sklearn内部使用的求解器 sklearn.linear_model._logistic._minimize_lbfgsb = custom_minimize # 正常训练模型 from sklearn.linear_model import LogisticRegression model = LogisticRegression(solver='lbfgs', max_iter=100000) model.fit(X, y)
注意:该方法依赖sklearn内部实现,版本更新后可能失效,需验证对应版本兼容性。
3. 切换其他求解器
若场景允许,可尝试saga或sag求解器,这类求解器更适合大规模数据,且sklearn对其迭代控制更灵活,无需调整maxfun即可收敛。
内容的提问来源于stack exchange,提问作者VaM
相关产品推荐
相关产品推荐

