1.训练的瓶颈在哪里GPU利用率低:模型训练的时候GPU内存满了,但是GPU利用率比较不稳定,时而0%,时而90%%,忽高忽低。训练数据量大:训练数据量大,在百万/千万量级,训练一个Epoch需要很长时间,模型迭代周期过长。2.提高GPU利用率:CPUvsGPUGPU利用率低,主要原因是CPU处理效率跟不上GPU2.1CPUvsGPU通信CPU负责加载数据+数据预处理,不断在内存和显存之间交互数据GPU负责模型训练(图片来自网络)2.2方案采用多进程并行处理,加快CPU加载数据的性能。keraskeras提供workeruse_multiprocessing采用多进程模式,并行处理数据,推送到队列,共GPU模型训练。因为processes之间可能会互相影响资源,越大越好,workers可以设置为2、4、8。run_model.fit_generator(generator=training_generator,class_weight={0:config.weights,1:1},epochsepochs=epochs,verbose=1,steps_per_epochsteps_per_epoch=steps_per_epoch,callbacks=callbacks_list,validation_data=validuff_generator,validation_stepsvalidation_steps,le,Tr=ueworkers=8,use_multiprocessing=True,max_queue_size=20pytorchtorch在加载数据时提供了类似的参数num_workers。pin_memory=True可以直接加载到显存,不用显存torch.utils.data.DataLoader(image_datasets[x],batch_sizebatch_size=batch_size,shuffle=True,num_workers=8,pin_memory=True)3.分布式并行训练3.1并行模式时训练数据量大,可以采用多机多GPU的方式来提高训练效率。与hadoop、spark等分布式数据处理框架不同,深度学习训练由于涉及参数的前向传播和反向传播,有两种并行方式:模型并行(modelparallelism):分布式系统中的不同机器(GPU/CPU等))负责网络模型的不同部分,通常神经网络模型的不同网络层分配给不同的机器,或者同一层内的不同参数分配给不同的机器。一般是显卡装不下的超大模型,比如NLP模型。模型并行的缺点是层与层之间可能存在依赖关系,不能完全并行化。(图片来自网络)数据并行(dataparallelism):不同的机器有同一个模型的多个副本,每台机器分配不同的数据,然后所有机器的计算结果按照一定的方式组合起来。这更适合大数据的情况。数据并行要解决的问题是数据的切分和传输,以及参数的更新。3.2DataParallelismFacebook在《Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour》介绍了使用256个GPU进行ResNet-50网络“数据并行”训练的方法。数据切分:选择一个大的batch-size,按照worker的个数拆分,分发给不同的worker进行参数更新:参数更新有两种模式(1)parameterserver(2)ring环更新(serverless模式)3.2.1参数服务器模式参数服务器模式,见下图。每个worker执行完一批训练后,在对参数进行反向传播时,所有worker都会将参数传给parameterserver,汇总计算均值后传给每个worker,进入第二批训练。(图片来自网络)参数服务器有一种或多种结构模式。可以看出,这种数据并行方式的效率是否提高取决于parameterserver和worker之间的通信效率,即最慢的worker的训练时间和parameterserver接收更新的时间参数,然后返回。如果worker的数量很大,参数服务器可能会出现瓶颈。(图片来自网络)3.2.2ring-reduce百度提出的ring-reduce摒弃了parameterserver,采用环形结构更新参数。Ring-reduce将所有worker形成一个相邻的环形结构。每个工人只与相邻工人交换参数。经过多次交换,所有worker都包含了其他worker的参数信息,达到更新的目的。(图片来自网络)在下面的图片中,可以看到几个步骤;为了加快ring-reduce,它不会一次性交换所有参数;相反,它首先划分参数并不断交换划分的参数。4.实现框架:HorovodHorovod是Uber开源的另一款深度学习工具。它的开发吸收了Facebook的“一小时训练ImageNet论文”和百度RingAllreduce的优点,可以帮助用户实现分布式训练。https://github.com/horovod/horovod使用NCCL替代百度的ring-allreduce实现。NCCL是NVIDIA的集体通信库,它提供了一个高度优化的ring-allreduce版本。NCCL2允许在多台机器上运行ring-allreduc。如果想将单机的训练代码修改为分布式代码,只需要几步就可以实现分布式训练的改造:horovod安装建议安装dockerhorovod,省去安装环境的麻烦。horovod依赖NCCL2打开MPI$mkdirhorovod-docker-gpu$wget-Ohorovod-docker-gpu/Dockerfilehttps://raw.githubusercontent.com/horovod/horovod/master/Dockerfile.gpu$dockerbuild-thorovod:latesthorovod-docker-gpuMachineworker机器间SSH修改训练代码horovod支持tf、keras、pytorch、mxnet等不同的深度学习框架。以keras为例,修改主要六步(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中,Dropout,Flattenfromkeras。layersimportConv2D,MaxPooling2DfromkerasimportbackendasKimportmathimporttensorflowastfimporthorovod.kerasashvd#Horovod:initializeHorovod.hvd.init()#Horovod:pinGPUtobeusedtoprocesslocalrank(oneGPUperprocess)config=tf.ConfigProto()config.gpu_options.allow_growth=Trueconfig.gpu_device_options.vis本地配置.gpu_device_options.vis.set_session(tf.Session(configconfig=config))batch_size=128num_classes=10#Horovod:adjustnumberofepochsbasedonnumberofGPUs.epochs=int(math.ceil(12.0/hvd.size()))#Inputimagedimensionsimg_rows,img_cols=28,28#数据,shuffledandsplitbetweentrainandtestsets(x_train,y_train),(x_test,y_test)=mnist.load_data()ifK.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/=255x_test/=255print('x_trainshape:',x_train.shape)print(x_train.shape[0],'trainsamples')print(x_test.shape[0],'testsamples')#Convertclassvectorstobinaryclassmatricesy_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:adjustlearningratebasedonnumberofGPUs.opt=keras.optimizers.Adadelta(1.0*hvd.size())#Horovod:addHorovodDistributedOptimizer.opt=hvd.DistributedOptimizer(opt)model.compile(loss=keras.losses.categorical_crossentropy,optoptimizer=opt,metrics=['accuracy'])callbacks=[#Horovod:broadcastinitialvariablestatesfromrank0toallotherprocesses.#Thisisnecessarytoensureconsistentinitializationofallworkerswhenstoredhcheckfrom#traindomaweightrestarted启动时.callbacks.BroadcastGlobalVariablesCallback(0),]#Horovod:savecheckpointsononlyonworker0topreventotherworkersfromcorruptingthem.ifhvd.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('Testloss:',score[0])print('测试准确性:',score[1])使用horovodrun进行分布式训练horovodrun-np16-Hserver1:4,server2:4,server3:4,server4:4pythontrain.py5.Horovod框架用于改进深度学习训练和并行CPU数据加载和预处理,让GPU不再等待CPU。使用Horovod进行数据并行化,提高大数据量训练的迭代时间
