【发布时间】:2017-02-17 23:12:32
【问题描述】:
我正在 R 中进行优化。我的问题涉及在循环大量数据列表的目标函数上运行 nlm。我想通过并行运行目标函数来加速优化。我该怎么做呢?
在下面的示例中,我设置了一个玩具问题,其中并行解决方案比原始解决方案慢。如何修改代码以减少开销并加快我的nlm 调用的并行化版本?
library(parallel)
## What is the right way to do optimization when the objective function is run in parallel?
## Don't want very_big_list to be copied more than necessary
set.seed(952)
my_objfn <- function(list_element, parameter) {
return(sum((list_element - parameter) ^ 2)) # Simple example
}
apply_my_objfn_in_parallel <- function(parameter, very_big_list, max_cores=3) {
cluster <- makeCluster(min(max_cores, detectCores() - 1))
objfn_values <- parLapply(cluster, very_big_list, my_objfn, parameter=parameter)
stopCluster(cluster)
return(Reduce("+", objfn_values))
}
apply_my_objfn <- function(parameter, very_big_list) {
objfn_values <- lapply(very_big_list, my_objfn, parameter=parameter)
return(Reduce("+", objfn_values))
}
my_big_list <- replicate(2 * 10^6, sample(seq_len(100), size=5), simplify=FALSE)
parameter_guess <- 20
mean(c(my_big_list, recursive=TRUE)) # Should be close to 50
system.time(test_parallel <- nlm(apply_my_objfn_in_parallel, parameter_guess,
very_big_list=my_big_list, print.level=0)) # 84.2 elapsed
system.time(test_regular <- nlm(apply_my_objfn, parameter_guess,
very_big_list=my_big_list, print.level=0)) # 63.6 elapsed
我在笔记本电脑上运行了它(4 个 CPU,所以makeCluster(min(max_cores, detectCores() - 1)) 返回的集群有 3 个内核)。在上面的最后几行中,apply_my_objfn_in_parallel 比apply_my_objfn 花费的时间更长。我认为这是因为(1)我只有 3 个内核,并且(2)每次nlm 调用并行化目标函数时,它都会建立一个新集群并分解并复制所有my_big_list。这似乎很浪费——如果我以某种方式设置集群并在每次nlm 调用时只复制一次列表,我会得到更好的结果吗?如果是这样,我该怎么做?
在 Erwin 回答后进行编辑(“考虑创建和停止集群一次,而不是在每次评估中”):
## Modify function to use single cluster per nlm call
apply_my_objfn_in_parallel_single_cluster <- function(parameter, very_big_list, my_cluster) {
objfn_values <- parLapply(my_cluster, very_big_list, my_objfn, parameter=parameter)
return(Reduce("+", objfn_values))
}
run_nlm_single_cluster <- function(very_big_list, parameter_guess, max_cores=3) {
cluster <- makeCluster(min(max_cores, detectCores() - 1))
nlm_result <- nlm(apply_my_objfn_in_parallel_single_cluster, parameter_guess,
very_big_list=very_big_list, my_cluster=cluster, print.level=0)
stopCluster(cluster)
return(nlm_result)
}
system.time(test_parallel <- nlm(apply_my_objfn_in_parallel, parameter_guess,
very_big_list=my_big_list, print.level=0)) # 49.0 elapsed
system.time(test_regular <- nlm(apply_my_objfn, parameter_guess,
very_big_list=my_big_list, print.level=0)) # 36.8 elapsed
system.time(test_single_cluster <- run_nlm_single_cluster(my_big_list,
parameter_guess)) # 38.4 elapsed
除了我的笔记本电脑(上面以 cmets 表示的经过时间)之外,我还在一台 30 核的服务器上运行了代码。 apply_my_objfn 的经过时间为 107 次,run_nlm_single_cluster 的经过时间为 74 次。令我惊讶的是,时间比我的小笔记本电脑要长,但是当您拥有更多内核时,单集群并行优化击败常规非并行版本是有道理的。
为了完整起见,另一个编辑(请参阅 Erwin 的回答下的 cmets):这是使用分析梯度的非平行解决方案。令人惊讶的是,它比数值梯度慢。
## Add gradients
my_objfn_value_and_gradient <- function(list_element, parameter) {
return(c(sum((list_element - parameter) ^ 2), -2*sum(list_element - parameter)))
}
apply_my_objfn_with_gradient <- function(parameter, very_big_list) {
## Returns objfn value with gradient attribute, see ?nlm
objfn_values_and_grads <- lapply(very_big_list, my_objfn_value_and_gradient, parameter=parameter)
objfn_value_and_grad <- Reduce("+", objfn_values_and_grads)
stopifnot(length(objfn_value_and_grad) == 2) # First is objfn value, second is gradient
objfn_value <- objfn_value_and_grad[1]
attr(objfn_value, "gradient") <- objfn_value_and_grad[2]
return(objfn_value)
}
system.time(test_regular <- nlm(apply_my_objfn, parameter_guess,
very_big_list=my_big_list, print.level=0)) # 37.4 elapsed
system.time(test_regular_grad <- nlm(apply_my_objfn_with_gradient, parameter_guess,
very_big_list=my_big_list, print.level=0,
check.analyticals=FALSE)) # 45.0 elapsed
我很想知道这里发生了什么。也就是说,我的问题仍然是如何使用并行化加速这种优化问题?
【问题讨论】:
-
我认为我正在寻找的内容与 stackoverflow.com/questions/6689937/… 中的 cmets 相关——尤其是希望“在每个节点中单独加载数据块”
-
labs.hpe.com/research/systems-research/R-workshop/… 的“示例:数据移动成本”幻灯片是我在上面的示例代码中写下“不希望 very_big_list 被复制超过必要”时的想法
-
也许github.com/vertica/DistributedR 就是我要找的东西
-
以下答案可能会有所帮助:stackoverflow.com/questions/3757321/…
标签: r optimization parallel-processing