跟着吴恩达学深度学习:用Scala实现神经网络-第二课:用Scala实现多层神经网络

上一章我们讲了如何使用Scala实现LogisticRegression,这一张跟随着吴恩达的脚步我们用Scala实现基础的深度神经网络。顺便再提一下,吴恩达对于深度神经网络的解释是我如今听过的最清楚的课,感叹一句果然越是大牛知识解释得越清晰明了。

 

本文分为以下四个部分。按照软件开发top-down的思路,第一部分我先展示一下使用构建好的神经网络对Gas Censor数据进行分类的demo,这一部分重点关注接口的顶层设计,如何达到易用、简洁、逻辑清晰以及新手友好,这是软件工程一个比较难的领域,所以我的接口设计不一定是最优的,欢迎在评论区提出自己的想法,我们共用探讨~

 

第二个部分给出了NeuralNetworkModel类的具体实现。从接口的角度来看,NeuralNetworkModel实现了名为Model的trait(类似于Java中的interface),所有的model都拥有一些common protocol,在本项目中所有的model都拥有setLearningRate,setIterationTime,train,predict,accuracy,getCostHistory方法,此外NeuralNetworkModel也拥有自己独有的setHiddenLayerStructure,setOutputLayerStructure方法。

 

第三部分我们介绍了各种神经网络Layer的实现,包含ReluLayer,SigmoidLayer,TanhLayer这三个类。所有的layer都实现了名叫Layer的trait,提供etNumHiddenUnits,forward和backward三个方法。

 

最后一部分我们介绍了各种Utils类,代码以及功能注释都附在文章最后,感兴趣的可以自己看一下。我的GitHub地址为:https://github.com/pan5431333/coursera-deeplearning-practice-in-scala-remote,欢迎clone代码,指正问题,共同进步!

 

第一部分:demo介绍

 

首先我们先看看使用神经网络模型的使用demo。

package org.mengpan.deeplearning.demo

import breeze.stats.{mean, stddev}
import org.mengpan.deeplearning.data.{Cat, GasCensor}
import org.mengpan.deeplearning.helper.{CatDataHelper, DlCollection, GasCensorDataHelper}
import org.mengpan.deeplearning.model.{Model, NeuralNetworkModel, ShallowNeuralNetworkModel}
import org.mengpan.deeplearning.utils.{MyDict, NormalizeUtils, PlotUtils}

/**
  * Created by mengpan on 2017/8/15.
  */
object ClassThreeNeuralNetworkDemo extends App{
  
// Dataset Download Website:http://archive.ics.uci.edu/ml/machine-learning-databases/00224/
  //
加载Gas Censor的数据集
 
val data: DlCollection[GasCensor] = GasCensorDataHelper.getAllData

 
//归一化数据特征矩阵
 
val normalizedCatData = NormalizeUtils.normalizeBy(data){col =>
    (col - mean(col)) / stddev(col)
  }

 
//获取training settest set
 
val (training, test) = normalizedCatData.split(0.8)

 
//分别获取训练集和测试集的featurelabel
 
val trainingFeature = training.getFeatureAsMatrix
 
val trainingLabel = training.getLabelAsVector
 
val testFeature = test.getFeatureAsMatrix
 
val testLabel = test.getLabelAsVector

 
//初始化算法模型
 
val nnModel: Model = new NeuralNetworkModel()
    .setHiddenLayerStructure(
Map(
      (
200, MyDict.ACTIVATION_RELU),
     
(100, MyDict.ACTIVATION_RELU)
    ))
    .setOutputLayerStructure((
1, MyDict.ACTIVATION_SIGMOID))
    .setLearningRate(
0.01)
    .setIterationTime(
5000)

 
//用训练集的数据训练算法
 
val trainedModel: Model = nnModel.train(trainingFeature, trainingLabel)

 
//测试算法获得算法优劣指标
 
val yPredicted = trainedModel.predict(testFeature)
 
val trainYPredicted = trainedModel.predict(trainingFeature)

 
val testAccuracy = trainedModel.accuracy(testLabel, yPredicted)
 
val trainAccuracy = trainedModel.accuracy(trainingLabel, trainYPredicted)
  println(
"\n The trainaccuracy of this model is: " + trainAccuracy)
  println(
"\n The testaccuracy of this model is: " + testAccuracy)

 
//对算法的训练过程中cost与迭代次数变化关系进行画图
 
val costHistory = trainedModel.getCostHistory
  PlotUtils.plotCostHistory(
costHistory)
}

 

对于神经网络的模型接口,我们采用了Scala中典型的链式编程法:

//初始化算法模型
val nnModel: Model = new NeuralNetworkModel()
  .setHiddenLayerStructure(Map(
    (200, MyDict.ACTIVATION_RELU),
    (100, MyDict.ACTIVATION_RELU)
  ))
  .setOutputLayerStructure((1, MyDict.ACTIVATION_SIGMOID))
  .setLearningRate(0.01)
  .setIterationTime(5000)

 

setHiddenLayerStructure方法用于设置神经网络隐含层的结构,接受多个二元元祖为参数,二元元祖的第一个参数为该层隐含层层的神经单元个数;第二个参数为该层隐含层层的激活函数类型。如上图中构建的神经网络拥有两个隐含层,第一个隐含层拥有200个神经元,激活函数类型为ReLU,第二个隐含层拥有100个神经元,激活函数类型也为ReLU。setOutputLayerStructure方法接受一个二元元祖为参数,元祖的含义与前面相同。

 

第二部分:NeuralNetworkModel类的具体实现

 

首先我把此类中的重要的方法解释一下,完整的代码附在本部分最后面。首先我们来看一下train()方法:

override def train(feature: DenseMatrix[Double], label: DenseVector[Double]): NeuralNetworkModel.this.type = {
  val numExamples = feature.rows
  val inputDim = feature.cols

  logger.debug("hidden layers: " + hiddenLayers)
  logger.debug("output layer: " + outputLayer)

  //随机初始化模型参数
  var paramsList: List[(DenseMatrix[Double], DenseVector[Double])] =
    initializeParams(numExamples, inputDim, hiddenLayers, outputLayer)

  (0 until this.iterationTime).foreach{i =>
    val forwardResList: List[ForwardRes] = forward(feature, paramsList,
      hiddenLayers, outputLayer)

    logger.debug(forwardResList)

    val cost = calCost(forwardResList.last, label)
    if (i % 100 == 0) {
      logger.info("Cost in " + i + "th time of iteration: " + cost)
    }
    costHistory.put(i, cost)

    val backwardResList: List[BackwardRes] = backward(feature, label, forwardResList,
      paramsList, hiddenLayers, outputLayer)

    logger.debug(backwardResList)

    paramsList = updateParams(paramsList, this.learningRate, backwardResList, i, cost)
  }

  this.paramsList = paramsList
  this
}

 

可以看到,在神经网络模型的train()方法中,有五个主要的私有功能函数:首先用initializeParams(numExamples, inputDim, hiddenLayers, outputLayer)初始化参数;然后在每一次迭代过程中:

l   先使用forward()计算前向传播的结果;

l   在使用calCost()计算此次的损失函数值;

l   然后使用backward()计算反向传播的结果;

l   最后使用updateParams()更新参数;

 

接下来看看随机初始化参数的方法init

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值