Slim模型部署多GPU
1、多GPU原理
单GPU时,思路很简单,前向、后向都在一个GPU上进行,模型参数更新时只涉及一个GPU。多GPU时,有模型并行和数据并行两种情况。模型并行指模型的不同部分在不同GPU上运行。数据并行指不同GPU上训练数据不同,但模型是同一个(相当于是同一个模型的副本)。TensorFlow支持的是数据并行。数据并行的原理:CPU负责梯度平均和参数更新,在GPU上训练模型的副本。多GPU并行计算的过程:
1)模型副本定义在指定的GPU/CPU上;
2)对于每一个GPU, 都是从CPU获得数据,前向传播进行计算,得到loss,并计算出梯度;
3)CPU接到GPU的梯度,取平均值,然后进行梯度更新。
这个在tf的实现思路如下:
模型参数保存在一个指定gpu/cpu上,模型参数的副本在不同gpu上,每次训练,提供batch_size*gpu_num数据,并等量拆分成多个batch,分别送入不同GPU。前向在不同gpu上进行,模型参数更新时,将多个GPU后向计算得到的梯度数据进行平均,并在指定GPU/CPU上利用梯度数据更新模型参数。假设有两个GPU(gpu0,gpu1),模型参数实际存放在cpu0上,实际一次训练过程如下图所示:
2、model_deploy.py文件及其用法
为了能让一个Slim模型在多个GPU上训练更加容易,这个模块提供了一系列帮助函数,比如create_clones()、optimize_clones()、deploy()、gather_clone_loss()、_add_gradients_summaries()、_sum_clones_gradients()等,该模块位于:https://github.com/tensorflow/models/blob/master/research/slim/deployment/model_deploy.py
详细步骤:
(1)创建DeploymentConfig对象:config = model_deploy.DeploymentConfig()
Deployment类定义的源码如下:
class DeploymentConfig(object):''' 这个配置类描述了如何将一个模型部署在多个单机的多个GPU上,在每个单机上,模型将挥被复制num_clones次'''def __init__(self,num_clones=1, clone_on_cpu=False,replica_id=0, num_replicas=1,num_ps_tasks=0,worker_job_name='worker',ps_job_name='ps'):参数:num_clones : 每个单机部署多少个clone(即部署在多少个GPU)clone_on_cpu : 如果为True,则单机中的每个clone将被放在CPU中replica_id : 整数,模型所部署的单机的索引,通常是0. num_replicas: 使用多少个单机,通常为1,表示单机部署。此时`worker_device`, `num_ps_tasks`和 `ps_device`这几个参数将被忽略。num_ps_tasks: ‘ps’作业(分布式作业)使用多少个单机,如果为0表示不使用单机worker_job_name: 默认为“worker”ps_job_name:默认为'ps'if num_replicas > 1:if num_ps_tasks < 1:raise ValueError('When using replicas num_ps_tasks must be positive')if num_replicas > 1 or num_ps_tasks > 0:if not worker_job_name:raise ValueError('Must specify worker_job_name when using replicas')if not ps_job_name:raise ValueError('Must specify ps_job_name when using parameter server')if replica_id >= num_replicas:raise ValueError('replica_id must be less than num_replicas')self._num_clones = num_clonesself._clone_on_cpu = clone_on_cpuself._replica_id = replica_idself._num_replicas = num_replicasself._num_ps_tasks = num_ps_tasksself._ps_device = '/job:' + ps_job_name if num_ps_tasks > 0 else ''self._worker_device = '/job:' + worker_job_name if num_ps_tasks > 0 else ''@propertydef num_clones(self):return self._num_clones@propertydef clone_on_cpu(self):return self._clone_on_cpu@propertydef replica_id(self):return self._replica_id@propertydef num_replicas(self):return self._num_replicas@propertydef num_ps_tasks(self):return self._num_ps_tasks@propertydef ps_device(self):return self._ps_device@propertydef worker_device(self):return self._worker_devicedef caching_device(self):"""缓存变量的设备 Returns:如果不需要被缓存则返回None,否则返回设备号"""if self._num_ps_tasks > 0:return lambda op: op.deviceelse:return Nonedef clone_device(self, clone_index):"""根据索引号返回用来创建克隆的设备号Args:clone_index: 克隆的索引值Returns:tf.device()的一个合适的值."""if clone_index >= self._num_clones:raise ValueError('clone_index must be less than num_clones')device = ''if self._num_ps_tasks > 0:device += self._worker_deviceif self._clone_on_cpu:device += '/device:CPU:0'else:device += '/device:GPU:%d' % clone_indexreturn devicedef clone_scope(self, clone_index):"""根据索引号返回所创建的克隆的scope名字Args:clone_index: 克隆的索引号Returns:tf.name_scope()的一个合适值."""if clone_index >= self._num_clones:raise ValueError('clone_index must be less than num_clones')scope = ''if self._num_clones > 1:scope = 'clone_%d' % clone_indexreturn scopedef optimizer_device(self):"""模型参数更新的设备,单机时为CPUReturns:tf.device()的一个合适值"""if self._num_ps_tasks > 0 or self._num_clones > 0:return self._worker_device + '/device:CPU:0'else:return ''def inputs_device(self):"""建立输入的设备,即读取数据的设备,单机时为CPUReturns:tf.device()的一个合适值"""device = ''if self._num_ps_tasks > 0:device += self._worker_devicedevice += '/device:CPU:0'return devicedef variables_device(self):"""创建模型变量的设备,单机时为CPUReturns:`tf.device()的一个合适值"""device = ''if self._num_ps_tasks > 0:device += self._ps_devicedevice += '/device:CPU:0'class _PSDeviceChooser(object):"""Slim device chooser for variables when using PS."""def __init__(self, device, tasks):self._device = deviceself._tasks = tasksself._task = 0def choose(self, op):if op.device:return op.devicenode_def = op if isinstance(op, tf.NodeDef) else op.node_defif node_def.op.startswith('Variable'):t = self._taskself._task = (self._task + 1) % self._tasksd = '%s/task:%d' % (self._device, t)return delse:return op.deviceif not self._num_ps_tasks:return deviceelse:chooser = _PSDeviceChooser(device, self._num_ps_tasks)return chooser.choose
(2) 获取数据集的输入数据
# 定义输入with tf.device(config.inputs_device()):images, labels = LoadData(...)inputs_queue = slim.data.prefetch_queue((images, labels))
(3) 创建模型并clone到多个GPU
with tf.device(config.variables_device()):global_step = tf.train.get_or_create_global_step()# 定义模型并创建多GPU克隆model_fn = build_modelmodel_args = ....# 创建多个克隆值,config对象、创造模型的函数以及函数的参数clones = model_deploy.create_clones(config, model_fn, args=model_args)
其中 create_clones()的源码如下:
Clone = collections.namedtuple('Clone',['outputs', # Whatever model_fn() returned.'scope', # The scope used to create it.'device', # The device used to create.])def create_clones(config, model_fn, args=None, kwargs=None):"""返回值是一个命名元组Clone的列表,Clone包装了model_fn函数的返回值、scope和device。注意,这里假设被model_fn创建的任何损失被加入到tf.GraphKeys.LOSSES collection中。为了恢复losses、summaries和updata_op,可以使用:losses = tf.get_collection(tf.GraphKeys.LOSSES, clone.scope)summaries = tf.get_collection(tf.GraphKeys.SUMMARIES, clone.scope)update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS, clone.scope)model_fn函数被调用了config.num_clones次,被克隆到多个GPU。Args:config: DeploymentConfig对象model_fn: 一个创建模型的函数,调用方式为:model_fn(*args, **kwargs)args: 传递给model_fn的可选的参数列表kwargs: 可选参数列表Returns:命名元组Clone的一个列表"""clones = []args = args or []kwargs = kwargs or {}with slim.arg_scope([slim.model_variable, slim.variable],device=config.variables_device()):# 创建克隆for i in range(0, config.num_clones):with tf.name_scope(config.clone_scope(i)) as clone_scope:clone_device = config.clone_device(i)with tf.device(clone_device):with tf.variable_scope(tf.get_variable_scope(),reuse=True if i > 0 else None):outputs = model_fn(*args, **kwargs)clones.append(Clone(outputs, clone_scope, clone_device))return clones
(4) 设置学习率和创建优化器
with tf.device(config.optimizer_device()):learning_rate = ...optimizer = tf.train.MomentumOptimizer(learning_rate, momentum)
(5) 计算总的损失计算梯度并进行权重更新
with tf.device(config.variables_device()):# 计算总共的损失和每个变量的梯度total_loss, grads_and_vars = model_deploy.optimize_clones(clones, optimizer)# 创建梯度更新操作grad_updates = optimizer.apply_gradients(grads_and_vars, global_step=global_step)
optimize_clones()方法的源码定义如下:
def _gather_clone_loss(clone, num_clones, regularization_losses):sum_loss = None# Individual components of the loss that will need summaries.clone_loss = Noneregularization_loss = None# Compute and aggregate losses on the clone device.with tf.device(clone.device):all_losses = []clone_losses = tf.get_collection(tf.GraphKeys.LOSSES, clone.scope)if clone_losses:clone_loss = tf.add_n(clone_losses, name='clone_loss')if num_clones > 1:clone_loss = tf.div(clone_loss, 1.0 * num_clones,name='scaled_clone_loss')all_losses.append(clone_loss)if regularization_losses:regularization_loss = tf.add_n(regularization_losses,name='regularization_loss')all_losses.append(regularization_loss)if all_losses:sum_loss = tf.add_n(all_losses)# Add the summaries out of the clone device block.if clone_loss is not None:tf.summary.scalar('/'.join(filter(None,['Losses', clone.scope, 'clone_loss'])),clone_loss)if regularization_loss is not None:tf.summary.scalar('Losses/regularization_loss', regularization_loss)return sum_lossdef _optimize_clone(optimizer, clone, num_clones, regularization_losses,**kwargs): sum_loss = _gather_clone_loss(clone, num_clones, regularization_losses)clone_grad = Noneif sum_loss is not None:with tf.device(clone.device):clone_grad = optimizer.compute_gradients(sum_loss, **kwargs)return sum_loss, clone_graddef optimize_clones(clones, optimizer,regularization_losses=None,**kwargs):Args:clones:由create_clones()函数创建的Clones列表optimizer: 一个 `Optimizer`对象regularization_losses: (可选)正则化损失列表,如果为None,则可以从tf.GraphKeys.REGULARIZATION_LOSSES获取。如果传入[],则表示忽略正则化损失。**kwargs: 可选参数Returns:一个元素:(total_loss, grads_and_vars).- total_loss: 一个Tensor,包含每个Clone的平均损失(包括正则化损失)。- grads_and_vars: 一个元组(gradient, variable)列表,包含每个变量总的梯度值。"""grads_and_vars = []clones_losses = []num_clones = len(clones)if regularization_losses is None:regularization_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)for clone in clones:with tf.name_scope(clone.scope):clone_loss, clone_grad = _optimize_clone(optimizer, clone, num_clones, regularization_losses, **kwargs)if clone_loss is not None:clones_losses.append(clone_loss)grads_and_vars.append(clone_grad)# Only use regularization_losses for the first cloneregularization_losses = None# Compute the total_loss summing all the clones_losses.total_loss = tf.add_n(clones_losses, name='total_loss')# Sum the gradients across clones.grads_and_vars = _sum_clones_gradients(grads_and_vars)return total_loss, grads_and_vars
(6) 开始训练:slim.learning.train()
Slim模型部署多GPU相关推荐
- 安全帽佩戴检测——从数据处理、训练数据到模型部署落地(带有数据集、训练代码,可使用GPU的C++模型部署代码)
前言 1.这是一个检测是否佩戴安全帽的完整项目,包含了数据集,训练代码,检测模型代码,转换模型与如何部署模型并应用到项目上的完整过程. 2.训练和开发环境是win10,显卡RTX3080;cuda10 ...
- TensorFlow与PyTorch模型部署性能比较
TensorFlow与PyTorch模型部署性能比较 前言 2022了,选 PyTorch 还是 TensorFlow?之前有一种说法:TensorFlow 适合业界,PyTorch 适合学界.这种说 ...
- tvm模型部署c++ 分析
tvm模型部署c++ 分析 tvm c++部署官方教程 https://github.com/apache/tvm/tree/main/apps/howto_deploy https://tvm.ap ...
- 深度学习模型部署简要介绍
一.模型部署简介 近几年来,随着算力的不断提升和数据的不断增长,深度学习算法有了长足的发展.深度学习算法也越来越多的应用在各个领域中,比如图像处理在安防领域和自动驾驶领域的应用,再比如语音处理和自然语 ...
- 如何将深度学习模型部署到实际工程中?(分类+检测+分割)
应用背景介绍 早在遥远的1989年,一家叫做ALVIVN的公司首次将神经网络用在汽车上,进行车道线检测和地面分割.时至今日,深度学习已经应用在自动驾驶系统的多个分支领域.首先是感知领域,常用的传感器有 ...
- 一文详解AI模型部署及工业落地方式
点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 Hello大家好,我是老潘,好久不见各位~ 最近在复盘今年上半年做的一些事情,不管是训练模型.部署模型 ...
- AI 框架部署方案之模型部署概述
0 概述 模型训练重点关注的是如何通过训练策略来得到一个性能更好的模型,其过程似乎包含着各种"玄学",被戏称为"炼丹".整个流程包含从训练样本的获取(包括数据采 ...
- 移动端video隐藏进度条_机器学习模型部署--打通前后端任督二脉
前言 没有 GPU,没有服务器?滴滴云 GPU 服务器,8GB内存/8GB显卡,优惠期内2200元包年.别急,输入AI 大师码:8754,折扣价基础上,再加9折优惠.快速链接:滴滴云 AI 特权 学历 ...
- 手把手教你使用TF服务将TensorFlow模型部署到生产环境
2019独角兽企业重金招聘Python工程师标准>>> 介绍 将机器学习(ML)模型应用于生产环境已成为一个火热的的话题,许多框架提供了旨在解决此问题的不同解决方案.为解决这一问题, ...
最新文章
- 计算机科学与技术 单片机,单片机-兰州交通大学计算机科学与技术实验教学中心...
- 软考已报名可以更改科目吗
- mysql 两张表合并查询_中级数据分析-多表查询
- freeMarker 遍历 list,map,listmap
- CyclicBarrier底层实现和原理
- 一次性存入多少钱就可以有资格跟银行商谈利息了?
- 使用JavaScript(jQuery或Vanilla)选中/取消选中复选框?
- linux根据端口号找目录,linux篇---根据端口号查看进程位置
- 【DirectX3D - 5】渲染多路视频(单一窗口)
- 集成海康威视Sadp SDK实现设备激活
- 微信小程序项目实例——打卡时钟
- 转载 ADB logcat 过滤方法(抓取日志)
- 计算机与网络技术 英语,计算机与网络英语词汇(S3)
- borlndmm.dll(宝兰妹妹)DELPHI核心DLL之一简单分析
- 中兴机试 通信算法方向 秋招春招
- opencv2读取摄像头并保存为视频
- 18级的谷歌卫星地图纹理如何匹配到15级的高程
- tomcat发布前端项目
- hdu 6217 BBP Formula
- 开启收费模式,ChatGPT 试行 20 美元即享 Plus 体验,谷歌慌了!
热门文章
- 多线程“死锁”之“哲学家就餐”代码实现
- 纵深防御体系建设-终端防护之EDR
- HTML5前端入门教程:Ajax 异步请求技术
- 坐标转换流程与公式 七参数 四参数
- layui如何实现枚举值
- Python Day_2
- 计算机网络技术与未来岗位的需求,2019-对计算机专业毕业生需求调查分析报告(精选多篇)-范文word版 (15页)...
- 2020年11月18日起施行!最高法发布《关于知识产权民事诉讼证据的若干规定》
- iview ui框架
- 颤抖了吗?九步逆向破解银行安全令牌