互联网资讯 / 人工智能 · 2023年11月8日 0

提高深度学习训练效率的两个调参技巧

1. 训练的瓶颈在哪里

GPU利用率低:模型训练时GPU显存沾满了,但是GPU的利用率比较不稳定,有时候0%,有时候90%,忽高忽低。

提高深度学习训练效率的两个调参技巧

训练的数据量大:训练数据大,在百万/千万的量级,训练一个Epoch需要很长时间,模型迭代周期过长。

2. 提高GPU利用率:CPU vs GPU

GPU利用率低, 主要原因是CPU处理的效率跟不上GPU

2.1 CPU vs GPU的通信

CPU负责加载数据+数据预处理,并不断的在内存和显存之间交互数据 GPU负责模型训练(图片来自网络)

提高深度学习训练效率的两个调参技巧

提高深度学习训练效率的两个调参技巧

2.2 解决方案

采用多进程并行处理,加快CPU加载数据的性能 keRas keRas 中提供了woRkeRs use_MultIPRoceSSing来采用多进程方式,并行处理数据,并pUSh到队列中,共GPU模型训练。因为进程之间可能相互影响资源,并不是越大越好,woRkeRs可以设置2,4,8。

3. 分布式并行训练

3.1 并行模式

当训练的数据量很大时,可以通过多个机器多个GPU来提高训练的效率。不同于hadoop和spaRk等分布式数据处理框架,深度学习训练因为要涉及参数的前项传播和反向传播,有两种并行方式:

模型并行( Model paRalleliSM ):分布式系统中的不同机器(GPU/CPU等)负责网络模型的不同部分,通常是神经网络模型的不同网络层被分配到不同的机器,或者同一层内部的不同参数被分配到不同机器。一般是超大的模型,一张显卡放不下的情况,如NLP的模型。模型并行的缺点是层和层之间可能存在依赖关系,不能完全的并行。(图片来自网络)

提高深度学习训练效率的两个调参技巧

数据并行( data paRalleliSM ):不同的机器有同一个模型的多个副本,每个机器分配到不同的数据,然后将所有机器的计算结果按照某种方式合并。这种就比较适合大数据的情况。数据并行要解决的问题是数据的分割和传输,以及参数的更新.

提高深度学习训练效率的两个调参技巧

3.2 数据并行

FACEbook在《AccuRate, LaRge miniBATch SGD: TRAIning imageNet in 1 HouR》介绍了使用 256 块 GPU 进行 ResNet-50 网络「数据并行」训练的方法

数据分割: 选用大的BATch-size, 按照woRkeR数量进行分割, 分发到不同woRkeR执行 参数更新:参数的更新有两种模式(1)参数服务器 (2) Ring环状更新(无服务器模式)

3.2.1 参数服务器模式

参数服务器模式,见下图。在每个woRkeR执行完一个BATch的训练后,反向传播参数的时候,所有的woRkeR都会把参数传给参数服务器,进行汇总求均值,之后再传给每个woRkeR,进入第二个BATch的训练。(图片来自网络)

提高深度学习训练效率的两个调参技巧

参数服务器有一个或者多个的结构模式,可以看出这种数据并行的模式效率是否提升取决于参数服务器与woRkeR之间的通信效率,也就是最慢的woRkeR的训练时间和参数服务器的接收和更新参数后再回传的时间。woRkeR数量多的话,参数服务器可能存在瓶颈。(图片来自网络)

提高深度学习训练效率的两个调参技巧

3.2.2 Ring-RedUCe

百度提出的Ring-RedUCe摒弃了参数服务器,采用环状结构来更新参数。Ring-RedUCe把所有的woRkeR组成一个两两相邻的环形结构。每个woRkeR只与相邻的woRkeR交换参数。经过几次交换之后,所有的woRkeR都包含其他woRkeR的参数信息,达到更新的目的。(图片来自网络)

提高深度学习训练效率的两个调参技巧

下面几张图,可以看到其中的几个步骤;Ring-RedUCe为了加快速度,并不是一次性交换所有的参数;而是先把参数进行分割,不断交换分割后参数。

提高深度学习训练效率的两个调参技巧

提高深度学习训练效率的两个调参技巧

提高深度学习训练效率的两个调参技巧

4. 实现框架:HoROVod

HoROVod 是 UbeR 开源的又一个深度学习工具,它的发展吸取了 FACEbook「一小时训练 imageNet 论文」与百度 Ring AllRedUCe 的优点,可为用户实现分布式训练提供帮助。

采用NCCL 替换百度的 Ring-allRedUCe 实现。NCCL 是英伟达的集合通信库,提供高度优化的 Ring-allRedUCe 版本。NCCL 2 允许在多个机器之间运行 Ring-allRedUC。

如果要把单机的训练代码修改成分布式的代码,只要几个步骤就可以了 改造分布式训练:

hoROVod安装 建议安装dockeR的hoROVod,省去安装环境的麻烦。hoROVod依赖NCCL 2 open MPI $MkdiR hoROVod-dockeR-gpu $wget -O hoROVod-dockeR-gpu/DockeRfile https://Raw.GithubUsercontent.coM/hoROVod/hoROVod/Master/DockeRfile.gpu $dockeR build -t hoROVod:latest hoROVod-dockeR-gpu 机器woRkeR机器之间SSh打通 修改训练代码 hoROVod支持tf,keRas,pyTorch和Mxnet等不同的深度学习框架。以keRas为例,修改主要6个步骤 (1) 初始化:hvd.inIT() (2)分配GPU计算资源:config.gpu_options.visible_device_list = stR(hvd.local_Rank())(3)分布式的优化器来实现参数的分布式更新:opt = hvd.DistRibutedOptiMizeR(opt)(4)定义所有woRkeR模型初始化一致性 hvd.callbacks.BRoadcastGlobalVaRiablesCallback(0)(5)模型保存在某一个woRkeR fRoM __futuRe__ iMpoRt pRint_function iMpoRt keRas fRoM keRas.datasets iMpoRt Mnist fRoM keRas.Models iMpoRt Sequential fRoM keRas.layeRs iMpoRt Dense, DRopout, Flatten fRoM keRas.layeRs iMpoRt Conv2D, MaxPooling2D fRoM keRas iMpoRt backend as K iMpoRt Math iMpoRt tensoRflow as tf iMpoRt hoROVod.keRas as hvd # HoROVod: inITialize HoROVod. hvd.inIT() # HoROVod: pin GPU to be USed to ProceSS local Rank (one GPU peR ProceSS) config = tf.ConfigPRoto() config.gpu_options.allow_gRowth = TRue config.gpu_options.visible_device_list = stR(hvd.local_Rank()) K.set_seSSion(tf.SeSSion(configconfig=config)) BATch_size = 128 nuM_claSSes = 10 # HoROVod: adjUSt nuMbeR of epochs based on nuMbeR of GPUS. epochs = int(Math.ceil(12.0 / hvd.size())) # Input image diMensions iMg_Rows, iMg_cols = 28, 28 # The data, shuFFled and splIT between tRAIn and test sets (x_tRAIn, y_tRAIn), (x_test, y_test) = Mnist.load_data() if K.iMage_data_foRMat() == ‘channels_fiRst’: x_tRAInx_tRAIn = x_tRAIn.Reshape(x_tRAIn.shape[0], 1, iMg_Rows, iMg_cols) x_testx_test = x_test.Reshape(x_test.shape[0], 1, iMg_Rows, iMg_cols) input_shape = (1, iMg_Rows, iMg_cols) else: x_tRAInx_tRAIn = x_tRAIn.Reshape(x_tRAIn.shape[0], iMg_Rows, iMg_cols, 1) x_testx_test = x_test.Reshape(x_test.shape[0], iMg_Rows, iMg_cols, 1) input_shape = (iMg_Rows, iMg_cols, 1) x_tRAInx_tRAIn = x_tRAIn.astype(‘float32’) x_testx_test = x_test.astype(‘float32’) x_tRAIn /= 255 x_test /= 255 pRint(‘x_tRAIn shape:’, x_tRAIn.shape) pRint(x_tRAIn.shape[0], ‘tRAIn saMples’) pRint(x_test.shape[0], ‘test saMples’) # ConveRt claSS vecTors to BInaRy claSS MatRices y_tRAIn = keRas.utils.to_categoRical(y_tRAIn, nuM_claSSes) y_test = keRas.utils.to_categoRical(y_test, nuM_claSSes) Model = Sequential() Model.add(Conv2D(32, keRnel_size=(3, 3), activation=’Relu’, input_shapEINput_shape=input_shape)) Model.add(Conv2D(64, (3, 3), activation=’Relu’)) Model.add(MaxPooling2D(pool_size=(2, 2)) Model.add(DRopout(0.25)) Model.add(Flatten()) Model.add(Dense(128, activation=’Relu’)) Model.add(DRopout(0.5)) Model.add(Dense(nuM_claSSes, activation=’softMax’)) # HoROVod: adjUSt leaRning Rate based on nuMbeR of GPUS. opt = keRas.optiMizeRs.Adadelta(1.0 * hvd.size()) # HoROVod: add HoROVod DistRibuted OptiMizeR. opt = hvd.DistRibutedOptiMizeR(opt) Model.coMpile(loSS=keRas.loSSes.categoRical_cRoSSentRopy, optoptiMizeR=opt, MetRics=[‘accuRacy’]) callbacks = [ # HoROVod: bRoadcast inITial vaRiable states fRoM Rank 0 to all otheR ProceSSes. # THis is neceSSaRy to ensuRe consistent inITialization of all woRkeRs when # tRAIning is staRted wITh Random weights oR ResTored fRoM a checkpoint. hvd.callbacks.BRoadcastGlobalVaRiablesCallback(0), ] # HoROVod: save checkpoints only on woRkeR 0 to pRevent otheR woRkeRs fRoM coRRupting theM. if hvd.Rank() == 0: callbacks.append(keRas.callbacks.ModelCheckpoint(‘./checkpoint-{epoch}.h5’)) Model.fIT(x_tRAIn, y_tRAIn, BATch_sizeBATch_size=BATch_size, callbackscallbacks=callbacks, epochsepochs=epochs, veRbose=1, validation_data=(x_test, y_test)) scoRe = Model.evaluate(x_test, y_test, veRbose=0) pRint(‘test loSS:’, scoRe[0]) pRint(‘test accuRacy:’, scoRe[1)

利用hoROVodRun 执行分布式训练

hoROVodRun -np 16 -H seRveR1:4,seRveR2:4,seRveR3:4,seRveR4:4 Python tRAIn.py

5. 总结

本文分享了通过GPU利用率和分布式训练HoRO