Caret包 - 使用平滑和线性预测变量进行交叉验证GAM

11

我希望使用caret对GAM模型进行交叉验证。我的GAM模型具有二元结果变量,纬度和经度坐标对的各向同性平滑以及线性预测因子。当使用mgcv时,典型的语法如下:

gam1 <- gam( y ~ s(lat , long) + x1 + x2, family = binomial(logit) )

我不太确定如何使用caret中的train函数来指定这个模型。我的语法大致如下:

我不太确定如何使用caret中的train函数来指定这个模型。我的语法大致如下:

cv <- train(y ~ lat + long + x1 + x2, 
            data = data, 
            method = "gam", 
            family = "binomial", 
            trControl = trainControl(method = "LOOCV", number=1, repeats=), 
            tuneGrid = data.frame(method = "GCV.Cp", select = FALSE))

问题是我只希望平滑纬度和经度,并将x1和x2视为线性。

谢谢!


同意@李哲源ZheyuanLi的观点,但是caret::train函数在其公式参数中是否接受回归或平滑样条函数呢? - IRTFM
method="glm" 时,在公式参数中使用样条或多项式函数,我没有发现任何困难。 - IRTFM
1个回答

20

看到有人在mgcv之外使用它非常有趣。经过一番研究,我来告诉你一个令人沮丧的消息:当前版本的caretmgcv的支持不太好,所以在caret中使用mgcv是个坏主意。

如果你正在使用caret,让我问你几个基本问题:

  1. 如何指定平滑函数的节点数和样条基类?
  2. 如何指定二维平滑函数?
  3. 如何使用teti指定张量积样条?
  4. 如何微调平滑参数?

如果你想知道caret::train在使用method = "gam"时在做什么,请查看其拟合例程:

getModelInfo(model = "gam", regex = FALSE)$gam$fit

function(x, y, wts, param, lev, last, classProbs, ...) { 
            dat <- if(is.data.frame(x)) x else as.data.frame(x)
            modForm <- caret:::smootherFormula(x)
            if(is.factor(y)) {
              dat$.outcome <- ifelse(y == lev[1], 0, 1)
              dist <- binomial()
            } else {
              dat$.outcome <- y
              dist <- gaussian()
            }
            modelArgs <- list(formula = modForm,
                              data = dat,
                              select = param$select, 
                              method = as.character(param$method))
            ## Intercept family if passed in
            theDots <- list(...)
            if(!any(names(theDots) == "family")) modelArgs$family <- dist
            modelArgs <- c(modelArgs, theDots)                 
            out <- do.call(getFromNamespace("gam", "mgcv"), modelArgs)
            out    
            }

你看到了 modForm <- caret:::smootherFormula(x) 这一行吗?这行是关键,其他行只是模型调用的例行构造。因此,让我们来检查一下 caret 构造的 GAM 公式:

caret:::smootherFormula

function (data, smoother = "s", cut = 10, df = 0, span = 0.5, 
    degree = 1, y = ".outcome") 
{
    nzv <- nearZeroVar(data)
    if (length(nzv) > 0) 
        data <- data[, -nzv, drop = FALSE]
    numValues <- sort(apply(data, 2, function(x) length(unique(x))))
    prefix <- rep("", ncol(data))
    suffix <- rep("", ncol(data))
    prefix[numValues > cut] <- paste(smoother, "(", sep = "")
    if (smoother == "s") {
        suffix[numValues > cut] <- if (df == 0) 
            ")"
        else paste(", df=", df, ")", sep = "")
    }
    if (smoother == "lo") {
        suffix[numValues > cut] <- paste(", span=", span, ",degree=", 
            degree, ")", sep = "")
    }
    if (smoother == "rcs") {
        suffix[numValues > cut] <- ")"
    }
    rhs <- paste(prefix, names(numValues), suffix, sep = "")
    rhs <- paste(rhs, collapse = "+")
    form <- as.formula(paste(y, rhs, sep = "~"))
    form
}

简而言之,它创建了加法的、一元的平滑曲线。这是GAM最初提出时的经典形式。

为此,您在mgcv上失去了大量控制权,如先前所列。

为了验证这一点,让我构建一个类似于您情况的示例:

set.seed(0)
dat <- gamSim(eg = 2, scale = 0.2)$data[1:3]
dat$a <- runif(400)
dat$b <- runif(400)
dat$y <- with(dat, y + 0.3 * a - 0.7 * b)

#            y         x         z          a         b
#1 -0.30258559 0.8966972 0.1478457 0.07721866 0.3871130
#2 -0.59518832 0.2655087 0.6588776 0.13853856 0.8718050
#3 -0.06978648 0.3721239 0.1850700 0.04752457 0.9671970
#4 -0.17002059 0.5728534 0.9543781 0.03391887 0.8669163
#5  0.55452069 0.9082078 0.8978485 0.91608902 0.4377153
#6 -0.17763650 0.2016819 0.9436971 0.84020039 0.1919378

因此,我们的目标是拟合模型:y ~ s(x, z) + a + b。数据y是高斯分布的,但这并不重要,它不会影响caret如何使用mgcv

cv <- train(y ~ x + z + a + b, data = dat, method = "gam", family = "gaussian",
            trControl = trainControl(method = "LOOCV", number=1, repeats=1), 
            tuneGrid = data.frame(method = "GCV.Cp", select = FALSE))

您可以提取最终模型:

fit <- cv[[11]]

那么它使用的是什么公式?

fit$formula
#.outcome ~ s(x) + s(z) + s(a) + s(b)

看到了吗?除了是“可加的、单变量的”之外,它还将 mgcv::s 的所有内容都保留为默认值:默认bs = "tp",默认k = 10等等。


1
谢谢,所有的观点都很好。如果我想比较自适应平滑和tp到te平滑,我不知道如何在caret中实现。所以我想可能有一种硬编码交叉验证的方法,但这超出了我的技能范围。有人可以指向一些代码让我做吗?谢谢 - Paul Lantos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接