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=2x1−x2+ϵ,ϵ∼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 p ∼ N ( 0 , 1 )x1x2xp∼N(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:
- Eu usei (praticamente) variáveis preditoras normalizadas. Você precisará levar em conta a normalização.
- O mesmo vale para a não normalização da interceptação.
- Eu usei
optim
o argumento L-BFGS-B . É o resolvedor de baunilha mais R que aceita limites. Estou certo de que você encontrará dezenas de melhores solucionadores.
- 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.
- 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.
- 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 .
- 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