Aggiornamento del lazo con nuove osservazioni


12

Sto adattando una regressione lineare regolarizzata L1 a un set di dati molto grande (con n >> p.) Le variabili sono note in anticipo, ma le osservazioni arrivano in piccoli blocchi. Vorrei mantenere il lazo in forma dopo ogni blocco.

Posso ovviamente ri-montare l'intero modello dopo aver visto ogni nuova serie di osservazioni. Questo, tuttavia, sarebbe piuttosto inefficiente dato che ci sono molti dati. La quantità di nuovi dati che arrivano ad ogni passaggio è molto piccola e è improbabile che l'adattamento cambi molto tra i passaggi.

C'è qualcosa che posso fare per ridurre l'onere computazionale complessivo?

Stavo esaminando l'algoritmo LARS di Efron et al., Ma sarei felice di prendere in considerazione qualsiasi altro metodo di adattamento se si potesse fare un "avvio a caldo" nel modo sopra descritto.

Appunti:

  1. Sto principalmente cercando un algoritmo, ma i puntatori a pacchetti software esistenti che possono farlo possono anche rivelarsi perspicaci.
  2. Oltre alle attuali traiettorie del lazo, l'algoritmo è ovviamente il benvenuto per mantenere un altro stato.

Bradley Efron, Trevor Hastie, Iain Johnstone e Robert Tibshirani, Regressione del minimo angolo , Annals of Statistics (con discussione) (2004) 32 (2), 407--499.

Risposte:


7

Il lazo viene inserito attraverso LARS (un processo iterativo, che inizia con una stima iniziale ). Di default ma puoi cambiarlo nella maggior parte dell'implementazione (e sostituirlo con l'ottimale hai già). Il più vicino è , minore è il numero di iterazioni LARS che dovrai passare per arrivare a .β 0 = 0 p β o l d β o l d β n e w β n e wβ0β0=0pβoldβoldβnewβnew

MODIFICARE:

A causa dei commenti di user2763361aggiungo ulteriori dettagli alla mia risposta originale.

Dai commenti sottostanti mi risulta che l'utente2763361 suggerisce di integrare la mia risposta originale per trasformarla in una che può essere utilizzata direttamente (dagli scaffali) pur essendo molto efficiente.

Per fare la prima parte, illustrerò la soluzione che propongo passo dopo passo su un esempio di giocattolo. Per soddisfare la seconda parte, lo farò usando un recente solutore di punti per interni di alta qualità. Questo perché, è più facile ottenere un'implementazione ad alte prestazioni della soluzione che propongo utilizzando una libreria in grado di risolvere il problema del lazo con l'approccio del punto interno piuttosto che cercare di hackerare l'algoritmo LARS o simplex per avviare l'ottimizzazione da un non punto di partenza standard (sebbene sia possibile anche quella seconda sede).

Si noti che a volte si afferma (nei libri più vecchi) che l'approccio dei punti interni per risolvere i programmi lineari è più lento dell'approccio simplex e che potrebbe essere stato molto tempo fa, ma generalmente non è vero oggi e certamente non è vero per problemi su larga scala (questo è il motivo per cui la maggior parte delle biblioteche professionali cplexusa l'algoritmo del punto interno) e la domanda riguarda almeno implicitamente problemi su larga scala. Nota anche che il solutore di punti interni che uso gestisce completamente le matrici sparse, quindi non credo che ci sarà un grande divario di prestazioni con LARS (una motivazione originale per l'utilizzo di LARS era che molti solutori di LP popolari all'epoca non gestivano bene le matrici sparse e queste sono le caratteristiche del problema LASSO).

Una (molto) buona implementazione open source dell'algoritmo del punto interno è ipopt, nella COIN-ORlibreria. Un altro motivo Userò ipoptè che ha ha un'interfaccia R, ipoptr. Troverai una guida all'installazione più completa qui , di seguito fornisco i comandi standard per installarlo ubuntu.

nel bash, fare:

sudo apt-get install gcc g++ gfortran subversion patch wget
svn co https://projects.coin-or.org/svn/Ipopt/stable/3.11 CoinIpopt
cd ~/CoinIpopt
./configure
make 
make install

Quindi, come root, in Rdo (presumo svnabbia copiato il file di sovversione ~/come per impostazione predefinita):

install.packages("~/CoinIpopt/Ipopt/contrib/RInterface",repos=NULL,type="source")

Da qui, sto dando un piccolo esempio (principalmente dall'esempio di giocattolo fornito da Jelmer Ypma come parte del suo Rinvolucro a ipopt):

library('ipoptr')
# Experiment parameters.
lambda <- 1                                # Level of L1 regularization.
n      <- 100                              # Number of training examples.
e      <- 1                                # Std. dev. in noise of outputs.
beta   <- c( 0, 0, 2, -4, 0, 0, -1, 3 )    # "True" regression coefficients.
# Set the random number generator seed.
ranseed <- 7
set.seed( ranseed )
# CREATE DATA SET.
# Generate the input vectors from the standard normal, and generate the
# responses from the regression with some additional noise. The variable 
# "beta" is the set of true regression coefficients.
m     <- length(beta)                           # Number of features.
A     <- matrix( rnorm(n*m), nrow=n, ncol=m )   # The n x m matrix of examples.
noise <- rnorm(n, sd=e)                         # Noise in outputs.
y     <- A %*% beta + noise                     # The outputs.
# DEFINE LASSO FUNCTIONS
# m, lambda, y, A are all defined in the ipoptr_environment
eval_f <- function(x) {
    # separate x in two parts
    w <- x[  1:m ]          # parameters
    u <- x[ (m+1):(2*m) ]

    return( sum( (y - A %*% w)^2 )/2 + lambda*sum(u) )
}
# ------------------------------------------------------------------
eval_grad_f <- function(x) {
    w <- x[ 1:m ]
    return( c( -t(A) %*% (y - A %*% w),  
               rep(lambda,m) ) )
}
# ------------------------------------------------------------------
eval_g <- function(x) {
    # separate x in two parts
    w <- x[  1:m ]          # parameters
    u <- x[ (m+1):(2*m) ]
    return( c( w + u, u - w ) )
}
eval_jac_g <- function(x) {
    # return a vector of 1 and minus 1, since those are the values of the non-zero elements
    return( c( rep( 1, 2*m ), rep( c(-1,1), m ) ) )
}
# ------------------------------------------------------------------
# rename lambda so it doesn't cause confusion with lambda in auxdata
eval_h <- function( x, obj_factor, hessian_lambda ) {
    H <- t(A) %*% A
    H <- unlist( lapply( 1:m, function(i) { H[i,1:i] } ) )
    return( obj_factor * H )
}
eval_h_structure <- c( lapply( 1:m, function(x) { return( c(1:x) ) } ),
                       lapply( 1:m, function(x) { return( c() ) } ) )
# The starting point.
x0 = c( rep(0, m), 
        rep(1, m) )
# The constraint functions are bounded from below by zero.
constraint_lb = rep(   0, 2*m )
constraint_ub = rep( Inf, 2*m )
ipoptr_opts <- list( "jac_d_constant"   = 'yes',
                     "hessian_constant" = 'yes',
                     "mu_strategy"      = 'adaptive',
                     "max_iter"         = 100,
                     "tol"              = 1e-8 )
# Set up the auxiliary data.
auxdata <- new.env()
auxdata$m <- m
    auxdata$A <- A
auxdata$y <- y
    auxdata$lambda <- lambda
# COMPUTE SOLUTION WITH IPOPT.
# Compute the L1-regularized maximum likelihood estimator.
print( ipoptr( x0=x0, 
               eval_f=eval_f, 
               eval_grad_f=eval_grad_f, 
               eval_g=eval_g, 
               eval_jac_g=eval_jac_g,
               eval_jac_g_structure=eval_jac_g_structure,
               constraint_lb=constraint_lb, 
               constraint_ub=constraint_ub,
               eval_h=eval_h,
               eval_h_structure=eval_h_structure,
               opts=ipoptr_opts,
               ipoptr_environment=auxdata ) )

Il punto è che, se si dispone di nuovi dati, è sufficiente

  1. aggiorna ( non sostituisce) la matrice del vincolo e il vettore della funzione obiettivo per tenere conto delle nuove osservazioni.
  2. cambiare i punti di partenza del punto interno da

    x0 = c (rep (0, m), rep (1, m))

    al vettore di soluzione che hai trovato in precedenza (prima che fossero aggiunti nuovi dati). La logica qui è la seguente. Indica il nuovo vettore di coefficienti (quelli corrispondenti al set di dati dopo l'aggiornamento) e quelli originali. anche il vettore nel codice sopra (questo è il solito inizio per il metodo del punto interno). Quindi l'idea è che se: β o l d β i n i tβnewβoldβinitx0

|βinitβnew|1>|βnewβold|1(1)

quindi, si può ottenere molto più velocemente avviando il punto interno da anziché . Il guadagno sarà tanto più importante quando le dimensioni del set di dati ( e ) sono maggiori.βnewβoldβinitnp

Per quanto riguarda le condizioni alle quali sussiste la disuguaglianza (1), sono:

  • quando è grande rispetto a (questo è di solito il caso in cui , il numero di variabili di progettazione è grande rispetto a , il numero di osservazioni)λ|βOLS|1pn
  • quando le nuove osservazioni non sono patologicamente influenti, ad esempio quando sono coerenti con il processo stocastico che ha generato i dati esistenti.
  • quando la dimensione dell'aggiornamento è piccola rispetto alla dimensione dei dati esistenti.

Grazie, ma temo di non seguirlo. LARS produce un percorso lineare a tratti (con esattamente punti per gli angoli minimi e possibilmente più punti per il lazo). Ogni punto ha il proprio set di . Quando aggiungiamo più osservazioni, tutte le beta possono spostarsi (tranne , che è sempre .) espandere la tua risposta? Grazie. p+1ββ00p
NPE,

@aix: vuoi aggiornare l'intero percorso del lazo o solo la soluzione? (cioè la penalità di sparsità è fissa?).
user603

Stavo cercando di aggiornare l'intero percorso. Tuttavia, se c'è un buon modo per farlo per una penalità fissa ( nella formula seguente), questo potrebbe essere un buon inizio. È questo ciò che stai proponendo? λ
β^lasso=argminβ{12i=1N(yiβ0j=1pxijβj)2+λj=1p|βj|}
NPE,

@aix. Sì, tutto dipende dall'implementazione utilizzata e dalle strutture a cui hai accesso. Ad esempio: se si ha accesso a un buon solutore lp, è possibile alimentarlo con i valori ottimali passati di e porterà il passaggio 1-2 alla nuova soluzione in modo molto efficiente. È necessario aggiungere questi dettagli alla domanda. β
user603

1
Qualche libreria che conosci può farlo senza dover modificare il codice sorgente?
user2763361
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.