R H2O - 内存管理

7
我想通过R使用H2O来构建一个大型数据集(~10GB)的多个模型子集。这个数据集是一年的数据,我尝试着构建51个模型(即在第一周进行训练,在第二周进行预测等),每周有约1.5-2.5百万行和8个变量。
我已经在循环中完成了这个任务,但我知道这不是R中最好的方法之一。另一个问题是H2O实体会累积先前的对象,因此我创建了一个函数来删除除主数据集以外的所有对象。
h2o.clean <- function(clust = localH2O, verbose = TRUE, vte = c()){
  # Find all objects on server
  keysToKill <- h2o.ls(clust)$Key
  # Remove items to be excluded, if any
  keysToKill <- setdiff(keysToKill, vte)
  # Loop thru and remove items to be removed
  for(i in keysToKill){
    h2o.rm(object = clust, keys = i)

    if(verbose == TRUE){
      print(i);flush.console()

    }    
  }
  # Print remaining objects in cluster.
  h2o.ls(clust)
}

脚本运行一段时间后就会崩溃,经常会出现内存不足并开始交换到磁盘的错误。以下是伪代码描述该过程。
# load h2o library
library(h2o)
# create h2o entity
localH2O = h2o.init(nthreads = 4, max_mem_size = "6g")
# load data
dat1.hex = h2o.importFile(localH2O, inFile, key = "dat1.hex")

# Start loop
for(i in 1:51){
# create test/train hex objects
train1.hex <- dat1.hex[dat1.hex$week_num == i,]
test1.hex <- dat1.hex[dat1.hex$week_num == i + 1,]
# train gbm
dat1.gbm <- h2o.gbm(y = 'click_target2', x = xVars, data = train1.hex
                      , nfolds = 3
                      , importance = T
                      , distribution = 'bernoulli' 
                      , n.trees = 100
                      , interaction.depth = 10,
                      , shrinkage = 0.01
  )
# calculate out of sample performance
test2.hex <- cbind.H2OParsedData(test1.hex,h2o.predict(dat1.gbm, test1.hex))
colnames(test2.hex) <- names(head(test2.hex))
gbmAuc <- h2o.performance(test2.hex$X1, test2.hex$click_target2)@model$auc

# clean h2o entity
h2o.clean(clust = localH2O, verbose = F, vte = c('dat1.hex'))

} # end loop

我的问题是,在一个独立实体(这不是在hadoop或群集上运行-只是一个大型EC2实例(~ 64gb RAM + 12个CPU))中管理数据和内存的正确方法是什么? 在每次循环后杀死并重新创建H2O实体(这是原始流程,但每次从文件读取数据需要增加约10分钟的迭代时间)? 是否有适当的方式在每次循环后进行垃圾回收或释放内存?如有建议,请告知。

你可以通过以下代码删除任何你想要的内容:h2o.rm(localH2O, "keyDataWhichIWantDelete") - Fedorenko Kristina
4个回答

9

这个答案是针对原始的H2O项目(版本2.x.y.z)。

在原始的H2O项目中,H2O R包在H2O集群DKV(分布式键/值存储)中创建许多临时H2O对象,这些对象都以“Last.value”前缀命名。

这些对象可以通过Web UI中的Store View或通过从R调用h2o.ls()来查看。

我建议做以下事情:

  • 在每次循环迭代的底部,使用h2o.assign()将任何想要保存到已知键名的内容进行深度复制
  • 使用h2o.rm()删除您不想保留的任何内容,特别是“Last.value”临时对象
  • 在循环中的某个地方显式调用R中的gc()

这是一个为您删除Last.value临时对象的函数。将H2O连接对象作为参数传递:

removeLastValues <- function(conn) {
    df <- h2o.ls(conn)
    keys_to_remove <- grep("^Last\\.value\\.", perl=TRUE, x=df$Key, value=TRUE)
    unique_keys_to_remove = unique(keys_to_remove)
    if (length(unique_keys_to_remove) > 0) {
        h2o.rm(conn, unique_keys_to_remove)
    }
}

这里是一个链接,指向H2O的Github存储库中的R测试,使用了这种技术,可以无限运行而不会耗尽内存:

https://github.com/h2oai/h2o/blob/master/R/tests/testdir_misc/runit_looping_slice_quantile.R


4
2015年12月15日后的新建议:更新到最新的稳定版本(Tibshirani 3.6.0.8或更高版本)。我们已经完全重构了R和H2O处理内部临时变量的方式,内存管理更加流畅。
接下来:H2O温度可以被R死变量保持“活着”...所以每次循环迭代运行R gc()函数。一旦R的GC删除了死变量,H2O将会释放该内存。
之后,您的群集应该仅保留特定名称的东西,例如已加载的数据集和模型。您需要尽可能快地删除它们,以避免在K/V商店中积累大量数据。
如果您有任何问题,请通过发布到Google组h2o stream让我们知道: https://groups.google.com/forum/#!forum/h2ostream Cliff

Python怎么样? - user90772
1
这只是循环中h20临时对象的问题吗?如果我调用一个函数并创建临时的h20对象,R会保留函数作用域外的死变量吗? - Meep

2

最新的答案是,你应该使用h2o.grid()函数而不是编写循环。


0

使用 H2O 的新版本(当前为 3.24.0.3),建议按照以下建议进行操作:

my for loop {
 # perform loop

 rm(R object that isn’t needed anymore)
 rm(R object of h2o thing that isn’t needed anymore)

 # trigger removal of h2o back-end objects that got rm’d above, since the rm can be lazy.
 gc()
 # optional extra one to be paranoid.  this is usually very fast.
 gc()

 # optionally sanity check that you see only what you expect to see here, and not more.
 h2o.ls()

 # tell back-end cluster nodes to do three back-to-back JVM full GCs.
 h2o:::.h2o.garbageCollect()
 h2o:::.h2o.garbageCollect()
 h2o:::.h2o.garbageCollect()
}

这里是源代码:http://docs.h2o.ai/h2o/latest-stable/h2o-docs/faq/general-troubleshooting.html


是的,这是我几年前为h2o-3开发的方法。然后可以将Java GC日志输出发送到gceasy.io进行可视化。 - TomKraljevic

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