如何在不影响传递给predict()函数的情况下,最小化lm类对象的大小?

10
我想在一份包含2个预测变量和5000万行数据的大型数据集上运行`lm()`。这个分析是在只有10GB空间存储数据的远程服务器上运行的。我从数据中取出了一万行进行了测试,结果生成的对象大小为2GB+。 我仅需要`lm()`函数返回的"lm"类对象来生成模型的摘要统计信息(`summary(lm_object)`)和进行预测(`predict(lm_object)`)。 我曾尝试过使用`lm`函数的`model`、`x`、`y`和`qr`选项,将它们全部设置为`FALSE`后可以减少38%的模型大小。
library(MASS)
fit1=lm(medv~lstat,data=Boston)
size1 <- object.size(fit1)
print(size1, units = "Kb")
# 127.4 Kb bytes
fit2=lm(medv~lstat,data=Boston,model=F,x=F,y=F,qr=F)
size2 <- object.size(fit2)
print(size2, units = "Kb")
# 78.5 Kb Kb bytes
- ((as.integer(size1) - as.integer(size2)) / as.integer(size1)) * 100
# -38.37994

但是
summary(fit2)
# Error in qr.lm(object) : lm object does not have a proper 'qr' component.
#  Rank zero or should not have used lm(.., qr=FALSE).
predict(fit2,data=Boston)
# Error in qr.lm(object) : lm object does not have a proper 'qr' component.
#  Rank zero or should not have used lm(.., qr=FALSE).

显然,我需要保留qr=TRUE,这将使对象大小仅比默认对象减少9%。

fit3=lm(medv~lstat,data=Boston,model=F,x=F,y=F,qr=T)
size3 <- object.size(fit3)
print(size3, units = "Kb")
# 115.8 Kb
- ((as.integer(size1) - as.integer(size3)) / as.integer(size1)) * 100
# -9.142752

如何在不将大量不必要的信息存储在内存和存储器中的情况下将“lm”对象的大小最小化?


+1 有趣的问题。你还没有尝试过自己切换每个选项吗?顺便说一下,更安全的方法是写出TRUE和FALSE,因为你可能会忘记并稍后使用这些名称创建变量。 - Frank
2
我相信你可以在这里找到答案:https://dev59.com/_2Up5IYBdhLWcg3wdXaa#15260950 或者在相关问题中的其中一个链接中找到。 - mnel
1
我不明白如何使用仅10000个观测值的lm可能导致2GB的对象。你的数据集中有多少列? - Hong Ooi
@HongOoi 我在模型中使用了两个预测器。我认为数据集包括我没有建模的变量共有5列。 - CptNemo
1
一个10000x5的数据集不可能导致一个2GB的对象。我会检查一下,确保你没有意外地包含大环境。你是在另一个函数内调用lm,并操作了你的大数据集吗? - Hong Ooi
显示剩余2条评论
3个回答

5
这里的链接提供了一个相关的答案(针对glm对象,它与lm输出对象非常相似)。

http://www.win-vector.com/blog/2014/05/trimming-the-fat-from-glm-models-in-r/

基本上,预测只使用glm输出的非常小的系数部分。下面的函数(从链接中复制)修剪将不被预测使用的信息。
它确实有一个警告。在修剪后,它不能被summary(fit)或其他摘要函数使用,因为这些函数需要比预测所需更多的信息。
cleanModel1 = function(cm) {
  # just in case we forgot to set
  # y=FALSE and model=FALSE
  cm$y = c()
  cm$model = c()

  cm$residuals = c()
  cm$fitted.values = c()
  cm$effects = c()
  cm$qr$qr = c()
  cm$linear.predictors = c()
  cm$weights = c()
  cm$prior.weights = c()
  cm$data = c()
  cm
}

该文章声称对于小模型可实现99.7%的减少,在大模型中可达到99.985%的减少。在修剪模型之前,执行summary(fit2)并将其保存到文本文件中。 - smci
我最近进行了测试,发现输出对象中的元素或子元素可以进一步缩小,只需要尝试将它们中的每一个都清空,并使用结果对predict()进行测试,如果仍然有效就可以了。另一方面,对象中唯一有用的部分是fit$coefficients。在引导实践中,我重新拟合了模型1000次,并仅保存用于预测的系数,这比保存1000个glm结果对象节省了更多的内存。 - xappppp

1

xappp的回答很好,但并非全部内容。你还可以处理一个巨大的环境变量(请参见:https://blogs.oracle.com/R/entry/is_the_size_of_your

或者将此添加到xappp的函数中

     e <- attr(cm$terms, ".Environment")
     parent.env(e) <- emptyenv()
     rm(list=ls(envir=e), envir=e)

或者使用这个版本,它减少了更少的数据,但仍然允许您使用summary()。

      cleanModel1 = function(cm) {
      # just in case we forgot to set
      # y=FALSE and model=FALSE
      cm$y = c()
      cm$model = c()

      e <- attr(cm$terms, ".Environment")
      parent.env(e) <- emptyenv()
      rm(list=ls(envir=e), envir=e)
      cm
      }

1
只有在使用其他函数拟合模型时才应该使用此选项。在全局环境中使用普通的 lm 会开始删除搜索路径上的各种对象。 - Axeman

0

我也在尝试处理同样的问题。我使用的方法并不完美,但对于预测来说是有效的,你可以基本上取出 lm 中的 qr 插槽中的 qr 插槽:

lmFull <- lm(Volume~Girth+Height,data=trees)
lmSlim <- lmFull
lmSlim$fitted.values <- lmSlim$qr$qr <- lmSlim$residuals <- lmSlim$model <- lmSlim$effects <- NULL
pred1 <- predict(lmFull,newdata=data.frame(Girth=c(1,2,3),Height=c(2,3,4)))
pred2 <- predict(lmSlim,newdata=data.frame(Girth=c(1,2,3),Height=c(2,3,4)))
identical(pred1,pred2)
[1] TRUE

as.numeric((object.size(lmFull) - object.size(lmSlim)) / object.size(lmFull))
[1] 0.6550523

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