ค่าเริ่มต้นเริ่มต้นที่เหมาะสมกับการถดถอยโลจิสติกกับ GLM


10

ฉันสงสัยว่ามีการระบุค่าเริ่มต้นเริ่มต้นglmอย่างไร

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

ฉันพยายามจัดรูปแบบการถดถอยแบบโลจิสติกส์แบบเรียบง่ายด้วยการติดตามอัลกอริทึม:

set.seed(123)

x <- rnorm(100)
p <- 1/(1 + exp(-x))
y <- rbinom(100, size = 1, prob = p)

# to see parameter estimates in each step
trace(glm.fit, quote(print(coefold)), at = list(c(22, 4, 8, 4, 19, 3)))

ก่อนไม่มีข้อกำหนดของค่าเริ่มต้น:

glm(y ~ x, family = "binomial")

Tracing glm.fit(x = structure(c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  .... step 22,4,8,4,19,3 
NULL
Tracing glm.fit(x = structure(c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  .... step 22,4,8,4,19,3 
[1] 0.386379 1.106234
Tracing glm.fit(x = structure(c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  .... step 22,4,8,4,19,3 
[1] 0.3991135 1.1653971
Tracing glm.fit(x = structure(c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  .... step 22,4,8,4,19,3 
[1] 0.3995188 1.1669508

ในขั้นตอนแรก, NULLค่าเริ่มต้นเป็น

ประการที่สองฉันตั้งค่าเริ่มต้นเป็นศูนย์:

glm(y ~ x, family = "binomial", start = c(0, 0))

Tracing glm.fit(x = structure(c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  .... step 22,4,8,4,19,3 
[1] 0 0
Tracing glm.fit(x = structure(c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  .... step 22,4,8,4,19,3 
[1] 0.3177530 0.9097521
Tracing glm.fit(x = structure(c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  .... step 22,4,8,4,19,3 
[1] 0.3909975 1.1397163
Tracing glm.fit(x = structure(c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  .... step 22,4,8,4,19,3 
[1] 0.3994147 1.1666173
Tracing glm.fit(x = structure(c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  .... step 22,4,8,4,19,3 
[1] 0.3995191 1.1669518

และเราจะเห็นว่าการวนซ้ำระหว่างวิธีแรกและวิธีที่สองนั้นแตกต่างกัน

หากต้องการดูค่าเริ่มต้นที่ระบุโดยglmฉันพยายามปรับให้เข้ากับแบบจำลองด้วยการวนซ้ำเพียงครั้งเดียว:

glm(y ~ x, family = "binomial", control = list(maxit = 1))

Tracing glm.fit(x = structure(c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  .... step 22,4,8,4,19,3 
NULL

Call:  glm(formula = y ~ x, family = "binomial", control = list(maxit = 1))

Coefficients:
(Intercept)            x  
     0.3864       1.1062  

Degrees of Freedom: 99 Total (i.e. Null);  98 Residual
Null Deviance:      134.6 
Residual Deviance: 115  AIC: 119

การประมาณค่าพารามิเตอร์ (ไม่น่าประหลาดใจ) สอดคล้องกับการประมาณการของวิธีแรกในการทำซ้ำครั้งที่สองคือ[1] 0.386379 1.106234 การตั้งค่าเหล่านี้เป็นค่าเริ่มต้นนำไปสู่ลำดับการทำซ้ำเดียวกันเช่นเดียวกับในวิธีแรก:

glm(y ~ x, family = "binomial", start = c(0.386379, 1.106234))

Tracing glm.fit(x = structure(c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  .... step 22,4,8,4,19,3 
[1] 0.386379 1.106234
Tracing glm.fit(x = structure(c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  .... step 22,4,8,4,19,3 
[1] 0.3991135 1.1653971
Tracing glm.fit(x = structure(c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  .... step 22,4,8,4,19,3 
[1] 0.3995188 1.1669508

ดังนั้นคำถามคือวิธีการคำนวณค่าเหล่านี้?


มันซับซ้อน. หากคุณระบุstartค่าจะใช้ในการคำนวณสิ่งที่ส่งผ่านไปยังC_Cdqrlsรูทีน หากคุณไม่ได้ค่าที่ถูกส่งผ่านได้รับการคำนวณ (รวมถึงการโทรeval(binomial()$initialize)) แต่ไม่เคยชัดเจนคำนวณค่าglm.fit startใช้เวลาหนึ่งหรือสองชั่วโมงและศึกษาglm.fitรหัส
Roland

ขอบคุณสำหรับความคิดเห็น ฉันพยายามศึกษาglm.fitโค้ด แต่ฉันก็ยังไม่รู้ว่าจะคำนวณค่าเริ่มต้นได้อย่างไร
Adela

คำตอบ:


6

TL; DR

  • start=c(b0,b1)เริ่มต้น eta ถึงb0+x*b1(mu ถึง 1 / (1 + exp (-eta)))
  • start=c(0,0) เริ่มต้น eta ถึง 0 (mu ถึง 0.5) โดยไม่คำนึงถึงค่า y หรือ x
  • start=NULL เริ่มต้น eta = 1.098612 (mu = 0.75) ถ้า y = 1 โดยไม่คำนึงถึงค่า x
  • start=NULL เริ่มต้น eta = -1.098612 (mu = 0.25) ถ้า y = 0 โดยไม่คำนึงถึงค่า x

  • เมื่อการทางพิเศษแห่งประเทศไทย (และดังนั้นหมู่บ้านและ var (MU)) ได้รับการคำนวณwและการzคำนวณและส่งไปยังแก้ QR qr.solve(cbind(1,x) * w, z*w)ในจิตวิญญาณของ

ยาวนาน

เสริมสร้างความคิดเห็นของ Roland: ฉันสร้างglm.fit.truncated()ที่ที่ฉันglm.fitลงไปที่C_Cdqrlsสายแล้วแสดงความคิดเห็นออกมา glm.fit.truncatedเอาต์พุตzและwค่า (รวมถึงค่าของปริมาณที่ใช้ในการคำนวณzและw) ซึ่งจะถูกส่งผ่านไปยังการC_Cdqrlsโทร:

## call Fortran code via C wrapper
fit <- .Call(C_Cdqrls, x[good, , drop = FALSE] * w, z * w,
             min(1e-7, control$epsilon/1000), check=FALSE) 

เพิ่มเติมสามารถอ่านข้อมูลเกี่ยวกับที่นี่C_Cdqrls โชคดีที่ฟังก์ชั่นqr.solveใน base R แตะตรงกับรุ่น LINPACK ที่ถูกเรียกเข้าglm.fit()มา

ดังนั้นเราจึงเรียกใช้glm.fit.truncatedข้อมูลจำเพาะค่าเริ่มต้นที่แตกต่างกันแล้วทำการเรียกใช้qr.solveด้วยค่า w และ z และเราจะเห็นว่ามีการคำนวณ "ค่าเริ่มต้น" (หรือค่าการทำซ้ำที่แสดงครั้งแรก) อย่างไร โรลันด์ระบุระบุstart=NULLหรือstart=c(0,0)ใน GLM () ส่งผลกระทบต่อการคำนวณสำหรับ W และ Z, ไม่ได้startสำหรับ

สำหรับ start = NULL: zเป็นเวกเตอร์ที่องค์ประกอบมีค่า 2.431946 หรือ -2.431946 และwเป็นเวกเตอร์ที่องค์ประกอบทั้งหมดคือ 0.4330127:

start.is.null <- glm.fit.truncated(x,y,family=binomial(), start=NULL)
start.is.null
w <- start.is.null$w
z <- start.is.null$z
## if start is NULL, the first displayed values are:
qr.solve(cbind(1,x) * w, z*w)  
# > qr.solve(cbind(1,x) * w, z*w)  
#                 x 
# 0.386379 1.106234 

สำหรับ start = c (0,0): zเป็นเวกเตอร์ที่องค์ประกอบมีค่า 2 หรือ -2 และwเป็นเวกเตอร์ที่องค์ประกอบทั้งหมดคือ 0.5:

## if start is c(0,0)    
start.is.00 <- glm.fit.truncated(x,y,family=binomial(), start=0)
start.is.00
w <- start.is.00$w
z <- start.is.00$z
## if start is c(0,0), the first displayed values are:    
qr.solve(cbind(1,x) * w, z*w)  
# > qr.solve(cbind(1,x) * w, z*w)  
#                   x 
# 0.3177530 0.9097521 

นั่นคือทั้งหมดที่ดีและดี แต่เราจะคำนวณwและzอย่างไร ใกล้ด้านล่างของที่glm.fit.truncated()เราเห็น

z <- (eta - offset)[good] + (y - mu)[good]/mu.eta.val[good]
w <- sqrt((weights[good] * mu.eta.val[good]^2)/variance(mu)[good])

ดูการเปรียบเทียบต่อไปนี้ระหว่างค่าเอาท์พุทของปริมาณที่ใช้ในการคำนวณzและw:

cbind(y, start.is.null$mu, start.is.00$mu)
cbind(y, start.is.null$eta, start.is.00$eta)
cbind(start.is.null$var_mu, start.is.00$var_mu)
cbind(start.is.null$mu.eta.val, start.is.00$mu.eta.val)

โปรดทราบว่าstart.is.00จะมีเวกเตอร์ที่muมีค่า 0.5 เท่านั้นเนื่องจาก eta ถูกตั้งค่าเป็น 0 และ mu (eta) = 1 / (1 + exp (-0)) = 0.5 start.is.nullตั้งค่า y = 1 ให้เป็น mu = 0.75 (ซึ่งตรงกับ eta = 1.098612) และ y = 0 เป็น mu = 0.25 (ซึ่งสอดคล้องกับ eta = -1.098612) และvar_mu= 0.75 * 0.25 = 0.1875

อย่างไรก็ตามมันเป็นเรื่องที่น่าสนใจที่จะทราบว่าฉันเปลี่ยนเมล็ดและเรียนใหม่ทุกอย่างและ mu = 0.75 สำหรับ y = 1 และ mu = 0.25 สำหรับ y = 0 (และปริมาณอื่น ๆ ยังคงเหมือนเดิม) กล่าวคือเริ่มต้น = NULL ก่อให้เกิดเหมือนกันwและzไม่คำนึงถึงสิ่งyและxเป็นเพราะพวกเขาเริ่มต้นการทางพิเศษแห่งประเทศไทย = 1.098612 (MU = 0.75) ถ้า y = 1 และการทางพิเศษแห่งประเทศไทย = -1.098612 (MU = 0.25) ถ้า y = 0

ดังนั้นจึงปรากฏว่าค่าเริ่มต้นสำหรับค่าสัมประสิทธิ์การสกัดกั้นและค่าสัมประสิทธิ์ X ไม่ได้ถูกตั้งค่าสำหรับ start = NULL แต่ค่าเริ่มต้นจะถูกกำหนดให้กับกทพ. ขึ้นอยู่กับค่า y และเป็นอิสระจากค่า x จากที่นั่นwและzคำนวณแล้วส่งไปพร้อมกับxqr.solver

รหัสที่จะเรียกใช้ก่อนหน้าชิ้นด้านบน:

set.seed(123)

x <- rnorm(100)
p <- 1/(1 + exp(-x))
y <- rbinom(100, size = 1, prob = p)


glm.fit.truncated <- function(x, y, weights = rep.int(1, nobs), 
start = 0,etastart = NULL, mustart = NULL, 
offset = rep.int(0, nobs),
family = binomial(), 
control = list(), 
intercept = TRUE,
singular.ok = TRUE
){
control <- do.call("glm.control", control)
x <- as.matrix(x)
xnames <- dimnames(x)[[2L]]
ynames <- if(is.matrix(y)) rownames(y) else names(y)
conv <- FALSE
nobs <- NROW(y)
nvars <- ncol(x)
EMPTY <- nvars == 0
## define weights and offset if needed
if (is.null(weights))
  weights <- rep.int(1, nobs)
if (is.null(offset))
  offset <- rep.int(0, nobs)

## get family functions:
variance <- family$variance
linkinv  <- family$linkinv
if (!is.function(variance) || !is.function(linkinv) )
  stop("'family' argument seems not to be a valid family object", call. = FALSE)
dev.resids <- family$dev.resids
aic <- family$aic
mu.eta <- family$mu.eta
unless.null <- function(x, if.null) if(is.null(x)) if.null else x
valideta <- unless.null(family$valideta, function(eta) TRUE)
validmu  <- unless.null(family$validmu,  function(mu) TRUE)
if(is.null(mustart)) {
  ## calculates mustart and may change y and weights and set n (!)
  eval(family$initialize)
} else {
  mukeep <- mustart
  eval(family$initialize)
  mustart <- mukeep
}
if(EMPTY) {
  eta <- rep.int(0, nobs) + offset
  if (!valideta(eta))
    stop("invalid linear predictor values in empty model", call. = FALSE)
  mu <- linkinv(eta)
  ## calculate initial deviance and coefficient
  if (!validmu(mu))
    stop("invalid fitted means in empty model", call. = FALSE)
  dev <- sum(dev.resids(y, mu, weights))
  w <- sqrt((weights * mu.eta(eta)^2)/variance(mu))
  residuals <- (y - mu)/mu.eta(eta)
  good <- rep_len(TRUE, length(residuals))
  boundary <- conv <- TRUE
  coef <- numeric()
  iter <- 0L
} else {
  coefold <- NULL
  eta <-
    if(!is.null(etastart)) etastart
  else if(!is.null(start))
    if (length(start) != nvars)
      stop(gettextf("length of 'start' should equal %d and correspond to initial coefs for %s", nvars, paste(deparse(xnames), collapse=", ")),
           domain = NA)
  else {
    coefold <- start
    offset + as.vector(if (NCOL(x) == 1L) x * start else x %*% start)
  }
  else family$linkfun(mustart)
  mu <- linkinv(eta)
  if (!(validmu(mu) && valideta(eta)))
    stop("cannot find valid starting values: please specify some", call. = FALSE)
  ## calculate initial deviance and coefficient
  devold <- sum(dev.resids(y, mu, weights))
  boundary <- conv <- FALSE

  ##------------- THE Iteratively Reweighting L.S. iteration -----------
  for (iter in 1L:control$maxit) {
    good <- weights > 0
    varmu <- variance(mu)[good]
    if (anyNA(varmu))
      stop("NAs in V(mu)")
    if (any(varmu == 0))
      stop("0s in V(mu)")
    mu.eta.val <- mu.eta(eta)
    if (any(is.na(mu.eta.val[good])))
      stop("NAs in d(mu)/d(eta)")
    ## drop observations for which w will be zero
    good <- (weights > 0) & (mu.eta.val != 0)

    if (all(!good)) {
      conv <- FALSE
      warning(gettextf("no observations informative at iteration %d",
                       iter), domain = NA)
      break
    }
    z <- (eta - offset)[good] + (y - mu)[good]/mu.eta.val[good]
    w <- sqrt((weights[good] * mu.eta.val[good]^2)/variance(mu)[good])
    # ## call Fortran code via C wrapper
    # fit <- .Call(C_Cdqrls, x[good, , drop = FALSE] * w, z * w,
    #              min(1e-7, control$epsilon/1000), check=FALSE)
    # 

    #print(iter)
    #print(z)
    #print(w)
  }


  }
  return(list(z=z, w=w, mustart=mustart, etastart=etastart, eta=eta, offset=offset, mu=mu, mu.eta.val=mu.eta.val,
              weight=weights, var_mu=variance(mu)))

}

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