Skip to content

Commit

Permalink
Save Keras-like model to pure keras or tensorflow protobuf. (intel-an…
Browse files Browse the repository at this point in the history
…alytics#1600)

* checkpoint

* some update

* refine api

* some update

* fix build fail

* meet code review

* style check

* fix typo

* fix style check
  • Loading branch information
qiuxin2012 committed Sep 9, 2019
1 parent c8000cd commit 1cdef2e
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 2 deletions.
18 changes: 18 additions & 0 deletions layers/Dense.scala
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,24 @@ class Dense[T: ClassTag](
override val inputShape: Shape = null)(implicit ev: TensorNumeric[T])
extends BigDLDense[T](outputDim, init, activation, wRegularizer, bRegularizer, bias,
inputShape) with Net {

override private[zoo] def toKeras2(dir: String): String = {
val params = Net.inputShapeToString(inputShape) ++
Net.activationToString(activation) ++
Net.param(getName()) ++
Net.param(bias, "use_bias") ++
Net.param(outputDim, "units")
Net.kerasDef(this, params)
}

override private[zoo] def getKerasWeights(): Array[Tensor[Float]] = {
val weights = this.parameters()._1
val kWeights = Array.tabulate(weights.length)(_ => Tensor[Float]())
weights(0) = weights(0).t().contiguous()
weights(0).cast[Float](kWeights(0).resizeAs(weights(0)))
weights(1).cast[Float](kWeights(1).resizeAs(weights(1)))
kWeights
}
}

object Dense {
Expand Down
11 changes: 10 additions & 1 deletion layers/Dropout.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,16 @@ class Dropout[T: ClassTag](
override val p: Double,
override val inputShape: Shape = null)
(implicit ev: TensorNumeric[T])
extends com.intel.analytics.bigdl.nn.keras.Dropout[T](p, inputShape) with Net {}
extends com.intel.analytics.bigdl.nn.keras.Dropout[T](p, inputShape) with Net {

override private[zoo] def toKeras2(dir: String): String = {
val params = Net.inputShapeToString(inputShape) ++
Net.param(getName()) ++
Net.param(p, "rate")
Net.kerasDef(this, params)
}

}

object Dropout {
def apply[@specialized(Float, Double) T: ClassTag](
Expand Down
35 changes: 35 additions & 0 deletions layers/LSTM.scala
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,41 @@ class LSTM[T: ClassTag](
uRegularizer = uRegularizer,
bRegularizer = bRegularizer)
}

override private[zoo] def toKeras2(dir: String): String = {
val params = Net.inputShapeToString(inputShape) ++
Net.activationToString(activation) ++
Net.activationToString(innerActivation, "recurrent_activation") ++
Net.param(returnSeq, "return_sequences") ++
Net.param(outputDimension, "units")
Net.param(getName())
Net.kerasDef(this, params)
}

override private[zoo] def getKerasWeights(): Array[Tensor[Float]] = {
val weights = this.parameters()._1
val kWeights = Array.tabulate(weights.length)(_ => Tensor[Float]())
weights(0) = weights(0).t().contiguous()
weights(2) = weights(2).t().contiguous()
weights(0).cast[Float](kWeights(0).resizeAs(weights(0)))
weights(2).cast[Float](kWeights(1).resizeAs(weights(2)))
weights(1).cast[Float](kWeights(2).resizeAs(weights(1)))
// map to keras's weight
switch(kWeights(0), 2)
switch(kWeights(1), 2)
switch(kWeights(2), 1)

kWeights
}

private def switch(t: Tensor[Float], dim: Int): Unit = {
val tmpWeight = t.narrow(dim, 1, outputDimension).clone()
tmpWeight.copy(t.narrow(dim, 1 + outputDimension, outputDimension))
t.narrow(dim, 1 + outputDimension, outputDimension)
.copy(t.narrow(dim, 2 * outputDimension + 1, outputDimension))
t.narrow(dim, 2 * outputDimension + 1, outputDimension).copy(tmpWeight)
}

}

object LSTM {
Expand Down
7 changes: 7 additions & 0 deletions layers/Permute.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ class Permute[T: ClassTag](
override val inputShape: Shape = null)(implicit ev: TensorNumeric[T])
extends BigDLPermute[T](
dims, inputShape) with Net {

override private[zoo] def toKeras2(dir: String): String = {
val params = Net.inputShapeToString(inputShape) ++
Net.param(getName()) ++
Net.arrayToString(dims, "dims")
Net.kerasDef(this, params)
}
}

object Permute {
Expand Down
7 changes: 7 additions & 0 deletions layers/Reshape.scala
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ class Reshape[T: ClassTag](
}
layer.asInstanceOf[AbstractModule[Tensor[T], Tensor[T], T]]
}

override private[zoo] def toKeras2(dir: String): String = {
val params = Net.inputShapeToString(inputShape) ++
Net.param(getName()) ++
Net.arrayToString(targetShape, "target_shape")
Net.kerasDef(this, params)
}
}

object Reshape {
Expand Down
28 changes: 27 additions & 1 deletion layers/utils/KerasUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import com.intel.analytics.bigdl.Criterion
import com.intel.analytics.bigdl.nn.Graph.ModuleNode
import com.intel.analytics.bigdl.nn._
import com.intel.analytics.bigdl.nn.keras.{KerasIdentityWrapper, KerasLayer, KerasLayerWrapper, Sequential => KSequential, SoftMax => KSoftMax}
import com.intel.analytics.bigdl.nn.abstractnn.{AbstractModule, Activity, DataFormat}
import com.intel.analytics.bigdl.nn.abstractnn.{AbstractModule, Activity, DataFormat, TensorModule}
import com.intel.analytics.bigdl.optim._
import com.intel.analytics.bigdl.tensor.Tensor
import com.intel.analytics.bigdl.tensor.TensorNumericMath.TensorNumeric
Expand Down Expand Up @@ -84,6 +84,32 @@ object KerasUtils {
}
}

def getActivationName[T: ClassTag](activation: AbstractModule[_, _, T]): String = {
if (activation == null) {
throw new IllegalArgumentException("activation is null")
} else {
activation match {
case _: Tanh[T] => "tanh"
case _: Sigmoid[T] => "sigmoid"
case _: ReLU[T] => "relu"
case _: com.intel.analytics.bigdl.nn.SoftMax[T] => "softmax"
case _: SoftPlus[T] => "softplus"
case _: SoftSign[T] => "softsign"
case _: HardSigmoid[T] => "hard_sigmoid"
case _: ReLU6[T] => "relu6"
case _: TanhShrink[T] => "tanh_shrink"
case _: SoftMin[T] => "softmin"
case _: LogSigmoid[T] => "log_sigmoid"
case _: LogSoftMax[T] => "log_softmax"
case _: Identity[T] => "linear"
case _: com.intel.analytics.zoo.pipeline.api.keras.layers.SoftMax[T] => "softmax"
case _ => throw new IllegalArgumentException("unkown activation"
+ activation.getClass.getName)
}
}

}

def getTorchActivation[T : ClassTag] (activation: String)
(implicit ev: TensorNumeric[T]): AbstractModule[Tensor[T], Tensor[T], T] = {
if (activation == null) null
Expand Down
36 changes: 36 additions & 0 deletions models/Topology.scala
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,29 @@ abstract class KerasNet[T](implicit val tag: ClassTag[T], implicit val ev: Tenso

def toModel(): Model[T]


/**
* Save model to keras2 h5 file. Only for inference
* @param filePath path to save model.
* @param python python path, need analytics-zoo and tensorflow installed.
*/
def saveToKeras2[T: ClassTag](
filePath: String,
python: String = "python")(implicit ev: TensorNumeric[T]): Unit = {
Net.saveToKeras2[T](this, filePath, python)
}

/**
* Save model to tensorflow protobuf. Only for inference.
* @param dir directory to save model.
* @param python python path, need analytics-zoo and tensorflow installed.
*/
def saveToTf[T: ClassTag](
dir: String,
python: String = "python")(implicit ev: TensorNumeric[T]): Unit = {
Net.saveToTf[T](this, dir, python)
}

/**
* Print out the summary information of an Analytics Zoo Keras Model.
*
Expand Down Expand Up @@ -892,6 +915,19 @@ class Sequential[T: ClassTag] private ()
val graph = this.toModel()
graph.summary(lineLength, positions)
}

override private[zoo] def getKerasWeights(): Array[Tensor[Float]] = {
val weights = new ArrayBuffer[Tensor[Float]]()
modules(0).asInstanceOf[TSequential[T]].modules.foreach(m => {
val params = m.asInstanceOf[Net].getKerasWeights()
if (params != null) {
params.foreach{p =>
weights += p
}
}
})
weights.toArray
}
}

object Sequential extends KerasLayerSerializable {
Expand Down

0 comments on commit 1cdef2e

Please sign in to comment.