逻辑回归对象在 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数据数据为模型预测后的混淆矩阵比较多个分类模型分类性能(逻辑回归决策树随机森林支持向量机)
R语言广义线性模型函数GLMR中有几种logistic回归扩展和变异robust包中的glmRob函数鲁棒logistic回归ms包中的lrm函数拟合序数逻辑回归
R语言使用glm函数构建logistic回归模型,使用forestmodel包的forest_model函数可视化逻辑回归模型对应的森林图
R语言使用glm函数构建logistic回归模型,使用forestmodel包的forest_model函数可视化逻辑回归模型对应的森林图