Package

com.thoughtworks

deeplearning

Permalink

package deeplearning

Visibility
  1. Public
  2. All

Type Members

  1. trait Layer extends AnyRef

    Permalink

    一个Layer表示一个神经网络。每个Layer可以作为子网络被包含在其它Layer中,构成更复杂的神经网络。Layer的嵌套结构可以用来表示数学公式或粗粒度神经网络结构。 当神经网络被编写完成后,其中大部分元素都是占位符,当网络开始训练时数据才真正进入到网络。

    一个Layer表示一个神经网络。每个Layer可以作为子网络被包含在其它Layer中,构成更复杂的神经网络。Layer的嵌套结构可以用来表示数学公式或粗粒度神经网络结构。 当神经网络被编写完成后,其中大部分元素都是占位符,当网络开始训练时数据才真正进入到网络。

    Layer 的树结构
    val myLayer: Layer.Aux[Tape.Aux[Double, Double], Tape.Aux[Double, Double]] = {
      Times(
        Plus(
          Literal(1.0),
          Identity[Double, Double]()
        ),
        Weight(2.0)
      )
    }

    以上代码等价的数学公式可以用Symbolic写作:(1.0 + x) * 2.0.toWeight2.0.toWeight表示一个变量,其初始值是2,在神经网络迭代时,其值会更新。 TimesPlus都是 case class, 因此myLayer是一个case class构成的嵌套结构的树。TimesPlus都是占位符。

    Weight是一个包含权重的Layer,初始值是2.0Identity是一个输入和输出相同的Layer,它会将输入原样返回。Identity在这里是Input的占位符。 Literal是一个包含常量的Layer

    迭代

    网络每次训练称为一个迭代,分为forwardbackward两个阶段,构成一次完整的反向传播流程。

    forward

    Layer.Aux[A,B]中调用forward时,A是输入类型,B是输出类型,AB都是Tape。下面开始逐段解释代码:

    例如:

    val inputTape: Tape.Aux[Double, Double] = Literal(a)
    val outputTape = myLayer.forward(inputTape)

    当调用myLayer.forward(inputData)时,首先调用Timesforward,其伪代码如下:

    final case class Times(operand1: Layer, operand2: Layer) extends Layer {
      def forward(inputData: Tape): Output = {
        val upstream1 = operand1.forward(input)
        val upstream2 = operand2.forward(input)
        new Output(upstream1, upstream2)//这里忽略具体实现,而关注递归细节
      }
      final class Output(upstream1: Tape, upstream2: Tape) extends Tape { ... }
    }

    myLayer.operand1Plus,myLayer.operand2Weight,因此,upstream1upstream2分别是operand1operand2 forward 的结果。

    以此类推,Plusforward代码与Timesforward类似,当调用Plusforward时,operand1Literaloperand2Identity,这时会各自调用LiteralIdentityforward

    当调用Identityforward时会原样返回输入, Identityforward的伪代码如下:

    def forward(inputTape: Tape.Aux[Double, Double]) = inputTape

    所以Input即 数学公式(1.0 + x) * 2.0.toWeight 中的x,这样Input就被传递给了神经网络。

    myLayer.forward的返回值outputTapeTape类型,所以最终会生成一棵Tape构成的树,结构和myLayer一样。 因此,通过层层传播 myLayer.forward(inputTape)最终被Identity原样返回,组合进新生成的Tape树。

    outputTape 的包含forward 的计算结果,计算结果可以用来 backward 比如:

    try {
      val loss = outputTape.value
      outputTape.backward(loss)
      loss
    } finally {
      outputTape.close()
    }

    outputTape.value 是数学公式 (1.0 + x) * 2.0.toWeight 的计算结果。

    backward

    outputTape.backwardTimes.Outputbackward ,伪代码如下:

    case class Times(operand1: Layer, operand2: Layer) extends Layer {
      def forward = ...
      class Output(upstream1, upstream2) extends Tape {
        private def upstreamDelta1(outputDelta: Double) = ???
        private def upstreamDelta2(outputDelta: Double) = ???
        override protected def backward(outputDelta: Double): Unit = {
          upstream1.backward(upstreamDelta1(outputDelta))
          upstream2.backward(upstreamDelta2(outputDelta))
        }
      }
    }

    outputTape.upstream1outputTape.upstream2分别是operand1operand2 forward 的结果。然后outputTape.upstream1outputTape.upstream2分别进行backward

    以此类推,Plusbackward代码与Timesbackward类似,当调用Plusbackward时,upstream1upstream2分别是LiteralIdentity forward的结果,这时会各自调用upstream1upstream2backward

    Weightbackward时会更新自己,参考updateDouble

    Aux & Symbolic API

    Layer.Aux[A,B]表示Input的类型是AOutput的类型是BTape.Aux[C,D]表示Data的类型是CDelta的类型是DLayer.AuxType.Aux可以组合起来使用,比如Layer.Aux[Tape.Aux[A,B],Tape.Aux[C,D]]可以用来表示一个layer的输入类型是一个Tape,这个Tape的数据类型为Adelta类型为Blayer的输出类型是一个Tape,这个Tape的数据类型为Cdelta类型为D

    Aux是一种实现了type refinement的设计模式,可以用来限制类型参数的范围。

    通常我们不会手写Aux类型,因为我们可以使用Symbolic实现同样的功能,例如在用于符号方法内部变量和返回值时:Layer.Aux[Tape.Aux[INDArray, INDArray], Tape.Aux[INDArray, INDArrayINDArray @Symbolic 是等价的,所以我们经常使用Symbolic来替代Aux的写法。

    See also

    Symbolic

    Backpropagation

    type refinement

    aux pattern evolution

    aux pattern

Value Members

  1. object Layer

    Permalink

Ungrouped