glmnet:相同的lambda但glmnet()&的系数不同cv.glmnet()

时间:2017-07-19 23:45:37

标签: r glm cross-validation glmnet

即使我使用相同的lambda,cv.glmnet()生成的系数似乎与glmnet()生成的系数不同。为什么是这样?它们不应该是一样的吗?

library(glmnet)

# Data dimensions
num.samples <- 30
num.genes <- 17000

# Data objects - note that both X and Y are scaled
set.seed(123)
Y <- matrix(rnorm(num.samples), ncol=1)
set.seed(1234)
X <- matrix(rnorm(num.samples*num.genes), ncol=num.genes)

# Run cv.glmnet: get lambda.min and coef
fit.cv <- cv.glmnet(X, Y, nfolds=num.samples, intercept=FALSE)
fit.cv.lambda <- fit.cv$lambda.min
fit.cv.coef <- coef(fit.cv, s = fit.cv.lambda)[,1][2:(num.genes+1)]

# Run glmnet with lambda.min from cv.glmnet: get coef
second.lambda=fit.cv.lambda-0.0001 ## second.lambda included because glmnet manual recommends using >1 lambda for glmnet()
fit <- glmnet(X, Y, lambda=c(fit.cv.lambda,second.lambda), intercept=FALSE) 
fit.lambda <- fit$lambda[1]
fit.coef <- coef(fit, s = fit.cv.lambda)[,1][2:(num.genes+1)]

# Lambda is the same, but coefficients are not
fit.cv.lambda==fit.lambda ## TRUE
not.equal = which(fit.cv.coef != fit.coef)
length(not.equal) ## 18
mean(abs(fit.cv.coef[not.equal] - fit.coef[not.equal])) ## 0.0004038209

(我也注意到glmnet()和cv.glmnet()的系数在某个alpha值没有差异,但似乎没有明显的模式。)

1 个答案:

答案 0 :(得分:1)

简答:这是数字准确性问题。您遇到的差异不是由cv.glmnetglmnet之间的差异造成的。相反,它们归结于以下各项:

  • 惩罚路径lambda在两个对象之间是不同的,我指的是整个惩罚路径,而不仅仅是感兴趣的惩罚是否在两者中
  • 默认收敛阈值thresh =

如果您希望从具有不同惩罚路径的两个glmnet或cv.glmnet对象获得的估计值相等(或至少非常接近),请在两个函数中使用thresh =选项来降低收敛阈值。此外,我建议在exact = TRUE中设置coef()

扩展答案:下面我们在几个示例中说明这一点。在此之前,了解coef()函数(使用predict.glmnet()调用type = "coefficients"函数)的逻辑也很重要。

  • 如果您要求对已经在对象的原始惩罚路径中计算的惩罚进行系数估计,coef()将只返回原始对象的估计值。

  • 如果您请求不在原始路径中的惩罚的系数估计值exact = FALSE(这是默认值),则系数将根据原始最近点数的估算值进行插值估算路径。

  • 如果您要求对原始路径中尚未包含的惩罚进行系数估计并exact = TRUE,则会将新惩罚值添加到原始路径中,并重新整理模型以获取估算值

示例1:相同的惩罚路径,相同的估算值

如果使用默认参数,glmnet()cv.glmnet()将计算给定数据集的相同惩罚路径(由于停止标准,路径中计算的惩罚数可能会有所不同)。我们在下面显示:

library(glmnet)

# Data dimensions
num.samples <- 30
num.genes <- 17000

# Data objects - note that both X and Y are scaled
set.seed(123)
Y <- matrix(rnorm(num.samples), ncol=1)
X <- matrix(rnorm(num.samples*num.genes), ncol=num.genes)

# Run cv.glmnet and glmnet, obtain same penalty path up to min(num penalty)
fit <- glmnet(X, Y, intercept=FALSE)
cvfit <- cv.glmnet(X, Y, intercept= FALSE)
min_num_lambdas = min(length(fit$lambda), length(cvfit$lambda))
all.equal(fit$lambda[1:min_num_lambdas], cvfit$lambda[1:min_num_lambdas]) #TRUE

然后我们可以使用coef()从两个对象获得相同的估计值,无论新惩罚是否在原始路径中。

# Requested penalty in original path
coef1 <- coef(fit, s = fit$lambda[10])
coef2 <- coef(cvfit, s = fit$lambda[10])
all.equal(coef1, coef2) #TRUE

# Requested penalty not in original path -- uses interpolation
coef1 <- coef(fit, s = 0.40)
coef2 <- coef(cvfit, s = 0.40)
all.equal(coef1, coef2) #TRUE

# Force glmnet to refit the model with s added to the penalty path
coef1 <- coef(fit, s = 0.40, exact = TRUE, x = X, y = Y)
coef2 <- coef(cvfit, s = 0.40, exact = TRUE, x = X, y = Y)
all.equal(coef1, coef2) #TRUE

示例2:不同的惩罚路径,不同的估计

我们通过在路径中请求99次惩罚而不是100来对glmnet()拟合进行小的更改。这使得惩罚路径在fit和cvfit之间不同。现在,即使我们使用exact = TRUE选项,估算也是不同的。

fit <- glmnet(X, Y, intercept=FALSE, nlambda = 99)
coef1 <- coef(fit, s = fit$lambda[10], exact = TRUE, x = X, y = Y)
coef2 <- coef(cvfit, s = fit$lambda[10], exact = TRUE, x = X, y = Y)
all.equal(coef1, coef2) # Mean relative difference: 0.002006215

修复

为确保估算值匹配,我们可以使用降低的阈值重新设置两个对象。

fit <- glmnet(X, Y, intercept=FALSE, nlambda = 99, thresh = 1e-20)
cvfit <- cv.glmnet(X, Y, intercept= FALSE, thresh = 1e-20)
coef1 <- coef(fit, s = fit$lambda[10], exact = TRUE, x = X, y = Y)
coef2 <- coef(cvfit, s = fit$lambda[10], exact = TRUE, x = X, y = Y)
all.equal(coef1, coef2) #TRUE

请注意,您也可以使用thresh =中的coef()选项,但只有当新惩罚不在任一对象的原始路径中时才会有效。

fit <- glmnet(X, Y, intercept=FALSE, nlambda = 99)
cvfit <- cv.glmnet(X, Y, intercept= FALSE)
coef1 <- coef(fit, s = 0.40, exact = TRUE, x = X, y = Y, thresh = 1e-20)
coef2 <- coef(cvfit, s = 0.40, exact = TRUE, x = X, y = Y, thresh = 1e-20)
all.equal(coef1, coef2) #TRUE

thresh =需要减少的值将完全取决于数据。