Seperti yang dinyatakan judul, saya mencoba mereplikasi hasil dari glmnet linear menggunakan pengoptimal LBFGS dari perpustakaan lbfgs
. Pengoptimal ini memungkinkan kita untuk menambahkan istilah regularizer L1 tanpa harus khawatir tentang diferensiabilitas, selama fungsi objektif kami (tanpa istilah regularizer L1) adalah cembung.
Kode di bawah ini mendefinisikan fungsi, dan kemudian menyertakan tes untuk membandingkan hasilnya. Seperti yang Anda lihat, hasilnya dapat diterima kapan alpha = 1
, tetapi jauh untuk nilai-nilai alpha < 1.
Kesalahan semakin memburuk saat kita beralih alpha = 1
ke alpha = 0
, seperti yang ditunjukkan plot berikut ("metrik perbandingan" adalah jarak Euclidean rata-rata antara estimasi parameter glmnet dan lbfgs untuk jalur regularisasi yang diberikan).
Oke, jadi ini kodenya. Saya telah menambahkan komentar sedapat mungkin. Pertanyaan saya adalah: Mengapa hasil saya berbeda glmnet
dengan nilai untuk alpha < 1
? Ini jelas ada hubungannya dengan istilah regularisasi L2, tapi sejauh yang saya tahu, saya sudah menerapkan istilah ini persis seperti yang tertulis di kertas. Bantuan apa pun akan sangat dihargai!
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 Saya mencoba kode Anda, berikut adalah beberapa tes (saya membuat beberapa perubahan kecil agar sesuai dengan struktur glmnet - perhatikan kami tidak mengatur istilah intersep, dan fungsi kerugian harus ditingkatkan). Ini untuk alpha = 0
, tetapi Anda dapat mencoba alpha
- hasilnya tidak cocok.
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)))
sumber
lbfgs
memunculkan poin tentangorthantwise_c
argumen tentangglmnet
kesetaraan.lbfgs
danorthantwise_c
, seperti kapanalpha = 1
, solusinya hampir sama persis denganglmnet
. Ini ada hubungannya dengan sisi regularisasi L2 hal yaitu kapanalpha < 1
. Saya pikir membuat semacam modifikasi untuk definisiSSE
danSSE_gr
harus memperbaikinya, tapi saya tidak yakin apa yang harus modifikasi - sejauh yang saya tahu, fungsi-fungsi tersebut didefinisikan persis seperti yang dijelaskan dalam kertas glmnet.Jawaban:
tl; versi dr:
Tujuannya secara implisit mengandung faktor penskalaan , di mana adalah standar deviasi sampel.s^=sd(y) sd(y)
Versi yang lebih panjang
Jika Anda membaca cetakan halus dari dokumentasi glmnet, Anda akan melihat:
Sekarang ini berarti tujuannya sebenarnya dan glmnet itu melaporkan .
Sekarang, ketika Anda menggunakan laso murni ( ), maka unstandardization dari glmnet's berarti bahwa jawabannya setara. Di sisi lain, dengan punggungan murni, maka Anda perlu skala hukuman dengan faktor agar jalan setuju, karena faktor tambahan muncul dari alun-alun dalam penalti . Untuk intermediate , tidak ada cara mudah untuk skala penalti koefisien untuk mereproduksi output.α=1 β~ 1/s^ s^ ℓ2 α
glmnet
glmnets
Setelah saya skala untuk memiliki varian unit, saya menemukany
yang masih belum sama persis. Ini sepertinya disebabkan oleh dua hal:
lambda[2]
untuk fit awal, tapi itu seharusnyalambda[1]
.Setelah saya memperbaiki item 1-3, saya mendapatkan hasil berikut (meskipun YMMV tergantung pada seed acak):
sumber