การจำลองผลลัพธ์สำหรับการถดถอยเชิงเส้น glmnet โดยใช้เครื่องมือเพิ่มประสิทธิภาพทั่วไป


10

ฐานะที่เป็นรัฐชื่อฉันพยายามที่จะทำซ้ำผลจากการ glmnet เชิงเส้นโดยใช้เพิ่มประสิทธิภาพ LBFGS 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 = 1ถึงalpha = 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_cโต้แย้งเกี่ยวกับความglmnetเท่าเทียม
Firebug

ปัญหาไม่ได้จริงๆกับlbfgsและorthantwise_cเป็นเมื่อการแก้ปัญหาคือใกล้ตรงเดียวกันกับalpha = 1 glmnetมันจะทำอย่างไรกับด้านกู L2 alpha < 1สิ่งเช่นเมื่อ ฉันคิดว่าการปรับเปลี่ยนบางอย่างเพื่อกำหนดSSEและSSE_grควรแก้ไข แต่ฉันไม่แน่ใจว่าการแก้ไขควรจะ - เท่าที่ฉันรู้ฟังก์ชั่นเหล่านั้นจะถูกกำหนดตามที่อธิบายไว้ในกระดาษ glmnet
user3294195

นี่อาจเป็นปัญหาการเขียนโปรแกรมสแต็คโอเวอร์มากกว่า
แมทธิวกันน์

3
ฉันคิดว่ามันเกี่ยวข้องกับการปรับให้เหมาะสม & การทำให้เป็นมาตรฐานมากกว่าโค้ดเองซึ่งเป็นสาเหตุที่ฉันโพสต์ไว้ที่นี่
user3294195

1
สำหรับคำถามการเพิ่มประสิทธิภาพที่แท้จริง scicomp.stackexchange.comก็เป็นตัวเลือกเช่นกัน ฉันไม่แน่ใจว่าคำถามเฉพาะภาษานั้นอยู่ในหัวข้อนั้นหรือไม่? (เช่น "ทำสิ่งนี้ใน R")
GeoMatt22

คำตอบ:


11

tl; dr เวอร์ชั่น:

วัตถุประสงค์มีปัจจัยการปรับโดยปริยายซึ่งเป็นค่าเบี่ยงเบนมาตรฐานตัวอย่างs^=sd(y)sd(y)

รุ่นอีกต่อไป

หากคุณอ่านเอกสาร glmnet อย่างละเอียดคุณจะเห็น:

โปรดทราบว่าฟังก์ชันวัตถุประสงค์สำหรับ "" gaussian "" คือ

               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 / s s2 αα=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.