更多深度文章,请关注:https://yq.aliyun.com/cloud

Keras是我最喜欢的Python深度学习框架,特别是在图像分类领域。我在很多地方都使用到了Keras,包括生产系统、我自己的深度学习项目,以及PyImageSearch博客。

我的新书“基于Keras的深度学习计算机视觉”有三分之二的篇幅都跟这个框架有关。然而,在该框架过程中遇到的最大的一个问题就是执行多GPU训练。

但是,这个问题将不复存在

随着Keras(v2.0.8)最新版本的发布,使用多GPU 训练深度神经网络将变得非常容易,就跟调用函数一样简单!

如何使用Keras进行多GPU训练

当我第一次使用Keras的时候,我深深爱上了它的API。它简单而又优雅,类似于scikit-learn。但是,它又非常强大,能够实现并训练最先进的深度神经网络。

然后,对于Keras我最失望的的地方之一就是在多GPU环境中非常难用。如果你使用的是Theano,请忘记这一点,因为多GPU训练不会发生。虽然TensorFlow可以实现的,但需要编写大量的代码以及做出大量的调整来使你的网络支持多GPU训练。我喜欢在执行多GPU训练的时候使用mxnet作为Keras的后端,但它需要配置很多东西。

这所有的一切都随着FrançoisChollet公告的出现而发生了改变,使用TensorFlow作为后端的多GPU支持现在已经放到了Keras v2.0.8中。这个功劳很大程度上归功于@kuza55和他们的keras-extras库。我使用这个多GPU功能已经有将近一年时间了,我非常高兴看到它成为Keras官方发布的一部分。

在这篇文章中,我将演示如何使用Keras、Python和深度学习来训练卷积神经网络进行图像分类。要获取相关代码,请访问这个网页中的“Downloads”章节。

MiniGoogLeNet深度学习架构


图1:MiniGoogLeNet架构是它的兄弟GoogLeNet/Inception的一个缩减版。

图1中,我们可以看到独立的convolution(左)、inception(中)和downsample(右)模块,下面则是由这些构建块构建而成的整个MiniGoogLeNet架构(底部)。我们将在本文后面的多GPU实验中使用MiniGoogLeNet架构。

MiniGoogLenet中的Inception模块是由Szegedy等人设计的原始Inception模块的一个变体。我最初是从@ericjang11和@pluskid的推文中了解到“Miniception”模块的,他们的推文详细描述了该模块和相关的MiniGoogLeNet架构。

然后,我用Keras和Python实现了MiniGoogLeNet架构,并将其作为“基于Keras的深度学习计算机视觉”一书的一部分。

对MiniGoogLeNet Keras实现的完整描述已经超出了本文的讨论范围,如果你对此感兴趣的话,请阅读我这本书。

用Keras和多GPU训练深度神经网络

下面我们开始使用Keras和多GPU来训练深度学习网络。

在开始之前,请确保你的环境中已经安装了Keras 2.0.8(或更高版本):

$ workon dl4cv
$ pip install --upgrade keras

创建一个新文件,将其命名为train.py,然后插入以下代码:

# set the matplotlib backend so figures can be saved in the background
# (uncomment the lines below if you are using a headless server)
# import matplotlib
# matplotlib.use("Agg")# import the necessary packages
from pyimagesearch.minigooglenet import MiniGoogLeNet
from sklearn.preprocessing import LabelBinarizer
from keras.preprocessing.image import ImageDataGenerator
from keras.callbacks import LearningRateScheduler
from keras.utils.training_utils import multi_gpu_model
from keras.optimizers import SGD
from keras.datasets import cifar10
import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np
import argparse

如果你使用的是无外设服务器,则需要取消第3行和第4行上注释符来配置matplotlib后端。 这能让matplotlib图保存到磁盘。 如果你没有使用无外设服务器(即键盘、鼠标、显示器已连接系统),则无需去掉注释符。

在这段代码中,导入了该脚本所需的包。第7行pyimagesearch模块导入MiniGoogLeNet。另一个需要注意的是第13行,我们导入了CIFAR10数据集。 这个帮助函数能让我们仅使用一行代码即可从磁盘加载CIFAR-10数据集。

现在我们来解析命令行参数:

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-o", "--output", required=True,help="path to output plot")
ap.add_argument("-g", "--gpus", type=int, default=1,help="# of GPUs to use for training")
args = vars(ap.parse_args())# grab the number of GPUs and store it in a conveience variable
G = args["gpus"]

我们在第20-25行使用argparse来解析一个必需的和一个可选的参数:

  • --output:训练完毕后输出图的路径。
  • --gpus:用于训练的GPU的个数。

在加载命令行参数后,为方便起见,我们将GPU数量保存在G中(第28行

接着,我们要初始化用于配置训练过程的两个重要变量,然后定义poly_decay,这是一个学习速率调度函数,类似于Caffe多项式学习速率衰减:


# definine the total number of epochs to train for along with the
# initial learning rate
NUM_EPOCHS = 70
INIT_LR = 5e-3def poly_decay(epoch):# initialize the maximum number of epochs, base learning rate,# and power of the polynomialmaxEpochs = NUM_EPOCHSbaseLR = INIT_LRpower = 1.0# compute the new learning rate based on polynomial decayalpha = baseLR * (1 - (epoch / float(maxEpochs))) ** power# return the new learning ratereturn alpha

设置NUM_EPOCHS = 70,这是训练数据通过网络(第32行)的迭代次数。我们还初始化了学习速率INIT_LR = 5e-3,这是我们在以前的试验中发现的值(第33行)。

这里,我们定义了poly_decay函数,这个函数类似于于Caffe的多项式学习速率衰减(第35-46行)。从本质上讲,这个函数更新了训练过程中的学习速度,在每次迭代之后能有效减少学习速率。 power = 1.0将把衰减从多项式变为线性变化。

接下来,我们将加载训练和测试数据,并将图像数据从整数转换为浮点数:

# load the training and testing data, converting the images from
# integers to floats
print("[INFO] loading CIFAR-10 data...")
((trainX, trainY), (testX, testY)) = cifar10.load_data()
trainX = trainX.astype("float")
testX = testX.astype("float")

接着对数据应用均值减法:

# apply mean subtraction to the data
mean = np.mean(trainX, axis=0)
trainX -= mean
testX -= mean

第56行,计算了所有训练图像的平均值,然后在第57和58行,将训练和测试集中的每个图像减去这个平均值。

然后,执行“独热编码(one-hot encoding)”:

# convert the labels from integers to vectors
lb = LabelBinarizer()
trainY = lb.fit_transform(trainY)
testY = lb.transform(testY)

独热编码将分类标签从单个整数转换为向量,这样,就可以对其应用分类交叉熵损失函数。 我们已经在第61-63行考虑到了这一点。

接下来,创建一个数据增强器和一组回调函数:

# construct the image generator for data augmentation and construct
# the set of callbacks
aug = ImageDataGenerator(width_shift_range=0.1,height_shift_range=0.1, horizontal_flip=True,fill_mode="nearest")
callbacks = [LearningRateScheduler(poly_decay)]

第67-69行,构建了图像生成器,用于数据扩充。数据扩充在是在训练过程中使用的一种方法,可通过对图像进行随机变换来随机改变图像。通过这些变换,网络将会持续看到增加的示例,这有助于网络更好地泛化到验证数据上,但同时也可能在训练集上表现得更差。 在多数情况下,这种权衡是值得的。

第70行创建了一个回调函数,这使得学习速率在每次迭代之后发生衰减。请注意,函数名为poly_decay

下面,我们来看看GPU变量:

# check to see if we are compiling using just a single GPU
if G <= 1:print("[INFO] training with 1 GPU...")model = MiniGoogLeNet.build(width=32, height=32, depth=3,classes=10)

如果GPU数量小于或等于1,则通过.build函数(第73-76行)来初始化model,否则在训练期间并行化模型:

# otherwise, we are compiling using multiple GPUs
else:print("[INFO] training with {} GPUs...".format(G))# we'll store a copy of the model on *every* GPU and then combine# the results from the gradient updates on the CPUwith tf.device("/cpu:0"):# initialize the modelmodel = MiniGoogLeNet.build(width=32, height=32, depth=3,classes=10)# make the model parallelmodel = multi_gpu_model(model, gpus=G)

在Keras中创建一个多GPU模型需要一些额外的代码,但不是很多!

首先,第84行,你会注意到我们已经指定使用CPU(而不是GPU)作为网络的上下文。为什么我们要用CPU呢?CPU可用于处理任何一种工作(比如在GPU内存上移动训练图像),而GPU本身则负责繁重的工作。在这种情况下,CPU将用于实例化基本模型。

然后,在第90行调用multi_gpu_model。 该函数将模型从CPU复制到所有的GPU上,从而获得单机多GPU的数据并行环境。

在训练网络图像的时候,训练任务将分批到每个GPU上执行。CPU将从每个GPU上获取梯度,然后执行梯度更新步骤。

然后,可以编译模型并启动训练过程了:

# initialize the optimizer and model
print("[INFO] compiling model...")
opt = SGD(lr=INIT_LR, momentum=0.9)
model.compile(loss="categorical_crossentropy", optimizer=opt,metrics=["accuracy"])# train the network
print("[INFO] training network...")
H = model.fit_generator(aug.flow(trainX, trainY, batch_size=64 * G),validation_data=(testX, testY),steps_per_epoch=len(trainX) // (64 * G),epochs=NUM_EPOCHS,callbacks=callbacks, verbose=2)

第94行,我们构建了随机梯度下降(SGD)优化器。随后,我们用SGD优化器和分类交叉熵损失函数来编译模型。

现在,我们要开始训练网络了!

要启动训练,我们调用了model.fit_generator并提供了必要的参数。我们希望每个GPU上的批处理大小为64,这是由batch_size=64 * G指定的。训练将持续70此迭代(我们之前指定的)。梯度更新的结果将合并到CPU上,然后在整个训练过程中应用在每个GPU上。

现在训练和测试完成了,让我们画出损失/准确性曲线,以使整个训练过程可视化:

# grab the history object dictionary
H = H.history# plot the training loss and accuracy
N = np.arange(0, len(H["loss"]))
plt.style.use("ggplot")
plt.figure()
plt.plot(N, H["loss"], label="train_loss")
plt.plot(N, H["val_loss"], label="test_loss")
plt.plot(N, H["acc"], label="train_acc")
plt.plot(N, H["val_acc"], label="test_acc")
plt.title("MiniGoogLeNet on CIFAR-10")
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy")
plt.legend()# save the figure
plt.savefig(args["output"])
plt.close()

最后一段代码只是使用matplotlib来绘制训练/测试的损失和准确性曲线(第112-121行),然后将数据保存到磁盘上(第124行)。

Keras的多GPU运行结果

来看下我们努力的结果。我们先在一个GPU上进行训练以获得基线结果:

$ python train.py --output single_gpu.png
[INFO] loading CIFAR-10 data...
[INFO] training with 1 GPU...
[INFO] compiling model...
[INFO] training network...
Epoch 1/70- 64s - loss: 1.4323 - acc: 0.4787 - val_loss: 1.1319 - val_acc: 0.5983
Epoch 2/70- 63s - loss: 1.0279 - acc: 0.6361 - val_loss: 0.9844 - val_acc: 0.6472
Epoch 3/70- 63s - loss: 0.8554 - acc: 0.6997 - val_loss: 1.5473 - val_acc: 0.5592
...
Epoch 68/70- 63s - loss: 0.0343 - acc: 0.9898 - val_loss: 0.3637 - val_acc: 0.9069
Epoch 69/70- 63s - loss: 0.0348 - acc: 0.9898 - val_loss: 0.3593 - val_acc: 0.9080
Epoch 70/70- 63s - loss: 0.0340 - acc: 0.9900 - val_loss: 0.3583 - val_acc: 0.9065
Using TensorFlow backend.real    74m10.603s
user    131m24.035s
sys     11m52.143s


图2:在单个GPU上使用CIFAR-10对MiniGoogLeNet网络进行训练和测试的实验结果。

在这个实验中,我在NVIDIA DevBox上使用了单个Titan X GPU进行了训练。 每一个迭代花了大概63秒,总训练时间为74分10秒

然后,执行以下命令在四个Titan X GPU上进行训练:

$ python train.py --output multi_gpu.png --gpus 4
[INFO] loading CIFAR-10 data...
[INFO] training with 4 GPUs...
[INFO] compiling model...
[INFO] training network...
Epoch 1/70- 21s - loss: 1.6793 - acc: 0.3793 - val_loss: 1.3692 - val_acc: 0.5026
Epoch 2/70- 16s - loss: 1.2814 - acc: 0.5356 - val_loss: 1.1252 - val_acc: 0.5998
Epoch 3/70- 16s - loss: 1.1109 - acc: 0.6019 - val_loss: 1.0074 - val_acc: 0.6465
...
Epoch 68/70- 16s - loss: 0.1615 - acc: 0.9469 - val_loss: 0.3654 - val_acc: 0.8852
Epoch 69/70- 16s - loss: 0.1605 - acc: 0.9466 - val_loss: 0.3604 - val_acc: 0.8863
Epoch 70/70- 16s - loss: 0.1569 - acc: 0.9487 - val_loss: 0.3603 - val_acc: 0.8877
Using TensorFlow backend.real    19m3.318s
user    104m3.270s
sys     7m48.890s


图3:针对CIFAR10数据集在多GPU(4个Titan X GPU)上使用Keras和MiniGoogLeNet的训练结果。训练结果与单GPU的训练结果差不多,训练时间减少约75%。

在这里,可以看到训练过程得到了准线性的提速:使用四个GPU,可以将每次迭代减少到16秒。整个网络的训练耗时19分3秒

正如你所看到的,使用Keras和多个GPU来训练深度神经网络不仅简单而且高效!

注意:在这种情况下,单GPU实验获得的精度略高于多GPU。这是因为在训练任何一种随机机器学习模型的时候,都会出现一些差异。如果将这些结果平均一下,那么它们(几乎)是相同的。

总结

在今天的博文中,我们学到了如何使用多个GPU来训练基于Keras的深度神经网络。利用多个GPU,我们获得了准线性的提速。为了验证这一点,我们使用CIFAR-10数据集训练了MiniGoogLeNet。使用单个GPU,单次迭代时间为63秒,总训练时间为74分10秒。然而,使用Keras和Python在多GPU上训练的时候,单次迭代时间缩短到了16秒,总训练时间为19分03秒

在Keras中启用多GPU训练就跟调用函数一样简单, 强烈建议你尽早使用多GPU进行训练。我猜想,在将来multi_gpu_model肯定会进一步得到改进,能让我们指定使用哪几个GPU进行训练,并最终实现多系统的训练。

文章原标题《How-To: Multi-GPU training with Keras, Python, and deep learning》,作者:Adrian Rosebrock,译者:夏天,审校:主题曲。

文章为简译,更为详细的内容,请查看原文

本文由北邮@爱可可-爱生活老师推荐,阿里云云栖社区组织翻译。

Keras多GPU训练指南相关推荐

  1. keras指定gpu_Keras多GPU训练指南

    摘要:随着Keras(v2.0.8)最新版本的发布,使用多GPU 训练深度神经网络将变得非常容易,就跟调用函数一样简单!利用多GPU,能够获得准线性的提速. Keras是我最喜欢的Python深度学习 ...

  2. 利用GPU训练的配置方法(Keras)

    利用GPU训练的配置方法(Keras) GPU的使用配置 配置GPU内存的占用率 本文主要针对Keras(Tensorflow2.0版本以后)深度学习框架如何对GPU进行配置,以及如何设置训练任务对G ...

  3. keras train_on_batch详解(train_on_batch的输出输入详解,train_on_batch多GPU训练详解,自定义学习率调整策略)

    利用 train_on_batch 精细管理训练过程 大部分使用 keras 的同学使用 fit() 或者 fit_generator() 进行模型训练, 这两个 api 对于刚接触深度学习的同学非常 ...

  4. 谷歌免费GPU训练星际2AI好难?你需要份debug指南

    Root 发自 凹非寺 量子位 出品 | 公众号 QbitAI 自从去年8月10号暴雪开放了星际争霸II的人工智能API之后,数不清的AI研究者前赴后继地加入了训练星际2 AI的队伍中. 但并非所有人 ...

  5. keras安装与配置指南_Keras-快速指南

    keras安装与配置指南 Keras-快速指南 (Keras - Quick Guide) Keras-简介 (Keras - Introduction ) Deep learning is one ...

  6. 学习笔记:Windows 下Keras安装和配置指南

    目录: 目录: Windows下Keras安装和配置指南 Keras 框架搭建 安装 本系列参考官方文档官方文档 这就是keras可以参考前篇:这就是keras 学习笔记 Keras:一些基本概念 一 ...

  7. 程序如何在两个gpu卡上并行运行_深度学习分布式训练相关介绍 - Part 1 多GPU训练...

    本篇文章主要是对深度学习中运用多GPU进行训练的一些基本的知识点进行的一个梳理 文章中的内容都是经过认真地分析,并且尽量做到有所考证 抛砖引玉,希望可以给大家有更多的启发,并能有所收获 介绍 大多数时 ...

  8. 远程GPU 使用指南(新)

    GPU 使用指南(新) by @yang 七月在线的 GPU 每周集中在周一和周四统一发送,本公告适用于有 GPU 的用户. Mac 用户可以使用本机的 terminal 软件来连接服务器.Windo ...

  9. TensorFlow 网络模型移植和训练指南

    TensorFlow 网络模型移植和训练指南(持续更新) 1.限制 tensorflow只兼容tensorflow1.15 3.网络迁移 3.1 使用 Estimator 迁移 关于估算器 Estim ...

最新文章

  1. 写于2018年底,有点丧
  2. 遗传算法对于神经网络的优生优育
  3. 用户都跑了,你却还分不清流失用户和流失率
  4. hibernate一对多
  5. Matlab中plot函数绘图基本用法
  6. MII/MDIO接口详解(转)
  7. mac mysql 列表_Mac终端操作Mysql,以及Mysql的操作方法详解
  8. 北森完成2.6亿美金融资,一体化×低代码引领HR数字化风潮
  9. 职场 | 工作五年之后,对技术和业务的思考
  10. 冻结html表格标题列,jQuery实现冻结表头的方法
  11. Spark稀疏向量和稠密向量
  12. 阿里免费开放一切AI算力的背后是什么?
  13. python隐式调用_python 的隐式指针特征与class inheritance
  14. 计算机毕业论文附录的模板,毕业论文附录格式要求
  15. Payment支付平台API接口文档
  16. 计算机多媒体教学一体机包括,学校使用多媒体教学触摸一体机应用效果的介绍...
  17. 电解电容(钽电容和吕电容)
  18. Unity_滚动文本字幕
  19. 什么是钉钉消息推送?
  20. 网上水果商城系统设计与实现

热门文章

  1. UML类图关系(泛化 、继承、实现、依赖、关联、聚合、组合)
  2. 公共技术点之 Java 反射 Reflection
  3. Java程序员从笨鸟到菜鸟之(十三)java网络通信编程
  4. 程序员面试题精选100题(32)-不能被继承的类[C/C++/C#]
  5. 程序员面试题精选100题(25)-在从1到n的正数中1出现的次数[算法]
  6. 深度学习(二十五)基于Mutil-Scale CNN的图片语义分割、法向量估计-ICCV 2015
  7. 【python图像处理】图像的缩放、旋转与翻转
  8. 用C语言实现三子棋游戏
  9. JavaScript- BOM, DOM
  10. OSChina 周六乱弹 —— 买楼出一块钱,你们出么?