Aktualisierung des Lassos mit neuen Beobachtungen

12

Ich passe eine L1-regulierte lineare Regression an einen sehr großen Datensatz an (mit n >> p.). Die Variablen sind im Voraus bekannt, aber die Beobachtungen treffen in kleinen Stücken ein. Ich möchte das Lasso nach jedem Stück fit halten.

Ich kann natürlich das gesamte Modell nach jedem neuen Satz von Beobachtungen wieder anpassen. Dies wäre jedoch ziemlich ineffizient, da viele Daten vorliegen. Die Menge der neuen Daten, die bei jedem Schritt eingehen, ist sehr gering, und es ist unwahrscheinlich, dass sich die Anpassung zwischen den Schritten stark ändert.

Kann ich irgendetwas tun, um die gesamte Rechenlast zu reduzieren?

Ich habe mir den LARS-Algorithmus von Efron et al. Angesehen, würde aber gerne eine andere Anpassungsmethode in Betracht ziehen, wenn er auf die oben beschriebene Weise zum "Warmstart" gebracht werden kann.

Anmerkungen:

  1. Ich bin hauptsächlich auf der Suche nach einem Algorithmus, aber Hinweise auf vorhandene Softwarepakete, die dies tun können, können sich auch als aufschlussreich erweisen.
  2. Zusätzlich zu den aktuellen Lasso-Trajektorien kann der Algorithmus natürlich auch andere Zustände beibehalten.

Bradley Efron, Trevor Hastie, Iain Johnstone und Robert Tibshirani, Least Angle Regression , Annals of Statistics (mit Diskussion) (2004) 32 (2), 407-499.

NPE
quelle

Antworten:

7

Das Lasso wird durch LARS angepasst (ein iterativer Prozess, der bei einer anfänglichen Schätzung von beginnt ). Standardmäßig ist β 0 = 0 p, aber Sie können dies in den meisten Implementierungen ändern (und es durch das optimale β o l d ersetzen, das Sie bereits haben). Je näher β o l d an β n e w liegt , desto geringer ist die Anzahl der LARS-Iterationen, die Sie ausführen müssen, um zu β n e w zu gelangen .β0β0=0pβoldβoldβnewβnew

BEARBEITEN:

Aufgrund der Kommentare von user2763361füge ich meiner ursprünglichen Antwort weitere Details hinzu.

Aus den Kommentaren unten geht hervor, dass user2763361 vorschlägt, meine ursprüngliche Antwort zu ergänzen, um sie in eine zu verwandeln, die direkt (von der Stange) verwendet werden kann und gleichzeitig sehr effizient ist.

Im ersten Teil werde ich die von mir vorgeschlagene Lösung anhand eines Spielzeugbeispiels schrittweise veranschaulichen. Um den zweiten Teil zu befriedigen, verwende ich einen neuen hochwertigen Innenpunktlöser. Dies liegt daran, dass es einfacher ist, eine Hochleistungsimplementierung der von mir vorgeschlagenen Lösung zu erhalten, indem eine Bibliothek verwendet wird, die das Lasso-Problem durch den Interior-Point-Ansatz lösen kann, anstatt zu versuchen, den LARS- oder Simplex-Algorithmus zu hacken, um die Optimierung von einem Nicht-Lasso-Algorithmus aus zu starten. Standardstartpunkt (obwohl dieser zweite Veranstaltungsort auch möglich ist).

Beachten Sie, dass manchmal (in älteren Büchern) behauptet wird, dass der interne Punktansatz zum Lösen von linearen Programmen langsamer ist als der Simplex-Ansatz und dass dies vor langer Zeit zutraf, aber heute im Allgemeinen nicht mehr zutrifft und sicherlich nicht für große Probleme zutrifft (Aus diesem Grund verwenden die meisten professionellen Bibliotheken gerne cplexden Algorithmus für Innenpunkte) und die Frage bezieht sich zumindest implizit auf große Probleme. Beachten Sie auch, dass der interne Punktlöser, den ich verwende, spärliche Matrizen vollständig handhabt, so dass ich nicht glaube, dass es eine große Leistungslücke bei LARS geben wird (eine ursprüngliche Motivation für die Verwendung von LARS war, dass viele beliebte LP-Löser zu dieser Zeit spärliche Matrizen nicht gut handhabten und Dies sind charakteristische Merkmale des LASSO-Problems.

Eine (sehr) gute Open-Source-Implementierung des Interior-Point-Algorithmus befindet ipoptsich in der COIN-ORBibliothek. Ein weiterer Grund, den ich verwenden werde, ipoptist, dass es eine R - Schnittstelle hat ipoptr. Eine ausführlichere Installationsanleitung finden Sie hier . Im Folgenden werden die Standardbefehle für die Installation aufgeführt ubuntu.

in der bash, mache:

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

Dann, als root, in Rdo (ich gehe davon aus svn, dass die Subversion-Datei ~/wie standardmäßig kopiert wurde ):

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

Von hier aus gebe ich ein kleines Beispiel (hauptsächlich aus dem Spielzeugbeispiel, das Jelmer Ypma als Teil seines RWrapers an gegeben hat 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 ) )

Mein Punkt ist, wenn Sie neue Daten haben, müssen Sie nur

  1. Aktualisieren ( nicht ersetzen) Sie die Beschränkungsmatrix und den Zielfunktionsvektor, um die neuen Beobachtungen zu berücksichtigen.
  2. Ändern Sie die Startpunkte des Innenpunkts von

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

    βnewβoldβinitx0

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

βnewβoldβinitnp

Die Bedingungen, unter denen Ungleichung (1) gilt, sind:

  • λ|βOLS|1pn
  • wenn die neuen Beobachtungen keinen pathologischen Einfluss haben, z. B. wenn sie mit dem stochastischen Prozess übereinstimmen, der die vorhandenen Daten erzeugt hat.
  • wenn die Größe der Aktualisierung im Verhältnis zur Größe der vorhandenen Daten klein ist.
user603
quelle
p+1ββ00p
@aix: möchtest du den gesamten Lasso-Pfad oder nur die Lösung aktualisieren? (dh ist die Sparsity Strafe fixiert?)
User603
λ
β^lasso=argminβ{12i=1N(yiβ0j=1pxijβj)2+λj=1p|βj|}
β
1
Gibt es Bibliotheken, die dies tun können, ohne den Quellcode bearbeiten zu müssen?
user2763361