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

本文介绍如何使用Scala实现吴恩达的深度学习课程中Logistic Regression的部分,包括项目结构、代码实现、数据处理和Demo。通过Scala的Breeze库进行向量化运算,构建Logistic Regression模型,实现训练、预测和评估,最终在CIFAR-10数据集上达到约62%的测试准确率。
摘要由CSDN通过智能技术生成


1.         Introduction

       2017年8月,前百度首席科学家吴恩达先生在twitter上宣布自己从百度离职后的第一个动作:在Coursera上推出一门从零开始构建神经网络的Deep Learning课程,一时间广为轰动。


       截止到今天(2017年8月17日星期四),本人已经注册该门课程并且完成了两周的课程学习和作业。在前两周的课程中,吴恩达先生利用Logistic Regression来深入浅出的说明了神经网络的工作原理,并且用通俗易懂的语言介绍了反向传播的原理即为链式求导。在第二周的编码作业中,学生被要求利用Python Notebook从头实现Logistic Regression模型,并且利用此模型对所给定的图像集进行二元分类,判断某张图片是否是猫,最终训练好的模型的Test Accuracy能达到70%。吴恩达先生还说,在接下来的课程中我们会进一步学习神经网络的优化方法,以进一步提高猫狗分辨的Accuracy。


       在编码测验中,吴恩达先生所强调的重点即为向量化运算,并且用实例说明了Python Numpy包的向量乘法比简单的for循环求和的速度快300多倍,这也意味着1分钟与5个小时的差距。然而,众所周知Python在实际的工程开发中更多是扮演者快速实验idea,快速得到结果的作用,一定程度上不适用于模型的正式开发及上线。本文中使用Scala实现吴恩达先生在Deep Learning课程中布置的所有作业,感谢您的阅读,期望共同进步。


       在Python中实现深度学习算法以及向量化运算所依赖的包叫做Numpy,即Number Python。Numpy中提供了Vector与Matrix的实现,以及矩阵的各种运算和分解的函数。对应地,在Scala中我们使用Breeze包,其中也提供了DenseVector和DenseMatrix的数据结构,并且在数据量特别稀疏的情况下还有SparseVector和SparseMatrix可供使用,一定程度上比Numpy更加强大。最重要地,作为静态类型语言Scala是类型安全的,意味着我们不仅可以用Scala来实现算法,还可以用其进行数据预处理和数据清洗,即ETL。


       本文分为四个部分。第一部分介绍整个项目结构;第二部分详细解释用Scala实现Logistic Regression的代码;第三部分给出其他功能性代码的解释,如数据预处理,画图工具,和一些其他的helper类;第四部分给出本文的demo结果和数据集的下载地址。另外,本项目的所有代码都可以在GitHub中找到,GitHub项目地址为https://github.com/pan5431333/coursera-deeplearning-practice-in-scala,跟随吴恩达先生的课程进度代码会及时保持更新,欢迎follow。

 

2.         项目结构

       本文拟使用的项目结构分为五个subpackage,分别为data,demo,helper,model和utils。


       data包中包含一个类Cat,其类型为Scala中的caseclass,特别适合用来表示真实世界中的一个entity。demo中即为每一节课后作业的运行实例;helper中现在包含两个类,CatDataHelper利用Java中的ImageIO从本地文件系统中读取图片,将其转化为RGB矩阵的表示形式,之后再reshape成向量形式。DlCollection为一个集合泛型类,其提供三个深度学习中常用的方法,分别为split,用来切分训练集和测试集;getFeatureAsMatrix返回算法所需要的特征矩阵;getLabelAsVector返回标签向量。Model包中现在仅包含Logistic Regression Model的实现。Utils包中现在有PlotUtils,其提供一个plotCostHistory方法,用来对cost随着迭代次数的变化情况画图。


       下面介绍Logistic Regression算法在Scala中的具体实现。

3.         Logistic Regression的Scala实战

首先,定义LogisticRegressionModel类:

classLogisticRegressionModel(){
 
var learningRate:Double= _
 
var iterationTime:Int = _
 
var w: DenseVector[Double] =_
 
var b:Double = _
 
val costHistory: mutable.TreeMap[Int, Double] =new mutable.TreeMap[Int,Double]()

此类包含五个InstanceVariables,其中前两个为超参数,learningRate表示学习率,iterationTime表示最大迭代次数;w和b即为模型参数,会随着迭代进行寻优;costHistory是一个用来保存迭代过程中cost变化情况的TreeMap,其key为迭代次数,value为cost值。

 

接下来是模型超参数的两个setter:

def setLearningRate(learningRate: Double): this.type = {
  this.learningRate = learningRate
  this
}

def setIterationTime(iterationTime: Int): this.type = {
  this.iterationTime = iterationTime
  this
}

注意这里的setter与Java中的setter不一样,我们采用了链式编程的开发模式,即用户在调用时可以写成:val model = new LogisticRegressionModel().setLearningRate(0.0001).setIterationTime(3000),会使得整个编码过程更加流畅。链式编程也在Spark中被广泛使用,特别是构造数据管道(Pipeline)时会显得很优雅。

 

接下来是模型训练方法:

def train(feature: DenseMatrix[Double], label: DenseVector[Double]): this.type = {

  var (w, b) = initializeParams(feature.cols)

  (1 to this.iterationTime)
    .foreach{i =>
      val (cost, dw, db) = propagate(feature, label, w, b)

      if (i % 100 == 0) println("INFO: Cost in " + i + "th time of iteration: " + cost)
      costHistory.put(i, cost)

      val adjustedLearningRate = this.learningRate / (log(i/1000 + 1) + 1)
      w :-= adjustedLearningRate * dw
      b -= adjustedLearningRate * db
    }

  this.w = w
  this.b = b
  this
}

注意在此方法中我们用了两个私有方法,分别为initializeParams()和propagate(),我们会在下面对这两个方法详细解释。另外,我们对learningRate进行了简单的调整,使其随着迭代次数的增加逐渐减小,以尽量减少寻优时跳过最优解的可能性。

 

接下来是模型参数初始化的方法:

private def initializeParams(featureSize: Int): (DenseVector[Double], Double) = {
  val w = DenseVector.rand[Double](featureSize)
  val b = DenseVector.rand[Double](1).data(0)
  (w, b)
}

这里我们对w和b赋予0到1之间的随机赋值。

 

接下来是LogisticRegression核心的正向传播与反向传播的实现方法:

  private def propagate(feature: DenseMatrix[Double], label: DenseVector[Double], w: DenseVector[Double], b: Double): (Double, DenseVector[Double], Double) = {
    val numExamples = feature.rows
    val labelHat = sigmoid(fe
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值