[Deeplearning4j应用教程04]_基于DL4J的神经网络实现

一、内容简介

Deeplearning4j(也称为“ DL4J”)是一种高性能的特定领域语言,用于配置由多层构成的深度神经网络。 Deeplearning4j是开源的,使用C ++,Java,Scala和Python编写,并由Eclipse Foundation和社区贡献者维护。
通过本次内容的学习,我们将会掌握使用DL4J:
1、加载神经网络的数据集。
2、格式化EMNIST以进行图像识别。
3、创建一个深度神经网络。
4、训练模型。
5、评估模型的性能。

二、准备工程与环境

在本次教程当中,默认Maven已经安装好(若没有安装,可参考前面的环境搭建教程)。下面,开始配置Scala。
1、首先,我们需要在IDEA的插件上找到Scala。步骤为:File -> Settings(如果使用的是Mac版的IDEA,则是:IntelliJ IDEA -> Preferences)
在这里插入图片描述
2、在Plugins中搜索Scala。然后,点击Install进行安装。注意:安装完成后需要重启IDEA。
在这里插入图片描述
重启
在这里插入图片描述
3、新建带DL4J的Maven项目(若不熟悉如何创建,可参见此次:link),可参考的项目结构如下:
在这里插入图片描述
4、(可选)在项目下,新建文件夹“scala”(当然,也可以叫其他名字)用于放Scala文件。
在这里插入图片描述
5、(可选)修改工程结构,将scala文件夹变为源文件。步骤为:File -> Project Structure -> Modules,然后选中scala,选择Sources。最后点击OK完成修改。如下列图所示:
在这里插入图片描述
在这里插入图片描述
6、此时,我们还并不能直接创建“Scala Class”文件。因为,还需要导入Scala SDK。步骤为:File -> Project Structure -> Libraries,点击上方的“+”号进行添加。
在这里插入图片描述
选择相应的版本,进行添加,本教程中使用的是Scala2.11.8。
在这里插入图片描述

7、最后,我们就可以新建Scala Class文件了。接下来我们就可以正式去实行DL4J中的神经网络了。
在这里插入图片描述

三、准备工作空间

像大多数编程语言一样,我们需要将要使用的类显式地导入到作用域中。 在下面,我们将导入常见的Deeplearning4j类,这将帮助我们配置和训练神经网络。 下面的代码是用Scala编写的。
请注意,我们从Scala的JavaConversions类导入方法,因为这将使我们能够使用本机Scala集合,同时保持与Deeplearning4j的Java集合的兼容性。

import scala.collection.JavaConversions._

import org.deeplearning4j.datasets.iterator._
import org.deeplearning4j.datasets.iterator.impl._
import org.deeplearning4j.nn.api._
import org.deeplearning4j.nn.multilayer._
import org.deeplearning4j.nn.graph._
import org.deeplearning4j.nn.conf._
import org.deeplearning4j.nn.conf.inputs._
import org.deeplearning4j.nn.conf.layers._
import org.deeplearning4j.nn.weights._
import org.deeplearning4j.optimize.listeners._
import org.deeplearning4j.datasets.datavec.RecordReaderMultiDataSetIterator
import org.nd4j.evaluation.classification._

import org.nd4j.linalg.learning.config._ // 可设置不同的更新器,比如Adam, Nesterovs等
import org.nd4j.linalg.activations.Activation // 定义不同的激活函数,比如RELU, SOFTMAX等
import org.nd4j.linalg.lossfunctions.LossFunctions // 均方误差, 多分类交叉熵损失等

四、准备要加载的数据

数据集迭代器是重要的代码段,可帮助在整个数据集中进行批处理和迭代,以进行神经网络的训练和推断。 Deeplearning4j带有EMNIST的BaseDatasetIterator的内置实现,称为EmnistDataSetIterator。 该特定的迭代器是一种便利实用程序,可以处理数据的下载和准备。
请注意,我们在下面创建了两个不同的迭代器,一个用于训练数据,另一个用于在训练后评估模型的准确性。 构造函数中的最后一个布尔参数指示我们是否要实例化测试/训练。
在本教程中,我们并不需要它,如果想要了解有关为神经网络加载数据的更多信息,我们可以在DL4J基础中找到。 DL4J带有许多记录读取器,它们可以从CSV,图像,视频,音频和序列中加载数据并将其转换为ND阵列。

具体代码如下:

import org.deeplearning4j.datasets.iterator.impl.EmnistDataSetIterator

object dl4j_minist {
    
    
    def main(args:Array[String]) {
    
    
val batchSize = 128 // 确定网络中要同时训练多少个样本
val emnistSet = EmnistDataSetIterator.Set.BALANCED //准备数据集
val emnistTrain = new EmnistDataSetIterator(emnistSet, batchSize, true) //训练数据
val emnistTest = new EmnistDataSetIterator(emnistSet, batchSize, false) //测试数据

}
}

五、神经网络的搭建

对于我们在Deeplearning4j中构建的任何神经网络,基础都是源于NeuralNetConfiguration类。 我们在这里配置超参数,定义网络体系结构的数量以及算法的学习方式。直观地讲,每个超参数就像做好一顿饭中的一种影响因素,比如:火候、时间等,影响着一顿饭是否能够做好。但幸运的是,如果超参数不能产生正确的结果,则可以对其进行调整。
list()方法指定网络中的层数; 此功能将我们的配置复制n次并构建分层配置。
我们都知道,什么网络整体上分为:输入层、输出层以及隐藏层。而其中,隐藏层是最为关键的地方。那么隐藏层到底是什么呢?首先,我们来看下基本的神经结构图:
在这里插入图片描述
隐藏层中的每个节点(圆圈)代表MNIST数据集中的手写数字特征。例如,假设我们输入的为手写数字6,那么一个节点可能代表圆形边缘特征,另一个节点可能代表卷曲线的交点特征,依此类推。 这些功能由模型系数按重要性进行加权,并在每个隐藏层中重新组合以帮助预测手写数字是否确实为6。节点的层数越多,它们可以捕获的复杂性和细微差别就越多,可以做出更好的预测。
我们可以将此层视为“隐藏”的层,因为尽管我们可以看到输入进入了网络,并且做出了决策,但人类很难理解神经网络如何在内部处理数据以及为什么在内部进行处理。 神经网络模型的参数只是机器可以读取的数字的长向量。了解完隐藏层后,我们就可以正式进行网络的搭建了。代码如下:

val outputNum = EmnistDataSetIterator.numLabels(emnistSet) // 总的输出类别
val rngSeed = 123 // 随机数种子,用于再现随机数
val numRows = 28 // 手写数字图片中每行的像素数量
val numColumns = 28 //手写数字图片中每列的像素数量

//神经网络搭建
val conf = new NeuralNetConfiguration.Builder()
            .seed(rngSeed)
            .updater(new Adam()) //优化器使用Adam
            .l2(1e-4) // L2正则
            .list() //指定网络层数
            .layer(new DenseLayer.Builder()
                .nIn(numRows * numColumns) // 输入数据点数。
                .nOut(1000) //输出数据点数。
                .activation(Activation.RELU) // 激活函数
                .weightInit(WeightInit.XAVIER) // 权重初始化方法,使用Xavier
                .build())
            .layer(new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD) //损失函数,使用负对数似然
                .nIn(1000) //输入数据点数
                .nOut(outputNum) //输出对应数据中类别数量
                .activation(Activation.SOFTMAX) //激活函数,使用softMax
                .weightInit(WeightInit.XAVIER) //权重初始化方法,使用Xavier
                .build())
            .build()

训练过程截图如下:
在这里插入图片描述

六、训练神经网络模型

现在,我们已经建立了NeuralNetConfiguration,那么,就可以使用该配置实例化MultiLayerNetwork了。当我们在网络上调用init()方法时,它将在网络上应用选定的权重初始化,并允许我们传递数据进行训练。 如果我们想在训练过程中看到损失分数,我们还可以将监听器传递给网络。
实例化的模型具有fit()方法。该方法接受数据集迭代器(扩展BaseDatasetIterator的迭代器),单个DataSet或ND-Array(INDArray的实现)。 由于我们的EMNIST迭代器已经扩展了迭代器基类,因此,我们可以直接通过它进行fit。 如果要进行多次迭代训练,可以将总的迭代次数放入fit()方法的第二个参数中。本次教程当中,我们采用第三种方式,进行100次迭代(如果只是为了练习或者学习,建议迭代次数为10左右,不然需要很长时间才能训练完)。

扫描二维码关注公众号,回复: 12194509 查看本文章

代码如下:

// 创建神经网络
val network = new MultiLayerNetwork(conf)
network.init()

// 调用训练监听器,该监听器每10次迭代报告一次得分
val eachIterations = 10
network.addListeners(new ScoreIterationListener(eachIterations))

// fit 数据并进行一次迭代
// network.fit(emnistTrain)

// 多次迭代
// val numEpochs = 2
// network.fit(emnistTrain, numEpochs)

// 使用循环,灵活控制迭代次数
for(i <- 1 to 100) {
    
    
   println("Epoch " + i + " / " + 100)
   network.fit(emnistTrain)
}

执行训练的过程,如下图所示:
在这里插入图片描述

七、评估模型

Deeplearning4j提供了多种工具来评估模型的性能。我们可以执行基本评估并获得诸如精度和准确性之类的指标,也可以使用ROC(Receiver Operating Characteristic)。 请注意,一般的ROC类适用于二分类,而ROCMultiClass则适用于多分类,例如我们在此构建的模型。
MultiLayerNetwork方便地内置了一些方法来帮助我们执行评估。我们可以将带有测试/验证数据的数据集迭代器传递给validate()方法。
代码如下:

// 评估模型的基本性能
val eval = network.evaluate[Evaluation](emnistTest)
println(eval.accuracy())
println(eval.precision())
println(eval.recall())

// 评估 ROC 以及计算AUC
val roc = network.evaluateROCMultiClass[ROCMultiClass](emnistTest, 0)
roc.calculateAUC(classIndex)

// (可选)我们可以打印评估中的所有统计信息
print(eval.stats())
print(roc.stats())

OK,到这里,我们基本上已经学习了如何使用Deeplearning4j进行最基本多神经网络搭建方法了。接下来,我们就可以迎接更高的挑战了,比如人脸识别、目标检测、文本分类、构建数据集迭代器等等,是不是已经迫不及待了呢!

最终结果如下图所示:

最后附上完整代码:

import scala.collection.JavaConversions._

import org.deeplearning4j.datasets.iterator._
import org.deeplearning4j.datasets.iterator.impl._
import org.deeplearning4j.nn.api._
import org.deeplearning4j.nn.multilayer._
import org.deeplearning4j.nn.graph._
import org.deeplearning4j.nn.conf._
import org.deeplearning4j.nn.conf.inputs._
import org.deeplearning4j.nn.conf.layers._
import org.deeplearning4j.nn.weights._
import org.deeplearning4j.optimize.listeners._
import org.deeplearning4j.datasets.datavec.RecordReaderMultiDataSetIterator
import org.nd4j.evaluation.classification._

import org.nd4j.linalg.learning.config._ // // 设置不同的更新器,比如Adam, Nesterovs等
import org.nd4j.linalg.activations.Activation // 定义不同的激活函数,比如RELU, SOFTMAX等
import org.nd4j.linalg.lossfunctions.LossFunctions // 均方误差, 多分类交叉熵损失等

object dl4j_minist{
    
    
  def main(args:Array[String]){
    
    
    val batchSize = 128 // 确定网络中要同时训练多少个样本
    val emnistSet = EmnistDataSetIterator.Set.BALANCED //准备数据集
    val emnistTrain = new EmnistDataSetIterator(emnistSet, batchSize, true) //训练数据
    val emnistTest = new EmnistDataSetIterator(emnistSet, batchSize, false) //测试数据
    val outputNum: Int = EmnistDataSetIterator.numLabels(emnistSet) // 总的输出类别
    val rngSeed = 123 // 随机数种子,用于再现随机数
    val numRows = 28 // 手写数字图片中每行的像素数量
    val numColumns = 28 //手写数字图片中每列的像素数量

    val conf: MultiLayerConfiguration = new NeuralNetConfiguration.Builder()
      .seed(rngSeed)
      .updater(new Adam(0.001)) //优化器使用Adam,学习率为0.001
      .l2(1e-4) // L2正则
      .list() //指定网络层数
      .layer(new DenseLayer.Builder()
        .nIn(numRows * numColumns) // 输入数据点数。
        .nOut(1000) // 输出数据点数。
        .activation(Activation.RELU) // 激活函数
        .weightInit(WeightInit.XAVIER) // 权重初始化方法,使用Xavier
        .build())
      .layer(new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD) //损失函数,使用负对数似然
        .nIn(1000) // 输入数据点数
        .nOut(outputNum) // 输出对应数据中类别数量
        .activation(Activation.SOFTMAX) // 激活函数,使用softMax
        .weightInit(WeightInit.XAVIER) // 权重初始化方法,使用Xavier
        .build())
      .build()
    // 创建神经网络
    val network = new MultiLayerNetwork(conf)
    network.init()

    // 调用训练监听器,该监听器每10次迭代报告一次得分
    val eachIterations = 10
    network.addListeners(new ScoreIterationListener(eachIterations))

    // fit 数据并进行一次迭代
    // network.fit(emnistTrain)

    // 多次迭代
    // val numEpochs = 2
    // network.fit(emnistTrain, numEpochs)

    // 使用循环,灵活控制迭代次数
    for(i <- 1 to 100) {
    
    
       println("Epoch " + i + " / " + 100)
       network.fit(emnistTrain)
    }
    // 评估模型的基本性能
    val eval = network.evaluate[Evaluation](emnistTest)
    println(eval.accuracy())
    println(eval.precision())
    println(eval.recall())

    // 评估 ROC 以及计算AUC
    val roc = network.evaluateROCMultiClass[ROCMultiClass](emnistTest, 0)
    roc.calculateAUC(0)

    //(可选)我们可以打印评估的所有统计信息
    print(eval.stats())
    print(roc.stats())
  }
}

猜你喜欢

转载自blog.csdn.net/weixin_33980484/article/details/112345367