I am using a standard linear regression using scikit-learn in python. However, I would like to force the weights to be all non-negative for every feature. is there any way I can accomplish that? I was looking in the documentation but could not find a way to accomplish that.
5 Answers
What you are looking for, is the Non-negative least square regression. It is a simple optimization problem in quadratic programming where your constraint is that all the coefficients(a.k.a weights) should be positive.
Having said that, there is no standard implementation of Non-negative least squares in Scikit-Learn. The pull request is still open.
But, looks like Scipy has implemented the same.
PS: I haven't tried the scipy version. I found it solely by googling around.
-
1$\begingroup$ what about ridge regression where it forced to positive? $\endgroup$ Jan 27, 2018 at 20:03
-
$\begingroup$ @CharlieParker I have never heard that ridge regression aims to force the coefficients to be positive. $\endgroup$ Nov 1, 2021 at 2:09
I use a workaround with Lasso on Scikit Learn (It is definitely not the best way to do things but it works well). Lasso has a parameter positive
which can be set to True
and force the coefficients to be positive. Further, setting the Regularization coefficient alpha
to lie close to 0 makes the Lasso mimic Linear Regression with no regularization. Here's the code:
from sklearn.linear_model import Lasso
lin = Lasso(alpha=0.0001,precompute=True,max_iter=1000,
positive=True, random_state=9999, selection='random')
lin.fit(X,y)
As of version 0.24, scikit-learn LinearRegression
includes a parameter positive
, which does exactly that; from the docs:
positive : bool, default=False
When set to
True
, forces the coefficients to be positive. This option is only supported for dense arrays.New in version 0.24.
There are is a constrained least squares method scipy.optimize.lsq_linear. Another option is to use an optimizing solver for Python. Here is one of the options (Gekko) that I maintain that includes coefficient constraints.
# Constrained Multiple Linear Regression
import numpy as np
nd = 100 # number of data sets
nc = 5 # number of inputs
x = np.random.rand(nd,nc)
y = np.random.rand(nd)
from gekko import GEKKO
m = GEKKO(remote=False); m.options.IMODE=2
c = m.Array(m.FV,nc+1)
for ci in c:
ci.STATUS=1
ci.LOWER=0
xd = m.Array(m.Param,nc)
for i in range(nc):
xd[i].value = x[:,i]
yd = m.Param(y); yp = m.Var()
s = m.sum([c[i]*xd[i] for i in range(nc)])
m.Equation(yp==s+c[-1])
m.Minimize((yd-yp)**2)
m.solve(disp=True)
a = [c[i].value[0] for i in range(nc+1)]
print('Solve time: ' + str(m.options.SOLVETIME))
print('Coefficients: ' + str(a))
It uses the nonlinear solver IPOPT
to solve the problem. It is a good option for problems that aren't too large because there is some waisted computational effort on calculating exact 1st and 2nd derivatives for possible nonlinear functions. It may be faster for larger problems with the APOPT
solver with m.options.SOLVER=1
.
Here is an example of why you would want to do it (and approximately how).
I have 3 predictive models of housing prices: linear, gradient boosting, neural network.
I want to blend them into a weighted average and find the best weights.
I run linear regression, and I get a solution with weights like -3.1, 2.5, 1.5, and some intercept.
So what I do instead of using sklearn is:
blendlasso = LassoCV(alphas=np.logspace(-6, -3, 7),
max_iter=100000,
cv=5,
fit_intercept=False,
positive=True)
And I get positive weights that sum (very close) to 1. In my example, I want the alpha that works best out-of-sample so I use LassoCV with cross-validation.
The sklearn docs state that you shouldn't set alpha to 0 for numerical reasons, however, you can also use straight Lasso() and set the alpha parameter as low as you can get away with to get a reasonable answer.
-
$\begingroup$ Your work converts LassoCV to Linear Regression (if it not exactly, maybe approximately) $\endgroup$– RedhwanOct 12, 2020 at 12:47
-
$\begingroup$ @Redhwan yes, if you set the lasso alphas close enough to 0 it approaches linear regression, with weights constrained to be positive, so it's a hack, and really the only thing it adds to adarsh-chavakula's answer above is it lets you try additional alphas $\endgroup$ Oct 13, 2020 at 17:36