El lazo se ajusta a través de LARS (un proceso iterativo, que comienza en una estimación inicial ). De forma predeterminada, pero puede cambiar esto en la mayoría de las implementaciones (y reemplazarlo por el óptimo que ya tiene). Lo más cercano a es , cuanto menor sea el número de iteraciones LARS que tendrá que realizar para llegar a .β 0 = 0 p β ∗ o l d β ∗ o l d β ∗ n e w β ∗ n e wβ0 0β0 0= 0pagβ∗o l dβ∗o l dβ∗n e wβ∗n e w
EDITAR:
Debido a los comentarios de user2763361
, agrego más detalles a mi respuesta original.
De los comentarios a continuación, deduzco que user2763361 sugiere complementar mi respuesta original para convertirla en una que se pueda usar directamente (fuera de los estantes) y al mismo tiempo ser muy eficiente.
Para hacer la primera parte, ilustraré la solución que propongo paso a paso en un ejemplo de juguete. Para satisfacer la segunda parte, lo haré utilizando un solucionador de puntos interior reciente y de alta calidad. Esto se debe a que es más fácil obtener una implementación de alto rendimiento de la solución que propongo usando una biblioteca que pueda resolver el problema del lazo mediante el enfoque de punto interior en lugar de intentar hackear el algoritmo LARS o simplex para comenzar la optimización desde un punto de partida estándar (aunque ese segundo lugar también es posible).
Tenga en cuenta que a veces se afirma (en libros más antiguos) que el enfoque de punto interior para resolver programas lineales es más lento que el enfoque simple y eso puede haber sido cierto hace mucho tiempo, pero generalmente no es cierto hoy en día y ciertamente no es cierto para problemas a gran escala (esta es la razón por la cual la mayoría de las bibliotecas profesionales cplex
usan el algoritmo de punto interior) y la pregunta es al menos implícitamente sobre problemas a gran escala. Tenga en cuenta también que el solucionador de puntos interiores que uso maneja completamente matrices dispersas, por lo que no creo que haya una gran brecha de rendimiento con LARS (una motivación original para usar LARS era que muchos solucionadores LP populares en ese momento no manejaban bien las matrices dispersas y Estos son rasgos característicos del problema LASSO).
Una (muy) buena implementación de código abierto del algoritmo de punto interior está ipopt
en la COIN-OR
biblioteca. Otra razón por la que va a utilizar ipopt
es que tiene tiene una interfaz R, ipoptr
. Encontrará una guía de instalación más exhaustiva aquí , a continuación le doy los comandos estándar para instalarla ubuntu
.
en el bash
, hacer:
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
Luego, como root, in R
do (supongo que svn
ha copiado el archivo de subversión ~/
como lo hace por defecto):
install.packages("~/CoinIpopt/Ipopt/contrib/RInterface",repos=NULL,type="source")
A partir de aquí, estoy dando un pequeño ejemplo (principalmente del ejemplo de juguete dado por Jelmer Ypma como parte de su R
wraper 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 ) )
Mi punto es que si tiene datos nuevos, solo necesita
- actualizar ( no reemplazar) la matriz de restricción y el vector de función objetivo para tener en cuenta las nuevas observaciones.
cambiar los puntos de partida del punto interior de
x0 = c (rep (0, m), rep (1, m))
al vector de solución que encontró anteriormente (antes de agregar nuevos datos). La lógica aquí es la siguiente. Denotan El nuevo vector de coeficientes (los correspondientes al conjunto de datos después de la actualización) y los originales. También denote el vector en el código anterior (este es el comienzo habitual para el método de punto interior). Entonces la idea es que si: β o l d β i n i tβn e wβo l dβi n i tx0
El | βi n i t- βn e wEl |1> | βn e w- βo l dEl |1( 1 )
entonces, uno puede obtener mucho más rápido al iniciar el punto interior desde
lugar de la ingenua . La ganancia será tanto más importante cuando las dimensiones del conjunto de datos ( y ) son más grandes.βn e wβo l dβi n i tnortepag
En cuanto a las condiciones bajo las cuales se mantiene la desigualdad (1), son:
- cuando es grande en comparación con (este suele ser el caso cuando , el número de variables de diseño es grande en comparación con , el número de observaciones)λEl | βO L SEl |1pagnorte
- cuando las nuevas observaciones no son patológicamente influyentes, por ejemplo, cuando son consistentes con el proceso estocástico que ha generado los datos existentes.
- cuando el tamaño de la actualización es pequeño en relación con el tamaño de los datos existentes.