66import numpy
77import keras
88from distutils .version import StrictVersion
9- from keras .layers import Conv1D , Conv2D , Conv3D , Conv2DTranspose , Conv3DTranspose
9+ from keras .layers import Conv1D , Conv2D , Conv3D , Conv2DTranspose , Conv3DTranspose , SeparableConv2D
1010if StrictVersion (keras .__version__ ) >= StrictVersion ('2.1.5' ):
1111 from keras .layers import DepthwiseConv2D
12+ if StrictVersion (keras .__version__ ) >= StrictVersion ('2.1.3' ):
13+ from keras .layers import SeparableConv1D
1214from ....proto import onnx_proto
1315from ...common ._apply_operation import apply_identity , apply_transpose
1416from ...common ._registration import register_converter
@@ -32,10 +34,42 @@ def _calc_explicit_padding(input_size, output_shape, output_padding, kernel_shap
3234 return pads
3335
3436
37+ def process_separable_conv_2nd (scope , operator , container , convolution_input_names , n_dims ,
38+ weight_perm_axes , parameters , auto_pad ):
39+ attrs = {'name' : operator .full_name + '1' }
40+
41+ weight_tensor_name = scope .get_unique_variable_name ('W' )
42+ weight_params = parameters [1 ].transpose (weight_perm_axes )
43+ container .add_initializer (weight_tensor_name , onnx_proto .TensorProto .FLOAT ,
44+ weight_params .shape , weight_params .flatten ())
45+ convolution_input_names .append (weight_tensor_name )
46+
47+ if len (parameters ) == 3 :
48+ bias_tensor_name = scope .get_unique_variable_name ('B' )
49+ container .add_initializer (bias_tensor_name , onnx_proto .TensorProto .FLOAT ,
50+ parameters [2 ].shape , parameters [2 ].flatten ())
51+ convolution_input_names .append (bias_tensor_name )
52+
53+ all_ones = numpy .ones (n_dims , numpy .int8 )
54+ attrs ['dilations' ] = all_ones
55+ attrs ['strides' ] = all_ones
56+ attrs ['kernel_shape' ] = all_ones
57+ attrs ['group' ] = 1
58+ attrs ['auto_pad' ] = auto_pad
59+
60+ intermediate_output_name = scope .get_unique_variable_name ('convolution_output' )
61+ container .add_node ('Conv' , convolution_input_names ,
62+ intermediate_output_name , ** attrs )
63+ return intermediate_output_name
64+
65+
3566def convert_keras_conv_core (scope , operator , container , is_transpose , n_dims , input_perm_axes ,
3667 output_perm_axes , weight_perm_axes ):
3768 op = operator .raw_operator
3869
70+ is_separable_conv = isinstance (op , SeparableConv2D ) or \
71+ (StrictVersion (keras .__version__ ) >= StrictVersion ('2.1.3' ) and isinstance (op , SeparableConv1D ))
72+
3973 channels_first = n_dims > 1 and op .data_format == 'channels_first'
4074
4175 # Unless channels_first is the Keras data format, the inputs and weights in Keras v.s. ONNX
@@ -48,10 +82,15 @@ def convert_keras_conv_core(scope, operator, container, is_transpose, n_dims, in
4882
4983 op_type = 'ConvTranspose' if is_transpose else 'Conv'
5084 convolution_input_names = [adjusted_input_name ]
51- attrs = {'name' : operator .full_name }
52-
5385 parameters = op .get_weights ()
54- assert (len (parameters ) == 2 if op .use_bias else 1 )
86+
87+ if is_separable_conv :
88+ attrs = {'name' : operator .full_name + '0' }
89+ assert (len (parameters ) == 3 if op .use_bias else 2 )
90+ else :
91+ attrs = {'name' : operator .full_name }
92+ assert (len (parameters ) == 2 if op .use_bias else 1 )
93+
5594 weight_params = parameters [0 ]
5695
5796 input_channels , output_channels = weight_params .shape [- 2 :]
@@ -68,6 +107,11 @@ def convert_keras_conv_core(scope, operator, container, is_transpose, n_dims, in
68107 new_shape = shape [:2 ] + (1 , shape [2 ] * shape [3 ])
69108 weight_params = numpy .reshape (weight_params , new_shape )
70109 weight_params = weight_params .transpose (weight_perm_axes )
110+ elif is_separable_conv :
111+ group = weight_params .shape [- 2 ]
112+ shape = weight_params .shape
113+ new_shape = shape [:- 2 ] + (1 , shape [- 2 ] * shape [- 1 ])
114+ weight_params = numpy .reshape (weight_params , new_shape ).transpose (weight_perm_axes )
71115 else :
72116 weight_params = weight_params .transpose (weight_perm_axes )
73117 group = 1
@@ -77,7 +121,7 @@ def convert_keras_conv_core(scope, operator, container, is_transpose, n_dims, in
77121 weight_params .shape , weight_params .flatten ())
78122 convolution_input_names .append (weight_tensor_name )
79123
80- if len (parameters ) == 2 :
124+ if len (parameters ) == 2 and not is_separable_conv :
81125 bias_tensor_name = scope .get_unique_variable_name ('B' )
82126 container .add_initializer (bias_tensor_name , onnx_proto .TensorProto .FLOAT ,
83127 parameters [1 ].shape , parameters [1 ].flatten ())
@@ -114,6 +158,10 @@ def convert_keras_conv_core(scope, operator, container, is_transpose, n_dims, in
114158 container .add_node (op_type , convolution_input_names ,
115159 intermediate_output_name , ** attrs )
116160
161+ if is_separable_conv :
162+ intermediate_output_name = process_separable_conv_2nd (scope , operator , container , [intermediate_output_name ], n_dims ,
163+ weight_perm_axes , parameters , attrs ['auto_pad' ])
164+
117165 # The construction of convolution is done. Now, we create an activation operator to apply the activation specified
118166 # in this Keras layer.
119167 apply_activation_function = _activation_map [op .activation ]
@@ -166,10 +214,23 @@ def convert_keras_conv_transpose_3d(scope, operator, container):
166214 convert_keras_conv_core (scope , operator , container , is_transpose , n_dims , input_perm , output_perm , weight_perm )
167215
168216
217+ def convert_keras_separable_conv1d (scope , operator , container ):
218+ is_transpose , n_dims , input_perm , output_perm , weight_perm = get_converter_config (1 , False )
219+ convert_keras_conv_core (scope , operator , container , is_transpose , n_dims , input_perm , output_perm , weight_perm )
220+
221+
222+ def convert_keras_separable_conv2d (scope , operator , container ):
223+ is_transpose , n_dims , input_perm , output_perm , weight_perm = get_converter_config (2 , False )
224+ convert_keras_conv_core (scope , operator , container , is_transpose , n_dims , input_perm , output_perm , weight_perm )
225+
226+
169227register_converter (Conv1D , convert_keras_conv1d )
170228register_converter (Conv2D , convert_keras_conv2d )
171229register_converter (Conv3D , convert_keras_conv3d )
172230register_converter (Conv2DTranspose , convert_keras_conv_transpose_2d )
173231register_converter (Conv3DTranspose , convert_keras_conv_transpose_3d )
174232if StrictVersion (keras .__version__ ) >= StrictVersion ('2.1.5' ):
175233 register_converter (DepthwiseConv2D , convert_keras_depthwise_conv_2d )
234+ register_converter (SeparableConv2D , convert_keras_separable_conv2d )
235+ if StrictVersion (keras .__version__ ) >= StrictVersion ('2.1.3' ):
236+ register_converter (SeparableConv1D , convert_keras_separable_conv1d )
0 commit comments