Scikit-learn不允许对系数施加这样的限制。
但是,如果您实现自己的估算器,则可以对系数强制任何约束并使用坐标下降法优化损失。在无约束情况下,坐标下降在合理的迭代次数内产生与OLS相同的结果。
我写了一个类,对LinearRegression系数施加了上限和下限。如果需要,您可以扩展它以使用Ridge或Lasso惩罚:
from sklearn.linear_model.base import LinearModel
from sklearn.base import RegressorMixin
from sklearn.utils import check_X_y
import numpy as np
class ConstrainedLinearRegression(LinearModel, RegressorMixin):
def __init__(self, fit_intercept=True, normalize=False, copy_X=True, nonnegative=False, tol=1e-15):
self.fit_intercept = fit_intercept
self.normalize = normalize
self.copy_X = copy_X
self.nonnegative = nonnegative
self.tol = tol
def fit(self, X, y, min_coef=None, max_coef=None):
X, y = check_X_y(X, y, accept_sparse=['csr', 'csc', 'coo'], y_numeric=True, multi_output=False)
X, y, X_offset, y_offset, X_scale = self._preprocess_data(
X, y, fit_intercept=self.fit_intercept, normalize=self.normalize, copy=self.copy_X)
self.min_coef_ = min_coef if min_coef is not None else np.repeat(-np.inf, X.shape[1])
self.max_coef_ = max_coef if max_coef is not None else np.repeat(np.inf, X.shape[1])
if self.nonnegative:
self.min_coef_ = np.clip(self.min_coef_, 0, None)
beta = np.zeros(X.shape[1]).astype(float)
prev_beta = beta + 1
hessian = np.dot(X.transpose(), X)
while not (np.abs(prev_beta - beta)<self.tol).all():
prev_beta = beta.copy()
for i in range(len(beta)):
grad = np.dot(np.dot(X,beta) - y, X)
beta[i] = np.minimum(self.max_coef_[i],
np.maximum(self.min_coef_[i],
beta[i]-grad[i] / hessian[i,i]))
self.coef_ = beta
self._set_intercept(X_offset, y_offset, X_scale)
return self
例如,您可以使用此类来使所有系数为非负数。
from sklearn.datasets import load_boston
from sklearn.linear_model import LinearRegression
X, y = load_boston(return_X_y=True)
model = ConstrainedLinearRegression(nonnegative=True)
model.fit(X, y)
print(model.intercept_)
print(model.coef_)
这将生成类似于以下输出:
-36.99292986145538
[0. 0.05286515 0. 4.12512386 0. 8.04017956
0. 0. 0. 0. 0. 0.02273805
0. ]
您可以看到大多数系数都是零。一个普通的线性模型会使它们变成负数:
model = LinearRegression()
model.fit(X, y)
print(model.intercept_)
print(model.coef_)
它会返回给你
36.49110328036191
[-1.07170557e-01 4.63952195e-02 2.08602395e-02 2.68856140e+00
-1.77957587e+01 3.80475246e+00 7.51061703e-04 -1.47575880e+00
3.05655038e-01 -1.23293463e-02 -9.53463555e-01 9.39251272e-03
-5.25466633e-01]
你也可以对任何系数施加任意限制 - 这就是你所要求的。例如,在这种设置中
model = ConstrainedLinearRegression()
min_coef = np.repeat(-np.inf, X.shape[1])
min_coef[0] = 0
min_coef[4] = -1
max_coef = np.repeat(4, X.shape[1])
max_coef[3] = 2
model.fit(X, y, max_coef=max_coef, min_coef=min_coef)
print(model.intercept_)
print(model.coef_)
你将会得到一个输出
24.060175576410515
[ 0. 0.04504673 -0.0354073 2. -1. 4.
-0.01343263 -1.17231216 0.2183103 -0.01375266 -0.7747823 0.01122374
-0.56678676]
更新。这个解决方案可以适应对系数的线性组合的约束(例如它们的总和) - 在这种情况下,每个系数的独立约束将在每一步重新计算。这个 Github Gist 提供了一个例子。
更新 由于此问题的受欢迎程度,我创建了一个包,其中包含我的实现有约束的线性回归:https://github.com/avidale/constrained-linear-regression。
您可以使用 pip install constrained-linear-regression
安装它。欢迎提交拉取请求!