使用 %dopar% 时如何打印

Posted

技术标签:

【中文标题】使用 %dopar% 时如何打印【英文标题】:How can I print when using %dopar% 【发布时间】:2012-06-09 20:28:42 【问题描述】:

我有一个foreach 循环,它使用%dopar%doSNOW 作为后端。我怎样才能让循环在每次迭代时打印出一些东西?

我下面的代码是我目前正在使用的,但它没有打印任何东西。

foreach(ntree=rep(25,2),.combine=combine,.packages='randomForest',
    .inorder=FALSE) %dopar% 
        print("RANDOM FOREST")
        randomForest(classForm,data=data,na.action=na.action,do.trace=do.trace,ntree=ntree,mtry=mtry)
       

【问题讨论】:

啊,但它正在打印,只是不在主节点上...... 哦,好的。在那种情况下,有没有办法可以查看它的打印内容或让它打印到主节点? 我什么都不知道,也不知道怎么办。 是否可以编写一个打印到控制台的迭代器函数?迭代器应该由master运行,不是吗? @NoamRoss 是的,迭代器只在主服务器上运行,所以它可以写入控制台。这将允许您监控发送给工作人员的任务,而不是工作人员实际执行任务的时间。 【参考方案1】:

cat("blah-blah-blah\n", file=stdout()) 倾向于为我工作 (linux/emacs/ess)。我想它也适用于其他一些平台。

【讨论】:

奇怪的是,如果您跟踪您在 makeCluster 上指定的 outfile,则会记录此日志。这让我想知道 print 的作用与此不同。 我在 rstudio 控制台中没有看到任何打印输出,但它可能会打印到 makeCluster 中设置的输出文件中【参考方案2】:

默认情况下,雪工人产生的输出会被丢弃,但您可以使用 makeCluster “outfile” 选项来更改它。将 outfile 设置为空字符串 ("") 将防止 snow 重定向输出,这通常会导致您的打印消息的输出显示在主进程的终端上。

只需使用以下内容创建和注册您的集群:

library(doSNOW)
cl <- makeCluster(4, outfile="")
registerDoSNOW(cl)

您的 foreach 循环根本不需要更改。

这适用于我的 SOCK 集群和 MPI 集群,使用 Rmpi​​ 构建 Open MPI。在 Windows 上,如果您使用 Rgui,您将看不到任何输出。如果你改用 Rterm.exe,你会的。

请注意,除了您自己的输出之外,您还会看到由 snow 生成的消息,这些消息也很有用。


要使用进度条,doSNOW 1.0.14 版有一个progress 选项。这是一个完整的例子:

library(doSNOW)
library(tcltk)
library(randomForest)
cl <- makeSOCKcluster(3)
registerDoSNOW(cl)

ntasks <- 100
pb <- tkProgressBar(max=ntasks)
progress <- function(n) setTkProgressBar(pb, n)
opts <- list(progress=progress)

x <- matrix(runif(500), 100)
y <- gl(2, 50)

rf <- foreach(ntree=rep(25, ntasks), .combine=combine,
        .multicombine=TRUE, .packages='randomForest',
        .options.snow=opts) %dopar% 
  randomForest(x, y, ntree=ntree)

progress 选项相当通用,因此您可以使用以下函数简单地打印一条消息:

progress <- function(n) cat(sprintf("task %d is complete\n", n))

该函数可以采用 0、1 或 2 个参数。提供的第一个参数是已完成任务的总数,第二个是刚刚完成的任务的任务号。

最简单的示例只是在任务完成时打印.

progress <- function() cat('.')

此示例显示两个参数,可用于证明任务并不总是按顺序完成:

progress <- function(nfin, tag) 
  cat(sprintf('tasks completed: %d; tag: %d\n', nfin, tag))

【讨论】:

是否有等效的解决方案可以使用doMC 将打印消息的输出定向到控制台? @MattSM 使用 doMC,使用标准 R 时,worker 的输出默认显示在控制台上。使用 RStudio 时可能会遇到问题,但我认为使用时不建议使用 doMC RStudio 由于分叉问题。另外,由于并行包的限制,doMC 不支持进度条。 @SteveWeston 我喜欢进度条解决方案。但有时我想在每次迭代中查看结果的 sn-ps。有什么方法可以做相当于 cat("result at iter i") 的方法吗? @horaceT 如果您正在使用一个并行后端来动态调用 combine 函数(例如 doSNOW 或 doMPI),那么您可以轻松地显示来自 combine 函数的结果 sn-ps。但请记住,除非您减少 .maxcombine 的值,否则只会对每 100 个结果调用 combine 函数。 @SteveWeston 第一次发生在我身上:当我用谷歌搜索 .options.snow 时,我绝对看不到任何文档,除了你的这个 SO 线程。你能解释一下引擎盖下发生了什么吗? progress 是从哪里得到n 的?【参考方案3】:

另一种方法是使用文件日志记录(例如,log4r 包)并在屏幕上单独打印输出(例如,通过'tail -f')。

如果您仍然考虑创建日志,这很有效,并且您可以使用现有的包以及所有相关的花里胡哨。

【讨论】:

知道对性能有什么影响吗? IE。文件 IO 中的额外处理时间。 没有。但是你可以测试一下。我只将它用于相对较慢的任务。【参考方案4】:

这里发布了许多好的解决方案,但我发现最简单的方法是登录到套接字并使用单独的进程在控制台中输出日志调用。

我使用以下函数:

log.socket <- make.socket(port=4000)

Log <- function(text, ...) 
  msg <- sprintf(paste0(as.character(Sys.time()), ": ", text, "\n"), ...)
  cat(msg)
  write.socket(log.socket, msg)

然后您可以在代码中放置日志语句,例如:

Log("Processing block %d of %d", i, n.blocks)

可以使用任何简单的套接字侦听工具实时查看日志输出。例如,在 Linux 上使用 netcat:

nc -l 4000

上面的日志语句会在netcat终端显示为:

2014-06-25 12:30:45: Processing block 2 of 13

此方法具有远程工作的优势,并提供您希望记录的详细输出。

p.s. 对于 Windows 用户,请参阅 Jon Craton's netcat port。

p.p.s 我猜write.socket R 函数可能不是线程安全的,但除非您以高频率登录,否则您不太可能遇到任何问题。不过有一点需要注意。

【讨论】:

需要在linux终端中运行nc -l 4000R中执行log.sock = make.socket(port=4000) 好的,但是当你有10个不同的过程时,你怎么能正确地记录它们呢?为每个R进程建立一个nc服务器和R套接字?或者只是让不同进程中的R套接字连接到同一个nc服务器?【参考方案5】:

我在长时间操作期间跟踪节点进度的一种方法是使用 tcltk 包中的 tkProgressBar 创建进度条。这不是您所要求的,但它应该让您从节点中看到一些东西。至少当集群是在本地主机(这是一台 Windows 机器)上运行的套接字集群时是这样。潜在的问题是进度条要么保留并弄乱你的显示器,要么得到closed 并且打印的信息消失了。不过对我来说,这不是问题,因为我只是想知道当前的状态。

library(parallel)
library(doSNOW)
cl<-makeCluster(detectCores(),type="SOCK")
registerDoSNOW(cl)

使用您的代码,

foreach(ntree=rep(25,2),.combine=combine,.packages=c('randomForest','tcltk'),
    .inorder=FALSE) %dopar% 
        mypb <- tkProgressBar(title = "R progress bar", label = "",
          min = 0, max = 1, initial = 0, width = 300)
        setTkProgressBar(mypb, 1, title = "RANDOM FOREST", label = NULL)
    ans <- randomForest(classForm,data=data,na.action=na.action,do.trace=do.trace,ntree=ntree,mtry=mtry)
    close(mypb)
    ans
    

这是一个更通用的使用示例:

jSeq <- seq_len(30)

foreach(i = seq_len(2), .packages = c('tcltk', 'foreach')) %dopar% 
    mypb <- tkProgressBar(title = "R progress bar", label = "",
        min = 0, max = max(jSeq), initial = 0, width = 300)
    foreach(j = jSeq) %do% 
        Sys.sleep(.1)
        setTkProgressBar(mypb, j, title = "RANDOM FOREST", label = NULL)
    
    NULL

【讨论】:

嗨,我尝试使用进度条,但无法使用并行 for 循环使其工作。每次我运行它时,我都会收到“XIO:致命的 IO 错误”。你怎么解决这个问题? @BenBarnes @TAllieri,请查看更新后的示例(原始示例不是很能说明问题)。如果您仍有问题,请提供更多信息并考虑提出新问题。【参考方案6】:

我也有同样的问题。我正在使用 foreach 包调整随机森林的参数,并希望在每次迭代后打印一个“结果”行,但如果不显示进度条等就无法弄清楚。

这就是我所做的, 在我的功能中, 我添加了这一行

write.table(result, file=paste("RF_ntree_",ntree,"_dims_",dims,".txt", sep=""),
  sep="\t", row.names=F)

因此,每次迭代后,结果都会写入一个名称为 RF_ntree_250_dims_100.txt 的文本文件。

因此,如果我想跟踪进度,我只需刷新要写入文本文件的文件夹即可。

PS:结果也在数据框中累积。

【讨论】:

以上是关于使用 %dopar% 时如何打印的主要内容,如果未能解决你的问题,请参考以下文章

如何以编程方式在 foreach 中的 %do% 和 %dopar% 之间切换?

R:如何在 foreach %dopar% 中拆分数据帧

使用 foreach 进行并行处理时出错:“找不到函数“%dopar%””

R:在 foreach %dopar% 中显示错误和警告消息

保存 foreach dopar 循环的多个输出

foreach %dopar% 使用带有 PSock 集群的顺序工作程序设置?