欢迎光临散文网 会员登陆 & 注册

【TF/Guide笔记】 09. Distributed training

2022-03-04 14:35 作者:纪一希  | 我要投稿

    感觉toc里安排的顺序有点怪,我就跳着看了,直接来到最关键的分布式计算部分。


    MirroredStrategy

    适合单机多卡,原理是所有GPU上拷贝所有Variable,并且保持这些Variable始终是相同的,训练的时候每个卡分配一部分数据。

    代码的嵌套太绕了,说实在的看不懂,只能推测是怎么实现的。所谓的保证Variable始终同步,其实是保证了每次对Variable的更新是相同的,这是文档原话,但相同的是什么却没说,显然这个值应该是梯度,所以MirroredStrategy做的隐式假设是,你在跑的算法得是基于梯度下降的(虽然ML应该都是)。

    梯度下降里的梯度是可加的,大概只有这一步保证可以allreduce,因为它明确说了allreduce里使用的是加法。所以Strategy应该是识别了代码中tape.gradient这一步,对这个接口产出的结果进行allreduce,mirrored就实现了,原理上应该也是对的。所以这东西看似很通用的包一下就行了,但是里面的限制应该还是蛮多的,或者说你也可以把MirroredStrategy当mpi用,但它内部对tape.gradient做了特化操作。

    至于所谓基于NCCL的高效allreduce,我在源码的注释里找到了一句描述,他的实现方法就是把值都拷贝到一个设备上,计算完事儿后再广播回去。对于单机多卡来说这种方法的确是很够用了,毕竟谁会在一台机器上插它10张GPU呢。当然不排除我这代码看叉了。

    感觉MirroredStrategy适合计算任务很重的模型,因为他的前提是一个device必须得装得下全部模型。

    TPUStrategy

    懒得看了,咱也不大可能买得到这玩意。

    MultiWorkerMirroredStrategy

    多机多卡,原理上与单机多卡相同,都是保证每个device上的Variable同步。多了一个选项是配置多机间怎么通信,除了NCCL之外提供了用rpc跑ring。

    以前我们的allreduce实现的是树状的,这里并没有这个选项,我估计tf还是假设了你不会搞那么多机器,也许是机器数太多带来的overhead不如少放几台让他慢慢跑。

    在另一篇引用的文档里明确说了,如果一个device挂了,整个程序就会gg,tf没有自动的灾备,但你可以通过checkpoint来自己实现,比如每跑一轮checkpoint一下,如果挂了就从最近的恢复。但是文档里还说这种方法将来有其他接口实现,因为让用户自己写的话,他可能无法保证每个机器上的checkpoint是同一次dump下去的,他提供的BackupAndRestore帮用户管理了模型版本。

    其实MirroredStrategy就是Syncronize training,以前我们最苦恼的就是sync模式的训练速度,换成tf的这个思路来看的话,如果把所有参数都放在本地存replica,然后数据并行的话,那么每一个batch有且仅有一次all_reduce就可以了,不再需要其他任何网络开销,应该会比我们原来写的快很多。

    ParameterServerStrategy

    文档里说,默认情况下,ps strategy里的worker之间是不同步的,所以ps模式就是asyncronize training。

    在看Mirrored时没注意到一个问题,似乎需要用户自己管理输入文件,来保证每个worker拿到的数据是没有交集的,也许dataset做了特殊操作,但strategy这里是没管的。

    ps模式更类似于spark,需要有个chief节点负责分发任务,而这个任务是以batch为单位的。首先coordinator.create_per_worker_dataset相当于coordinator告诉了每个worker都自己先去把数据读好,拿回来的iterator应该是个类似Variable的东西,分发任务的时候再作为参数带回给每个worker,所以框架针对任务的执行是不用管数据流的,函数内部要自己调用next()。

    根据文档的说明,他不希望你依赖OutOfRangeError,也就是数据被读完了,这时候step function会失败退出,也许外面需要写while (coordinator.schedule().is_ok())之类的才行。tf推荐的是你确定的执行多少个step来结束训练,并且最好你的数据是可以无限iterate的。

    这样的好处大概是你的训练时长与数据大小无关,如果数据太小,也许每个epoch实际执行了1.5次数据,数据太大也不至于跑不完。但一个epoch不严格等于一份数据对我来说有点儿反常识了。

    虽然默认下,ps模式会把分散在ps上的所有Variable shard合成一个tensor再给到运算,让下游无感知的使用,但他也提供了特化的函数,例如embedding_lookup,让你可以只获取需要的Variable shard,离散模型大概可以用这套接口实现。

    step_fn返回的这个loss,应该是个类似Variable的某种handler,因为它的Variable涉及同步问题,所以不可能直接用Variable,应该是strategy内部给了某种专门用来reduce的接口。文档里特意又调用了一次schedule来拿到这个handler,然后才loss.fetch(),不得不说有点奇怪。

    tf不支持batch级的callback函数,这东西也确实不合理。

    文档说tf的数据必须在create_per_worker_dataset全部读完,不过从框架并没有对输入做假设来看,其实你完全可以自己启一个数据流,实现一个基于rpc的iterator。

    CentralStorageStrategy

    与mirrored类似,适用于单机多卡,不过Variable统一存放在CPU内存上,每次拷贝到GPU上做操作。目前处于试验阶段,我也想不出有什么适用场景。

    DefaultStrategy

    一个相当于啥也不干的壳子,主要用于测试代码。例如你不必一级一级往下传strategy,可以通过tf.distributed.get_strategy获取当前scope的strategy,用来操作reduce之类的,但在本地测试的时候可以先不声明真正的strategy,这时候get_strategy返回的就是Default Strategy,它是一个全局唯一的实例,不能自己声明。

    OneDeviceStrategy

    顾名思义只用一个device,与default差不多,不过会在run之前把声明在别处的Variable先拷贝到目标device上去。

【TF/Guide笔记】 09. Distributed training的评论 (共 条)

分享到微博请遵守国家法律