¿Cómo realizar una regresión de cresta no negativa?

10

¿Cómo realizar una regresión de cresta no negativa? El lazo no negativo está disponible en scikit-learn, pero para cresta, no puedo imponer la no negatividad de las versiones beta, y de hecho, estoy obteniendo coeficientes negativos. ¿Alguien sabe a que se debe esto?

Además, ¿puedo implementar la cresta en términos de mínimos cuadrados regulares? Moví esto a otra pregunta: ¿Puedo implementar la regresión de cresta en términos de regresión OLS?

El baron
fuente
1
Aquí hay dos preguntas bastante ortogonales, consideraría dividir el "puedo implementar la cresta en términos de mínimos cuadrados" como una pregunta separada.
Matthew Drury

Respuestas:

8

La respuesta más bien anti-climática a " ¿Alguien sabe por qué es esto? " Es que simplemente a nadie le importa lo suficiente como para implementar una rutina de regresión de cresta no negativa. Una de las razones principales es que las personas ya han comenzado a implementar rutinas de red elásticas no negativas (por ejemplo, aquí y aquí ). Elastic net incluye la regresión de cresta como un caso especial (uno esencialmente configura la parte LASSO para que tenga una ponderación cero). Estos trabajos son relativamente nuevos, por lo que aún no se han incorporado a scikit-learn o un paquete de uso general similar. Es posible que desee consultar a los autores de estos documentos para obtener el código.

EDITAR:

Como @amoeba y yo discutimos sobre los comentarios, la implementación real de esto es relativamente simple. Digamos que uno tiene el siguiente problema de regresión para:

y=2X1-X2+ϵ,ϵnorte(0 0,0.2 0.22)

donde y x 2 son ambos normales estándar, tales como: x p ~ N ( 0 , 1 ) . Tenga en cuenta que uso variables predictoras estandarizadas para no tener que normalizar después. Por simplicidad, tampoco incluyo una intercepción. Podemos resolver de inmediato este problema de regresión utilizando la regresión lineal estándar. Entonces en R debería ser algo como esto:X1X2Xpagnorte(0 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

βXλyopagypag(XTX+λyo)-1XTy(X¯TX¯)-1X¯Ty¯¯simboliza la versión aumentada. Revise las diapositivas 18-19 de estas notas para ver si están completas, las encontré bastante sencillas. Entonces, en R, a algunos nos gustaría lo siguiente:

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

minβEl |El |y¯-X¯βEl |El |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

minβEl |El |y¯-X¯βEl |El |22β0 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

lo que muestra que la tarea original de regresión de cresta no negativa se puede resolver reformulando como un simple problema de optimización restringida. Algunas advertencias:

  1. Usé (prácticamente) variables predictoras normalizadas. Deberá tener en cuenta la normalización usted mismo.
  2. Lo mismo ocurre con la no normalización de la intercepción.
  3. Solía optim's L-BFGS-B argumento. Es el solucionador más vainilla R que acepta límites. Estoy seguro de que encontrará docenas de mejores solucionadores.
  4. En general, los problemas lineales de mínimos cuadrados se presentan como tareas de optimización cuadrática . Esta es una exageración para esta publicación, pero tenga en cuenta que puede obtener una mejor velocidad si es necesario.
  5. Como se menciona en los comentarios, puede omitir la regresión de cresta como parte de regresión lineal aumentada y codificar directamente la función de costo de cresta como un problema de optimización. Esto sería mucho más simple y esta publicación significativamente más pequeña. En aras de la discusión, también añado esta segunda solución.
  6. No soy completamente conversacional en Python, pero esencialmente puede replicar este trabajo utilizando las funciones de optimización de NumPy linalg.solve y SciPy .
  7. λ

Código para el punto 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
fuente
1
Esto es algo engañoso. La regresión de cresta no negativa es trivial de implementar: se puede reescribir la regresión de cresta como la regresión habitual en datos extendidos (ver comentarios en stats.stackexchange.com/questions/203687 ) y luego usar rutinas de regresión no negativa.
ameba
Estoy de acuerdo en que es simple de implementar (+1 a eso). (He votado anteriormente el tuyo y el comentario de Glen sobre el otro hilo también). Sin embargo, la pregunta es por qué no se implementa, no si es difícil. En ese sentido, sospecho que formular directamente esta tarea de NNRR un problema de optimización es aún más simple que formularla primero como una regresión de datos extendida y luego usar Quad. Prog. optimización para resolver esta regresión. No dije esto en mi respuesta porque se aventuraría en la parte de implementación.
usεr11852
O simplemente escríbelo en stan.
Sycorax dice Reinstate Monica
Ah bien; Entendí que la Q preguntaba principalmente cómo hacer una cresta no negativa (y solo preguntaba por qué no se implementa de pasada); Incluso edité para poner esto en el título. En cualquier caso, cómo hacerlo me parece una pregunta más interesante. Si puede actualizar su respuesta con explicaciones sobre cómo implementar una cresta no negativa, creo que será muy útil para futuros lectores (y estaré encantado de votar :).
ameba
1
Genial, lo haré más tarde (no noté el nuevo título, lo siento). Probablemente daré la implementación en términos de OLS / pseudoobservaciones, así que también contestaremos la otra pregunta.
usεr11852
4

R paquete glmnet que implementa una red elástica y, por lo tanto, el lazo y la cresta lo permiten. Con los parámetros lower.limitsy upper.limits, puede establecer un valor mínimo o máximo para cada peso por separado, por lo que si establece límites inferiores a 0, se realizará una red elástica no negativa (lazo / cresta).

También hay un contenedor de python https://pypi.python.org/pypi/glmnet/2.0.0

rep_ho
fuente
2

Recordemos que estamos tratando de resolver:

minimizarXUNAX-y22+λX22S t X>0 0

es equivalente a:

minimizarXUNAX-y22+λXyoXS t X>0 0

con algo más de álgebra:

minimizarXXT(UNATUNA+λyo)X+(-2UNATy)TXS t X>0 0

La solución en pseudo-python es simplemente hacer:

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

KXRkX

para una respuesta un poco más general.

Charlie Parker
fuente
¿Debería la línea c = - A'y no leer c = A'y? Creo que esto es correcto, aunque uno debe tener en cuenta que la solución es ligeramente diferente de scipy.optimize.nnls (newMatX, newVecY), donde newMatX es una fila X aumentada con una matriz diagonal con sqrt (lambda) a lo largo de la diagonal y NewVecY es Y aumentado con nvar ceros. Creo que la solución que mencionas es la correcta ...
Tom Wenseleers