일반 옵티 마이저를 사용하여 glmnet 선형 회귀에 대한 결과 복제


10

제목에서 알 수 있듯이 라이브러리의 LBFGS 옵티 마이저를 사용하여 glmnet linear의 결과를 복제하려고합니다 lbfgs. 이 옵티마이 저는 목적 함수 (L1 정규화 용어가없는)가 볼록한 한, 미분에 대해 걱정할 필요없이 L1 정규화 용어를 추가 할 수 있습니다.

의 탄성 순 선형 회귀 문제 glmnet 용지가 주어진다 여기서 X \ in \ mathbb {R} ^ {n \ times p} 는 설계 행렬이고, y \ in \ mathbb {R} ^ p 는 관측치 벡터이며, \ alpha \ in [0,1] 은 탄성 순 모수이고 \ lambda> 0 은 정규화 모수입니다. 연산자 \ Vert x \ Vert_p 는 일반적인 Lp 규범을 나타냅니다.

minβRp12nβ0+Xβy22+αλβ1+12(1α)λβ22
XRn×pyRpα[0,1]λ>0xp

아래 코드는 함수를 정의한 다음 결과를 비교하는 테스트를 포함합니다. 당신이 볼 수 있듯이, 결과는 때 허용되는 alpha = 1값에 대한 해제되지만,이 방법은 alpha < 1.우리가에서 이동과 같은 오류가 악화 alpha = 1alpha = 0다음과 같은 그래프에서 볼 수 있듯이 (이하 "비교 메트릭은"glmnet의 매개 변수 추정 사이의 평균 유클리드 거리이다 주어진 정규화 경로에 대한 lbfgs).

여기에 이미지 설명을 입력하십시오

자, 여기 코드가 있습니다. 가능한 한 의견을 추가했습니다. 내 질문은 : 내 결과가 왜 glmnet값과 다른가 alpha < 1? 그것은 L2 정규화 용어와 분명히 관련이 있지만, 내가 알 수있는 한, 나는이 용어를 논문에 따라 정확하게 구현했습니다. 어떤 도움이라도 대단히 감사하겠습니다!

library(lbfgs)
linreg_lbfgs <- function(X, y, alpha = 1, scale = TRUE, lambda) {
  p <- ncol(X) + 1; n <- nrow(X); nlambda <- length(lambda)

  # Scale design matrix
  if (scale) {
    means <- colMeans(X)
    sds <- apply(X, 2, sd)
    sX <- (X - tcrossprod(rep(1,n), means) ) / tcrossprod(rep(1,n), sds)
  } else {
    means <- rep(0,p-1)
    sds <- rep(1,p-1)
    sX <- X
  }
  X_ <- cbind(1, sX)

  # loss function for ridge regression (Sum of squared errors plus l2 penalty)
  SSE <- function(Beta, X, y, lambda0, alpha) {
    1/2 * (sum((X%*%Beta - y)^2) / length(y)) +
      1/2 * (1 - alpha) * lambda0 * sum(Beta[2:length(Beta)]^2) 
                    # l2 regularization (note intercept is excluded)
  }

  # loss function gradient
  SSE_gr <- function(Beta, X, y, lambda0, alpha) {
    colSums(tcrossprod(X%*%Beta - y, rep(1,ncol(X))) *X) / length(y) + # SSE grad
  (1-alpha) * lambda0 * c(0, Beta[2:length(Beta)]) # l2 reg grad
  }

  # matrix of parameters
  Betamat_scaled <- matrix(nrow=p, ncol = nlambda)

  # initial value for Beta
  Beta_init <- c(mean(y), rep(0,p-1)) 

  # parameter estimate for max lambda
  Betamat_scaled[,1] <- lbfgs(call_eval = SSE, call_grad = SSE_gr, vars = Beta_init, 
                              X = X_, y = y, lambda0 = lambda[2], alpha = alpha,
                              orthantwise_c = alpha*lambda[2], orthantwise_start = 1, 
                              invisible = TRUE)$par

  # parameter estimates for rest of lambdas (using warm starts)
  if (nlambda > 1) {
    for (j in 2:nlambda) {
      Betamat_scaled[,j] <- lbfgs(call_eval = SSE, call_grad = SSE_gr, vars = Betamat_scaled[,j-1], 
                                  X = X_, y = y, lambda0 = lambda[j], alpha = alpha,
                                  orthantwise_c = alpha*lambda[j], orthantwise_start = 1, 
                                  invisible = TRUE)$par
    }
  }

  # rescale Betas if required
  if (scale) {
    Betamat <- rbind(Betamat_scaled[1,] -
colSums(Betamat_scaled[-1,]*tcrossprod(means, rep(1,nlambda)) / tcrossprod(sds, rep(1,nlambda)) ), Betamat_scaled[-1,] / tcrossprod(sds, rep(1,nlambda)) )
  } else {
    Betamat <- Betamat_scaled
  }
  colnames(Betamat) <- lambda
  return (Betamat)
}

# CODE FOR TESTING
# simulate some linear regression data
n <- 100
p <- 5
X <- matrix(rnorm(n*p),n,p)
true_Beta <- sample(seq(0,9),p+1,replace = TRUE)
y <- drop(cbind(1,X) %*% true_Beta)

library(glmnet)

# function to compare glmnet vs lbfgs for a given alpha
glmnet_compare <- function(X, y, alpha) {
  m_glmnet <- glmnet(X, y, nlambda = 5, lambda.min.ratio = 1e-4, alpha = alpha)
  Beta1 <- coef(m_glmnet)
  Beta2 <- linreg_lbfgs(X, y, alpha = alpha, scale = TRUE, lambda = m_glmnet$lambda)
  # mean Euclidean distance between glmnet and lbfgs results
  mean(apply (Beta1 - Beta2, 2, function(x) sqrt(sum(x^2))) ) 
}

# compare results
alpha_seq <- seq(0,1,0.2)
plot(alpha_seq, sapply(alpha_seq, function(alpha) glmnet_compare(X,y,alpha)), type = "l", ylab = "Comparison metric")

@ hxd1011 코드를 시험해 보았습니다. 여기에 몇 가지 테스트가 있습니다 (glmnet의 구조와 일치하도록 약간의 조정을했습니다. 요격 기간을 정규화하지 않으며 손실 함수를 조정해야합니다). 이 (는)위한 alpha = 0것이지만 alpha결과는 일치하지 않습니다.

rm(list=ls())
set.seed(0)
# simulate some linear regression data
n <- 1e3
p <- 20
x <- matrix(rnorm(n*p),n,p)
true_Beta <- sample(seq(0,9),p+1,replace = TRUE)
y <- drop(cbind(1,x) %*% true_Beta)

library(glmnet)
alpha = 0

m_glmnet = glmnet(x, y, alpha = alpha, nlambda = 5)

# linear regression loss and gradient
lr_loss<-function(w,lambda1,lambda2){
  e=cbind(1,x) %*% w -y
  v= 1/(2*n) * (t(e) %*% e) + lambda1 * sum(abs(w[2:(p+1)])) + lambda2/2 * crossprod(w[2:(p+1)])
  return(as.numeric(v))
}

lr_loss_gr<-function(w,lambda1,lambda2){
  e=cbind(1,x) %*% w -y
  v= 1/n * (t(cbind(1,x)) %*% e) + c(0, lambda1*sign(w[2:(p+1)]) + lambda2*w[2:(p+1)])
  return(as.numeric(v))
}

outmat <- do.call(cbind, lapply(m_glmnet$lambda, function(lambda) 
  optim(rnorm(p+1),lr_loss,lr_loss_gr,lambda1=alpha*lambda,lambda2=(1-alpha)*lambda,method="L-BFGS")$par
))

glmnet_coef <- coef(m_glmnet)
apply(outmat - glmnet_coef, 2, function(x) sqrt(sum(x^2)))

귀하의 질문이 주제에 있는지 잘 모르겠습니다 (기본 최적화 기술에 관한 것일 수도 있습니다). 지금 코드를 실제로 확인할 수는 없지만 동등성 에 관한 논쟁에 lbfgs대한 요점을 제기합니다 . orthantwise_cglmnet
Firebug

문제는 정말 아닙니다 lbfgsorthantwise_c때와 같이 alpha = 1솔루션이 근처에 정확히와 동일하다 glmnet. 그것은 L2 정규화 측면과 관련이 alpha < 1있습니다. 나는 정의에 수정의 일종을 생각 SSE하고 SSE_gr그것을 수정해야하지만, 나는 확실히 수정이 있어야 할 것 아니에요 - glmnet 종이에 설명 된대로 지금까지 내가 아는 한, 그 기능을 정확히 정의되어있다.
user3294195

이것은 스택 오버 플로우 프로그래밍 문제 일 수 있습니다.
Matthew Gunn

3
코드 자체보다는 최적화 및 정규화와 관련이 있다고 생각했기 때문에 여기에 게시했습니다.
user3294195

1
순수한 최적화 질문의 경우 scicomp.stackexchange.com 도 옵션입니다. 그래도 언어 관련 질문이 있는지 확실하지 않습니까? (예 : "R에서 이것을하십시오")
GeoMatt22

답변:


11

tl; dr 버전 :

목표는 내재적으로 스케일링 계수 . 여기서 는 샘플 표준 편차입니다.s^=sd(y)sd(y)

더 긴 버전

glmnet 설명서의 작은 글씨를 읽으면 다음을 볼 수 있습니다.

'가우시안'에 대한 목적 함수는

               1/2  RSS/nobs + lambda*penalty,                  

다른 모델의 경우

               -loglik/nobs + lambda*penalty.                   

' "gaussian"'의 경우 'glmnet'은 람다 시퀀스를 계산하기 전에 y가 단위 분산을 갖도록 표준화 한 다음 결과 계수를 표준화하지 않습니다. 다른 소프트웨어와 결과를 재생산 / 비교하려면 표준화 된 y를 제공하는 것이 가장 좋습니다.

이것은 목표가 실제로 , glmnet은 합니다.

12ny/s^Xβ22+λαβ1+λ(1α)β22,
β~=s^β

이제 순수한 올가미 ( )를 사용했을 때 glmnet의 를 표준화 하지 않으면 답이 동일하다는 것을 의미합니다. 반면에, 순수한 능선으로, 당신은 요인에 의해 형벌을 확장 할 필요가 의 순서 의 추가 요인이 있기 때문에, 동의 경로 광장에서 튀어 에서 페널티 킥. 중간 의 경우 출력 을 재현하기 위해 계수의 페널티를 스케일링하는 쉬운 방법이 없습니다 .α=1β~1/s^glmnets^2αglmnets

단위 분산을 갖도록 크기를 조정하면y여기에 이미지 설명을 입력하십시오

여전히 정확히 일치하지 않습니다. 이것은 두 가지 때문인 것으로 보입니다.

  1. 웜-스타트 순환 좌표 하강 알고리즘이 완전히 수렴 되기에는 람다 시퀀스가 ​​너무 짧을 수 있습니다.
  2. 데이터에 오류 항이 없습니다 ( 회귀 의 는 1).R2
  3. 코드 lambda[2]에 초기 적합을 위해 제공되는 버그가 있지만 참고 해야합니다 lambda[1].

항목 1-3을 수정하면 다음 결과가 나옵니다 (임의의 시드에 따라 YMMV가 발생 함).

여기에 이미지 설명을 입력하십시오

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.