使用R提高{raster}处理的速度:第2/3部分并行化

嗨,好人!

欢迎来到我的教程的第二部分,该教程有关如何提高R {raster}包的处理速度。在里面 前一部分 我向您展示了如何通过增加最大内存限制来加速R。这部分会更多“sophisticated”因为我们将看看如何并行化R进程。

平行吗?等一下

“Paralellise”是一定牛遗漏花哨的词,但这是什么意思呢?好吧,您可能知道您的CPU(如果您没有’请牢记在90年代)拥有多个核心来处理您的请求。例如,我的笔记本电脑有4个核,而dektop PC有8个核。如果使用R进行计算,通常只有一定牛遗漏核用于处理此计算,而其他核则基本上处于休眠状态或正在处理一些开销操作,例如复制数据并确保您的其他程序运行正常。下图显示了在典型的R会话期间,我的4个内核中的CPU使用情况:

CPU单核您可以看到只有一定牛遗漏核心(在这种情况下’s的核心3)正在使用,而其他核心的利用率基本上为0%。
想象我们有一定牛遗漏巨大的处理任务,我们必须一遍又一遍地执行(例如inisde 对于循环)。这就是R在这种情况下正在做的事情:它仅激活一定牛遗漏内核,并使其处理循环的迭代, 一步一步地,一次迭代。 这意味着一定牛遗漏内核将完成所有工作,而另一定牛遗漏内核本质上将(几乎)什么都不做,因为默认情况下R会使用单内核处理。真是浪费,对吧?为什么不使用所有内核进行循环?例如,核心1处理迭代1,而核心2处理迭代2,而核心3处理迭代3,…等本质上是什么 并行化 意味着:同时(并行)使用多个内核来执行重复任务。那么我们如何在R中实现呢?

并行化– Step by Step

没有并行化的示例

让 ’例如,我们有一定牛遗漏包含八个层堆栈的文件夹,我们想计算每个堆栈的NDVI。这就是我使用for循环计算NDVI的方式 没有 并行处理:

#load 栅格 package
library(raster)

#path to directory with folders
path       <- "Downloads/Stacks/"

#get file names using list.files() function
stack_list <- list.files(path, 模式=".tif$", full.names=T)

#Loop over every stack in filelist
for(我在1:长度(stack_list)) {  
  
#stack image i
img  <- stack(stack_list[i])

#calc ndvi 对于 image i
ndvi <- (img[[4]]-img[[3]]) / (img[[4]]+img[[3]])

#construct filename
outname <- sub(pattern     = ".tif",
               replacement = "_ndvi.tif", 
               x           = stack_list [i])

#export 栅格
writeRaster(ndvi, 
            filename  = outname,
            overwrite = T)
}

对于较小的栅格(4000x4000px),此代码可以正常工作,大约需要2分钟才能执行。但是,假设您有100个要处理的栅格。在这里使用多个内核将为您节省大量时间。这是您的操作方式:

并行化示例

library(raster)
library(doParallel)  #Foreach Parallel Adaptor 
library(foreach)     #Provides 前言 looping construct

#Define how many cores you want to use
UseCores <- detectCores() -1

#Register CoreCluster
cl       <-makeCluster(UseCores)
registerDoParallel(cl)

path       <- "Downloads/Stacks/"
stack_list <- list.files(path, 模式=".tif$", full.names=T)

#Use 前言 loop 和 %dopar% command
foreach(i = 1:length(stack_list))%dopar%{
library(raster)
  
img  <- stack(stack_list[i])
ndvi <- (img[[4]]-img[[3]]) / (img[[4]]+img[[3]])

outname <- sub(pattern     = ".tif",
               replacement = "_ndvi.tif", 
               x           = stack_list [i])

writeRaster(ndvi, 
            filename  = outname,
            overwrite = T)

}

#end cluster
stopCluster(cl)
该代码看起来非常相似,仅在某些部分有所不同:

首先,您需要加载 doParallel前言 包。它们使R能够执行并行处理。

在第二步中,您定义了要用于计算的核心数量:

#Define how many cores you want to use
UseCores <- detectCores() -1

#Register CoreCluster
cl       <-makeCluster(UseCores)
registerDoParallel(cl)  

功能 detectCores() 返回可用的内核数。注意:您应始终减少使用一定牛遗漏内核,因此将有一定牛遗漏内核可用于处理诸如复制等开销任务。否则,您的PC可能会崩溃。用 makeCluster()registerDoParallel() 您注册内核以进行并行化。

最后的变化是 前言() 循环而不是 对于 循环:

前言(i = 1:length(stack_list))%dopar%{
    library(raster)
    #calulation here
}

注意:

  • 方括号内的语法不同于您要编写的常规for循环 我在1:长度(stack_list))在这里写 i = 1:length(stack_list))。
  • 在循环括号之后,您必须编写 %dopar% 代表什么“do parallel”.
  • 您必须在循环内加载计算所需的库!在这种情况下,我们将需要光栅包,因此我们使用library(raster)加载光栅包。 循环语句。

最后,我们通过调用关闭并行化 stopCluster()。

基准测试

通过使用并行化代码,当我使用三个内核而不是一定牛遗漏内核时,我设法将处理时间增加了50%。这是我使用单核处理(右上角)时的CPU使用情况:

单核

您会看到两个内核正在工作,一定牛遗漏正在执行计算,第二个正在处理开销。

这是使用所有可用核心(右上角)时我的CPU使用情况的样子:

多核工艺

下一定牛遗漏教程将介绍如何使用 簇() 该函数可以使用某些栅格函数的多核处理。

再见!

 

关于作者

马丁 was born in Czech Republic 和 studied at the University of Natural Resources 和 Life Sciences, Vienna. He is 当前ly working at GeoVille - an Earth Observation Company 基础d in Austria, specialised in Land Monitoring. His main interests are: Open-source applications like R, (geospatial) statistics 和 数据管理, 网络映射 和 visualization. He loves travelling, geocaching, photography 和 sports.

13条留言

您可以在这篇文章中发表评论。


  • 非常有用的帖子。
    感谢您的简单解释。

    地亚哥 5年前 回复


  • 别客气。感谢您的反馈。

    马丁 5年前 回复


  • 是的,谢谢你的这篇文章!最全面,易于理解的示例我’关于如何使栅格处理与R并行化,我们深表感谢。

    塞思·G。 3年前 回复


    • 感谢您的好评!它’s very appreciated!!

      马丁 3年前 回复


  • 谢谢你的帖子,马丁。我尝试使用您的方法并行化dismo :: predict()。我想预测多个RasterStack的拟合模型(即,未来的替代方案)。使用下面的示例,我得到以下错误— ‘{中的错误:任务1失败– “缺少图层(或名称错误)”‘?

    如果我指定了RasterStack(例如,‘future5’),但如果我指向一定牛遗漏列表(即,‘stack_list[i]’)?

    有什么建议?

    #哪里‘m’=在dismo中导出的maxent模型
    前言(i = 1:length(stack_list))%dopar%{
    图书馆(展示)
    p <- predict(m, stack_list [i])
    }

    杰森M. 3年前 回复


    • 嘿!
      Try stack_list[[i]] instead of stack_list [i]?
      祝好运!

      马丁 3年前 回复


      • 感谢您及时的回复!不幸的是,仍然会遇到相同的错误(粘贴在下面)?

        {中的错误:任务1失败– “缺少图层(或名称错误)”
        3. stop(simpleError(msg,call = expr))
        2. e $ fun(obj,replace(ex),parent.frame(),e $ 数据)
        1. 前言(i = 1:length(stack_list))%dopar%{
        图书馆(展示)
        pc <-预测(m,stack_list [[i]])
        }

        杰森M. 3年前 回复


        • 嗯..如果您使用简单的for循环,是否可以正常工作?
          此外,您可以发布str(stack_list)吗?
          干杯

          马丁 3年前 回复


  • 对不起反应迟钝,我请了几天假…我认为这几乎可以解决,但是仍然存在错误?任何帮助,不胜感激!

    我发现我需要创建一定牛遗漏rasterStacks列表(即,‘stacks <-as.list(c(base,s01,s02,s03))'),而不是字符串的向量(即,'stacks pc
    类别:RasterStack
    层数:0

    有什么建议?

    ################################

    图书馆(展示)
    图书馆(rgdal)
    图书馆(读者)

    #objects()的描述
    # ‘species’ = list of 种类
    # ‘me’=单个物种的最大模型
    # ‘t’=单个物种的阈值
    # ‘base’ = 栅格Stack 对于 基础line climate (1990-2009)
    # ‘s01’ – ‘s12’= 12种未来气候方案中的每一定牛遗漏的rasterStacks
    # ‘stacks’=堆栈名称列表
    # ‘pc’=预测的栅格堆栈(连续)

    #导入用于多种气候场景的rasterStacks
    l_0<- list.files(path="问:/ refugia / climate / epoch_1990-2009", 模式='.tif',
    full.names = T)
    基础 <-堆栈(l_0 [c(2,4,5,6,13,14,15)])

    l_1<- list.files(path="问:/ refugia / climate / epoch_2020-2039 / future01",
    模式='.tif', full.names = T)
    s01<-堆栈(l_1 [c(2,4,5,6,13,14,15)])

    l_2<- list.files(path="问:/ refugia / climate / epoch_2020-2039 / future02",
    模式='.tif', full.names = T)
    s02<-堆栈(l_2 [c(2,4,5,6,13,14,15)])

    l_3<- list.files(path="问:/ refugia / climate / epoch_2020-2039 / future03",
    模式='.tif', full.names = T)
    s03<-堆栈(l_3 [c(2,4,5,6,13,14,15)])

    # create list of 种类
    setwd("问:/避难所/观察/")
    spp<- read_csv("species_obs.csv", col_names=TRUE,
    col_types = cols(speciesModel = col_character()))
    种类<-唯一(spp $ 种类Model)

    #创建气候栅格列表
    栈<-as.list(c(base,s01,s02,s03))

    #创建用于栅格输出的场景名称列表
    情景<- c('current', 'future1', 'future2', 'future3')

    对于(j in 1:length(species)){

    pc <- stack()

    路径<- paste('Q:/refugia/maxent/', 种类[j], '_modelfit.rdata', sep = '')
    附加(路径);我<- me;
    分离(粘贴('file:',path,sep=''),character.only = TRUE)

    图书馆(doParallel)
    库(foreach)

    UseCores<- detectCores() -1
    cl <-makeCluster(UseCores)
    registerDoParallel(cl)

    前言(i = 1:4)%dopar%{

    图书馆(展示)
    pc <- predict(me, 栈[[i]])

    setwd('Q:/refugia/test')
    writeRaster(pc [[i]],filename = paste(species [j],"_", scenario[i], "_prediction.tif",
    sep =""), 对于mat="GTiff",覆盖= T,进度='text')
    }

    stopCluster(cl)
    rm(路径,我,个人电脑)

    }

    杰森M. 3年前 回复


  • CATPCHA代码不成功时,先前的帖子已损坏?抱歉,代码中出现了换行符(在“注释”窗口中看起来不错),但最上段应阅读…

    我发现我需要创建一定牛遗漏rasterStacks列表(即,‘stacks <-as.list(c(base,s01,s02,s03))'),而不是字符串的向量(即,'stacks <- c('base', 's01', 's02', 's03')')。代码(粘贴在下面)现在运行—我所有的处理器都具有相似/平行的性能曲线。我认为dismo :: predict()步骤已成功运行(即,我的处理器都旋转了20分钟=预期时间),但随后该脚本因尝试将预测写为rasterStack而失败。任何帮助,不胜感激!

    杰森M. 3年前 回复


  • 尴尬的… here is the error…

    {中的错误:任务2失败– “not a valid subset”
    > pc
    类别:RasterStack
    层数:0

    杰森M. 3年前 回复


  • […]通过R:提高{raster}处理的速度:第2/3部分并行化[…]

    栅格并行化– Avian Ecologist 2年前 回复


  • 嘿,谢谢你的有趣帖子。

    在运行并行进程时,您是否对rasterOptions的设置有见解?

    特别是我不清楚分配给一定牛遗漏进程的内存份额(memfrac选项)是否应考虑已注册的内核数量(即,确保使用的内核数量乘以内存份额)< 1)?

    最好,
    瓦伦丁

    瓦伦丁 10个月前 回复


发表回复

*