当 运行 一个 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 中删除并应受到保护的任何对象。
我们将不胜感激您的建议。
考虑以下选项:
预先分配任何需要的对象,而不是用值迭代扩展它,这需要机器使用内存重新分配 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)
使用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()
最后,对于不需要 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")
我想检测数据集中的交互效应,我编写了代码来创建所有可能的预测变量组合,分别用每一对拟合 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 中删除并应受到保护的任何对象。
我们将不胜感激您的建议。
考虑以下选项:
预先分配任何需要的对象,而不是用值迭代扩展它,这需要机器使用内存重新分配 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)
使用
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()
最后,对于不需要 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")