当 运行 一个 for 循环多次执行 glm 函数时,RAM 需求呈指数增长

RAM demands increase exponentially when running a for-loop executing the glm function multiple times

我想检测数据集中的交互效应,我编写了代码来创建所有可能的预测变量组合,分别用每一对拟合 gml 模型,并存储模型和模型本身的重要统计数据。

我的数据集由 100,000 多个观察值组成,我想检查 200,000 多个可能的对组合。

代码运行没有错误,但问题是在第 2,000 次迭代后,我 PC 的 60 GB RAM 已被填满(当我开始 运行 代码时,有 58 GB 的可用 RAM)

对于可重现的示例,我将使用 mtcars 数据集:

data(mtcars)
setDT(mtcars)

predictor_names <- setdiff(names(mtcars) , "am")

combinations <- combn(length(predictor_names) , 2)
combinations <- t(combinations)
combinations <- as.data.frame(combinations)

models_glm <- list()

Coefficients_dt <- data.table(Predictor_1 = character() , Predictor_2 = character(), dev_ratio = numeric() , 
                              Estimate = numeric(), p.value = numeric())

system.time(

  for (i in (1 : (nrow(combinations) - 1 ))) {

    # Extracts the index positions of the names of the pairs
    #----------------------------------------------------------

    m <- combinations[i, 1]

    k <- combinations[i, 2]

    # Extracts the names of the predictors from a vector that holds them
    #------------------------------------------------------------------------

    m_name <- predictor_names[m]

    k_name <- predictor_names[k]

    # Uses the names of the predictors to construct a formula
    #------------------------------------------------------------------

    formula_glm <- paste0( "am ~ " , m_name, " * " , k_name)

    formula_glm <- as.formula(formula_glm )

    # Passes the formula to a glm model
    #-------------------------------------------------------------------

    model <- glm(formula_glm , mtcars, family = "binomial")

    # Stores the model to a list
    #-------------------------------------------------------------------

    models_glm [[ paste0(m_name , "_*_" , k_name)]] <- model

    # Calculates the dev.ratio
    #---------------------------------------------------------------

    residual.deviance <- model$deviance
    null.deviance <- model$null.deviance  
    dev.ratio <- (null.deviance - residual.deviance) / null.deviance

    # Extracts the Coefficient estimate and p-value from the model
    #-------------------------------------------------------------------

    Coefficients_df <- as.data.frame(summary(model)$coefficients)

    names(Coefficients_df) <- c("Estimate" , "SE" , "Z", "p.value")

    if(dim(Coefficients_df)[1] == 4){

      Coefficients_dt <- rbind(Coefficients_dt , data.table(

        Predictor_1 = m_name , 

        Predictor_2 = k_name, 

        dev_ratio = dev.ratio,

        Estimate =  Coefficients_df$Estimate[4] , 

        p.value = Coefficients_df$p.value[4] 


      ))

    }


  }

)

我该怎么做才能克服这个问题?

即我想了解问题的根本原因:RAM 中的 space 是什么?与可用 RAM 相比,所涉及的对象不是很大。具体来说 Coefficients_dt data.table 最多会变成 200,000 行 x 5 列。

随着 for 循环中迭代的增加,其他事情正在发生并消耗越来越多的 RAM。

接下来我想了解在执行 for 循环的过程中是否可以采取一些行动 -- 例如命令嵌套在 for 循环内的 if 语句中——这将释放 RAM space,同时可能保存将从 RAM 中删除并应受到保护的任何对象。

我们将不胜感激您的建议。

考虑以下选项:

  1. 预先分配任何需要的对象,而不是用值迭代扩展它,这需要机器使用内存重新分配 space,您可以将值分配给现有元素:

    models_glm <- vector(mode = "list", length = 45)
    

    事实上,甚至考虑事先命名元素:

    pnames <- sapply(1:nrow(combinations)-1, function(i){
         paste0(predictor_names[combinations[i,1]], "_*_", 
                predictor_names[combinations[i,2]])
    })
    
    models_glm <- setNames(vector(mode="list", length=45), pnames)
    
  2. 使用data.table::rbindlist()在一次调用中将数据表列表行绑定到一个大数据帧中,而不是在循环中逐行扩展数据帧。下面使用 lapply 返回一个等于输入长度的对象。另外,注意空数据表以避免 NULL returns,遗漏 rbindlist:

    dTList <- lapply(seq(nrow(combinations)-1), function(i) {
    
            #... same as above
    
            # use <<- operator to update environment object outside function
            models_glm[[paste0(m_name, "_*_", k_name)]] <<- model
    
            #...
            Coefficients_df <- setNames(as.data.frame(summary(model)$coefficients), 
                                        c("Estimate", "SE", "Z", "p.value"))
    
            if(dim(Coefficients_df)[1] == 4){
                  data.table(
                      Predictor_1 = m_name , 
                      Predictor_2 = k_name, 
                      dev_ratio = dev.ratio,
                      Estimate =  Coefficients_df$Estimate[4], 
                      p.value = Coefficients_df$p.value[4] 
                  )
            } else {
                  # RETURN EMPTY DT
                  data.table(
                      Predictor_1 = character(), 
                      Predictor_2 = character(), 
                      dev_ratio = numeric(),
                      Estimate =  numeric(), 
                      p.value = numeric()
                  )
            }
    })
    
    coefficients <- data.table::rbindlist(dTlist)
    rm(dTlist)
    gc()
    
  3. 最后,对于不需要 design/programming 工作的大型操作,请考虑在 RStudio 或 Rgui 上使用自动化 Rscript.exe,因为这些后续程序需要额外的资源。下面是可以从 PowerShell、CMD 提示符或批处理 (.bat) 文件 运行 假设 Rscript 是环境 PATH 变量的命令行:

    Rscript "C:\Path\To\ModelCoefficientDataBuild.R"
    

    具体来说,RStudio 在 Windows 上的 rsession.exe 往往不会在获得内存后将内存释放回 OS,直到会话结束。参见 RStudio forum posts on subject。当然一定要将你需要的对象保存到磁盘以备后用:

    saveRDS(coefficients, "coefficients_datatable.rds")