Como executar regressão não negativa da crista?

10

Como executar regressão não negativa da crista? Laço não negativo está disponível em scikit-learn, mas para cume, não posso impor a não-negatividade de betas e, de fato, estou obtendo coeficientes negativos. Alguém sabe por que isso é?

Além disso, posso implementar cume em termos de mínimos quadrados regulares? Movi isso para outra pergunta: Posso implementar a regressão de cume em termos de regressão de OLS?

O Barão
fonte
11
Há duas perguntas bastante ortogonais aqui, eu consideraria a quebra do "posso implementar cume em termos de mínimos quadrados" como uma pergunta separada.
Matthew Drury

Respostas:

8

A resposta bastante anticlimática a " Alguém sabe por que isso acontece? " É que simplesmente ninguém se importa o suficiente para implementar uma rotina de regressão não negativa da crista. Um dos principais motivos é que as pessoas já começaram a implementar rotinas líquidas elásticas não negativas (por exemplo, aqui e aqui ). A rede elástica inclui a regressão da crista como um caso especial (essencialmente, a parte do LASSO define uma ponderação zero). Esses trabalhos são relativamente novos e ainda não foram incorporados ao scikit-learn ou a um pacote de uso geral semelhante. Você pode consultar os autores desses documentos para obter código.

EDITAR:

Como @amoeba e eu discutimos nos comentários, a implementação real disso é relativamente simples. Digamos que um tenha o seguinte problema de regressão para:

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

onde e são normais normais, como: . Observe que eu uso variáveis ​​preditoras padronizadas para não precisar normalizar posteriormente. Para simplificar, também não incluo uma interceptação. Podemos resolver imediatamente esse problema de regressão usando regressão linear padrão. Portanto, em R, deve ser algo como isto: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

Observe a última linha. Quase toda rotina de regressão linear usa a decomposição QR para estimar . Gostaríamos de usar o mesmo para o nosso problema de regressão de crista. Neste ponto, leia este post por @whuber; estaremos implementando exatamente esse procedimento. Em resumo, aumentaremos nossa matriz de projeto original com uma matriz diagonal e nosso vetor de resposta com zeros de . Dessa maneira, poderemos reexprimir o problema original de regressão da crista como queX βXyp(XTX+λI) - 1 XTy( ˉ X T ˉ X ) - 1 ˉ X T ˉ y ¯λIpyp(XTX+λI)1XTy(X¯TX¯)1X¯Ty¯¯simboliza a versão aumentada. Verifique os slides 18-19 dessas anotações também para verificar se estão completos, eu os achei bastante diretos. Assim, em R, gostaríamos do seguinte:

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

e funciona. OK, então obtivemos a parte da regressão da crista. Poderíamos resolver de outra maneira, porém, poderíamos formulá-lo como um problema de otimização, em que a soma residual dos quadrados é a função de custo e, então, otimizar em relação a ela, ou seja. . Com certeza, podemos fazer isso: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

que, como esperado, novamente funciona. Então agora queremos apenas: que . O que é simplesmente o mesmo problema de otimização, mas restrito para que a solução não seja negativa. β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

que mostra que a tarefa original de regressão não negativa da crista pode ser resolvida reformulando-a como um simples problema de otimização restrita. Algumas advertências:

  1. Eu usei (praticamente) variáveis ​​preditoras normalizadas. Você precisará levar em conta a normalização.
  2. O mesmo vale para a não normalização da interceptação.
  3. Eu usei optimo argumento L-BFGS-B . É o resolvedor de baunilha mais R que aceita limites. Estou certo de que você encontrará dezenas de melhores solucionadores.
  4. Em geral, os problemas de mínimos quadrados lineares são colocados como tarefas de otimização quadrática . Este é um exagero para esta postagem, mas lembre-se de que você pode obter uma velocidade melhor, se necessário.
  5. Conforme mencionado nos comentários, você pode pular a regressão da crista como parte da regressão linear aumentada e codificar diretamente a função de custo da crista como um problema de otimização. Isso seria muito mais simples e este post significativamente menor. Por uma questão de argumento, anexo também esta segunda solução.
  6. Eu não sou totalmente conversador em Python, mas essencialmente você pode replicar esse trabalho usando as funções de otimização linalg.solve e SciPy de NumPy .
  7. Para escolher o hiperparâmetro etc., basta executar o passo CV usual, em qualquer caso; nada muda.λ

Código para o ponto 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
fonte
11
Isso é um pouco enganador. A regressão de crista não negativa é trivial de implementar: é possível reescrever a regressão de crista como regressão usual em dados estendidos (consulte os comentários em stats.stackexchange.com/questions/203687 ) e, em seguida, use rotinas de regressão não negativas.
Ameba
Concordo que é simples de implementar (+1 a isso). (Eu votei anteriormente o seu e o comentário de Glen sobre o outro tópico também). A questão é por que não é implementado, não se é difícil. Sobre esse assunto, eu suspeito fortemente que, ao formular diretamente essa tarefa do NNRR, um problema de otimização seja ainda mais simples do que formulá-lo primeiro como uma regressão estendida de dados e depois usar o Quad. Prog. otimização para resolver esta regressão. Eu não disse isso na minha resposta porque se aventuraria na parte da implementação.
usεr11852
Ou apenas escreva em stan.
Sycorax diz Restabelecer Monica
Ah ok; Eu entendi o Q como perguntando principalmente como fazer cume não negativo (e apenas perguntando por que não foi implementado de passagem); Eu até editei para colocar isso no título. De qualquer forma, como fazer isso me parece uma pergunta mais interessante. Se você puder atualizar sua resposta com explicações sobre como implementar cume não negativo, acho que será muito útil para futuros leitores (e ficarei feliz em votar :).
ameba
11
Legal, farei isso mais tarde (não percebi o novo título, desculpe por isso). Provavelmente darei a implementação em termos de OLS / pseudo-observações, portanto responderemos à outra pergunta também.
usεr11852
4

O pacote R glmnet que implementa rede elástica e, portanto, laço e cume permite isso. Com os parâmetros lower.limitse upper.limits, você pode definir um valor mínimo ou máximo para cada peso separadamente, portanto, se você definir limites inferiores para 0, ele executará uma rede elástica não-negativa (laço / cume).

Há também um wrapper python https://pypi.python.org/pypi/glmnet/2.0.0

rep_ho
fonte
2

Lembre-se de que estamos tentando resolver:

minimizexAxy22+λx22s.t. x>0

é equivalente a:

minimizexAxy22+λxIxs.t. x>0

com um pouco mais de álgebra:

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

A solução em pseudo-python é simplesmente:

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

veja: Como se faz os mínimos quadrados não negativos escassos usando regularizadores da forma ?x R k xKxRkx

para uma resposta um pouco mais geral.

Charlie Parker
fonte
A linha c = - A'y não deve ler c = A'y? Eu acho que isso está correto, embora se deva observar que a solução é um pouco diferente de scipy.optimize.nnls (newMatX, newVecY), em que newMatX é a linha X aumentada com uma matriz diagonal com sqrt (lambda) ao longo da diagonal e NewVecY é Y aumentado com zeros nvar. Acho que a solução que você menciona é o correto ...
Tom Wenseleers