要学的东西太多,无笔记不能学~~ 欢迎关注公众号,一起分享学习笔记,记录每一颗“贝壳”~

———————————————————————————

终于开始攻克并行这一块了,有点小兴奋,来看看网络上R语言并行办法有哪些:

赵鹏老师(R与并行计算)做的总结已经很到位。现在并行可以分为:

 隐式并行:隐式计算对用户隐藏了大部分细节,用户不需要知道具体数据分配方式 ,算法的实现或者底层的硬件资源分配。系统会根据当前的硬件资源来自动启动计算核心。显然,这种模式对于大多数用户来说是最喜闻乐见的。

显性并行:显式计算则要求用户能够自己处理算例中数据划分,任务分配,计算以及最后的结果收集。因此,显式计算模式对用户的要求更高,用户不仅需要理解自己的算法,还需要对并行计算和硬件有一定的理解。值得庆幸的是,现有R中的并行计算框架,如parallel (snow,multicores),Rmpi和foreach等采用的是映射式并行模型(Mapping),使用方法简单清晰,极大地简化了编程复杂度。R用户只需要将现有程序转化为*apply或者for的循环形式之后,通过简单的API替换来实现并行计算。

简单总结就是:

隐式并行:OpenBLAS,Intel MKL,NVIDIA cuBLAS,H2O(参考我的博客)等

显性并行:parallel(主打lapply应用)、foreach(主打for循环)、SupR、还有利用GPU的办法(gpuR)

同时并行时对内存的消耗极大,超级容易爆发内存问题,而且R的内存问题一直都是R很难解决的问题,这边笔者也把看到的一些方式列出来。

当然在使用一些高大上的并行包以及框架之前,如果你能够从编码小细节优化,效率也能提高很多,譬如:

方法:速度, nrow(df)/time_taken = n 行每秒
原始方法:1X, 856.2255行每秒(正则化为1)
向量化方法:738X, 631578行每秒
只考虑真值情况:1002X,857142.9行每秒
ifelse:1752X,1500000行每秒
which:8806X,7540364行每秒
Rcpp:13476X,11538462行每秒
apply处理并行

——————————————————————————————————————————————————————

在最后笔者在实践中遇到的问题,进行对应的解决:

应用一:使用parallel包时,能不能clusterExport整个函数呢?

应用二:在使用parallel包时,报错:Error in unserialize(node$con) : error reading from connection

——————————————————————————————————

一、parallel包的使用方法

多数内容参考:R语言并行化基础与提高

parallel是base包,所以不用install.packages就可以直接调用。

原理:是利用CPU的核心进行训练。

应用场景:跟apply族(lapply/sapply效果一致)(

R语言︱数据分组统计函数族——apply族用法与心得


1、使用步骤

设置核心数:no_cores <- detectCores() - 1

步骤分群环境:cl <- makeCluster(no_cores)

用到的变量与包复制给不同的核心:clusterEvalQ(包)、clusterExport(变量)

运行算法:clusterApply(cl, c(9,5), get("+"), 3)

关闭集群:

stopCluster(cl)

就OK啦。但是这里面很从前不一样的是,如果有环境里面的外置变量(自己定义)那么需要额外插入,复制到不同核上面,而且如果有不同包里面的函数,都要额外加载、复制多份给不同的电脑核心。

2、案例

library(parallel)
cl <- makeCluster(getOption("cl.cores", 2))
clusterApply(cl, c(9,5), get("+"), 3)   #加减乘除
parSapply(cl, c(9,5), get("+"), 3)   

案例一:c1就是设置的核心数,此时是2核心,然后就可以利用clusterApply/parSapply等函数进行调用。

xx <- 1
clusterExport(cl, "xx")
clusterCall(cl, function(y) xx + y, 2)

案例二:这个里面有xx这个变量是额外定义的,所以需要额外加载,需要用clusterExport函数,导入到并行环境中。

3、parallel内存优化与管理

(1)注意数据容量的均匀分布

parLapply <- function (cl = NULL, X, fun, ...)
{
    cl <- defaultCluster(cl)
    do.call(c, clusterApply(cl, x = splitList(X, length(cl)),
        fun = lapply, fun, ...), quote = TRUE)
}

注意到splitList(X, length(cl)) ,他会将任务分割成多个部分,然后将他们发送到不同的集群中。这里一个问题就是,譬如假设有一个list,里面数据量分别是:

(99,99,99,2,5,2)

如果是两个核数据分为了(99,99,99)、(2,5,2),第一个核分为到了那么多任务,第二个核很少,那么就会空闲,于是乎,效率还是不高,所以数据容量要尽量均匀分布。

(2)集群内存类型:FORK和PSOCK

FORK适用unix/max,实现内存共享以及节省内存,大数据环境下内存问题报错少

PSOCK适用所有(一般window都是这个)

parallel包中通过函数来设置:

makeCluster(4,type="FORK")

FORK对性能提升很显著,但是window下不可适用。

4、parallel万一报错了咋办?

lapply在使用的时候也会出现这样的问题,如果出现问题,那么就白跑了,而且也不可能给你停顿下来。那么如何让lapply运行中跳过报错的办法呢?

R语言相关的报错处理函数可见:R语言-处理异常值或报错的三个示例

用tryCatch跳过:

result = tryCatch(
        {expr},
        warning = function(w) {warning-handler-code},
        error = function(e) { error-handler-code},
        finally = {cleanup-code}
        )

出现warning、error时候怎么处理,就可以跳过了。例子:

result = tryCatch(
        {segmentCN(txt)},
        warning = function(w) {"出警告啦"},
        error = function(e) { "出错啦"},
        )

分词时候,容易因为Lapply中断之后,就不会运行了,这样功亏一篑所以可以用这个办法跳过。

5、parSapply/parLapply函数使用技巧

函数的大体结构是:

parSapply(cl,x,fun)

其中cl是预先设定好的,x是需要循环的变量,而fun是函数。

那么一般来说,fun之中要使用的任何内容都需要用clusterEvalQ(包)、clusterExport(变量)复制到不同的核心之中。

而x则可以不用布置到全局,因为他是在源环境下调用出来,并拆分任务的。

——————————————————————————————————

二、foreach包的使用方法

1、简单使用案例

设计foreach包的思想可能想要创建一个lapply和for循环的标准,初始化的过程有些不同,你需要register注册集群:

library(foreach)
library(doParallel)

cl<-makeCluster(no_cores)
registerDoParallel(cl)

要记得最后要结束集群(不是用stopCluster()):

stopImplicitCluster()

foreach函数可以使用参数.combine控制你汇总结果的方法:

> foreach(exponent = 2:4,
        .combine = c)  %dopar%
  base^exponent
  [1]  4  8 16
> foreach(exponent = 2:4, .combine = rbind)  %dopar%   base^exponent
    [,1]
result.1 4 result.2 8 result.3 16
foreach(exponent = 2:4, .combine = list, .multicombine = TRUE)  %dopar%   base^exponent
[[1]]
[1] 4 [[2]]
[1] 8 [[3]]
[1] 16

注意到最后list的combine方法是默认的。在这个例子中用到一个.multicombine参数,他可以帮助你避免嵌套列表。比如说list(list(result.1, result.2), result.3) :

> foreach(exponent = 2:4,
        .combine = list)  %dopar%
  base^exponent
[[1]]
[[1]][[1]]
[1] 4

[[1]][[2]]
[1] 8

[[2]]
[1] 16

2、变量作用域

在foreach中,变量作用域有些不同,它会自动加载本地的环境到函数中:

> base <- 2
> cl<-makeCluster(2)
> registerDoParallel(cl)
> foreach(exponent = 2:4,
        .combine = c)  %dopar%
  base^exponent
stopCluster(cl)
 [1]  4  8 16

但是,对于父环境的变量则不会加载,以下这个例子就会抛出错误:

test <- function (exponent) {
  foreach(exponent = 2:4,
          .combine = c)  %dopar%
    base^exponent
}
test()

 Error in base^exponent : task 1 failed - "object 'base' not found" 

为解决这个问题你可以使用.export这个参数而不需要使用clusterExport。注意的是,他可以加载最终版本的变量,在函数运行前,变量都是可以改变的:

base <- 2
cl<-makeCluster(2)
registerDoParallel(cl)

base <- 4
test <- function (exponent) {
  foreach(exponent = 2:4,
          .combine = c,
          .export = "base")  %dopar%
    base^exponent
}
test()

stopCluster(cl)

 [1]  4  8 16

相似的你可以使用.packages参数来加载包,比如说:.packages = c("rms", "mice")

——————————————————————————————————

三、SupR

通过对现有R 内核的改进实现在单机上的多线程和在集群上的分布式计算功能。SupR目前仍处在内部试用和补充完善阶段。

据说supR很好用,而且小象学院的讲师(游皓麟)已经开始教授这个系统的使用方法,挺好用的。

——————————————————————————————————

四、内存管理

方法有三:
      一、升级硬件
      二、改进算法
      三、修改操作系统分配给R的内存上限, memory.size(T)查看已分配内存

memory.size(F)#查看已使用内存
memory.limit()#查看内存上限
object.size()#看每个变量占多大内存。
memory.size()#查看现在的work space的内存使用
memory.limit()#查看系统规定的内存使用上限。如果现在的内存上限不够用,可以通过memory.limit(newLimit)更改到一个新的上限。注意,在32位的R中,封顶上限为4G,无法在一个程序上使用超过4G (数位上限)。这种时候,可以考虑使用64位的版本。

详情看:R语言︱大数据集下运行内存管理   以及  R语言之内存管理

——————————————————————————————————

应用一:使用parallel包时,能不能clusterExport整个函数呢?

R语言在使用Parallel时候,会出现这样的疑问,一些东西都需要广播给不同的核心,那么在clusterExport步骤怎么办呢?能不能clusterExport一整个函数?然后直接parLapply呢?

答案否定的。笔者在用的时候,怎么样都不能把整个函数加载进去,所以只能另想办法。

既然不能clusterExport整个函数,那就只能改造我们的函数去适应parallel包了。

来看几个函数“被”改造的例子,一般来说有两个办法:

1、方法一:通过.GlobalEnv广播成全局变量

clusterExport(cl=cl, varlist=c("text.var", "ntv", "gc.rate", "pos"), envir=environment())

在函数导入的时候,加入envir变量让其广播给不同的核心,这个可以放在函数之中来使用。

2、方法二:把parLapply嵌套进函数之中

par.test <- function(text.var, gc.rate=10){
    require(parallel)
    pos <-  function(i) {
        paste(sapply(strsplit(tolower(i), " "), nchar), collapse=" | ")
    }
    cl <- makeCluster(mc <- getOption("cl.cores", 4))
    parLapply(cl, text.var, function(text.vari, gc.rate, pos) {
        x <- pos(text.vari)
        if (i%%gc.rate==0) gc()
        x
    }, gc.rate, pos)
}

可以看到的这个例子,就是把内容嵌套到parLapply之中了。同时也可以学习到,parLapply使用方法也很不错,也可以学习一下。

再来看一个例子

mainFunction <- function(cl) {
    fa <- function(x) fb(x)
    fb <- function(x) fc(x)
    fc <- function(x) x
    y <- 7
    workerFunction <- function(i) {
        do.call(functionNames[[i]], list(y))
    }
    environment(workerFunction) <- .GlobalEnv
    environment(fa) <- .GlobalEnv
    environment(fb) <- .GlobalEnv
    environment(fc) <- .GlobalEnv
    functionNames <- c("fa", "fb", "fc")
    clusterExport(cl, varlist=c("functionNames", functionNames, "y"),
                  envir=environment())
    parLapply(cl, seq_along(functionNames), workerFunction)
}

library(parallel)
cl <- makeCluster(detectCores())
mainFunction(cl)
stopCluster(cl)

——————————————————————————————————

应用二:在使用parallel包时,报错:Error in unserialize(node$con) : error reading from connection

在R语言中使用并行算法的时候,会出现报错,无法连接到核心,即使在本来连接上的时候。通过查阅文献看到了,这是因为“调用核心数--计算机内存”的不匹配造成的。

如果你的数据集很大,调用了很多核心,那么你的计算机内存如果不够匹配,就会出现连接不上的不错,甚至还出现卡机,一动不动的情况(当然,只要耐心等待,其实他还是会继续运行的...等待的时候会有点长)

解决办法一:调用FORK,window不能...

FORK适用unix/max,实现内存共享以及节省内存,大数据环境下内存问题报错少

PSOCK适用所有(一般window都是这个)

不过调用FORK也还是治标不治本。

解决办法二:分开并行,小步迭代

譬如10万数据,那么就“2万+2万+2万+2万+2万”的跑,如果还出现脱机,就用之前tryCatch跳过,让损失降低到最小。

最好的办法了。

参考文献:How-to go parallel in R – basics + tips

——————————————————————————————————

参考文献

1、R语言并行化基础与提高

2、R与并行计算

3、sparklyr包:实现Spark与R的接口,会用dplyr就能玩Spark

4、Sparklyr与Docker的推荐系统实战

5、R语言︱H2o深度学习的一些R语言实践——H2o包

6、R用户的福音︱TensorFlow:TensorFlow的R接口

7、mxnet:结合R与GPU加速深度学习

8、碎片︱R语言与深度学习

R︱并行计算以及提高运算效率的方式(parallel包、clusterExport函数、SupR包简介)的更多相关文章

  1. oracle使用with as提高查询效率

    经常在开发过程中会用到视图或组合查询的情况,但由于涉及表数据经常达到千万级别的笛卡尔积,而且一段查询时会反复调用,但结果输出往往不需要那么多,可以使用with将过滤或处理后的结果先缓存到临时表(此处原 ...

  2. 提升R代码运算效率的11个实用方法——并行、效率

    转载于36大数据,原文作者:Selva Prabhakaran  译者:fibears 众所周知,当我们利用R语言处理大型数据集时,for循环语句的运算效率非常低.有许多种方法可以提升你的代码运算效率 ...

  3. 【R】提升R代码运算效率的11个实用方法

    低.有许多种方法可以提升你的代码运算效率,但或许你更想了解运算效率能得到多大的提升.本文将介绍几种适用于大数据领域的方法,包括简单的逻辑调整设计.并行处理和Rcpp的运用,利用这些方法你可以轻松地处理 ...

  4. Android 你可能忽略的提高敲代码效率的方式 (转)

    每日推荐 Eyepetizer-in-Kotlin:一款简约的小视频app,带你走进kotlin 作为学习kotlin的一款app,在撸代码的过程中学习kotlin的语法及特性. Eyepetizer ...

  5. Android 你可能忽略的提高敲代码效率的方式

    Android 你可能忽略的提高敲代码效率的方式

  6. 提升R代码运算效率的11个实用方法

    提升R代码运算效率的11个实用方法 众所周知,当我们利用R语言处理大型数据集时,for 循环语句的运算效率非常低.有许多种方法可以提升你的代码运算效率,但或许你更想了解运算效率能得到多大的提升.本文将 ...

  7. Lucene4.6查询时完全跳过打分,提高查询效率的实现方式

    由于索引的文件量比较大,而且应用中不需要对文档进行打分,只需要查询出所有满足条件的文档.所以需要跳过打分来提高查询效率.一开始想用ConstantScoreQuery,但是测试发现这个类虽然让所有返回 ...

  8. 在对文件进行随机读写,RandomAccessFile类,如何提高其效率

    花1K内存实现高效I/O的RandomAccessFile类 JAVA的文件随机存取类(RandomAccessFile)的I/O效率较低.通过分析其中原因,提出解决方案.逐步展示如何创建具备缓存读写 ...

  9. 成吨提高开发效率:Intellij Shortcuts精简子集与思维模式

    在线精简cheatsheet备查表:intellij.linesh.twGithub项目:intellij-mac-frequent-keymap Intellij的快捷键多而繁杂,从官方推荐的key ...

随机推荐

  1. 树莓派小车By 树莓派爱好者ITJoker(通过python socket通信实现树莓派视频小车)(一)

    本文由树莓派爱好者ITJoker 编辑,转载请注明出处.本人也有新浪博客同样是树莓派爱好者ITJoker 所需材料:树莓派2B或者2B以上,L2985n驱动板,若干排线,电池及电池盒,usb无线网卡( ...

  2. Effective Java 之 --- 用私有构造器或者枚举类型强化Singleton属性

    Singleton指仅仅被实例化一次的类,通常用来代表那些本质上唯一的系统组件,实现Singleton有三种方法: 1)公有静态成员是个final域,享有特权的用户可以调用AccessibleObje ...

  3. ABP官方文档翻译 6.6 Javascript API

    JavaScript API AJAX 通知 消息 UI Block和Busy 事件总线 日志 其他实用功能 ABP提供了一套对象和函数,用来简化.标准化javascript的开发. 这里是ABP提供 ...

  4. CentOS7 Nvidia Docker环境

    最近在搞tensorflow的一些东西,话说这东西是真的皮,搞不懂.但是环境还是磕磕碰碰的搭起来了 其实本来是没想到用docker的,但是就一台配置较好电的服务器,还要运行公司的其他环境,vmware ...

  5. 理解JavaScript中的作用域

     什么是变量,什么是作用域? 变量:简单来说就是在特定时间内保存特定值的一个名字而已,由于不存在定义某个变量必须要保存某种数据类型值的规则,所以变量的值及其数据类型可以在脚本生命周期内任意改变,变量可 ...

  6. UOJ Round #15 [构造 | 计数 | 异或哈希 kmp]

    UOJ Round #15 大部分题目没有AC,我只是水一下部分分的题解... 225[UR #15]奥林匹克五子棋 题意:在n*m的棋盘上构造k子棋的平局 题解: 玩一下发现k=1, k=2无解,然 ...

  7. bzoj 3996: [TJOI2015]线性代数 [最小割]

    3996: [TJOI2015]线性代数 题意:给出一个NN的矩阵B和一个1N的矩阵C.求出一个1*N的01矩阵A.使得 \(D=(A * B-C)* A^T\)最大.其中A^T为A的转置.输出D.每 ...

  8. CA证书扫盲,https讲解。

    很多关于CA证书的讲解. 1.什么是CA证书. 看过一些博客,写的比较形象具体. ◇ 普通的介绍信 想必大伙儿都听说过介绍信的例子吧?假设 A 公司的张三先生要到 B 公司去拜访,但是 B 公司的所有 ...

  9. jquary 单选,多选,select 获取和设置值 jquary自定义函数

    <%@ page contentType="text/html; charset=UTF-8"%> <%@ taglib prefix="c" ...

  10. [经典] 使用Python批量重命名iPhone拍摄的照片-按照拍摄时间重命名

    #!/usr/bin/env python # -*- coding: utf-8 -*- ''' 批量修改照片文件名称的Python脚本程序. 遍历指定目录(含子目录)的照片文件,根据拍照时间将照片 ...