【问题标题】:Formula interface for glmnetglmnet的公式接口
【发布时间】:2015-05-19 13:45:35
【问题描述】:

在过去的几个月里,我参与了许多项目,我使用glmnet 包来适应弹性网络模型。这很棒,但与大多数 R 建模功能相比,界面相当简单。特别是,您必须给出响应向量和预测矩阵,而不是指定公式和数据框。您还会失去常规界面提供的许多生活质量方面的东西,例如对因素的合理 (?) 处理、缺失值、将变量按正确顺序排列等。

所以我通常最终会编写自己的代码来重新创建公式/数据框界面。由于客户保密问题,我最终也留下了这段代码,不得不为下一个项目重新编写它。我想我不妨硬着头皮创建一个实际的包来做到这一点。但是,在我这样做之前有几个问题:

  • 在弹性网络模型中使用公式/数据框接口是否存在任何问题? (我知道standardisation and dummy variables,宽数据集可能需要稀疏模型矩阵。)
  • 是否有任何现有的软件包可以做到这一点?

【问题讨论】:

    标签: r formula glmnet


    【解决方案1】:

    嗯,好像没有预先构建的公式界面,所以我继续制作自己的。可以从 Github 下载:https://github.com/Hong-Revo/glmnetUtils

    或者在 R 中,使用 devtools::install_github:

    install.packages("devtools")
    library(devtools)
    install_github("hong-revo/glmnetUtils")
    library(glmnetUtils)
    

    来自自述文件:

    一些生活质量功能可简化拟合过程 带有glmnet 的弹性网络模型,具体来说:

    • glmnet.formulaglmnet 提供公式/数据框接口。
    • cv.glmnet.formulacv.glmnet 做了类似的事情。
    • predictcoef 的方法适用于上述两者。
    • 一个函数cvAlpha.glmnet通过交叉验证来选择alpha和lambda参数,遵循中描述的方法 cv.glmnet 的帮助页面。可选择在 并行。
    • plotpredictcoef 的上述方法。

    顺便说一句,在编写上述内容时,我想我意识到为什么以前没有人这样做过。 R 处理模型框架和模型矩阵的核心是terms 对象,它包括一个矩阵,每个变量一行,每个主效应和交互作用一列。实际上,这(至少)大致是一个 p x p 矩阵,其中 p 是模型中的变量数。当 p 为 16000 时(这在当今广泛数据中很常见),生成的矩阵大小约为 1 GB。

    不过,我在使用这些对象时(还)没有遇到任何问题。如果它成为一个主要问题,我会看看我是否可以找到解决方法。


    2016 年 10 月更新

    我已将更新推送到存储库,以解决上述问题以及与因素相关的问题。来自文档:

    glmnetUtils 可以通过两种方式从公式和数据框生成模型矩阵。第一种是使用由model.framemodel.matrix 组成的标准R 机器;第二个是一次构建一个变量的矩阵。这些选项将在下面讨论和对比。

    使用model.frame

    这是更简单的选项,也是与其他 R 建模功能最兼容的选项。 model.frame 函数采用公式和数据框并返回 模型框:带有特殊信息的数据框,可让 R 理解公式中的术语。例如,如果公式包含交互项,则模型框架将指定数据中的哪些列与交互相关,以及应如何处理它们。同样,如果公式在 RHS 上包含 exp(x)I(x^2) 之类的表达式,model.frame 将评估这些表达式并将它们包含在输出中。

    使用model.frame 的主要缺点是它会生成一个术语对象,该对象对变量和交互的组织方式进行编码。该对象的属性之一是一个矩阵,每个变量一行,每个主效应和交互作用一列。至少,这是(大约)一个 p x p 方阵,其中 p 是模型中主效应的数量。对于 p > 10000 的宽数据集,该矩阵的大小可能接近或超过千兆字节。即使有足够的内存来存储这样的对象,生成模型矩阵也可能需要大量时间。

    标准 R 方法的另一个问题是对因子的处理。通常,model.matrix 会将 N 级因子转换为具有 N-1 列的指标矩阵,其中一列被删除。这对于适合 lm 和 glm 的非正则化模型是必要的,因为 N 列的完整集是线性相关的。对于通常的处理对比,解释是删除的列表示基线水平,而其他列的系数表示响应相对于基线的差异。

    这可能不适合适合 glmnet 的正则化模型。正则化过程将系数缩小到零,这迫使估计的与基线的差异更小。但这只有在事先选择了基线水平时才有意义,或者作为默认值有意义;否则,它实际上使级别更类似于任意选择的级别。

    手动构建模型矩阵

    为了处理上述问题,glmnetUtils 默认会避免使用model.frame,而是逐项构建模型矩阵。这避免了创建terms 对象的内存成本,并且可以明显比标准方法快。它还将在模型矩阵中包含一列,用于一个因子的所有级别;也就是说,没有假设基线水平。在这种情况下,系数代表与整体平均响应的差异,将它们缩小到零是有意义的(通常)。

    不使用model.frame的主要缺点是公式只能比较简单。目前,代码只处理像y ~ x1 + x2 + ... + x_p 这样的简单公式,其中x 是数据中已经存在的列。不支持交互项和计算表达式。在可能的情况下,您应该事先计算此类表达式。


    2017 年 4 月更新

    几经折腾,终于是on CRAN.

    【讨论】:

    • 这太棒了!我讨厌不能使用glmnet 的公式。与使用 glmnet 的“标准”方式相比,您有任何可用的基准测试吗?
    • @AlexA。不确定为什么需要基准测试;它基本上只是在运行glmnet 之前调用model.framemodel.matrix。自然它会比单独的glmnet 慢,特别是对于宽数据。但是总时间应该和你自己打电话给model.frame/matrix差不多。
    • 啊,好吧。这就是我所追求的。谢谢你的解释。又干得漂亮!
    • 很棒的答案!顺便说一句,我认为您可能忘记在 cvAlpha 中为公式设置默认值:predict(cvAlpha.glmnet(fmla), newdata=data.frame(phase=phase)) Error in match(TRUE, abs(object$alpha - alpha) < 0.00000001) : argument "alpha" is missing, with no default
    • @rhombidodedecahedron 谢谢。我没有设置默认 alpha,因为似乎很难选择一个合适的值:(2-D)交叉验证表面可能相当嘈杂,尤其是对于小而宽的数据集。您可以做的是查看为每个 alpha 值绘制的 CV 曲线,然后选择能够提供最佳结果的曲线。
    猜你喜欢
    • 2018-05-22
    • 2013-09-17
    • 2013-01-22
    • 2011-12-07
    • 2010-11-18
    • 2017-07-25
    • 1970-01-01
    • 2013-05-10
    • 2021-10-22
    相关资源
    最近更新 更多