机器学习如何提高GPU利用率

前言

首先,如果你现在已经很熟悉tf.data+estimator了,可以把文章x掉了╮( ̄▽ ̄””)╭

但是!如果现在还是在进行session.run(…)的话!尤其是苦恼于GPU显存都塞满了利用率却上不去的童鞋,这篇文章或许可以给你打开新世界的大门噢( ̄∇ ̄)

如果发现经过一系列改良后训练效率大大提高了,记得回来给小夕发小红包( ̄∇ ̄)

不过,这并不是一篇怒贴一堆代码,言(三)简(言)意(两)赅(语)就结束的CSDN文风的文章。。。所以伸手党们也可以X掉了╮( ̄▽ ̄””)╭

缘起

很早很早之前,在小夕刚接触tensorflow和使用GPU加速计算的时候,就产生过一个疑惑。为什么显卡的显存都快满了,GPU利用率还显示这么低呢?好浪费呀,但是又无可奈何。当时GPU利用率100%的情况基本是仅存于一块显卡塞4、5个不费显存的小任务的情况。

大部分情况下写出来的代码train起来后是这样的:

在这里插入图片描述

可以看到,虽然显卡的显存都塞满了,但是显卡功率(最左边那一栏,114W和69W)和利用率(最右边那一栏,35%和38%)却远远没有达到极限。大部分人的想法是,算了算了这不重要,我去做实验了再见【wei笑】

然而!如果你在做大型实验,train一次跑几天呢?这个细节会极大的影响你的实验效率和DDL到来前的实验次数!想一下,完全一样的model和设置,你的代码要train一周,然而隔壁老王只需要train三天╮( ̄▽ ̄””)╭

路人甲:我有256张显卡
小夕:好了这篇文章你可以X掉了
那么,我们有没有可能一直这样呢:
在这里插入图片描述

是不是这功率和利用率看起来不可思议!不要怀疑这是PS的图!这只是小夕的日常截图!tricks用的好GPU利用率掉不下来99%,然鹅代码写的足够蠢,也可以上不去5%!

那么问题来了,到底是什么导致的这个差异呢?
不要急,我们来放大一下那些gpu利用率只有30%几的代码在训练时的gpu利用率的变化情况(好像句子有点长

watch -n 0.1 nvidia-smi
在这里插入图片描述
ps:(可能掉帧太严重了看着不连贯╮( ̄▽ ̄"")╭,建议在自己的机器上试一下,会直观的多~)
看!是不是一下子就发现问题啦?可以看到,其实gpu利用率并不是一直在比较低的水平,而是很有规律的周期性的从0涨到接近100再跌到0,再重新涨到100再跌回0。如果同时开着打印日志的窗口,你就会发现这个周期恰好跟每个训练step的时长一致!也就是说,在每个step,其实有一些时间并没有花在GPU里,那当然就是花在cpu里啦。

那在cpu里干什么的呢?当然就是load下一个batch、预处理这个batch以及在gpu上跑出结果后打印日志、后处理、写summary甚至保存模型等,这一系列的花销都要靠cpu去完成。回顾一下我们常写的代码:

create_graph()
create_model_saver()
create_summary_writer()
create_session()
do_init()
for i in range(num_train_steps):
    load_batch(...)                # cpu
    preprocess(...)                # cpu
    feed_dict = {...}              # cpu
    fetch_list = [...]             # cpu
    buf = session.run(fetch_list, feed_dict)    # gpu
    postprocess(buf)               # cpu
    print(...)                     # cpu
    if i % x == 0:
        summary_writer.write(...)  # cpu
    if i % xx == 0:
        model_saver.save(...)      # cpu

看,尤其是preprocess(…)任务比较重的话就容易导致代码在cpu里也要跑好一段时间,gpu利用率自然就会上不去而且呈现周期性变化啦。

那么有没有什么办法降低cpu时间,提高gpu时间呢?
一个很自(愚)然(蠢)的想法就是把一切训练代码都用tf的api重写不就好啦,甚至最外层的那个for i in range(num_train_steps)其实都可以用tf.while_loop重写呀。嗯,小夕还真的这么尝试过,然后发现

TF api这特喵的都是些什么鬼!各种跟numpy和python内置函数重名却行为不一致是什么鬼!卧槽这个api少了个参数我该怎么办?python里就一行代码就能搞定的事情我为什么写了几十行??

所以除了函数式编程的大牛,小夕极力的不建议重蹈覆辙!尤其是我们这些遇到汇编会哭,看到Lisp会崩溃的90后小仙女!

所以没办法把整个train loop都描述进计算图了?

别怕别怕,好在后来其实tensorflow已经封装了一个特别好(多)用(坑)的上层API来把整个train loop都能轻松的封装在计算图中,从而实现超级高的GPU利用率和训练效率!

Estimator

不用管它为啥叫Estimator,只需要知道,它把我们刚才想做的事情基本都给封装好了就行。把刚才的那个经典的写法搬过来

1. create_model()
2. create_model_saver()
3. create_summary_writer()
4. create_session()
5. do_init()
6. for i in range(num_train_steps):
7.      load_batch(...)                # cpu
8.      preprocess(...)                # cpu
9.      feed_dict = {...}              # cpu
10.     fetch_list = [...]             # cpu
11.     buf = session.run(fetch_list, feed_dict)    # gpu
12.     postprocess(buf)               # cpu
13.     print(...)                     # cpu
14.     if i % x == 0:
15.         summary_writer.write(...)  # cpu
16.     if i % xx == 0:
17.         model_saver.save(...)      # cpu

1-5行在estimator中都封装好啦,你只需要把相关配置塞进estimator的RunConfig就可以啦~

7-9行也封装好啦,你只需要把数据集载入和预处理的相关代码的函数塞给estimator.train的input_fn~

第10行也封装好啦,你只需要把要fetch的loss、train_op丢进estimator的EstimatorSpec~

第11行也封装好啦,你只需要把描述模型计算图的函数塞给estimator的model_fn~

第12-13行不用操心细节了,global_step和loss自动完成了,剩下的丢给tf.Print和LoggingTensorHook吧~

第14-17行不用你写了,自动完成了

╮(╯▽╰)╭

经过这么一顿折腾,我们发现GPU利用率大大提高啦~直逼80%甚至90%。那么还有没有可以压榨的空间呢?

其实这时仔细一分析就会发现虽然estimator把大部分的代码写进计算图里了,但是从数据的载入和预处理依然是在cpu里串行进行呀,而且比如一个batch有128个样本,那么estimaor内部在run每个step的时候还是要等着这128个样本串行的处理完才行。这显然就是最后的瓶颈啦!有没有办法消除掉呢?·当然有,那就是

tf.data

TF的dataset API可以说让人又爱又恨了,它确实看似提供了一种把整个预处理都搬进计算图进行并行化处理的途径,但是!如果你真的完全用tensorflow API来做复杂的预处理的话,真的会让人疯掉的QAQ因此,这里在用tf.data之前,小夕极力的建议先把数据集尽可能的transform成预处理后的样子,包括做分词、做截断、做word2id等,不过padding和input_mask可以留在TF里面做,毕竟都只需要一行。

那做完这些预处理后,数据该怎么存储会更方便后续的读取和处理呢?最最最建议的方式还是使用tf.records来存储,磁盘、内存的存储和IO效率都会相比传统方式更快一些,x和y也不用分开了。当然这样的唯一的坏处就是不能直接打开看数据集╮( ̄▽ ̄””)╭毕竟数据集被做成了二进制文件。

但是实在比较懒不想用tf.record的话,那么小夕极力建议把x和y分开存储,并且尽量让tf.data在读取数据的时候做完上面的那些必要的预处理,以避开难用的字符串基础操作API并且减轻训练时的cpu和内存压力。

tf.data还有一个很大的好处就是可以很天然的支持以streaming的方式读取数据,这样在面对大数据集时就不会发生数据load完后发现显卡被占的尴尬事件了╮( ̄▽ ̄””)╭

好像讲了这么久,还是没讲怎么用tf.data加速QAQ,来来来进入正题啦。

想想哈,没用tf.data的时候,我们写出来的代码实际跑起来就是这个样子的:
在这里插入图片描述

这也是文章开头小夕解释的为什么gpu利用率上不去并且周期性变化的重要原因。那么我们可以不可以消除idle,像下面这样让prepare和train的过程并行进行呢?
在这里插入图片描述

当然可以!那就是

prefetch

从prefetch的意思就可以理解,那就是预先获取下一个step要load的batch。使用tf.data里面的叫做prefetch的神奇api就可以轻松完成啦,这个api里的参数buffer_size就是讲的是额外的fetch多少份,比如buffer_size=1,然后我们要prefetch的是batch的话,那么模型每次prepare完一个batch后,就会自动再额外的prepare一个batch,这样下一个train step到来的时候就可以直接从内存中取走这个事先prepare好的batch啦。(详情见后面)

等下,看上图的话,有木有发现,如果prepare一个batch耗时很短的话确实两全齐美,但是如果耗时比较久,尤其一下子prefetch好几个batch的话,一旦prepare的用时超过了train一个step的用时,那么每个train step的性能就会受限于prepare的效率啦。放大一下这个问题的话如下图所示
在这里插入图片描述

看,prepare用时太久反而会导致train完一个step后gpu空闲了(虽然其实下个step的batch可能已经prepare好了)

那么能不能确保prepare阶段的用时小于train阶段的用时呢?

parallel mapping

一个很简单的想法当然就是让样本并行处理啦~如果batch size是128,prefetch size=1,那么准备一个batch要串行的跑128*2=256次的预处理,但是如果我们开4个线程去跑,是不是就看起来快多啦。幸运的是我们也不用自己手撸多线程了,tf.data.Dataset在map(预处理)函数里有一个参数num_parallel_calls,给这个参数赋值就可以并行parse啦。如图,
在这里插入图片描述

这样的话只要prefetch的buffer_size和map的num_parrellel_calls取得合适,基本就可以实现不间断的train啦,也就是几乎达到100%的GPU利用率!

好啦,思想明白了,代码就容易理解啦。不使用tf.record,直接从预处理好的纯文本格式的数据集load数据时的典型过程如下

def build_input(..):
    x = tf.data.XXDataset(..)
    x = x.map(..., num_parallel_calls=N)        # parellel

    y = tf.data.XXDataset(..)
    y = y.map(..., num_parallel_calls=N)

    dataset = tf.data.Dataset.zip((x, y))
    dataset = dataset.repeat(num_epochs)    
    if is_train:
        dataset = dataset.shuffle(..)
    dataset = dataset.batch(batch_size)
    dataset = dataset.prefetch(buffer_size=1)   # prefetch
    iterator = dataset.make_xx_iterator()
    return iterator.get_next()

当然,如果用上tf.record后,就不用分别从x和y俩文件中读数据啦,感兴趣的童鞋可自行去了解一下。

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
1 简介1 1.1 自动语音识别:更好的沟通之桥 1 1.1.1 人类之间的交流 2 1.1.2 人机交流 2 1.2 语音识别系统的基本结构 4 1.3 全书结构 6 1.3.1 第一部分:传统声学模型6 1.3.2 第二部分:深度神经网络6 1.3.3 第三部分:语音识别中的DNN-HMM 混合系统7 1.3.4 第四部分:深度神经网络中的特征表示学习 7 1.3.5 第五部分:高级的深度模型 7 第一部分传统声学模型9 2 混合高斯模型10 2.1 随机变量10 2.2 高斯分布和混合高斯随机变量11 2.3 参数估计13 2.4 采用混合高斯分布对语音特征建模 15 3 隐马尔可夫模型及其变体17 3.1 介绍17 3.2 马尔可夫链19 3.3 序列与模型 20 3.3.1 隐马尔可夫模型的性质21 3.3.2 隐马尔可夫模型的仿真22 3.3.3 隐马尔可夫模型似然度的计算22 3.3.4 计算似然度的高效算法24 3.3.5 前向与后向递归式的证明25 3.4 期望zui大化算法及其在学习HMM 参数中的应用 26 3.4.1 期望zui大化算法介绍 26 3.4.2 使用EM 算法来学习HMM 参数——Baum-Welch 算法 28 3.5 用于解码HMM 状态序列的维特比算法32 3.5.1 动态规划和维特比算法32 3.5.2 用于解码HMM 状态的动态规划算法33 3.6 隐马尔可夫模型和生成语音识别模型的变体35 3.6.1 用于语音识别的GMM-HMM 模型 36 3.6.2 基于轨迹和隐藏动态模型的语音建模和识别37 3.6.3 使用生成模型HMM 及其变体解决语音识别问题 38 第二部分深度神经网络41 4 深度神经网络42 4.1 深度神经网络框架42 4.2 使用误差反向传播来进行参数训练 45 4.2.1 训练准则 45 4.2.2 训练算法46 4.3 实际应用50 4.3.1 数据预处理51 4.3.2 模型初始化52 4.3.3 权重衰减52 4.3.4 丢弃法 53 4.3.5 批量块大小的选择55 4.3.6 取样随机化56 4.3.7 惯性系数 57 4.3.8 学习率和停止准则58 4.3.9 网络结构59 4.3.10 可复现性与可重启性 59 5 高级模型初始化技术61 5.1 受限玻尔兹曼机61 5.1.1 受限玻尔兹曼机的属性63 5.1.2 受限玻尔兹曼机参数学习66 5.2 深度置信网络预训练 69 5.3 降噪自动编码器预训练71 5.4 鉴别性预训练74 5.5 混合预训练75 5.6 采用丢弃法的预训练 75 第三部分语音识别中的深度神经网络–隐马尔可夫混合模型77 6 深度神经网络–隐马尔可夫模型混合系统78 6.1 DNN-HMM 混合系统 78 6.1.1 结构78 6.1.2 用CD-DNN-HMM 解码80 6.1.3 CD-DNN-HMM 训练过程81 6.1.4 上下文窗口的影响83 6.2 CD-DNN-HMM 的关键模块及分析 85 6.2.1 进行比较和分析的数据集和实验85 6.2.2 对单音素或者三音素的状态进行建模 87 6.2.3 越深越好88 6.2.4 利用相邻的语音帧89 6.2.5 预训练 90 6.2.6 训练数据的标注质量的影响 90 6.2.7 调整转移概率 91 6.3 基于KL 距离的隐马尔可夫模型91 7 训练和解码的加速93 7.1 训练加速93 7.1.1 使用多GPU 流水线反向传播94 7.1.2 异步随机梯度下降97 7.1.3 增广拉格朗日算法及乘子方向交替算法100 7.1.4 减小模型规模 101 7.1.5 其他方法102 7.2 加速解码103 7.2.1 并行计算103 7.2.2 稀疏网络105 7.2.3 低秩近似107 7.2.4 用大尺寸DNN 训练小尺寸DNN108 7.2.5 多帧DNN 109 8 深度神经网络序列鉴别性训练111 8.1 序列鉴别性训练准则 111 8.1.1 zui大相互信息 112 8.1.2 增强型MMI 113 8.1.3 zui小音素错误/状态级zui小贝叶斯风险114 8.1.4 统一的公式115 8.2 具体实现中的考量116 8.2.1 词图产生116 8.2.2 词图补偿117 8.2.3 帧平滑 119 8.2.4 学习率调整119 8.2.5 训练准则选择 120 8.2.6 其他考量120 8.3 噪声对比估计 121 8.3.1 将概率密度估计问题转换为二分类设计问题121 8.3.2 拓展到未归一化的模型123 8.3.3 在深度学习网络训练中应用噪声对比估计算法 124 第四部分深度神经网络中的特征表示学习127 9 深度神经网络中的特征表示学习128 9.1 特征和分类器的联合学习128 9.2 特征层级129 9.3 使用随意输入特征的灵活性 133 9.4 特征的鲁棒性 134 9.4.1 对说话人变化的鲁棒性134 9.4.2 对环境变化的鲁棒性 135 9.5 对环境的鲁棒性137 9.5.1 对噪声的鲁棒性138 9.5.2 对语速变化的鲁棒性 140 9.6 缺乏严重信号失真情况下的推广能力141 10 深度神经网络和混合高斯模型的融合144 10.1 在GMM-HMM 系统中使用由DNN 衍生的特征144 10.1.1 使用Tandem 和瓶颈特征的GMM-HMM 模型144 10.1.2 DNN-HMM 混合系统与采用深度特征的GMM-HMM 系统的比较147 10.2 识别结果融合技术149 10.2.1 识别错误票选降低技术(ROVER) 149 10.2.2 分段条件随机场(SCARF) 151 10.2.3 zui小贝叶斯风险词图融合153 10.3 帧级别的声学分数融合153 10.4 多流语音识别 154 11 深度神经网络的自适应技术157 11.1 深度神经网络中的自适应问题157 11.2 线性变换159 11.2.1 线性输入网络.159 11.2.2 线性输出网络 159 11.3 线性隐层网络 161 11.4 保守训练162 11.4.1 L2 正则项163 11.4.2 KL 距离正则项163 11.4.3 减少每个说话人的模型开销 165 11.5 子空间方法167 11.5.1 通过主成分分析构建子空间 167 11.5.2 噪声感知、说话人感知及设备感知训练168 11.5.3 张量172 11.6 DNN 说话人自适应的效果172 11.6.1 基于KL 距离的正则化方法 173 11.6.2 说话人感知训练174 第五部分先进的深度学习模型177 12 深度神经网络中的表征共享和迁移178 12.1 多任务和迁移学习178 12.1.1 多任务学习 178 12.1.2 迁移学习180 12.2 多语言和跨语言语音识别180 12.2.1 基于Tandem 或瓶颈特征的跨语言语音识别181 12.2.2 共享隐层的多语言深度神经网络182 12.2.3 跨语言模型迁移185 12.3 语音识别中深度神经网络的多目标学习188 12.3.1 使用多任务学习的鲁棒语音识别188 12.3.2 使用多任务学习改善音素识别189 12.3.3 同时识别音素和字素(graphemes) 190 12.4 使用视听信息的鲁棒语音识别 190 13 循环神经网络及相关模型192 13.1 介绍192 13.2 基本循环神经网络中的状态-空间公式194 13.3 沿时反向传播学习算法195 13.3.1 zui小化目标函数 196 13.3.2 误差项的递归计算196 13.3.3 循环神经网络权重的更新197 13.4 一种用于学习循环神经网络的原始对偶技术199 13.4.1 循环神经网络学习的难点199 13.4.2 回声状态(Echo-State)性质及其充分条件 199 13.4.3 将循环神经网络的学习转化为带约束的优化问题 200 13.4.4 一种用于学习RNN 的原始对偶方法201 13.5 结合长短时记忆单元(LSTM)的循环神经网络203 13.5.1 动机与应用203 13.5.2 长短时记忆单元的神经元架构204 13.5.3 LSTM-RNN 的训练205 13.6 循环神经网络的对比分析205 13.6.1 信息流方向的对比:自上而下还是自下而上 206 13.6.2 信息表征的对比:集中式还是分布式208 13.6.3 解释能力的对比:隐含层推断还是端到端学习209 13.6.4 参数化方式的对比:吝啬参数集合还是大规模参数矩阵 209 13.6.5 模型学习方法的对比:变分推理还是梯度下降210 13.6.6 识别正确率的比较211 13.7 讨论212 14 计算型网络214 14.1 计算型网络214 14.2 前向计算215 14.3 模型训练 218 14.4 典型的计算节点222 14.4.1 无操作数的计算节点 223 14.4.2 含一个操作数的计算节点223 14.4.3 含两个操作数的计算节点228 14.4.4 用来计算统计量的计算节点类型235 14.5 卷积神经网络 236 14.6 循环连接 239 14.6.1 只在循环中一个接一个地处理样本240 14.6.2 同时处理多个句子242 14.6.3 创建任意的循环神经网络243 15 总结及未来研究方向245 15.1 路线图 245 15.1.1 语音识别中的深度神经网络启蒙245 15.1.2 深度神经网络训练和解码加速248 15.1.3 序列鉴别性训练248 15.1.4 特征处理249 15.1.5 自适应 250 15.1.6 多任务和迁移学习251 15.1.7 卷积神经网络 251 15.1.8 循环神经网络和长短时记忆神经网络251 15.1.9 其他深度模型 252 15.2 技术前沿和未来方向 252 15.2.1 技术前沿简析252 15.2.2 未来方向253

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值