DeepLearning4j 实战——手写体数字识别GPU实现与性能比较

在之前的博客中已经用单机、Spark分布式两种训练的方式对深度神经网络进行训练,但其实DeepLearning4j也是支持多GPU训练的。这篇文章我就总结下用GPU来对DNN/CNN进行训练和评估过程。并且我会给出CPU、GPU和多卡GPU之前的性能比较图表。不过,由于重点在于说明Mnist数据集在GPU上训练的过程,所以对于一些环境的部署,比如Java环境和CUDA的安装就不再详细说明了。

软件环境的部署主要在于两个方面,一个是JDK的安装,另外一个是CUDA。目前最新版本的DeepLearning4j以及Nd4j支持CUDA-8.0,JDK的话1.7以上。

环境部署完后,分别用java -version和nvidia-smi来确认环境是否部署正确,如果出现类似以下的信息,则说明环境部署正确,否则需要重新安装。

GPU配置:

Java环境截图:

从系统返回的信息可以看到,jdk是openJDK1.7,GPU是2张P40的卡。

下面说明下代码的构成:

由于我这里用了DeepLearning4j最新的版本--v0.8,所以和之前博客的pom文件有些修改,具体如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>DeepLearning</groupId>
  <artifactId>DeepLearning</artifactId>
  <version>2.0</version>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <nd4j.version>0.8.0</nd4j.version>
  	<dl4j.version>0.8.0</dl4j.version>
  	<datavec.version>0.8.0</datavec.version>
  	<scala.binary.version>2.11</scala.binary.version>
  </properties>

 <dependencies>
	   <dependency>
	     <groupId>org.nd4j</groupId>
	     <artifactId>nd4j-native</artifactId>
	     <version>${nd4j.version}</version>
	   </dependency>
	   <dependency>
	        <groupId>org.deeplearning4j</groupId>
	        <artifactId>deeplearning4j-core</artifactId>
	        <version>${dl4j.version}</version>
	    </dependency>
	    <dependency>
		 <groupId>org.nd4j</groupId>
		 <artifactId>nd4j-cuda-8.0</artifactId>
		 <version>${nd4j.version}</version>
		</dependency>
		<dependency>
            <groupId>org.deeplearning4j</groupId>
            <artifactId>deeplearning4j-parallel-wrapper_${scala.binary.version}</artifactId>
            <version>${dl4j.version}</version>
        </dependency>
  	</dependencies>
  <build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>2.4</version>
            <configuration>
            	<source>1.7</source>
				<target>1.7</target>
                <archive>
                    <manifest>
                        <mainClass>cn.live.wangongxi.cv.CNNMnist</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
    </plugins>
</build>
</project>

创建完Maven工程以及添加了上面POM文件的内容之后,就可以开始着手上层应用逻辑的构建。这里我参考了官网的例子,具体由以下几个部分构成:

1.初始化CUDA的环境(底层逻辑包括硬件检测、CUDA版本校验和一些GPU参数)

2.读取Mnist二进制文件(和之前的博客内容一致)

3.CNN的定义,这里我还是用的LeNet

4.训练以及评估模型的指标

首先贴一下第一部分的代码:

    	//精度设置,常用精度有单、双、半精度
    	//HALF : 半精度
    	DataTypeUtil.setDTypeForContext(DataBuffer.Type.HALF);
    	//FLOAT : 单精度
    	//DataTypeUtil.setDTypeForContext(DataBuffer.Type.FLOAT);
    	//DOUBLE : 双精度
    	//DataTypeUtil.setDTypeForContext(DataBuffer.Type.DOUBLE);

    	//创建CUDA上下文实例并设置参数
        CudaEnvironment.getInstance().getConfiguration()
        	//是否允许多GPU
            .allowMultiGPU(false)
            //设置显存中缓存数据的容量,单位:字节
            .setMaximumDeviceCache(2L * 1024L * 1024L * 1024L)
            //是否允许多GPU间点对点(P2P)的内存访问
            .allowCrossDeviceAccess(false);

通常我们需要根据需要来设置GPU计算的精度,常用的就像代码中写的那样有单、双、半精度三种。通过选择DataBuffer中定义的enum类型Type中的值来达到设置精度的目的。如果不设置,默认的是单精度。

再下面就是设置CUDA的一些上下文参数,比如代码中罗列的cache数据的显存大小,P2P访问内存和多GPU运行的标志位等等。对于网络结构相对简单,数据量不大的情况下,默认的参数就够用了。这里我们也只是简单设置了几个参数,这对于用LeNet来训练Mnist数据集来说已经足够了。

从2~4部分的逻辑和之前的博客里几乎是一样的,就直接上代码了:

        int nChannels = 1;
        int outputNum = 10;

        int batchSize = 128;
        int nEpochs = 10;
        int iterations = 1;
        int seed = 123;

        log.info("Load data....");
        DataSetIterator mnistTrain = new MnistDataSetIterator(batchSize,true,12345);
        DataSetIterator mnistTest = new MnistDataSetIterator(batchSize,false,12345);

        log.info("Build model....");
        MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()
            .seed(seed)
            .iterations(iterations)
            .regularization(true).l2(0.0005)
            .learningRate(.01)
            .weightInit(WeightInit.XAVIER)
            .optimizationAlgo(OptimizationAlgorithm.STOCHASTIC_GRADIENT_DESCENT)
            .updater(Updater.NESTEROVS).momentum(0.9)
            .list()
            .layer(0, new ConvolutionLayer.Builder(5, 5)
                .nIn(nChannels)
                .stride(1, 1)
                .nOut(20)
                .activation(Activation.IDENTITY)
                .build())
            .layer(1, new SubsamplingLayer.Builder(SubsamplingLayer.PoolingType.MAX)
                .kernelSize(2,2)
                .stride(2,2)
                .build())
            .layer(2, new ConvolutionLayer.Builder(5, 5)
                .stride(1, 1)
                .nOut(50)
                .activation(Activation.IDENTITY)
                .build())
            .layer(3, new SubsamplingLayer.Builder(SubsamplingLayer.PoolingType.MAX)
                .kernelSize(2,2)
                .stride(2,2)
                .build())
            .layer(4, new DenseLayer.Builder().activation(Activation.RELU)
                .nOut(500).build())
            .layer(5, new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
                .nOut(outputNum)
                .activation(Activation.SOFTMAX)
                .build())
            .setInputType(InputType.convolutionalFlat(28,28,1))
            .backprop(true).pretrain(false).build();
        MultiLayerNetwork model = new MultiLayerNetwork(conf);
        model.init();
        log.info("Train model....");
        model.setListeners(new ScoreIterationListener(100));
        long timeX = System.currentTimeMillis();

        for( int i=0; i<nEpochs; i++ ) {
            long time1 = System.currentTimeMillis();
            model.fit(mnistTrain);
            long time2 = System.currentTimeMillis();
            log.info("*** Completed epoch {}, time: {} ***", i, (time2 - time1));
        }
        long timeY = System.currentTimeMillis();

        log.info("*** Training complete, time: {} ***", (timeY - timeX));

        log.info("Evaluate model....");
        Evaluation eval = new Evaluation(outputNum);
        while(mnistTest.hasNext()){
            DataSet ds = mnistTest.next();
            INDArray output = model.output(ds.getFeatureMatrix(), false);
            eval.eval(ds.getLabels(), output);
        }
        log.info(eval.stats());

        log.info("****************Example finished********************");

以上逻辑就是利用一块GPU卡进行Mnist数据集进行训练和评估的逻辑。如果想在多GPU下进行并行训练的话,需要修改一些设置,例如在之前第一步的创建CUDA环境上下文的时候,需要允许多GPU和P2P内存访问,即设置为true。然后在逻辑里添加并行训练的逻辑:

        ParallelWrapper wrapper = new ParallelWrapper.Builder(model)
            .prefetchBuffer(24)
            .workers(4)
            .averagingFrequency(3)
            .reportScoreAfterAveraging(true)
            .useLegacyAveraging(true)
            .build();

这样如果有多张GPU卡就可以进行单机多卡的并行训练。

下面贴一下训练Mnist数据集在CPU/GPU/多GPU下的性能比较还有训练时候的GPU使用情况:

单卡训练截图:

双卡并行训练截图:

训练时间评估:

最后做下简单的总结。由于Deeplearning4j本身支持GPU单卡,多卡以及集群的训练方式,而且对于底层的接口都已经进行了很多的封装,暴露的接口都是比较hig-level的接口,一般设置一些属性就可以了。当然前提是硬件包括CUDA都要正确安装。

时间: 2024-10-11 09:34:11

DeepLearning4j 实战——手写体数字识别GPU实现与性能比较的相关文章

TensorFlow 手写体数字识别

TensorFlow 手写体数字识别 • 手写体数字 MNIST 数据集介绍 MNIST 数据集介绍 MNIST 是一套手写体数字的图像数据集,包含 60,000 个训练样例和 10,000 个测试样例, 由纽约大学的 Yann LeCun 等人维护. • MNIST Softmax 网络介绍 • 实战 MNIST Softmax 网络 • MNIST CNN 网络介绍 • 实战 MNIST CNN 网络 原文地址:https://www.cnblogs.com/LXL616/p/1124795

机器学习进阶-项目实战-信用卡数字识别 1.cv2.findContour(找出轮廓) 2.cv2.boudingRect(轮廓外接矩阵位置) 3.cv2.threshold(图片二值化操作) 4.cv2.MORPH_TOPHAT(礼帽运算突出线条) 5.cv2.MORPH_CLOSE(闭运算图片内部膨胀) 6. cv2.resize(改变图像大小) 7.cv2.putText(在图片上放上文本)

7. cv2.putText(img, text, loc, text_font, font_scale, color, linestick) # 参数说明:img表示输入图片,text表示需要填写的文本str格式,loc表示文本在图中的位置,font_size可以使用cv2.FONT_HERSHEY_SIMPLEX, font_scale表示文本的规格,color表示文本颜色,linestick表示线条大小 信用卡数字识别: 信用卡      数字模板涉及到的内容:主要是采用模板匹配的思想 思

Deeplearning4j 实战(2):Deeplearning4j 手写体数字识别Spark实现

在前两天的博客中,我们用Deeplearning4j做了Mnist数据集的分类.算是第一个深度学习的应用.像Mnist数据集这样图片尺寸不大,而且是黑白的开源图片集在本地完成训练是可以的,毕竟我们用了Lenet这样相对简单的网络结构,而且本地的机器配置也有8G左右的内存.但实际生产中,图片的数量要多得多,尺寸也大得多,用的网络也会是AlexNet.GoogLenet这样更多层数的网络,所以往往我们需要用集群来解决计算资源的问题.由于Deeplearning4j本身基于Spark实现了神经网络的分

TensorFlow深度学习实战---MNIST数字识别问题

1.滑动平均模型: 用途:用于控制变量的更新幅度,使得模型在训练初期参数更新较快,在接近最优值处参数更新较慢,幅度较小 方式:主要通过不断更新衰减率来控制变量的更新幅度. 衰减率计算公式 : decay = min{init_decay , (1 + num_update) / (10 + num_update)} 其中 init_decay 为设置的初始衰减率 ,num_update 为模型参数更新次数,由此可见,随着 num_update 更新次数的增加,(1 + num_update) /

BP神经网络(手写数字识别)

1实验环境 实验环境:CPU [email protected],内存8G,windows10 64位操作系统 实现语言:python 实验数据:Mnist数据集 程序使用的数据库是mnist手写数字数据库,数据库有两个版本,一个是别人做好的.mat格式,训练数据有60000条,每条是一个784维的向量,是一张28*28图片按从上到下从左到右向量化后的结果,60000条数据是随机的.测试数据有10000条.另一个版本是图片版的,按0~9把训练集和测试集分为10个文件夹.这里选取.mat格式的数据

Tensorflow实战 手写数字识别(Tensorboard可视化)

一.前言 为了更好的理解Neural Network,本文使用Tensorflow实现一个最简单的神经网络,然后使用MNIST数据集进行测试.同时使用Tensorboard对训练过程进行可视化,算是打响学习Tensorflow的第一枪啦. 看本文之前,希望你已经具备机器学习和深度学习基础. 机器学习基础可以看我的系列博文: https://cuijiahua.com/blog/ml/ 深度学习基础可以看吴恩达老师的公开课: http://mooc.study.163.com/smartSpec/

keras入门实战:手写数字识别

近些年由于理论知识的硬件的快速发展,使得深度学习达到了空前的火热.深度学习已经在很多方面都成功得到了应用,尤其是在图像识别和分类领域,机器识别图像的能力甚至超过了人类. 本文用深度学习Python库Keras实现深度学习入门教程mnist手写数字识别.mnist手写数字识别是机器学习和深度学习领域的"hello world",MNIST数据集是手写数字的数据集合,训练集规模为60000,测试集为10000. 本文的内容包括: 如何用Keras加载MNIST数据集 对于MNIST问题如何

实战Google深度学习框架-C5-MNIST数字识别问题

5.1 MNIST数据处理 MNIST是NIST数据集的一个子集,包含60000张图片作为训练数据,10000张作为测试数据,其中每张图片代表0~9中的一个数字,图片大小为28*28(可以用一个28*28矩阵表示) 为了清楚表示,用下图14*14矩阵表示了,其实应该是28*28矩阵 TF提供了一个类来处理MNIST数据: 准备工作:桌面新建MNIST数字识别->cd MNIST数字识别->shift + 右键->在此处新建命令窗口->jupyter notebook->新建g

深度学习面试题12:LeNet(手写数字识别)

目录 神经网络的卷积.池化.拉伸 LeNet网络结构 LeNet在MNIST数据集上应用 参考资料 LeNet是卷积神经网络的祖师爷LeCun在1998年提出,用于解决手写数字识别的视觉任务.自那时起,CNN的最基本的架构就定下来了:卷积层.池化层.全连接层.如今各大深度学习框架中所使用的LeNet都是简化改进过的LeNet-5(-5表示具有5个层),和原始的LeNet有些许不同,比如把激活函数改为了现在很常用的ReLu. 神经网络的卷积.池化.拉伸 前面讲了卷积和池化,卷积层可以从图像中提取特