逻辑回归对象在 R save() 函数后消耗大量磁盘空间

Posted

技术标签:

【中文标题】逻辑回归对象在 R save() 函数后消耗大量磁盘空间【英文标题】:Logistic regression objects consuming an enormous amount of disk space after R save() function 【发布时间】:2014-08-01 20:34:48 【问题描述】:

我拒绝提供示例代码,因为到目前为止我无法在较小的数据集上复制此示例。我正在使用不同的协变量选择训练几个逻辑回归(本例中为 50 个)并将输出保存为列表。我的训练数据有 +400K 行。

认识到有大量不必要的背景数据存储在 glm 对象中,我的训练脚本涉及以下代码行,旨在尽可能多地去除额外数据并减少内存占用输出对象:

fit[c('residuals', 'fitted.values', 'effects', 'weights', 'prior.weights', 'y', 'linear.predictors', 'data')] <- NULL
fit$qr$qr <- NULL
 gc() 

起初这似乎工作正常。 R/RStudio 控制台在执行我的代码后告诉我 glms 列表是 9.6Mb:

但是,当我使用 save(logitFire, file = 'logitFire.RData') 保存这个对象时,我发现它的内存占用绝对是巨大的,(磁盘上 1.32GB):

再次,我 100% 认识到不提供玩具示例是不好的形式。我尝试使用 iris 数据集,但无法重现该问题。这似乎是大型数据集的一个特征,但我不确定。有没有专家知道发生了什么?我的下一步,如果我不能使用基本包中的函数来解决这个问题,我将为“leanLogit”和“leanPredict”函数编写我自己的包装器,这些函数只是去掉模型协变量并折腾所有其余的辅助数据。

编辑:澄清一下,我在这个问题中包含的示例脚本嵌入在模型训练例程中,最终生成 glms logitFire 的列表。这不是完整的代码,而是嵌入在更大的脚本中。我将其包含在内,以便读者可以看到我正在剥离的数据对象。

编辑#2:这是一些额外的请求信息。为了尽可能清楚,logitFire 是我使用 glm 在 R 中生成的 50 个逻辑回归模型的列表。我已经在此列表的一个元素(即一个逻辑回归模型)上显示了 str 命令的输出:

> object.size(logitFire)
10113640 bytes
> str(logitFire[[1]])
List of 21
 $ coefficients : Named num [1:54] 18.361 -0.592 -1.043 -0.744 0.101 ...
  ..- attr(*, "names")= chr [1:54] "(Intercept)" "var32" "var33" "var34" ...
 $ R            : num [1:54, 1:54] -11.3 0 0 0 0 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:54] "(Intercept)" "var32" "var33" "var34" ...
  .. ..$ : chr [1:54] "(Intercept)" "var32" "var33" "var34" ...
 $ rank         : int 53
 $ qr           :List of 4
  ..$ rank : int 53
  ..$ qraux: num [1:54] 1 1 1 1 1 ...
  ..$ pivot: int [1:54] 1 2 3 4 5 6 7 8 9 10 ...
  ..$ tol  : num 1e-11
  ..- attr(*, "class")= chr "qr"
 $ family       :List of 12
  ..$ family    : chr "binomial"
  ..$ link      : chr "logit"
  ..$ linkfun   :function (mu)  
  ..$ linkinv   :function (eta)  
  ..$ variance  :function (mu)  
  ..$ dev.resids:function (y, mu, wt)  
  ..$ aic       :function (y, n, mu, wt, dev)  
  ..$ mu.eta    :function (eta)  
  ..$ initialize:  expression(     if (NCOL(y) == 1)          if (is.factor(y))                 y <- y != levels(y)[1L]         n <- rep.int(1, nobs)         y[weights == 0] <- 0         if (any(y < 0 | y > 1))              stop("y values must be 0 <= y <= 1")         mustart <- (weights * y + 0.5)/(weights + 1)         m <- weights * y         if (any(abs(m - round(m)) > 0.001))              warning("non-integer #successes in a binomial glm!")          else if (NCOL(y) == 2)          if (any(abs(y - round(y)) > 0.001))              warning("non-integer counts in a binomial glm!")         n <- y[, 1] + y[, 2]         y <- ifelse(n == 0, 0, y[, 1]/n)         weights <- weights * n         mustart <- (n * y + 0.5)/(n + 1)          else stop("for the 'binomial' family, y must be a vector of 0 and 1's\nor a 2 column matrix where col 1 is no. successes and col 2 is no. failures") )
  ..$ validmu   :function (mu)  
  ..$ valideta  :function (eta)  
  ..$ simulate  :function (object, nsim)  
  ..- attr(*, "class")= chr "family"
 $ deviance     : num 1648
 $ aic          : num 1754
 $ null.deviance: num 1783
 $ iter         : int 19
 $ df.residual  : int 49947
 $ df.null      : int 49999
 $ converged    : logi TRUE
 $ boundary     : logi FALSE
 $ call         : language glm(formula = fire ~ var3 + var1 + var12isNA + var4 + var11 + var13 + var6 + dummy + var9 + var16isNA + var10 + var17 + var8 + var7 + var15isNA +      var14isNA, family = binomial(), data = inData, model = FALSE)
 $ formula      :Class 'formula' length 3 fire ~ var3 + var1 + var12isNA + var4 + var11 + var13 + var6 + dummy + var9 + var16isNA + var10 + var17 + var8 + var7 + var15isNA + var14isNA
  .. ..- attr(*, ".Environment")=<environment: 0x7f98f5685ce8> 
 $ terms        :Classes 'terms', 'formula' length 3 fire ~ var3 + var1 + var12isNA + var4 + var11 + var13 + var6 + dummy + var9 + var16isNA + var10 + var17 + var8 + var7 + var15isNA + var14isNA
  .. ..- attr(*, "variables")= language list(fire, var3, var1, var12isNA, var4, var11, var13, var6, dummy, var9, var16isNA, var10, var17, var8, var7, var15isNA, var14isNA)
  .. ..- attr(*, "factors")= int [1:17, 1:16] 0 1 0 0 0 0 0 0 0 0 ...
  .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. ..$ : chr [1:17] "fire" "var3" "var1" "var12isNA" ...
  .. .. .. ..$ : chr [1:16] "var3" "var1" "var12isNA" "var4" ...
  .. ..- attr(*, "term.labels")= chr [1:16] "var3" "var1" "var12isNA" "var4" ...
  .. ..- attr(*, "order")= int [1:16] 1 1 1 1 1 1 1 1 1 1 ...
  .. ..- attr(*, "intercept")= int 1
  .. ..- attr(*, "response")= int 1
  .. ..- attr(*, ".Environment")=<environment: 0x7f98f5685ce8> 
  .. ..- attr(*, "predvars")= language list(fire, var3, var1, var12isNA, var4, var11, var13, var6, dummy, var9, var16isNA, var10, var17, var8, var7, var15isNA, var14isNA)
  .. ..- attr(*, "dataClasses")= Named chr [1:17] "numeric" "factor" "factor" "logical" ...
  .. .. ..- attr(*, "names")= chr [1:17] "fire" "var3" "var1" "var12isNA" ...
 $ offset       : NULL
 $ control      :List of 3
  ..$ epsilon: num 1e-08
  ..$ maxit  : num 25
  ..$ trace  : logi FALSE
 $ method       : chr "glm.fit"
 $ contrasts    :List of 12
  ..$ var3     : chr "contr.treatment"
  ..$ var1     : chr "contr.treatment"
  ..$ var12isNA: chr "contr.treatment"
  ..$ var4     : chr "contr.treatment"
  ..$ var6     : chr "contr.treatment"
  ..$ dummy    : chr "contr.treatment"
  ..$ var9     : chr "contr.treatment"
  ..$ var16isNA: chr "contr.treatment"
  ..$ var8     : chr "contr.treatment"
  ..$ var7     : chr "contr.treatment"
  ..$ var15isNA: chr "contr.treatment"
  ..$ var14isNA: chr "contr.treatment"
 $ xlevels      :List of 8
  ..$ var3 : chr [1:7] "1" "2" "3" "4" ...
  ..$ var1 : chr [1:6] "1" "2" "3" "4" ...
  ..$ var4 : chr [1:15] "99" "A1" "C1" "D1" ...
  ..$ var6 : chr [1:4] "A" "B" "C" "Z"
  ..$ dummy: chr [1:2] "A" "B"
  ..$ var9 : chr [1:3] "A" "B" "Z"
  ..$ var8 : chr [1:7] "1" "2" "3" "4" ...
  ..$ var7 : chr [1:9] "1" "2" "3" "4" ...
 - attr(*, "class")= chr [1:2] "glm" "lm"

【问题讨论】:

您更改对象-fit,然后保存logitFire。为什么要 fit 的 mods 更改 logitFire 的大小? 嗨@BondedDust,请参阅上面的编辑。 如果没有更好地描述logitFire,例如object.size(logitFire)str(logitFire),我认为这个问题无法回答。 logitFire(或其元素)是否有一个重要的存储环境? 我想知道——就像 C 级别的东西?有什么方法可以让我从 R 终端检查出来吗? 【参考方案1】:

你是说save() 到磁盘后膨胀了 135 倍。以下是一些不查看您的数据的提示:

    object.size 只是对内存使用量的浅显衡量,它不跟随指针,因此不计算字符串和因子。因此,在某些情况下可能会严重低估。相反,使用出色的 lsos() memory-reporting function 并告诉我们它报告的内容。 您的环境是否包含大量大字符串(例如文本或基因组学),或带有标签的高基数因子,它们已转换为字符串?还是不必要的字符串行标签?检查您是否设置了 options('stringsAsFactors'=F)read.csv()。还要确保你从不明确使用dataframe(..., stringsAsFactors=T)。 (但从str() 的转储看来,情况并非如此) 不仅告诉我们object.size()lsos() 数字,还告诉我们创建这些 lr 对象之前和之后的 R 会话的总内存大小,在运行垃圾之前和之后 -集合(gc(),gc(reset=T))。然后在save() 之后,rm() logitFire 的内容),然后 logitFire 本身,报告 R 的总内存使用情况,进行垃圾收集,报告总内存使用情况以及大小缩小了多少。 检查您的环境中还有哪些其他变量/数据帧/数据表/文件对象/模型:执行 ls() 并使用 lsos()) 查看它们的大小(如@BenBolker 提示) 在纯 R 下测试它,而不是 RStudio!,RStudio 偶尔会导致内存爆炸或与 gc 混淆(例如,如果您不小心将数据引用保留在 View() 窗口中)。 检查您的./.Rprofile~/.Rprofile 是否有可能已经潜入的古怪设置,即使是看起来无害的设置,例如加载不必要的包或以不同的加载顺序(可能会影响内置函数)。 您是否从另一个干净的环境/虚拟机/同事的登录中重现了这种行为?如果没有,请执行。 确保您从一个干净的环境开始,不要导入 .RData,尤其是一个散布着大量大型临时文件的文件。使用R --no-restore --no-save,也在 RStudio 中禁用常规设置“恢复 RData”和“保存 RData”。删除您不小心留下的所有 .RData。 检查save(..., ascii = FALSE, envir = parent.frame(), compress = isTRUE(!ascii), compression_level "integer: the level of compression to be used. Defaults to 6 for gzip compression and to 9 for bzip2 or xz compression") 的默认行为的一些明显愚蠢的事情 检查您的默认设置是否有任何问题,正如@BenBolker 所说,检查您的环境中的内容(执行ls() 并查看以他们的尺寸)。 所有这些都失败了,可以随时尝试在您的 .RData 上手动 gzip,看看它有多臃肿。 除上述所有内容外,请尝试将您的命令和参数减少到可重现问题的最低限度。使用随机种子数据。理想情况下,我们可以找到一个测试用例 :) 或解决方案。

【讨论】:

以上是关于逻辑回归对象在 R save() 函数后消耗大量磁盘空间的主要内容,如果未能解决你的问题,请参考以下文章

R语言使用Metropolis- Hasting抽样算法进行逻辑回归

R语言编写自定义函数计算分类模型评估指标:准确度特异度敏感度PPVNPV数据数据为模型预测后的混淆矩阵比较多个分类模型分类性能(逻辑回归决策树随机森林支持向量机)

休眠,保存 1Millon 实体,消耗大量内存

R语言广义线性模型函数GLMR中有几种logistic回归扩展和变异robust包中的glmRob函数鲁棒logistic回归ms包中的lrm函数拟合序数逻辑回归

R语言使用glm函数构建logistic回归模型,使用forestmodel包的forest_model函数可视化逻辑回归模型对应的森林图

R语言使用glm函数构建logistic回归模型,使用forestmodel包的forest_model函数可视化逻辑回归模型对应的森林图