当矩阵乘法对系数工作正常时,为什么lm会耗尽内存?

时间:2012-04-26 03:33:27

标签: r memory regression linear-regression lm

我正在尝试使用R进行固定效果线性回归。我的数据看起来像

dte   yr   id   v1   v2
  .    .    .    .    .
  .    .    .    .    .
  .    .    .    .    .

然后我决定通过将yr作为一个因素并使用lm来执行此操作:

lm(v1 ~ factor(yr) + v2 - 1, data = df)

然而,这似乎耗尽了内存。我的因子中有20个级别,而df是1400万行,大约需要2GB才能存储,我在一台专用于此过程的22 GB机器上运行它。

然后我决定以老式的方式尝试:通过执行以下操作,为{JER} t1创建虚拟变量:{/ p>

t20

并简单地计算:

df$t1 <- 1*(df$yr==1)
df$t2 <- 1*(df$yr==2)
df$t3 <- 1*(df$yr==3)
...

这没有问题,几乎立即产生了答案。

我特别好奇当我能够很好地计算系数时,lm会使内存耗尽吗?感谢。

5 个答案:

答案 0 :(得分:10)

除了idris所说的,还值得指出的是lm()并没有像你在问题中所说明的那样使用正规方程求解参数,而是使用QR分解,效率较低但往往会产生更准确的解决方案。

答案 1 :(得分:9)

您可能需要考虑使用biglm包。它通过使用较小的数据块来适应lm模型。

答案 2 :(得分:8)

到目前为止,答案中没有一个指向正确的方向。

@idr接受的答案正在lmsummary.lm之间产生混淆。 lm根本不计算诊断统计信息;相反,summary.lm会这样做。所以他在谈论summary.lm

@Jake的答案是关于QR分解和LU / Choleksy分解的数值稳定性的事实。 Aravindakshan的答案扩展了这一点,指出了两个操作背后的浮点运算量(尽管如他所说,他没有计算计算矩阵交叉产品的成本)。但是,不要将FLOP计数与内存成本混淆。实际上,这两种方法在LINPACK / LAPACK中具有相同的内存使用量。具体来说,他认为QR方法需要花费更多RAM来存储Q因子的论点是假的。 lm(): What is qraux returned by QR decomposition in LINPACK / LAPACK中解释的压缩存储阐明了如何计算和存储QR分解。 QR v.s.的速度问题Chol在我的回答中有详细说明:Why the built-in lm function is so slow in R?,我对faster lm的回答使用Choleksy方法提供了一个小例程lm.chol,比QR方法快3倍。

@Gregbiglm的回答/建议很好,但它没有回答这个问题。由于提及biglm,我会指出QR decomposition differs in lm and biglmbiglm计算住户反映,以便生成的R因子具有正对角线。有关详细信息,请参阅Cholesky factor via QR factorizationbiglm执行此操作的原因是,生成的R与Cholesky因子相同,请参阅QR decomposition and Choleski decomposition in R以获取相关信息。此外,除了biglm之外,您还可以使用mgcv。请阅读我的回答:biglm predict unable to allocate a vector of size xx.x MB了解更多信息。

摘要后,是时候发布我的回答了

为了适合线性模型,lm

  1. 生成模型框架;
  2. 生成模型矩阵;
  3. 调用lm.fit进行QR分解;
  4. 返回QR分解的结果以及lmObject中的模型框架。
  5. 您说您的5列输入数据帧需要2 GB才能存储。使用20个因子级别,得到的模型矩阵有大约25列,占用10 GB存储空间。现在让我们看看当我们调用lm时内存使用情况如何增长。

    • [全局环境] 最初,您有2 GB的数据存储空间;
    • [lm envrionment] 然后将其复制到模型框架,耗资2 GB;
    • [lm environment] 然后生成一个模型矩阵,耗资10 GB;
    • [lm.fit environment] 制作模型矩阵的副本,然后通过QR分解覆盖,耗资10 GB;
    • [lm环境] 返回lm.fit的结果,耗资10 GB;
    • [全球环境] lm.fit的结果将由lm进一步返回,耗资10 GB;
    • [全局环境] 模型框架由lm返回,耗资2 GB。

    因此,总共需要46 GB RAM,远远大于可用的22 GB RAM。

    实际上如果lm.fit可以“内联”到lm,我们可以节省20 GB的费用。但是没有办法在另一个R函数中内联R函数。

    也许我们可以举一个小例子来了解lm.fit周围发生的事情:

    X <- matrix(rnorm(30), 10, 3)    # a `10 * 3` model matrix
    y <- rnorm(10)    ## response vector
    
    tracemem(X)
    # [1] "<0xa5e5ed0>"
    
    qrfit <- lm.fit(X, y)
    # tracemem[0xa5e5ed0 -> 0xa1fba88]: lm.fit 
    

    确实,X在传递到lm.fit时会被复制。我们来看看qrfit有什么

    str(qrfit)
    #List of 8
    # $ coefficients : Named num [1:3] 0.164 0.716 -0.912
    #  ..- attr(*, "names")= chr [1:3] "x1" "x2" "x3"
    # $ residuals    : num [1:10] 0.4 -0.251 0.8 -0.966 -0.186 ...
    # $ effects      : Named num [1:10] -1.172 0.169 1.421 -1.307 -0.432 ...
    #  ..- attr(*, "names")= chr [1:10] "x1" "x2" "x3" "" ...
    # $ rank         : int 3
    # $ fitted.values: num [1:10] -0.466 -0.449 -0.262 -1.236 0.578 ...
    # $ assign       : NULL
    # $ qr           :List of 5
    #  ..$ qr   : num [1:10, 1:3] -1.838 -0.23 0.204 -0.199 0.647 ...
    #  ..$ qraux: num [1:3] 1.13 1.12 1.4
    #  ..$ pivot: int [1:3] 1 2 3
    #  ..$ tol  : num 1e-07
    #  ..$ rank : int 3
    #  ..- attr(*, "class")= chr "qr"
    # $ df.residual  : int 7
    

    请注意,紧凑QR矩阵qrfit$qr$qr与模型矩阵X一样大。它是在lm.fit内创建的,但在lm.fit退出时会被复制。总的来说,我们将有3个“副本”X

    • 全球环境中的原始文件;
    • 复制到lm.fit的那个,被QR分解覆盖;
    • lm.fit返回的那个。

    在您的情况下,X为10 GB,因此仅与lm.fit相关联的内存成本已为30 GB。更不用说与lm相关的其他费用。

    另一方面,我们来看看

    solve(crossprod(X), crossprod(X,y))
    

    X需要10 GB,但crossprod(X)只是一个25 * 25矩阵,crossprod(X,y)只是一个长度为25的向量。与X相比,它们非常小,因此内存使用量根本没有增加。

    也许您担心在调用X时会制作crossprod的本地副本?一点也不!与对lm.fit执行读写操作的X不同,crossprod只读取X,因此不会进行复制。我们可以使用我们的玩具矩阵X通过以下方式验证这一点:

    tracemem(X)
    crossprod(X)
    

    您将看不到复制讯息!

    如果您想要上述所有内容的简短摘要,请点击此处:

    • lm.fit(X, y)(甚至.lm.fit(X, y))的内存成本是solve(crossprod(X), crossprod(X,y))的内存成本的三倍;
    • 根据模型矩阵的大小比模型框架大,lm的内存成本是solve(crossprod(X), crossprod(X,y))的3到6倍。从未达到下限3,而当模型矩阵与模型框架相同时,达到上限6。如果没有因子变量或“因子相似”术语,例如bs()poly()等,就会出现这种情况。

答案 3 :(得分:7)

lm不仅可以找到输入要素的系数。例如,它提供诊断统计信息,告诉您有关自变量系数的更多信息,包括每个自变量的标准误差和t值。

我认为,在运行回归分析以了解回归的有效性时,了解这些诊断统计信息非常重要。

这些额外的计算导致lm比简单地解决回归的矩阵方程慢。

例如,使用mtcars数据集:

>data(mtcars)
>lm_cars <- lm(mpg~., data=mtcars)
>summary(lm_cars)

Call:                                                         
lm(formula = mpg ~ ., data = mtcars)                          

Residuals:                                                    
    Min      1Q  Median      3Q     Max                       
-3.4506 -1.6044 -0.1196  1.2193  4.6271                       

Coefficients:                                                 
            Estimate Std. Error t value Pr(>|t|)              
(Intercept) 12.30337   18.71788   0.657   0.5181              
cyl         -0.11144    1.04502  -0.107   0.9161              
disp         0.01334    0.01786   0.747   0.4635              
hp          -0.02148    0.02177  -0.987   0.3350              
drat         0.78711    1.63537   0.481   0.6353              
wt          -3.71530    1.89441  -1.961   0.0633 .            
qsec         0.82104    0.73084   1.123   0.2739              
vs           0.31776    2.10451   0.151   0.8814              
am           2.52023    2.05665   1.225   0.2340              
gear         0.65541    1.49326   0.439   0.6652              
carb        -0.19942    0.82875  -0.241   0.8122              
---                                                           
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.65 on 21 degrees of freedom        
Multiple R-squared: 0.869,      Adjusted R-squared: 0.8066    
F-statistic: 13.93 on 10 and 21 DF,  p-value: 3.793e-07       

答案 4 :(得分:5)

详细阐述杰克的观点。假设您的回归试图解决:y = Ax(A是设计矩阵)。 m个观测值和n个独立变量A是mxn矩阵。那么QR的成本是〜m*n^2。在你的情况下,它看起来像m = 14x10 ^ 6和n = 20。因此m*n^2 = 14*10^6*400是一项重大成本。

然而,使用正规方程式,您试图反转A'A('表示转置),它是方形且大小为nxn。解决方案通常使用LU进行,费用为n^3 = 8000。这远小于QR的计算成本。 当然这不包括矩阵乘法的成本。

此外,如果QR例程尝试存储大小为mxm=14^2*10^12(!)的Q矩阵,那么您的记忆力将不足。可以将QR写入没有这个问题。了解实际使用的QR版本会很有趣。为什么lm调用会耗尽内存。