多元线性回归,对 Python 上的每个系数都有特定约束
Posted
技术标签:
【中文标题】多元线性回归,对 Python 上的每个系数都有特定约束【英文标题】:Multiple Linear Regression with specific constraint on each coefficients on Python 【发布时间】:2018-10-28 19:39:57 【问题描述】:我目前正在对数据集运行多元线性回归。起初,我没有意识到我需要限制我的体重。事实上,我需要有特定的正负权重。
更准确地说,我正在做一个评分系统,这就是为什么我的一些变量应该对音符产生积极或消极影响的原因。然而,在运行我的模型时,结果并不符合我的预期,我的一些“正”变量得到负系数,反之亦然。
举个例子,假设我的模型是:
y = W0*x0 + W1*x1 + W2*x2
其中 x2 是一个“正”变量,我想将 W2 约束为正!
我一直在寻找关于这个问题的很多东西,但我没有发现任何关于特定权重/系数的限制,我发现的只是将所有系数设置为正数或将它们相加为一个。
我正在使用 ScikitLearn 软件包开发 Python。这就是我获得最佳模型的方式:
def ridge(Xtrain, Xtest, Ytrain, Ytest, position):
param_grid='alpha':[0.01 , 0.1, 1, 10, 50, 100, 1000]
gs = grid_search.GridSearchCV(Ridge(), param_grid=param_grid, n_jobs=-1, cv=3)
gs.fit(Xtrain, Ytrain)
hatytrain = gs.predict(Xtrain)
hatytest = gs.predict(Xtest)
知道如何为特定变量的系数分配约束吗?定义每个约束可能会很麻烦,但我不知道该怎么做。
【问题讨论】:
为什么需要为此使用 scikit-learn?这只是一个函数拟合问题,不是吗。我敢打赌有更好的包来完成这个任务,可以很容易地指定适合参数的约束 您可能会考虑实施“砖墙”约束,如果在其中拟合的函数内部违反了约束,则会返回一个非常大的值 - 因此会返回一个非常大的错误。这种方法有些粗糙,但实际上它很容易编码和测试。 【参考方案1】:Scikit-learn 不允许对系数进行此类限制。
但是,如果您实现自己的估计器,则可以对系数施加任何约束并使用坐标下降优化损失。在无约束情况下,坐标下降法在合理的迭代次数内产生与 OLS 相同的结果。
我编写了一个对线性回归系数施加上限和下限的类。如果需要,您可以将其扩展为使用 Ridge 或 evel 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. ]
您可以看到大多数系数为零。一个普通的 LinearModel 会使它们变成负数:
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]
更新。该解决方案可以适用于对系数的线性组合(例如它们的总和)的约束 - 在这种情况下,将在每个步骤中重新计算每个系数的各个约束。 This Github gist 提供了一个例子。
更新
由于这个问题很受欢迎,我创建了一个包含约束线性回归实现的包:https://github.com/avidale/constrained-linear-regression。
您可以使用pip install constrained-linear-regression
安装它。欢迎请求请求!
【讨论】:
感谢您的出色回答,看来这正是我想要的。我会试一试,然后给你反馈! 这是一个了不起的答案。谢谢分享。即使问得有点晚了,您是否也知道如何为所有系数的总和添加约束?例如,对于股票投资组合,不仅系数不能为负,而且它们的总和不能大于 1。谢谢! 如果你使用坐标下降,那么在每一步你只更新一个系数。因此,对系数总和的约束可以表示为该特定系数的下限/上限。唯一的区别是每一步都会重新计算这个约束的值。 当然,您需要提供一个可行的初始解决方案,找到它可能是一个单独的问题。如果变得困难,您可能需要寻找专门的包进行约束优化(如cvxopt
),而不是自己编写优化例程
请看一下这个要点:具有一般线性不等式约束的OLS gist.github.com/avidale/6668635c318aceebe0142de013a4cf77【参考方案2】:
在 scikit-learn 的 0.24.2 版本中,您可以通过参数 positive=True 强制算法使用正系数到 LinearRegression,通过将你想要负系数的列乘以 -1,你应该得到你想要的。
【讨论】:
以上是关于多元线性回归,对 Python 上的每个系数都有特定约束的主要内容,如果未能解决你的问题,请参考以下文章
SPSS 多元线性回归结果中,系数模型下的1,B,t,Sig.分别啥意思。在线等!!急求高手解答!!