Come eseguire la regressione della cresta non negativa?


10

Come eseguire la regressione della cresta non negativa? Il lazo non negativo è disponibile in scikit-learn, ma per la cresta, non posso applicare la non negatività dei beta e, in effetti, sto ottenendo coefficienti negativi. qualcuno sà perche è cosi?

Inoltre, posso implementare la cresta in termini di minimi quadrati regolari? Spostato questo in un'altra domanda: posso implementare la regressione della cresta in termini di regressione OLS?


1
Ci sono due domande abbastanza ortogonali qui, prenderei in considerazione l'idea di spezzare il "posso implementare la cresta in termini di minimi quadrati" come domanda separata.
Matthew Drury,

Risposte:


8

La risposta piuttosto anti-climatica a " Qualcuno sa perché questo è? " È che a nessuno importa abbastanza per implementare una routine di regressione della cresta non negativa. Uno dei motivi principali è che le persone hanno già iniziato a implementare routine di reti elastiche non negative (ad esempio qui e qui ). La rete elastica include la regressione della cresta come un caso speciale (uno essenzialmente imposta la parte LASSO per avere una ponderazione zero). Queste opere sono relativamente nuove, quindi non sono ancora state incorporate in scikit-learn o in un simile pacchetto di uso generale. Potresti chiedere informazioni agli autori di questi articoli per il codice.

MODIFICARE:

Come @amoeba ed io abbiamo discusso sui commenti, l'implementazione effettiva di questo è relativamente semplice. Supponiamo che uno abbia il seguente problema di regressione a:

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

dove e sono entrambi normali standard come: . Si noti che utilizzo variabili predittive standardizzate, quindi non è necessario normalizzarle in seguito. Per semplicità non includo neanche un'intercettazione. Possiamo risolvere immediatamente questo problema di regressione usando la regressione lineare standard. Quindi in R dovrebbe essere qualcosa del genere: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

Nota l'ultima riga. Quasi tutte le routine di regressione lineare utilizzano la decomposizione QR per stimare . Vorremmo usare lo stesso per il nostro problema di regressione della cresta. A questo punto leggi questo post di @whuber; implementeremo esattamente questa procedura. In breve, aumenteremo la nostra matrice di design originale con una matrice diagonale e il nostro vettore di risposta con zeri . In questo modo saremo in grado di riesprimere il problema di regressione della cresta originale as dove ilX βXyp(XTX+λI) - 1 XTy( ˉ X T ˉ X ) - 1 ˉ X T ˉ y ¯λIpyp(XTX+λI)1XTy(X¯TX¯)1X¯Ty¯¯simboleggia la versione aumentata. Dai un'occhiata alle diapositive 18-19 anche da queste note per completezza, le ho trovate abbastanza semplici. Quindi in R vorremmo alcuni come segue:

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 funziona. OK, quindi abbiamo ottenuto la parte di regressione della cresta. Potremmo risolvere in un altro modo, tuttavia, potremmo formularlo come un problema di ottimizzazione in cui la somma residua dei quadrati è la funzione di costo e quindi ottimizzare contro di essa, vale a dire. . Abbastanza sicuro possiamo farlo: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

che come previsto funziona di nuovo. Quindi ora vogliamo solo: dove . Che è semplicemente lo stesso problema di ottimizzazione ma vincolato in modo che la soluzione non sia negativa.minβ||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

che mostra che l'attività di regressione della cresta non negativa originale può essere risolta riformulandosi come un semplice problema di ottimizzazione vincolata. Alcuni avvertimenti:

  1. Ho usato (praticamente) variabili predittive normalizzate. Dovrai rendere conto della normalizzazione da solo.
  2. La stessa cosa vale per la non normalizzazione dell'intercetta.
  3. Ho usato optim's L-BFGS-B argomento. È il solutore R più vaniglia che accetta limiti. Sono sicuro che troverai decine di risolutori migliori.
  4. In generale, i problemi dei minimi quadrati lineari sono posti come attività di ottimizzazione quadratica . Questo è un eccessivo per questo post, ma tieni presente che puoi ottenere una migliore velocità se necessario.
  5. Come menzionato nei commenti, è possibile saltare la regressione della cresta come parte della regressione lineare aumentata e codificare direttamente la funzione di costo della cresta come problema di ottimizzazione. Questo sarebbe molto più semplice e questo post sarà significativamente più piccolo. Per ragioni di argomento, aggiungo anche questa seconda soluzione.
  6. Non sono completamente conversatore in Python ma essenzialmente puoi replicare questo lavoro usando le funzioni di ottimizzazione di NumPy linalg.solve e SciPy .
  7. Per scegliere l'iperparametro ecc. Fai semplicemente il solito CV-step che faresti in ogni caso; niente cambia.λ

Codice per il 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

1
Questo è in qualche modo fuorviante. La regressione della cresta non negativa è banale da implementare: si può riscrivere la regressione della cresta come al solito regressione su dati estesi (vedere i commenti a stats.stackexchange.com/questions/203687 ) e quindi utilizzare le routine di regressione non negative.
Amoeba,

Sono d'accordo che è semplice da implementare (+1 a quello). (Ho votato prima il tuo e il commento di Glen anche sull'altro thread). La domanda è: perché non viene implementata, non se è difficile. A tale proposito, sospetto fortemente che la formulazione diretta di questo compito NNRR un problema di ottimizzazione sia ancora più semplice che formularlo prima come una regressione dei dati estesa e quindi utilizzare Quad. Prog. ottimizzazione per risolvere questa regressione. Non ho detto questo nella mia risposta perché si sarebbe avventurato nella parte di implementazione.
usεr11852,

O semplicemente scrivilo in Stan.
Sycorax dice di reintegrare Monica il

Ah ok; Ho capito il Q come principalmente chiedendo come fare la cresta non negativa (e chiedendo solo perché non è implementato di passaggio); Ho anche modificato per inserire questo nel titolo. In ogni caso, come fare mi sembra una domanda più interessante. Se riesci ad aggiornare la tua risposta con spiegazioni su come implementare la cresta non negativa, penso che sarà molto utile per i futuri lettori (e sarò felice di votare :).
amoeba,

1
Bene, lo farò più tardi (non ho notato il nuovo titolo, mi dispiace per quello). Probabilmente darò l'implementazione in termini di OLS / pseudo-osservazioni, quindi risponderemo anche all'altra domanda.
usεr11852,

4

Il pacchetto R glmnet che implementa la rete elastica e quindi il lazo e la cresta lo consente. Con i parametri lower.limitse upper.limits, è possibile impostare un valore minimo o massimo per ciascun peso separatamente, quindi se si impostano limiti inferiori su 0, si eseguirà una rete elastica non negativa (lazo / cresta).

Esiste anche un wrapper Python https://pypi.python.org/pypi/glmnet/2.0.0


2

Ricordiamo che stiamo cercando di risolvere:

minimizexAxy22+λx22s.t. x>0

è equivalente a:

minimizexAxy22+λxIxs.t. x>0

con un po 'più di algebra:

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

La soluzione in pseudo-python è semplicemente fare:

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

vedi: In che modo si possono radunare i minimi quadrati non negativi usando i regolarizzatori del modulo ?x R k xKxRkx

per una risposta leggermente più generale.


La riga c = - A'y non legge c = A'y? Penso che sia corretto, anche se si dovrebbe notare che la soluzione è leggermente diversa da scipy.optimize.nnls (newMatX, newVecY), dove newMatX è la riga X aumentata con una matrice diagonale con sqrt (lambda) lungo la diagonale e NewVecY è Y aumentato con zeri nvar. Penso che la soluzione che menzioni sia quella giusta ...
Tom Wenseleers,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.