Mise à jour de l'ajustement au lasso avec de nouvelles observations

12

J'ajuste une régression linéaire régularisée L1 à un très grand ensemble de données (avec n >> p.) Les variables sont connues à l'avance, mais les observations arrivent en petits morceaux. Je voudrais maintenir l'ajustement du lasso après chaque morceau.

Je peux évidemment réajuster le modèle entier après avoir vu chaque nouvelle série d'observations. Cependant, cela serait assez inefficace étant donné qu'il y a beaucoup de données. La quantité de nouvelles données qui arrive à chaque étape est très petite et il est peu probable que l'ajustement change beaucoup entre les étapes.

Puis-je faire quelque chose pour réduire la charge de calcul globale?

Je regardais l'algorithme LARS d'Efron et al., Mais je serais heureux d'envisager toute autre méthode d'ajustement si elle pouvait être mise en "démarrage à chaud" de la manière décrite ci-dessus.

Remarques:

  1. Je recherche principalement un algorithme, mais des pointeurs vers des progiciels existants qui peuvent le faire peuvent également être utiles.
  2. En plus des trajectoires actuelles du lasso, l'algorithme est bien sûr le bienvenu pour garder un autre état.

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

NPE
la source

Réponses:

7

Le lasso est ajusté par LARS (un processus itératif, qui commence à une estimation initiale ). Par défaut β 0 = 0 p mais vous pouvez changer cela dans la plupart des implémentations (et le remplacer par le β o l d optimal que vous avez déjà). Plus β o l d est proche de β n e w , plus le nombre d'itérations LARS que vous devrez franchir pour arriver à β n e w est petit .β0β0=0pβolβolβnewβnew

ÉDITER:

En raison des commentaires de, user2763361j'ajoute plus de détails à ma réponse d'origine.

D'après les commentaires ci-dessous, je suppose que user2763361 suggère de compléter ma réponse originale pour la transformer en une qui peut être utilisée directement (hors des étagères) tout en étant très efficace.

Pour faire la première partie, je vais illustrer la solution que je propose pas à pas sur un exemple de jouet. Pour satisfaire la deuxième partie, je le ferai en utilisant un solveur de point intérieur récent et de haute qualité. En effet, il est plus facile d'obtenir une implémentation haute performance de la solution que je propose en utilisant une bibliothèque qui peut résoudre le problème du lasso par l'approche du point intérieur plutôt que d'essayer de pirater l'algorithme LARS ou simplex pour démarrer l'optimisation à partir d'un non point de départ standard (bien que ce deuxième lieu soit également possible).

Notez qu'il est parfois affirmé (dans des livres plus anciens) que l'approche du point intérieur pour résoudre des programmes linéaires est plus lente que l'approche simplex et cela peut être vrai il y a longtemps, mais ce n'est généralement pas vrai aujourd'hui et certainement pas vrai pour les problèmes à grande échelle (c'est pourquoi la plupart des bibliothèques professionnelles aiment cplexutiliser l'algorithme de point intérieur) et la question concerne au moins implicitement les problèmes à grande échelle. Notez également que le solveur de point intérieur que j'utilise gère entièrement les matrices clairsemées, donc je ne pense pas qu'il y aura un grand écart de performances avec LARS (une motivation originale pour utiliser LARS était que de nombreux solveurs LP populaires à l'époque ne manipulaient pas bien les matrices clairsemées et ce sont des caractéristiques du problème LASSO).

Une (très) bonne implémentation open source de l'algorithme de point intérieur se trouve ipoptdans la COIN-ORbibliothèque. Une autre raison pour laquelle je vais utiliser ipoptest qu'il a doté d' une interface R, ipoptr. Vous trouverez un guide d'installation plus complet ici , ci-dessous je donne les commandes standard pour l'installer ubuntu.

dans le bash, faites:

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

Ensuite, en tant que root, dans Rdo (je suppose qu'il svna copié le fichier de subversion ~/comme il le fait par défaut):

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

De là, je donne un petit exemple (principalement de l'exemple de jouet donné par Jelmer Ypma dans le cadre de son Remballage 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 ) )

Mon point est, si vous avez de nouvelles données, vous avez juste besoin de

  1. mettre à jour (et non remplacer) la matrice de contraintes et le vecteur de fonction objectif pour tenir compte des nouvelles observations.
  2. changer les points de départ du point intérieur de

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

    βnewβolβjenjetx0

|βjenjet-βnew|1>|βnew-βol|1(1)

βnewβolβjenjetnp

Quant aux conditions de détention de l'inégalité (1), elles sont:

  • λ|βOLS|1pn
  • lorsque les nouvelles observations n'ont pas d'influence pathologique, par exemple lorsqu'elles sont cohérentes avec le processus stochastique qui a généré les données existantes.
  • lorsque la taille de la mise à jour est petite par rapport à la taille des données existantes.
user603
la source
p+1ββ00p
@aix: voulez-vous mettre à jour tout le chemin du lasso ou juste la solution? (c.-à-d. la pénalité pour rareté est-elle fixée?).
user603
λ
β^lunesso=argminβ{12je=1N(yje-β0-j=1pXjejβj)2+λj=1p|βj|}
β
1
Y a-t-il des bibliothèques que vous connaissez qui peuvent le faire sans avoir besoin de modifier le code source?
user2763361