77

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

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

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)
    }   
4

6 回答 6

63

默认情况下,雪工产生的输出会被丢弃,但您可以使用 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 个参数。第一个提供的参数是已完成任务的总数,第二个是刚刚完成的任务的任务号。

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

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

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

progress <- function(nfin, tag) {
  cat(sprintf('tasks completed: %d; tag: %d\n', nfin, tag))
}
于 2013-02-25T23:28:46.837 回答
28

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

我使用以下功能:

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

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

ps对于 Windows 上的用户,请参阅Jon Craton 的 netcat 端口

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

于 2014-06-25T12:18:11.050 回答
12

我在长时间操作期间跟踪节点进度的一种方法是使用包创建进度tkProgressBartcltk。这不是您所要求的,但它应该让您从节点中看到一些东西。至少当集群是在本地主机(这是一台 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
}
于 2012-06-06T14:31:31.897 回答
11

我也有同样的问题。我正在使用 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:结果也在数据框中累积。

于 2012-07-07T18:26:37.010 回答
2

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

于 2018-02-09T02:38:29.773 回答
0

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

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

于 2017-05-26T18:57:15.753 回答