ความแปรปรวนในผลลัพธ์ cv.glmnet


18

ฉันใช้cv.glmnetเพื่อค้นหาผู้ทำนาย การตั้งค่าที่ฉันใช้มีดังนี้:

lassoResults<-cv.glmnet(x=countDiffs,y=responseDiffs,alpha=1,nfolds=cvfold)
bestlambda<-lassoResults$lambda.min

results<-predict(lassoResults,s=bestlambda,type="coefficients")

choicePred<-rownames(results)[which(results !=0)]

set.seed(1)เพื่อให้แน่ใจว่าผลจะทำซ้ำฉัน ผลลัพธ์มีความแปรปรวนสูง ฉันใช้รหัสเดียวกัน 100 เพื่อดูว่าผลลัพธ์เป็นอย่างไร ในการวิ่ง 98/100 มีตัวพยากรณ์หนึ่งตัวเลือกเสมอ (บางครั้งก็เป็นของตัวเอง); มีการเลือกตัวทำนายอื่น ๆ (co-efficient เป็น non-zero) โดยปกติ 50/100 ครั้ง

ดังนั้นมันบอกกับฉันว่าทุกครั้งที่การตรวจสอบความถูกต้องไขว้ทำงานมันอาจจะเป็นการเลือกแลมบ์ดาที่ดีที่สุดเพราะการสุ่มเริ่มต้นของโฟลเดอร์สำคัญ คนอื่น ๆ ได้เห็นปัญหานี้ ( ผลลัพธ์ CV.glmnet ) แต่ไม่มีวิธีการแก้ไขที่แนะนำ

ฉันคิดว่าบางทีสิ่งที่แสดงให้เห็นว่า 98/100 น่าจะสัมพันธ์กับคนอื่น ๆ ผลจะมีเสถียรภาพถ้าฉันเพียงแค่เรียกใช้ LOOCV ( fold-size=n ) แต่ผมอยากรู้ว่าทำไมพวกเขาจึงตัวแปรเมื่อnfold<n n


1
เพื่อความชัดเจนคุณหมายถึงการset.seed(1)วิ่งcv.glmnet()100 ครั้งหรือไม่? นั่นไม่ใช่วิธีการที่ยอดเยี่ยมสำหรับการทำซ้ำ ดีกว่าไปset.seed()ทางขวาก่อนการวิ่งแต่ละครั้ง การโทรไปยังแต่ละครั้งจะcv.glmnet()ถูกเรียกsample()N ครั้ง ดังนั้นหากความยาวของข้อมูลของคุณเปลี่ยนแปลงไป
smci

คำตอบ:


14

จุดที่นี่คือในcv.glmnetK folds ("ส่วน") จะถูกสุ่มเลือก

ในการตรวจสอบความถูกต้องข้าม K-fold ชุดข้อมูลจะถูกแบ่งออกเป็นส่วนส่วนK - 1จะใช้ในการทำนายส่วน K-th (นี่คือKครั้งที่ทำโดยใช้Kส่วนที่แตกต่างกันในแต่ละครั้ง) สิ่งนี้ทำสำหรับ lambdas ทั้งหมดและเป็นสิ่งที่ทำให้เกิดข้อผิดพลาดการตรวจสอบไขว้ที่เล็กที่สุดKK1KKlambda.min

นี่คือเหตุผลที่เมื่อคุณใช้ผลลัพธ์จะไม่เปลี่ยนแปลง: แต่ละกลุ่มประกอบด้วยหนึ่งกลุ่มดังนั้นจึงไม่มีทางเลือกมากนักสำหรับกลุ่มKnfolds=nK

จากcv.glmnet()คู่มืออ้างอิง:

โปรดทราบว่าผลลัพธ์ของ cv.glmnet เป็นแบบสุ่มเนื่องจากการเลือกแบบครึ่งจะเป็นการสุ่ม ผู้ใช้สามารถลดการสุ่มนี้โดยการเรียกใช้ cv.glmnet หลายครั้งและค่าเฉลี่ยของเส้นโค้งข้อผิดพลาด

### cycle for doing 100 cross validations
### and take the average of the mean error curves
### initialize vector for final data.frame with Mean Standard Errors
MSEs <- NULL
for (i in 1:100){
                 cv <- cv.glmnet(y, x, alpha=alpha, nfolds=k)  
                 MSEs <- cbind(MSEs, cv$cvm)
             }
  rownames(MSEs) <- cv$lambda
  lambda.min <- as.numeric(names(which.min(rowMeans(MSEs))))

MSE เป็นกรอบข้อมูลที่มีข้อผิดพลาดทั้งหมดสำหรับ lambdas ทั้งหมด (สำหรับการวิ่ง 100 ครั้ง) lambda.minเป็น lambda ของคุณที่มีข้อผิดพลาดเฉลี่ยขั้นต่ำ


สิ่งที่ฉันกังวลมากที่สุดคือการเลือกของ n ดูเหมือนจะมีความสำคัญ ฉันควรเชื่อถือผลลัพธ์ที่สามารถเปลี่ยนแปลงได้หรือไม่ หรือฉันควรชอล์คมันเป็นภาพร่างแม้ว่าฉันจะเรียกใช้หลายครั้ง?
4673

1
ขึ้นอยู่กับขนาดตัวอย่างคุณควรเลือก n เพื่อให้คุณมีการสังเกตอย่างน้อย 10 ต่อกลุ่ม ดังนั้นจึงเป็นการดีกว่าที่จะลดค่าเริ่มต้น n (= 10) ถ้าคุณมีขนาดตัวอย่างน้อยกว่า 100 คำสั่งนี้ให้ดูคำตอบที่แก้ไขด้วยชิ้นส่วนของรหัส: ด้วยสิ่งนี้สำหรับลูปคุณสามารถทำซ้ำ cv.glmnet ได้ 100 ครั้ง เส้นโค้งข้อผิดพลาด ลองสองสามครั้งแล้วคุณจะเห็นว่า lambda.min ไม่เปลี่ยนแปลง
อลิซ

2
ฉันชอบวิธีที่คุณทำ ฉันมีวงวนเดียวกัน แต่มีข้อยกเว้นอย่างหนึ่งในตอนท้าย: ฉันดูว่าคุณลักษณะที่แตกต่างกันปรากฏขึ้นบ่อยครั้งเมื่อเทียบกับ MSE ที่ต่ำที่สุดจากการทำซ้ำทั้งหมด ฉันเลือกจุดตัดโดยพลการ (เช่นแสดงซ้ำ 50/100) และใช้คุณสมบัติเหล่านั้น ตรงกันข้ามทั้งสองวิธีอยากรู้อยากเห็น
4673

1
ชื่อแถว (MSE) นี้ <- cv แลมบ์ดาในกรณีของฉันมีความยาวมากกว่า mses (ผมถือว่าเนื่องจากมีการบรรจบกัน ... )ล.aม.daอีRRโอR,sผมnอีโวลต์
user4581

ในฐานะที่เป็น user4581 cv.glmnet(...)$lambdaสังเกตฟังก์ชั่นนี้สามารถล้มเหลวเนื่องจากการแปรปรวนของความยาวของ ทางเลือกของฉันแก้ไขสิ่งนี้: stats.stackexchange.com/a/173895/19676
Max Ghenis สูงสุด

9

เมื่อเร็ว ๆ นี้ฉันประสบปัญหาเดียวกัน ฉันพยายามทำซ้ำ CV หลายครั้งเช่น 100, 200, 1,000 ในชุดข้อมูลของฉันพยายามหาสิ่งที่ดีที่สุดλααλα

αλ

จากนั้นสำหรับผู้ทำนายแต่ละคนฉันจะได้รับ:

  • หมายถึงค่าสัมประสิทธิ์
  • ส่วนเบี่ยงเบนมาตรฐาน
  • สรุปหมายเลข 5 (ค่ามัธยฐานควอไทล์ขั้นต่ำและสูงสุด)
  • ร้อยละของเวลาแตกต่างจากศูนย์ (เช่นมีอิทธิพล)

วิธีนี้ฉันได้คำอธิบายที่ชัดเจนเกี่ยวกับผลกระทบของตัวทำนาย เมื่อคุณมีการแจกแจงค่าสัมประสิทธิ์กว่าที่คุณสามารถเรียกใช้สิ่งทางสถิติใด ๆ ที่คุณคิดว่ามีค่าที่จะได้รับ CI, ค่า p, ฯลฯ ... แต่ฉันยังไม่ได้ตรวจสอบเรื่องนี้

วิธีนี้สามารถใช้กับวิธีการเลือกใด ๆ ที่ฉันคิดได้ไม่มากก็น้อย


4
กรุณาโพสต์รหัสของคุณที่นี่ได้ไหม
rbm

ใช่คุณช่วยกรุณาโพสต์รหัสของคุณที่นี่ได้ไหม
smci

4

ฉันจะเพิ่มโซลูชันอื่นซึ่งจัดการข้อผิดพลาดใน @ Alice เนื่องจากแกะ lambdas หายไป แต่ไม่ต้องการแพคเกจพิเศษเช่น @Max Ghenis ขอบคุณที่เป็นหนี้กับคำตอบอื่น ๆ - ทุกคนทำคะแนนให้เป็นประโยชน์!

lambdas = NULL
for (i in 1:n)
{
    fit <- cv.glmnet(xs,ys)
    errors = data.frame(fit$lambda,fit$cvm)
    lambdas <- rbind(lambdas,errors)
}
# take mean cvm for each lambda
lambdas <- aggregate(lambdas[, 2], list(lambdas$fit.lambda), mean)

# select the best one
bestindex = which(lambdas[2]==min(lambdas[2]))
bestlambda = lambdas[bestindex,1]

# and now run glmnet once more with it
fit <- glmnet(xy,ys,lambda=bestlambda)

3

คำตอบของอลิซทำงานได้ดีในกรณีส่วนใหญ่ แต่บางครั้งข้อผิดพลาดออกมาเนื่องจาก cv.glmnet$lambdaบางครั้งผลลัพธ์ที่มีความยาวต่างกันเช่น:

ข้อผิดพลาดใน rownames <- (tmp, value = c (0.135739830284452, 0.12368107787663,: ความยาวของ 'dimnames' [1] ไม่เท่ากับขอบเขตอาร์เรย์

OptimLambdaด้านล่างควรทำงานในกรณีทั่วไปและเร็วขึ้นด้วยการใช้ประโยชน์จากmclapplyการประมวลผลแบบขนานและหลีกเลี่ยงการวนซ้ำ

Lambdas <- function(...) {
  cv <- cv.glmnet(...)
  return(data.table(cvm=cv$cvm, lambda=cv$lambda))
}

OptimLambda <- function(k, ...) {
  # Returns optimal lambda for glmnet.
  #
  # Args:
  #   k: # times to loop through cv.glmnet.
  #   ...: Other args passed to cv.glmnet.
  #
  # Returns:
  #   Lambda associated with minimum average CV error over runs.
  #
  # Example:
  #   OptimLambda(k=100, y=y, x=x, alpha=alpha, nfolds=k)
  #
  require(parallel)
  require(data.table)
  MSEs <- data.table(rbind.fill(mclapply(seq(k), function(dummy) Lambdas(...))))
  return(MSEs[, list(mean.cvm=mean(cvm)), lambda][order(mean.cvm)][1]$lambda)
}

1

คุณสามารถควบคุมการสุ่มหากคุณตั้งค่า foldid อย่างชัดเจน นี่คือตัวอย่างสำหรับ CV 5 เท่า

library(caret)
set.seed(284)
flds <- createFolds(responseDiffs, k = cvfold, list = TRUE, returnTrain = FALSE)
foldids = rep(1,length(responseDiffs))
foldids[flds$Fold2] = 2
foldids[flds$Fold3] = 3
foldids[flds$Fold4] = 4
foldids[flds$Fold5] = 5

ตอนนี้เรียกใช้ cv.glmnet ด้วย foldids

lassoResults<-cv.glmnet(x=countDiffs,y=responseDiffs,alpha=1,foldid = foldids)

คุณจะได้รับผลลัพธ์เดียวกันทุกครั้ง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.