MXNet之ps-lite及parameter server原理

MXNet之ps-lite及parameter server原理

ps-lite框架是DMLC组自行实现的parameter server通讯框架,是DMLC其余项目的核心,例如其深度学习框架MXNET的分布式训练就依赖ps-lite的实现。html

parameter server原理

在机器学习和深度学习领域,分布式的优化已经成了一种先决条件,由于单机已经解决不了目前快速增加的数据与参数带来的问题。现实中,训练数据的数量可能达到1TB到1PB之间,而训练过程当中的参数可能会达到\(10^9\)\(10^{12}\)。而每每这些模型的参数须要被全部的worker节点频繁的访问,这就会带来不少问题和挑战:node

  • 访问这些巨量的参数,须要大量的网络带宽支持;
  • 不少机器学习算法都是连续型的,只有上一次迭代完成(各个worker都完成)以后,才能进行下一次迭代,这就致使了若是机器之间性能差距大(木桶理论),就会形成性能的极大损失;
  • 在分布式中,容错能力是很是重要的。不少状况下,算法都是部署到云环境中的(这种环境下,机器是不可靠的,而且job也是有可能被抢占的)。

分布式系统中的同步与异步机制

synchronous

图1 在同步的机制下,系统运行的时间是由最慢的worker节点与通讯时间决定的

asynchronous

图2 在异步的机制下,每一个worker不能等待其它workers完成再运行下一次迭代。这样能够提升效率,但从迭代次数的角度来看,会减慢收敛的速度。

parameter server架构

在parameter server中,每一个 server 实际上都只负责分到的部分参数(servers共同维持一个全局的共享参数),而每一个 work 也只分到部分数据和处理任务。算法

ps_struct

图3 parameter server的架构图,server 节点能够跟其余 server 节点通讯,每一个server负责本身分到的参数,server group 共同维持全部参数的更新。server manager node 负责维护一些元数据的一致性,好比各个节点的状态,参数的分配状况等;worker 节点之间没有通讯,只跟本身对应的server进行通讯。每一个worker group有一个task scheduler,负责向worker分配任务,而且监控worker的运行状况。当有新的worker加入或者退出,task scheduler 负责从新分配任务。

PS架构包括计算资源与机器学习算法两个部分。其中计算资源分为两个部分,参数服务器节点和工做节点:编程

  • 参数服务器节点用来存储参数
  • 工做节点部分用来作算法的训练

机器学习算法也分红两个部分,即参数和训练:bash

  • 参数部分即模型自己,有一致性的要求,参数服务器也能够是一个集群,对于大型的算法,好比DNN,CNN,参数上亿的时候,天然须要一个集群来存储这么多的参数,于是,参数服务器也是须要调度的。
  • 训练部分天然是并行的,否则没法体现分布式机器学习的优点。由于参数服务器的存在,每一个计算节点在拿到新的batch数据以后,都要从参数服务器上取下最新的参数,而后计算梯度,再将梯度更新回参数服务器。

这种设计有两种好处:服务器

  • 经过将机器学习系统的共同之处模块化,算法实现代码更加简洁。
  • 做为一个系统级别共享平台优化方法,PS结构可以支持不少种算法。

从而,PS架构有五个特色:网络

  • 高效的通讯:异步通讯不会拖慢计算
  • 弹性一致:将模型一致这个条件放宽松,容许在算法收敛速度和系统性能之间作平衡。
  • 扩展性强:增长节点无需重启网络
  • 错误容忍:机器错误恢复时间短,Vector Clock允许网络错误
  • 易用性: 全局共享的参数使用向量和矩阵表示,而这些又能够用高性能多线程库进行优化。

Push and Pull

在parameter server中,参数都是能够被表示成(key, value)的集合,好比一个最小化损失函数的问题,key就是feature ID,而value就是它的权值。对于稀疏参数,不存在的key,就能够认为是0。多线程

把参数表示成k-v, 形式更天然, 易于理,更易于编程解。workers跟servers之间经过push与pull来通讯的。worker经过push将计算好的梯度发送到server,而后经过pull从server更新参数。为了提升计算性能和带宽效率,parameter server容许用户使用Range Push跟Range Pull 操做。架构

Task:Synchronous and Asynchronous

Task也分为同步和异步,区别以下图所示:并发

图4 若是iter1须要在iter0 computation,push跟pull都完成后才能开始,那么就是Synchronous,反之就是Asynchronous。Asynchronous可以提升系统的效率(由于节省了不少等待的过程),可是,它可能会下降算法的收敛速率;

因此,系统性能跟算法收敛速率之间是存在一个平衡,你须要同时考虑:

  • 算法对于参数非一致性的敏感度
  • 训练数据特征之间的关联度
  • 硬盘的存储容量

考虑到用户使用的时候会有不一样的状况,parameter server 为用户提供了多种任务依赖方式:

consistency

图5 三种不一样的依赖方式
  • Sequential:这里实际上是 synchronous task,任务之间是有顺序的,只有上一个任务完成,才能开始下一个任务。
  • Eventual: 跟sequential相反,全部任务之间没有顺序,各自独立完成本身的任务。
  • Bounded Delay: 这是sequential 跟 eventual 之间的一个均衡,能够设置一个\(\tau\)做为最大的延时时间。也就是说,只有大于\(\tau\)以前的任务都被完成了,才能开始一个新的任务;极端的状况:
    • \(\tau=0\),状况就是 Sequential;
    • \(\tau=\infty\),状况就是 Eventual;

PS下的算法

算法1是没有通过优化的直接算法和它的流程图以下:

Algorithm1

图6 算法1

Algorithm_1_1

图7 算法1的流程

Algorithm3

图8 优化算法1后的算法3。

算法3中的KKT Filter能够是用户自定义过滤:
对于机器学习优化问题好比梯度降低来讲,并非每次计算的梯度对于最终优化都是有价值的,用户能够经过自定义的规则过滤一些没必要要的传送,再进一步压缩带宽消耗:

  1. 发送很小的梯度值是低效的:
    所以能够自定义设置,只在梯度值较大的时候发送;
  2. 更新接近最优状况的值是低效的:
    所以,只在非最优的状况下发送,可经过KKT来判断;

ps-lite实现

上面说了parameter server的原理,如今来看下这个是怎么实现的。ps-lite是DMLC实现parameter server的一个程序,也是MXNet的核心组件之一。

ps-lite角色

ps-lite包含三种角色:Worker、Server、Scheduler。具体关系以下图:

s_w_h

图9 三种角色的关系图

Worker节点负责计算参数,并发参数push到Server,同时从Serverpull参数回来。
Server节点负责管理Worker节点发送来的参数,并“合并”,以后供各个Worker使用。
Scheduler节点负责管理Worker节点和Server节点的状态,worker与server之间的链接是经过Scheduler的。

重要类

class

图10 重要类的关系图
  • Postoffice是全局管理类,单例模式建立。主要用来配置当前node的一些信息,例如当前node是哪一种类型(server,worker,scheduler),nodeid是啥,以及worker/server 的rank 到 node id的转换。

  • Van是负责通讯的类,是Postoffice的成员。Van中std::unordered_map<int, void*> senders_保存了node_id到链接的映射。Van只是定义了接口,具体实现是依赖ZMQ实现的ZMQVan,Van类负责创建起节点之间的互相链接(例如Worker与Scheduler之间的链接),而且开启本地的receiving thread用来监听收到的message。。

  • Customer用来通讯,跟踪request和response。每个链接对应一个Customer实例,链接对方的id和Customer实例的id相同。

  • SimpleApp是一个基类;提供了发送接收int型的head和string型的body消息,以及注册消息处理函数。它有2个派生类。

  • KVServer是SimpleApp的派生类,用来保存key-values数据。里面的Process()被注册到Customer对象中,当Customer对象的receiving thread接受到消息时,就调用Process()对数据进行处理。

  • KVWorker是SimpleApp的派生类,主要有Push()和Pull(),它们最后都会调用Send()函数,Send()对KVPairs进行切分,由于每一个Server只保留一部分参数,所以切分后的SlicedKVpairs就会被发送给不一样的Server。切分函数能够由用户自行重写,默认为DefaultSlicer,每一个SlicedKVPairs被包装成Message对象,而后用van::send()发送。

  • KVPairs封装了Key-Value结构,还包含了一个长度选项。

  • SArray是Shared array,像智能指针同样共享数据,接口相似vector。

  • Node封装了节点的信息,例如角色、ip、端口、是不是恢复节点。

  • Control封装了控制信息,例如命令类型、目的节点、barrier_group的id、签名。

  • Meta封装了元数据,发送者、接受者、时间戳、请求仍是相应等。

  • Message是要发送的信息,除了元数据外,还包括发送的数据。

运行脚本

为了更好地看到ps-lite的运行原理,咱们先来看下它在本地运行的脚本:

#!/bin/bash
# set -x
if [ $# -lt 3 ]; then
    echo "usage: $0 num_servers num_workers bin [args..]"
    exit -1;
fi

export DMLC_NUM_SERVER=$1
shift
export DMLC_NUM_WORKER=$1
shift
bin=$1
shift
arg="$@"

# start the scheduler
export DMLC_PS_ROOT_URI='127.0.0.1'
export DMLC_PS_ROOT_PORT=8000
export DMLC_ROLE='scheduler'
${bin} ${arg} &


# start servers
export DMLC_ROLE='server'
for ((i=0; i<${DMLC_NUM_SERVER}; ++i)); do
    export HEAPPROFILE=./S${i}
    ${bin} ${arg} &
done

# start workers
export DMLC_ROLE='worker'
for ((i=0; i<${DMLC_NUM_WORKER}; ++i)); do
    export HEAPPROFILE=./W${i}
    ${bin} ${arg} &
done

wait

这个脚本主要作了两件事,第一件是为不一样的角色设置环境变量,第二件是在本地运行多个不一样的角色。因此说ps-lite是要多个不一样的进程(程序)共同合做完成工做的,ps-lite采起的是用环境变量来设置角色的配置。

test_simple_app流程

test_simple_app.cc是一人很简单的app,其它复杂的流程原理这个程序差很少,因此咱们就说说这个程序是怎么运行的。先来看下刚开始运行程序时,worker(W)\Server(S)\Scheduler(H)之间是怎么链接的,这里没有写Customer处理普通讯息的流程。W\S\H表明上面脚本运行各个角色后在不一样角色程序内的处理流程。

  • W\S\H:初始化SimpleApp --> New Customer(绑定Process函数) --> Customer起一个Receiving线程
  • W\S\H:初始化static PostOffice,全局都用同一个PostOffice --> Create(Van)用来作通讯的发/发 --> 从环境变量中读入配置 --> 肯定不一样的角色。
  • W\S\H:Start() --> Van::Start(), my_node_/Scheduler的初始化
  • W\S:绑定port并链接到同一个Scheduler
  • W\S:发送信息到指定ID
  • W\S\h:在van中起一个Reciving的线程
  • H:收到信息并回发
  • W\S: 收到信息
  • W\S\H:Finalize()

Customer处理普通讯息流程以下:

  • H:app->requst() --> 放这个请求入到tracker_中 --> send(msg) --> app->wait()[等待收回发的信息]
  • W/S:收到信息后放到recv_queue_中
  • W/S:Customer的Reciving收到信息 --> call recv_handle_ --> process(recv)[处理信息] --> response_hadle_(recv) --> ReqHandle() --> response()[回发信息]
  • H:收到回发的信息 --> 放入到recv_queue_中处理 --> 在Customer中的Reciving中处理
  • H:当tracker_.first == tracker_.second时,释放app->wait()

参考引用:
[1] http://blog.csdn.net/stdcoutzyx/article/details/51241868
[2] http://blog.csdn.net/cyh_24/article/details/50545780
[3] https://www.zybuluo.com/Dounm/note/529299
[4] http://blog.csdn.net/KangRoger/article/details/73307685

【防止爬虫转载而致使的格式问题——连接】: http://www.cnblogs.com/heguanyou/p/7868596.html

相关文章
相关标签/搜索