Wie führe ich eine nicht negative Gratregression durch?

10

Wie führe ich eine nicht negative Gratregression durch? Nicht-negatives Lasso ist in verfügbar scikit-learn, aber für Ridge kann ich die Nicht-Negativität von Betas nicht erzwingen, und tatsächlich erhalte ich negative Koeffizienten. Weiß jemand warum das so ist?

Kann ich Ridge auch in Bezug auf reguläre kleinste Quadrate implementieren? Dies wurde auf eine andere Frage verschoben: Kann ich eine Ridge-Regression in Bezug auf die OLS-Regression implementieren?

Der Baron
quelle
1
Hier gibt es zwei recht orthogonale Fragen. Ich würde in Betracht ziehen, den "Kann ich Grat in Bezug auf die kleinsten Quadrate implementieren" als separate Frage auszubrechen.
Matthew Drury

Antworten:

8

Die eher antiklimatische Antwort auf " Weiß jemand, warum das so ist? " Ist, dass sich einfach niemand genug darum kümmert, eine nicht negative Gratregressionsroutine zu implementieren. Einer der Hauptgründe ist, dass die Menschen bereits damit begonnen haben, nicht negative elastische Netzroutinen zu implementieren (zum Beispiel hier und hier ). Das elastische Netz enthält als Sonderfall die Gratregression (man hat den LASSO-Teil im Wesentlichen so eingestellt, dass er eine Gewichtung von Null hat). Diese Werke sind relativ neu, daher wurden sie noch nicht in scikit-learn oder ein ähnliches Paket für den allgemeinen Gebrauch aufgenommen. Möglicherweise möchten Sie die Autoren dieser Artikel nach Code fragen.

BEARBEITEN:

Wie @amoeba und ich in den Kommentaren besprochen haben, ist die tatsächliche Implementierung relativ einfach. Angenommen, man hat das folgende Regressionsproblem:

y=2x1x2+ϵ,ϵN(0,0.22)

Dabei sind und beide Standardnormalen wie: . Beachten Sie, dass ich standardisierte Prädiktorvariablen verwende, damit ich mich danach nicht normalisieren muss. Der Einfachheit halber füge ich auch keinen Abschnitt hinzu. Wir können dieses Regressionsproblem sofort mit der linearen Standardregression lösen. In R sollte es also ungefähr so ​​sein:x 2 x pN ( 0 , 1 )x1x2xpN(0,1)

rm(list = ls()); 
library(MASS); 
set.seed(123);
N = 1e6;
x1 = rnorm(N)
x2 = rnorm(N)
y = 2 * x1 - 1 * x2 + rnorm(N,sd = 0.2)

simpleLR = lm(y ~ -1 + x1 + x2 )
matrixX = model.matrix(simpleLR); # This is close to standardised
vectorY = y
all.equal(coef(simpleLR), qr.solve(matrixX, vectorY), tolerance = 1e-7)  # TRUE

Beachten Sie die letzte Zeile. Fast alle linearen Regressionsroutinen verwenden die QR-Zerlegung, um zu schätzen . Wir möchten dasselbe für unser Ridge-Regressionsproblem verwenden. An dieser Stelle lesen Sie diesen Beitrag von @whuber; Wir werden genau dieses Verfahren implementieren . Kurz gesagt, wir werden unsere ursprüngliche Entwurfsmatrix mit einer Diagonalmatrix und unseren Antwortvektor mit Nullen erweitern. Auf diese Weise können wir das ursprüngliche Gratregressionsproblem als erneut ausdrücken wobei derX βXyp(XTX+λI) - 1 XTy( ˉ X T ˉ X ) - 1 ˉ X T ˉ y ¯λIpyp(XTX+λI)1XTy(X¯TX¯)1X¯Ty¯¯symbolisiert die erweiterte Version. Überprüfen Sie auch die Folien 18-19 aus diesen Notizen auf Vollständigkeit. Ich fand sie recht einfach. In R möchten wir also Folgendes:

myLambda = 100;  
simpleRR = lm.ridge(y ~ -1 + x1 + x2, lambda = myLambda)
newVecY = c(vectorY, rep(0, 2))
newMatX = rbind(matrixX, sqrt(myLambda) * diag(2))
all.equal(coef(simpleRR), qr.solve(newMatX, newVecY), tolerance = 1e-7)  # TRUE

und es funktioniert. OK, also haben wir den Ridge-Regressionsteil bekommen. Wir könnten es jedoch auf andere Weise lösen, wir könnten es als Optimierungsproblem formulieren, bei dem die verbleibende Quadratsumme die Kostenfunktion ist, und dann dagegen optimieren, d. H. . Sicher können wir das tun:minβ||y¯X¯β||22

myRSS <- function(X,y,b){ return( sum( (y - X%*%b)^2 ) ) }
bfgsOptim = optim(myRSS, par = c(1,1), X = newMatX, y= newVecY, 
                  method = 'L-BFGS-B')
all.equal(coef(simpleRR), bfgsOptim$par, check.attributes = FALSE, 
          tolerance = 1e-7) # TRUE

was wie erwartet wieder funktioniert. Jetzt wollen wir nur noch: wobei . Das ist einfach das gleiche Optimierungsproblem, aber eingeschränkt, so dass die Lösung nicht negativ ist. β0minβ||y¯X¯β||22β0

bfgsOptimConst = optim(myRSS, par = c(1,1), X=newMatX, y= newVecY, 
                       method = 'L-BFGS-B', lower = c(0,0))
all(bfgsOptimConst$par >=0)  # TRUE
(bfgsOptimConst$par) # 2.000504 0.000000

Dies zeigt, dass die ursprüngliche nicht negative Gratregressionsaufgabe gelöst werden kann, indem sie als einfaches eingeschränktes Optimierungsproblem umformuliert wird. Einige Einschränkungen:

  1. Ich habe (praktisch) normalisierte Prädiktorvariablen verwendet. Sie müssen die Normalisierung selbst berücksichtigen.
  2. Das Gleiche gilt für die nicht Normalisierung des Abschnitts.
  3. Früher habe ich optim‚s L-BFGS-B - Argument. Es ist der Vanille-R-Löser, der Grenzen akzeptiert. Ich bin sicher, dass Sie Dutzende besserer Löser finden werden.
  4. Im Allgemeinen werden lineare Probleme mit kleinsten Quadraten als quadratische Optimierungsaufgaben gestellt . Dies ist ein Overkill für diesen Beitrag, aber denken Sie daran, dass Sie bei Bedarf eine bessere Geschwindigkeit erzielen können.
  5. Wie in den Kommentaren erwähnt, können Sie die Gratregression als Teil der erweiterten linearen Regression überspringen und die Gratkostenfunktion direkt als Optimierungsproblem codieren. Dies wäre viel einfacher und dieser Beitrag deutlich kleiner. Aus Gründen der Argumentation füge ich auch diese zweite Lösung hinzu.
  6. Ich bin in Python nicht vollständig im Gespräch, aber im Wesentlichen können Sie diese Arbeit mithilfe der Optimierungsfunktionen von NumPy linalg.solve und SciPy replizieren .
  7. Um den Hyperparameter usw. auszuwählen, führen Sie einfach den üblichen CV-Schritt aus, den Sie auf jeden Fall ausführen würden. Nichts verändert sich.λ

Code für Punkt 5:

myRidgeRSS <- function(X,y,b, lambda){ 
                return( sum( (y - X%*%b)^2 ) + lambda * sum(b^2) ) 
              }
bfgsOptimConst2 = optim(myRidgeRSS, par = c(1,1), X = matrixX, y = vectorY,
                        method = 'L-BFGS-B', lower = c(0,0), lambda = myLambda)
all(bfgsOptimConst2$par >0) # TRUE
(bfgsOptimConst2$par) # 2.000504 0.000000
usεr11852
quelle
1
Das ist etwas irreführend. Die Implementierung einer nicht negativen Ridge-Regression ist trivial: Sie können die Ridge-Regression wie gewohnt in erweiterte Daten umschreiben (siehe Kommentare zu stats.stackexchange.com/questions/203687 ) und dann nicht negative Regressionsroutinen verwenden.
Amöbe
Ich bin damit einverstanden, dass es einfach zu implementieren ist (+1 dazu). (Ich habe früher Ihren und Glen's Kommentar zu dem anderen Thread ebenfalls positiv bewertet). Die Frage ist, warum nicht implementiert wird, nicht wenn es schwierig ist. In dieser Hinsicht vermute ich stark, dass die direkte Formulierung dieser NNRR-Aufgabe als Optimierungsproblem noch einfacher ist, als sie zuerst als erweiterte Datenregression zu formulieren und dann Quad zu verwenden. Prog. Optimierung zur Lösung dieser Regression. Ich habe dies in meiner Antwort nicht gesagt, weil es sich in den Implementierungsteil wagen würde.
usεr11852
Oder schreiben Sie es einfach in Stan.
Sycorax sagt Reinstate Monica
Ah, okay; Ich habe das Q so verstanden, dass ich hauptsächlich gefragt habe, wie man einen nicht negativen Grat macht (und nur gefragt, warum es nicht im Vorbeigehen implementiert wird). Ich habe sogar bearbeitet, um dies in den Titel zu setzen. Wie es geht, scheint mir auf jeden Fall eine interessantere Frage zu sein. Wenn Sie Ihre Antwort mit Erklärungen zur Implementierung eines nicht negativen Kamms aktualisieren können, wird dies meiner Meinung nach für zukünftige Leser sehr nützlich sein (und ich werde mich freuen, dies zu verbessern :).
Amöbe
1
Cool, ich werde es später tun (ich habe den neuen Titel nicht bemerkt, tut mir leid). Ich werde die Implementierung wahrscheinlich in Bezug auf OLS / Pseudo-Beobachtungen geben, also beantworten wir auch die andere Frage.
usεr11852
4

Das R-Paket glmnet, das ein elastisches Netz und damit Lasso und Ridge implementiert, ermöglicht dies. Mit den Parametern lower.limitsund upper.limitskönnen Sie für jedes Gewicht separat einen Minimal- oder Maximalwert festlegen. Wenn Sie also die Untergrenzen auf 0 setzen, wird ein nicht negatives elastisches Netz (Lasso / Grat) ausgeführt.

Es gibt auch einen Python-Wrapper https://pypi.python.org/pypi/glmnet/2.0.0

rep_ho
quelle
2

Denken Sie daran, wir versuchen zu lösen:

minimizexAxy22+λx22s.t. x>0

ist äquivalent zu:

minimizexAxy22+λxIxs.t. x>0

mit etwas mehr Algebra:

minimizexxT(ATA+λI)x+(2ATy)Txs.t. x>0

Die Lösung in Pseudo-Python ist einfach zu tun:

Q = A'A + lambda*I
c = - A'y
x,_ = scipy.optimize.nnls(Q,c)

siehe: Wie macht man spärliche nicht negative kleinste Quadrate mit Regularisierern der Form ?x R k xKxRkx

für eine etwas allgemeinere Antwort.

Charlie Parker
quelle
Sollte die Zeile c = - A'y nicht c = A'y lauten? Ich denke, das ist richtig, obwohl man beachten sollte, dass sich die Lösung geringfügig von scipy.optimize.nnls (newMatX, newVecY) unterscheidet, wobei newMatX eine X-Zeile ist, die mit einer diagonalen Matrix mit sqrt (Lambda) entlang der Diagonale erweitert ist, und NewVecY Y ist ergänzt mit nvar Nullen. Ich denke, die Lösung, die Sie erwähnen, ist die richtige ...
Tom Wenseleers