From 746be931082423c3ca6d762bd348f268ae6a0ba8 Mon Sep 17 00:00:00 2001 From: Yuhao Yang Date: Tue, 20 Aug 2019 12:15:59 -0700 Subject: [PATCH] support multi input models for nnframes (#1553) * support multi input for nnframes * update ut * add doc and unit test * doc update * scala style --- .../bigdl/dllib/nnframes/NNClassifier.scala | 46 +++++++++++++++++ .../bigdl/dllib/nnframes/NNEstimator.scala | 50 +++++++++++++++++++ .../nnframes/python/PythonNNFrames.scala | 8 ++- 3 files changed, 102 insertions(+), 2 deletions(-) diff --git a/scala/dllib/src/main/scala/com/intel/analytics/bigdl/dllib/nnframes/NNClassifier.scala b/scala/dllib/src/main/scala/com/intel/analytics/bigdl/dllib/nnframes/NNClassifier.scala index 3a025f6cfaa..c274864c973 100644 --- a/scala/dllib/src/main/scala/com/intel/analytics/bigdl/dllib/nnframes/NNClassifier.scala +++ b/scala/dllib/src/main/scala/com/intel/analytics/bigdl/dllib/nnframes/NNClassifier.scala @@ -114,6 +114,32 @@ object NNClassifier { FeatureLabelPreprocessing(SeqToTensor(featureSize), ScalarToTensor())) } + + /** + * Construct a [[NNClassifier]] with multiple input sizes. The constructor is useful + * when the feature column and label column contains the following data types: + * Float, Double, Int, Array[Float], Array[Double], Array[Int] and MLlib Vector. The feature + * data are converted to Tensors with the specified sizes before sending to the model. + * + * This API is used for multi-input model, where user need to specify the tensor sizes for + * each of the model input. + * + * @param model module to be optimized + * @param criterion criterion method + * @param featureSize The sizes (Tensor dimensions) of the feature data. + */ + def apply[T: ClassTag]( + model: Module[T], + criterion: Criterion[T], + featureSize : Array[Array[Int]] + )(implicit ev: TensorNumeric[T]): NNClassifier[T] = { + new NNClassifier(model, criterion) + .setSamplePreprocessing(FeatureLabelPreprocessing( + SeqToMultipleTensors(featureSize), ScalarToTensor() + ) + ) + } + /** * Construct a [[NNClassifier]] with a feature Preprocessing. * @@ -216,6 +242,26 @@ object NNClassifierModel extends MLReadable[NNClassifierModel[_]] { .setSamplePreprocessing(SeqToTensor(featureSize) -> TensorToSample()) } + /** + * Construct a [[NNClassifierModel]] with sizes of multiple model inputs. The constructor is + * useful when the feature column contains the following data types: + * Float, Double, Int, Array[Float], Array[Double], Array[Int] and MLlib Vector. The feature + * data are converted to Tensors with the specified sizes before sending to the model. + * + * This API is used for multi-input model, where user need to specify the tensor sizes for + * each of the model input. + * + * @param model model to be used, which should be a multi-input model. + * @param featureSize The sizes (Tensor dimensions) of the feature data. + */ + def apply[T: ClassTag]( + model: Module[T], + featureSize : Array[Array[Int]] + )(implicit ev: TensorNumeric[T]): NNClassifierModel[T] = { + new NNClassifierModel(model) + .setSamplePreprocessing(SeqToMultipleTensors(featureSize) -> MultiTensorsToSample()) + } + /** * Construct a [[NNClassifierModel]] with a feature Preprocessing. * diff --git a/scala/dllib/src/main/scala/com/intel/analytics/bigdl/dllib/nnframes/NNEstimator.scala b/scala/dllib/src/main/scala/com/intel/analytics/bigdl/dllib/nnframes/NNEstimator.scala index 95f83a515ee..025b9a96e7d 100644 --- a/scala/dllib/src/main/scala/com/intel/analytics/bigdl/dllib/nnframes/NNEstimator.scala +++ b/scala/dllib/src/main/scala/com/intel/analytics/bigdl/dllib/nnframes/NNEstimator.scala @@ -515,6 +515,35 @@ object NNEstimator { ) } + /** + * Construct a [[NNEstimator]] with a feature size and label size. The constructor is useful + * when the feature column and label column contains the following data types: + * Float, Double, Int, Array[Float], Array[Double], Array[Int] and MLlib Vector. The feature and + * label data are converted to Tensors with the specified sizes before sending to the model. + * + * This API is used for multi-input model, where user need to specify the tensor sizes for + * each of the model input. + * + * @param model BigDL module to be optimized + * @param criterion BigDL criterion method + * @param featureSize The sizes (Tensor dimensions) of the feature data. e.g. an image may be with + * width * height = 28 * 28, featureSize = Array(28, 28). + * @param labelSize The size (Tensor dimensions) of the label data. + */ + def apply[T: ClassTag]( + model: Module[T], + criterion: Criterion[T], + featureSize : Array[Array[Int]], + labelSize : Array[Int] + )(implicit ev: TensorNumeric[T]): NNEstimator[T] = { + new NNEstimator(model, criterion) + .setSamplePreprocessing(FeatureLabelPreprocessing( + SeqToMultipleTensors(featureSize), + SeqToTensor(labelSize) + ) + ) + } + /** * Construct a [[NNEstimator]] with a feature Preprocessing and label Preprocessing. * @@ -702,6 +731,27 @@ object NNModel extends MLReadable[NNModel[_]] { .setSamplePreprocessing(SeqToTensor(featureSize) -> TensorToSample()) } + + /** + * Construct a [[NNModel]] with sizes of multiple model inputs. The constructor is useful + * when the feature column contains the following data types: + * Float, Double, Int, Array[Float], Array[Double], Array[Int] and MLlib Vector. The feature + * data are converted to Tensors with the specified sizes before sending to the model. + * + * This API is used for multi-input model, where user need to specify the tensor sizes for + * each of the model input. + * + * @param model model to be used, which should be a multi-input model. + * @param featureSize The sizes (Tensor dimensions) of the feature data. + */ + def apply[T: ClassTag]( + model: Module[T], + featureSize : Array[Array[Int]] + )(implicit ev: TensorNumeric[T]): NNModel[T] = { + new NNModel(model) + .setSamplePreprocessing(SeqToMultipleTensors(featureSize) -> MultiTensorsToSample()) + } + /** * Construct a [[NNModel]] with a feature Preprocessing. * diff --git a/scala/dllib/src/main/scala/com/intel/analytics/bigdl/dllib/nnframes/python/PythonNNFrames.scala b/scala/dllib/src/main/scala/com/intel/analytics/bigdl/dllib/nnframes/python/PythonNNFrames.scala index 37b2a4be106..6a403694bfe 100644 --- a/scala/dllib/src/main/scala/com/intel/analytics/bigdl/dllib/nnframes/python/PythonNNFrames.scala +++ b/scala/dllib/src/main/scala/com/intel/analytics/bigdl/dllib/nnframes/python/PythonNNFrames.scala @@ -106,6 +106,10 @@ class PythonNNFrames[T: ClassTag](implicit ev: TensorNumeric[T]) extends PythonZ SeqToTensor(size.asScala.toArray) } + def createSeqToMultipleTensors(size: JArrayList[JArrayList[Int]]): SeqToMultipleTensors[T] = { + SeqToMultipleTensors(size.asScala.map(x => x.asScala.toArray).toArray) + } + def createArrayToTensor(size: JArrayList[Int]): ArrayToTensor[T] = { ArrayToTensor(size.asScala.toArray) } @@ -121,9 +125,9 @@ class PythonNNFrames[T: ClassTag](implicit ev: TensorNumeric[T]) extends PythonZ def createFeatureLabelPreprocessing( featureTransfomer: Preprocessing[Any, Tensor[T]], labelTransformer: Preprocessing[Any, Tensor[T]] - ): FeatureLabelPreprocessing[Any, Any, Sample[T]] = { + ): FeatureLabelPreprocessing[Any, Any, Any, Sample[T]] = { FeatureLabelPreprocessing(featureTransfomer, labelTransformer) - .asInstanceOf[FeatureLabelPreprocessing[Any, Any, Sample[T]]] + .asInstanceOf[FeatureLabelPreprocessing[Any, Any, Any, Sample[T]]] } def createChainedPreprocessing(list: JList[Preprocessing[Any, Any]]): Preprocessing[Any, Any] = {