This is an automated email from the ASF dual-hosted git repository. niketanpansare pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/systemml.git
The following commit(s) were added to refs/heads/master by this push: new 7cab282 [SYSTEMML-540] Throw exception whenever parameter of a Keras layer is not supported by SystemML 7cab282 is described below commit 7cab282faa77b3bc66200396803f97ec1375544a Author: Niketan Pansare <npan...@us.ibm.com> AuthorDate: Fri Mar 22 19:21:40 2019 -0700 [SYSTEMML-540] Throw exception whenever parameter of a Keras layer is not supported by SystemML --- docs/reference-guide-caffe2dml.md | 15 ++++++ src/main/python/systemml/mllearn/keras2caffe.py | 64 +++++++++++++++++-------- 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/docs/reference-guide-caffe2dml.md b/docs/reference-guide-caffe2dml.md index 381b96d..6242e03 100644 --- a/docs/reference-guide-caffe2dml.md +++ b/docs/reference-guide-caffe2dml.md @@ -450,6 +450,21 @@ layer { ## Utility Layers +### Flatten Layer + +The Flatten layer is a utility layer that flattens an input of shape n * c * h * w to a simple vector output of shape n * (c*h*w). + + +**Sample Usage:** +``` +layer { + name: "flatten_1" + type: "Flatten" + bottom: "max_pooling2d_2" + top: "flatten_1" +} +``` + ### Eltwise Layer Element-wise operations such as product or sum between two blobs. diff --git a/src/main/python/systemml/mllearn/keras2caffe.py b/src/main/python/systemml/mllearn/keras2caffe.py index 2b97560..39a9755 100755 --- a/src/main/python/systemml/mllearn/keras2caffe.py +++ b/src/main/python/systemml/mllearn/keras2caffe.py @@ -192,6 +192,7 @@ def _parseKerasLayer(layer): def _parseBatchNorm(layer): + # TODO: Ignoring axis bnName = layer.name + '_1' config = layer.get_config() bias_term = 'true' if config['center'] else 'false' @@ -215,44 +216,51 @@ def getPadding(kernel_size, padding): else: raise ValueError('Unsupported padding:' + str(padding)) +# Used by padding to extract different types of possible padding: +# int: the same symmetric padding is applied to height and width. +# tuple of 2 ints: interpreted as two different symmetric padding values for height and width: (symmetric_height_pad, symmetric_width_pad) +# tuple of 2 tuples of 2 ints: interpreted as ((top_pad, bottom_pad), (left_pad, right_pad)) +def get2Tuple(val): + return [val, val] if isinstance(val, int) else [val[0], val[1]] + # Helper method to return Caffe's ConvolutionParameter in JSON-like data structure def getConvParam(layer): - stride = (1, 1) if layer.strides is None else layer.strides + # TODO: dilation_rate, kernel_constraint and bias_constraint are not supported + stride = (1, 1) if layer.strides is None else get2Tuple(layer.strides) + kernel_size = get2Tuple(layer.kernel_size) config = layer.get_config() + if not layer.use_bias: + raise Exception('use_bias=False is not supported for the Conv2D layer. Consider setting use_bias to true.') return {'num_output': layer.filters, 'bias_term': str(config['use_bias']).lower( - ), 'kernel_h': layer.kernel_size[0], 'kernel_w': layer.kernel_size[1], 'stride_h': stride[0], 'stride_w': stride[1], - 'pad_h': getPadding(layer.kernel_size[0], layer.padding), 'pad_w': getPadding(layer.kernel_size[1], layer.padding)} + ), 'kernel_h': kernel_size[0], 'kernel_w': kernel_size[1], 'stride_h': stride[0], 'stride_w': stride[1], + 'pad_h': getPadding(kernel_size[0], layer.padding), 'pad_w': getPadding(kernel_size[1], layer.padding)} # Helper method to return newly added UpsampleParameter # (search for UpsampleParameter in the file src/main/proto/caffe/caffe.proto) in JSON-like data structure def getUpSamplingParam(layer): - return {'size_h': layer.size[0], 'size_w': layer.size[1]} - -# Used by padding to extract different types of possible padding: -# int: the same symmetric padding is applied to height and width. -# tuple of 2 ints: interpreted as two different symmetric padding values for height and width: (symmetric_height_pad, symmetric_width_pad) -# tuple of 2 tuples of 2 ints: interpreted as ((top_pad, bottom_pad), (left_pad, right_pad)) -def getPaddingTuple(padding): - return [padding, padding] if isinstance(padding, int) else [padding[0], padding[1]] + # TODO: Skipping interpolation type + size = get2Tuple(layer.size) + return {'size_h': size[0], 'size_w': size[1]} # Helper method to return newly added PaddingParameter # (search for UpsampleParameter in the file src/main/proto/caffe/caffe.proto) in JSON-like data structure def getPaddingParam(layer): if isinstance(layer.padding, int): - padding = getPaddingTuple(layer.padding) + getPaddingTuple(layer.padding) + padding = get2Tuple(layer.padding) + get2Tuple(layer.padding) elif hasattr(layer.padding, '__len__') and len(layer.padding) == 2: - padding = getPaddingTuple(layer.padding[0]) + getPaddingTuple(layer.padding[1]) + padding = get2Tuple(layer.padding[0]) + get2Tuple(layer.padding[1]) else: raise ValueError('padding should be either an int, a tuple of 2 ints or or a tuple of 2 tuples of 2 ints. Found: ' + str(layer.padding)) return {'top_pad': padding[0], 'bottom_pad': padding[1], 'left_pad': padding[2], 'right_pad': padding[3], 'pad_value':0} # Helper method to return Caffe's PoolingParameter in JSON-like data structure def getPoolingParam(layer, pool='MAX'): - stride = (1, 1) if layer.strides is None else layer.strides - return {'pool': pool, 'kernel_h': layer.pool_size[0], 'kernel_w': layer.pool_size[1], - 'stride_h': stride[0], 'stride_w': stride[1], 'pad_h': getPadding(layer.pool_size[0], layer.padding), - 'pad_w': getPadding(layer.pool_size[1], layer.padding)} + stride = (1, 1) if layer.strides is None else get2Tuple(layer.strides) + pool_size = get2Tuple(layer.pool_size) + return {'pool': pool, 'kernel_h': pool_size[0], 'kernel_w': pool_size[1], + 'stride_h': stride[0], 'stride_w': stride[1], 'pad_h': getPadding(pool_size[0], layer.padding), + 'pad_w': getPadding(pool_size[1], layer.padding)} # Helper method to return Caffe's RecurrentParameter in JSON-like data structure def getRecurrentParam(layer): @@ -270,21 +278,39 @@ def getInnerProductParam(layer): if len(layer.output_shape) != 2: raise Exception('Only 2-D input is supported for the Dense layer in the current implementation, but found ' + str(layer.input_shape) + '. Consider adding a Flatten before ' + str(layer.name)) + if not layer.use_bias: + raise Exception('use_bias=False is not supported for the Dense layer. Consider setting use_bias to true.') return {'num_output': layer.units} +# Helper method to return Caffe's DropoutParameter in JSON-like data structure +def getDropoutParam(layer): + if layer.noise_shape is not None: + supported = True + if len(layer.input_shape) != len(layer.noise_shape): + supported = False + else: + for i in range(len(layer.noise_shape)-1): + # Ignore the first dimension + if layer.input_shape[i+1] != layer.noise_shape[i+1]: + supported = False + if not supported: + raise Exception('noise_shape=' + str(layer.noise_shape) + ' is not supported for Dropout layer with input_shape=' + + str(layer.input_shape)) + return {'dropout_ratio': l.rate} + layerParamMapping = { keras.layers.InputLayer: lambda l: {'data_param': {'batch_size': l.batch_size}}, keras.layers.Dense: lambda l: {'inner_product_param': getInnerProductParam(l)}, keras.layers.Dropout: lambda l: - {'dropout_param': {'dropout_ratio': l.rate}}, + {'dropout_param': getDropoutParam(l)}, keras.layers.Add: lambda l: {'eltwise_param': {'operation': 'SUM'}}, keras.layers.Concatenate: lambda l: {'concat_param': {'axis': _getCompensatedAxis(l)}}, keras.layers.Conv2DTranspose: lambda l: - {'convolution_param': getConvParam(l)}, + {'convolution_param': getConvParam(l)}, # will skip output_padding keras.layers.UpSampling2D: lambda l: {'upsample_param': getUpSamplingParam(l)}, keras.layers.ZeroPadding2D: lambda l: