def _CloneWithNewOperands(layer_op, input_tensor, weight_tensor): """Clones layer_op with input_tensor and weight_tensor as new inputs.""" new_layer_name = layer_op.name.split('/')[-1] + '_Fold' if layer_op.type == 'Conv2D': return nn_ops.conv2d( input_tensor, weight_tensor, strides=layer_op.get_attr('strides'), padding=layer_op.get_attr('padding'), use_cudnn_on_gpu=layer_op.get_attr('use_cudnn_on_gpu'), data_format=layer_op.get_attr('data_format'), name=new_layer_name) elif layer_op.type == 'MatMul': return math_ops.matmul( input_tensor, weight_tensor, transpose_a=layer_op.get_attr('transpose_a'), transpose_b=layer_op.get_attr('transpose_b'), name=new_layer_name) elif layer_op.type == 'DepthwiseConv2dNative': return nn.depthwise_conv2d( input_tensor, weight_tensor, strides=layer_op.get_attr('strides'), padding=layer_op.get_attr('padding'), name=new_layer_name) else: raise ValueError('Cannot handle operation of type: %s' % layer_op.type)
def testDepthwiseConv3x1(self): with self.session() as sess: with ops.device("/device:IPU:0"): pa = array_ops.placeholder(np.float32, [1, 2, 2, 3], name="a") pb = array_ops.placeholder(np.float32, [1, 1, 3, 1], name="b") pc = array_ops.placeholder(np.float32, [3], name="c") c = nn.depthwise_conv2d(pa, pb, strides=[1, 1, 1, 1], padding="SAME") output = c + pc report = tu.ReportJSON(self, sess) report.reset() fd = { pa: [[[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]], pb: [[[[6], [4], [2]]]], pc: [1, 1, 1] } result = sess.run(output, fd) self.assertAllClose( result, [[[[7, 9, 7], [25, 21, 13]], [[43, 33, 19], [61, 45, 25]]]]) report.parse_log() # pylint: disable=line-too-long ok = [ '__seed*', 'host-exchange-local-copy-', 'Copy_', 'depthwise/convolution.*/Conv_1x1', 'add/fusion*/Add' ] # pylint: enable=line-too-long report.assert_all_compute_sets_and_list(ok)
def _CloneWithNewOperands(layer_op, input_tensor, weight_tensor): """Clones layer_op with input_tensor and weight_tensor as new inputs.""" new_layer_name = layer_op.name.split('/')[-1] + '_Fold' if layer_op.type == 'Conv2D': return nn_ops.conv2d( input_tensor, weight_tensor, strides=layer_op.get_attr('strides'), padding=layer_op.get_attr('padding'), use_cudnn_on_gpu=layer_op.get_attr('use_cudnn_on_gpu'), data_format=layer_op.get_attr('data_format'), name=new_layer_name) elif layer_op.type == 'MatMul': return math_ops.matmul( input_tensor, weight_tensor, transpose_a=layer_op.get_attr('transpose_a'), transpose_b=layer_op.get_attr('transpose_b'), name=new_layer_name) elif layer_op.type == 'DepthwiseConv2dNative': return nn.depthwise_conv2d( input_tensor, weight_tensor, strides=layer_op.get_attr('strides'), padding=layer_op.get_attr('padding'), name=new_layer_name) else: raise ValueError('Cannot handle operation of type: %s' % layer_op.type)
def _CloneDepthwiseConv2d(self, op, inputs, new_name): input_tensor = inputs[0] weights = inputs[1] self._AssertConvShapes(op.name, input_tensor, weights) return nn.depthwise_conv2d(input_tensor, weights, strides=op.get_attr('strides'), padding=op.get_attr('padding'), name=new_name).op
def reducer(x): shape = array_ops.shape(x) x = array_ops.reshape(x, shape=array_ops.concat([[-1], shape[-3:]], 0)) y = nn.depthwise_conv2d(x, kernel, strides=[1, 1, 1, 1], padding='VALID') return array_ops.reshape( y, array_ops.concat([shape[:-3], array_ops.shape(y)[1:]], 0))
def _CloneDepthwiseConv2d(self, op, inputs, new_name): input_tensor = inputs[0] weights = inputs[1] self._AssertConvShapes(op.name, input_tensor, weights) return nn.depthwise_conv2d( input_tensor, weights, strides=op.get_attr('strides'), padding=op.get_attr('padding'), name=new_name).op
def testSeparableConvWithResourceVar(self): graph = ops.Graph() with graph.as_default(): with variable_scope.variable_scope('', use_resource=True): batch_size, height, width, depth = 5, 128, 128, 3 input1 = array_ops.zeros((batch_size, height, width, depth)) kernel_size, depth_multiplier = 3, 1 depthwise_shape = [ kernel_size, kernel_size, depth, depth_multiplier ] depthwise_weights = variables.model_variable( 'depthwise_weights', shape=depthwise_shape) strides = [1, 1, 1, 1] with variable_scope.variable_scope('depthwise_conv_1'): conv1 = nn.depthwise_conv2d(input1, depthwise_weights, strides, padding='SAME') with variable_scope.variable_scope('depthwise_conv_2'): conv2 = nn.depthwise_conv2d(conv1, depthwise_weights, strides, padding='SAME') math_ops.add(conv2, input1, name='add') quantize.Quantize(graph, True) # Test that the weights and activations of all convs have been quantized. quant_node_name = 'FakeQuantWithMinMaxVars' weights_quant = graph.get_operation_by_name( 'depthwise_conv_1/weights_quant/' + quant_node_name) self.assertEqual(weights_quant.type, quant_node_name) act_quant = graph.get_operation_by_name('depthwise_conv_1/act_quant/' + quant_node_name) self.assertEqual(act_quant.type, quant_node_name) weights_quant = graph.get_operation_by_name( 'depthwise_conv_2/weights_quant/' + quant_node_name) self.assertEqual(weights_quant.type, quant_node_name) act_quant = graph.get_operation_by_name('depthwise_conv_2/act_quant/' + quant_node_name) self.assertEqual(act_quant.type, quant_node_name)
def _CloneWithNewOperands(layer_op, input_tensor, weight_tensor, batch_to_space_op): """Clones layer_op with input_tensor and weight_tensor as new inputs.""" new_layer_name = layer_op.name.split('/')[-1] + '_Fold' if layer_op.type == 'Conv2D': conv = nn_ops.conv2d( input_tensor, weight_tensor, strides=layer_op.get_attr('strides'), padding=layer_op.get_attr('padding'), use_cudnn_on_gpu=layer_op.get_attr('use_cudnn_on_gpu'), data_format=layer_op.get_attr('data_format'), name=new_layer_name) if batch_to_space_op: batch_to_space_op = layer_op.outputs[0].consumers()[0] # TODO(suharshs): It's hard to make this name match with the unfused name. # Restructure this code to not rely on scope at all. new_batch_to_space_name = batch_to_space_op.name.split( '/')[-1] + '_Fold' conv = array_ops.batch_to_space_nd(conv, batch_to_space_op.inputs[1], batch_to_space_op.inputs[2], name=new_batch_to_space_name) return conv elif layer_op.type == 'MatMul': return math_ops.matmul(input_tensor, weight_tensor, transpose_a=layer_op.get_attr('transpose_a'), transpose_b=layer_op.get_attr('transpose_b'), name=new_layer_name) elif layer_op.type == 'DepthwiseConv2dNative': # We don't copy dilation rate because we reuse the input SpaceToBatch # and create our own BatchToSpace operation below. conv = nn.depthwise_conv2d(input_tensor, weight_tensor, strides=layer_op.get_attr('strides'), padding=layer_op.get_attr('padding'), name=new_layer_name) # Copy the batch to space operation if we have a atrous convolution. if batch_to_space_op: batch_to_space_op = layer_op.outputs[0].consumers()[0] # TODO(suharshs): It's hard to make this name match with the unfused name. # Restructure this code to not rely on scope at all. new_batch_to_space_name = batch_to_space_op.name.split( '/')[-1] + '_Fold' conv = array_ops.batch_to_space_nd(conv, batch_to_space_op.inputs[1], batch_to_space_op.inputs[2], name=new_batch_to_space_name) return conv else: raise ValueError('Cannot handle operation of type: %s' % layer_op.type)
def test_group_conv_depthwise(self): if test.is_gpu_available(cuda_only=True): with testing_utils.use_gpu(): inputs = random_ops.random_uniform(shape=(3, 27, 27, 32)) layer = keras.layers.Conv2D(32, 3, groups=32, use_bias=False) layer.build((3, 27, 27, 32)) weights_dw = array_ops.reshape(layer.kernel, [3, 3, 32, 1]) expected_outputs = nn.depthwise_conv2d( inputs, weights_dw, strides=[1, 1, 1, 1], padding='VALID') self.assertAllClose(layer(inputs), expected_outputs, rtol=1e-5)
def testSeparableConvWithResourceVar(self): graph = ops.Graph() with graph.as_default(): with variable_scope.variable_scope('', use_resource=True): batch_size, height, width, depth = 5, 128, 128, 3 input1 = array_ops.zeros((batch_size, height, width, depth)) kernel_size, depth_multiplier = 3, 1 depthwise_shape = [kernel_size, kernel_size, depth, depth_multiplier] depthwise_weights = variables.model_variable( 'depthwise_weights', shape=depthwise_shape) strides = [1, 1, 1, 1] with variable_scope.variable_scope('depthwise_conv_1'): conv1 = nn.depthwise_conv2d( input1, depthwise_weights, strides, padding='SAME') with variable_scope.variable_scope('depthwise_conv_2'): conv2 = nn.depthwise_conv2d( conv1, depthwise_weights, strides, padding='SAME') math_ops.add(conv2, input1, name='add') quantize.Quantize(graph, True) # Test that the weights and activations of all convs have been quantized. quant_node_name = 'FakeQuantWithMinMaxVars' weights_quant = graph.get_operation_by_name( 'depthwise_conv_1/weights_quant/' + quant_node_name) self.assertEqual(weights_quant.type, quant_node_name) act_quant = graph.get_operation_by_name('depthwise_conv_1/act_quant/' + quant_node_name) self.assertEqual(act_quant.type, quant_node_name) weights_quant = graph.get_operation_by_name( 'depthwise_conv_2/weights_quant/' + quant_node_name) self.assertEqual(weights_quant.type, quant_node_name) act_quant = graph.get_operation_by_name('depthwise_conv_2/act_quant/' + quant_node_name) self.assertEqual(act_quant.type, quant_node_name)
def call(self, inputs): filter = np.array(self.filter, np.float32) if filter.ndim == 1: filter = filter[:, np.newaxis] * filter[np.newaxis, :] if self.normalize: filter /= np.sum(filter) filter = filter[:, :, np.newaxis, np.newaxis] filter = K.constant(filter, dtype=inputs.dtype, name='filter') filter = K.tile(filter, [1, 1, K.shape(inputs)[-1], 1]) outputs = nn.depthwise_conv2d( inputs, filter, strides=(1, self.stride, self.stride, 1), padding='SAME') return outputs
def forward(self, inputs): strides = (1,) + self.strides + (1,)\ if self.data_format[-1] == 'C' else (1, 1) + self.strides outputs = nn.depthwise_conv2d(input=inputs, filter=self.kernel, strides=strides, padding=self.padding, data_format=self.data_format, rate=self.dilation_rate) if self.use_bias: outputs = F.bias_add(outputs, self.bias, data_format=self.data_format) if self.activation is not None: outputs = self.activation(outputs) return outputs
def call(self, inputs): if self.padding == 'causal': inputs = array_ops.pad(inputs, self._compute_causal_padding()) if self.data_format == 'channels_last': strides = (1, ) + self.strides * 2 + (1, ) spatial_start_dim = 1 else: strides = (1, 1) + self.strides * 2 spatial_start_dim = 2 # Explicitly broadcast inputs and kernels to 4D. inputs = array_ops.expand_dims(inputs, spatial_start_dim) if self.common_kernel == True: #Need to replicate kernel {channels} times over axis 1 dw_kernel = tf.tile(self.depthwise_kernel, (1, self.channels, 1)) bias_kernel = tf.tile(self.bias, (self.channels, )) else: dw_kernel = self.depthwise_kernel bias_kernel = self.bias dw_kernel = array_ops.expand_dims(dw_kernel, 0) if self.padding == 'causal': op_padding = 'valid' else: op_padding = self.padding outputs = nn.depthwise_conv2d( inputs, dw_kernel, strides=strides, padding=op_padding.upper(), data_format=conv_utils.convert_data_format(self.data_format, ndim=4)) outputs = array_ops.squeeze(outputs, [spatial_start_dim]) if self.use_bias: outputs = backend.bias_add(outputs, bias_kernel, data_format=self.data_format) if self.activation is not None: return self.activation(outputs) return outputs
def _CloneWithNewOperands(layer_op, input_tensor, weight_tensor, batch_to_space_op): """Clones layer_op with input_tensor and weight_tensor as new inputs.""" new_layer_name = layer_op.name.split('/')[-1] + '_Fold' if layer_op.type == 'Conv2D': return nn_ops.conv2d( input_tensor, weight_tensor, strides=layer_op.get_attr('strides'), padding=layer_op.get_attr('padding'), use_cudnn_on_gpu=layer_op.get_attr('use_cudnn_on_gpu'), data_format=layer_op.get_attr('data_format'), name=new_layer_name) elif layer_op.type == 'MatMul': return math_ops.matmul( input_tensor, weight_tensor, transpose_a=layer_op.get_attr('transpose_a'), transpose_b=layer_op.get_attr('transpose_b'), name=new_layer_name) elif layer_op.type == 'DepthwiseConv2dNative': # We don't copy dilation rate because we reuse the input SpaceToBatch # and create our own BatchToSpace operation below. conv = nn.depthwise_conv2d( input_tensor, weight_tensor, strides=layer_op.get_attr('strides'), padding=layer_op.get_attr('padding'), name=new_layer_name) # Copy the batch to space operation if we have a atrous convolution. if batch_to_space_op: batch_to_space_op = layer_op.outputs[0].consumers()[0] # TODO(suharshs): It's hard to make this name match with the unfused name. # Restructure this code to not rely on scope at all. new_batch_to_space_name = batch_to_space_op.name.split('/')[-1] + '_Fold' conv = array_ops.batch_to_space_nd( conv, batch_to_space_op.inputs[1], batch_to_space_op.inputs[2], name=new_batch_to_space_name) return conv else: raise ValueError('Cannot handle operation of type: %s' % layer_op.type)
def sobel_edges_tfpad(image): """Returns a tensor holding Sobel edge maps. Note: image is CONSTANT(0) padded instead of REFLECT padded. Arguments: image: Image tensor with shape [batch_size, h, w, d] and type float32 or float64. The image(s) must be 2x2 or larger. Returns: Tensor holding edge maps for each channel. Returns a tensor with shape [batch_size, h, w, d, 2] where the last two dimensions hold [[dy[0], dx[0]], [dy[1], dx[1]], ..., [dy[d-1], dx[d-1]]] calculated using the Sobel filter. """ # Define vertical and horizontal Sobel filters. static_image_shape = image.get_shape() image_shape = array_ops.shape(image) num_kernels = 2 sobel_kn = tf.constant([[[-1, -2, -1], [0, 0, 0], [1, 2, 1]], [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]], dtype=tf.float32) kernels_tf = tf.expand_dims(tf.transpose(sobel_kn, perm=(1, 2, 0)), axis=-2) kernels_tf = array_ops.tile(kernels_tf, [1, 1, image_shape[-1], 1], name='sobel_filters') # Use depth-wise convolution to calculate edge maps per channel. #pad_sizes = [[0, 0], [1, 1], [1, 1], [0, 0]] pad_sizes = tf.constant([[0, 0], [1, 1], [1, 1], [0, 0]], dtype=tf.int32) #padded = array_ops.pad(image, pad_sizes, mode='REFLECT') #padded = tf.pad(tensor=image, paddings=pad_sizes, mode='REFLECT') padded = tf.pad(tensor=image, paddings=pad_sizes, mode='CONSTANT') # Output tensor has shape [batch_size, h, w, d * num_kernels]. strides = [1, 1, 1, 1] output = nn.depthwise_conv2d(padded, kernels_tf, strides, 'VALID') # Reshape to [batch_size, h, w, d, num_kernels]. shape = array_ops.concat([image_shape, [num_kernels]], 0) output = array_ops.reshape(output, shape=shape) output.set_shape(static_image_shape.concatenate([num_kernels])) return output
def depthwise_conv2d(x, depthwise_kernel, strides=(1, 1), padding='valid', data_format=None, dilation_rate=(1, 1)): """2D convolution with separable filters. Arguments: x: input tensor depthwise_kernel: convolution kernel for the depthwise convolution. strides: strides tuple (length 2). padding: padding mode, "valid" or "same". data_format: data format, "channels_first" or "channels_last". dilation_rate: tuple of integers, dilation rates for the depthwise convolution. Returns: Output tensor. Raises: ValueError: if `data_format` is neither `channels_last` or `channels_first`. """ if data_format is None: data_format = K.image_data_format() if data_format not in {'channels_first', 'channels_last'}: raise ValueError('Unknown data_format ' + str(data_format)) x = K._preprocess_conv2d_input(x, data_format) padding = K._preprocess_padding(padding) strides = (1, ) + strides + (1, ) x = nn.depthwise_conv2d(x, depthwise_kernel, strides=strides, padding=padding, rate=dilation_rate) return K._postprocess_conv2d_output(x, data_format)
def testDepthwiseConv3x1(self): with ops.device("/device:IPU:0"): pa = array_ops.placeholder(np.float32, [1, 2, 2, 3], name="a") pb = array_ops.placeholder(np.float32, [1, 1, 3, 1], name="b") pc = array_ops.placeholder(np.float32, [3], name="c") c = nn.depthwise_conv2d(pa, pb, strides=[1, 1, 1, 1], padding="SAME") output = c + pc with ops.device('cpu'): report = gen_ipu_ops.ipu_event_trace() tu.configure_ipu_system() with tu.ipu_session() as sess: sess.run(report) fd = { pa: [[[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]], pb: [[[[6], [4], [2]]]], pc: [1, 1, 1] } result = sess.run(output, fd) self.assertAllClose( result, [[[[7, 9, 7], [25, 21, 13]], [[43, 33, 19], [61, 45, 25]]]]) result = sess.run(report) s = tu.extract_all_strings_from_event_trace(result) cs_list = tu.get_compute_sets_from_report(s) ok = [ '__seed*', 'host-exchange-local-copy-', 'Copy_', 'depthwise/convolution.*/Conv_1x1', 'Copy_depthwise/convolution.*/Conv_1x1/partials_to_depthwise/convolution.*/Conv_1x1/partials[[]cloned[]]', 'add/fusion*/addToChannel' ] self.assertTrue(tu.check_all_compute_sets_and_list(cs_list, ok))
def depthwise_leaders_convolution2d( inputs, kernel_size, stride=1, padding='SAME', rates=[1, 2], pooling_sizes=[3, 1], pooling_type='MAX', activation_fn=nn.relu, normalizer_fn=None, normalizer_params=None, weights_initializer=initializers.xavier_initializer(), weights_regularizer=None, biases_initializer=init_ops.zeros_initializer(), biases_regularizer=None, reuse=None, variables_collections=None, outputs_collections=None, trainable=True, data_format='NHWC', scope=None): """Adds a depthwise 2D convolution with optional batch_norm layer. This op performs a depthwise convolution that acts separately on channels, creating a variable called `depthwise_weights`. Then, if `normalizer_fn` is None, it adds bias to the result, creating a variable called 'biases', otherwise, the `normalizer_fn` is applied. It finally applies an activation function to produce the end result. Args: inputs: A tensor of size [batch_size, height, width, channels]. num_outputs: The number of pointwise convolution output filters. If is None, then we skip the pointwise convolution stage. kernel_size: A list of length 2: [kernel_height, kernel_width] of of the filters. Can be an int if both values are the same. depth_multiplier: The number of depthwise convolution output channels for each input channel. The total number of depthwise convolution output channels will be equal to `num_filters_in * depth_multiplier`. stride: A list of length 2: [stride_height, stride_width], specifying the depthwise convolution stride. Can be an int if both strides are the same. padding: One of 'VALID' or 'SAME'. rate: A list of length 2: [rate_height, rate_width], specifying the dilation rates for atrous convolution. Can be an int if both rates are the same. If any value is larger than one, then both stride values need to be one. activation_fn: Activation function. The default value is a ReLU function. Explicitly set it to None to skip it and maintain a linear activation. normalizer_fn: Normalization function to use instead of `biases`. If `normalizer_fn` is provided then `biases_initializer` and `biases_regularizer` are ignored and `biases` are not created nor added. default set to None for no normalizer function normalizer_params: Normalization function parameters. weights_initializer: An initializer for the weights. weights_regularizer: Optional regularizer for the weights. biases_initializer: An initializer for the biases. If None skip biases. biases_regularizer: Optional regularizer for the biases. reuse: Whether or not the layer and its variables should be reused. To be able to reuse the layer scope must be given. variables_collections: Optional list of collections for all the variables or a dictionary containing a different list of collection per variable. outputs_collections: Collection to add the outputs. trainable: Whether or not the variables should be trainable or not. scope: Optional scope for variable_scope. Returns: A `Tensor` representing the output of the operation. """ with variable_scope.variable_scope(scope, 'DepthwiseLeadersConv2d', [inputs], reuse=reuse) as sc: inputs = ops.convert_to_tensor(inputs) dtype = inputs.dtype.base_dtype kernel_h, kernel_w = utils.two_element_tuple(kernel_size) stride_h, stride_w = utils.two_element_tuple(stride) if data_format == 'NHWC': num_filters_in = utils.last_dimension(inputs.get_shape(), min_rank=4) strides = [1, stride_h, stride_w, 1] else: num_filters_in = inputs.get_shape().as_list()[1] strides = [1, 1, stride_h, stride_w] # Depthwise weights + biases variables. depth_multiplier = 1 num_outputs = depth_multiplier * num_filters_in weights_collections = utils.get_variable_collections( variables_collections, 'weights') depthwise_shape = [ kernel_h, kernel_w, num_filters_in, depth_multiplier ] depthwise_weights = variables.model_variable( 'depthwise_weights', shape=depthwise_shape, dtype=dtype, initializer=weights_initializer, regularizer=weights_regularizer, trainable=trainable, collections=weights_collections) biases_collections = utils.get_variable_collections( variables_collections, 'biases') biases = variables.model_variable('biases', shape=[ num_outputs, ], dtype=dtype, initializer=biases_initializer, regularizer=biases_regularizer, trainable=trainable, collections=biases_collections) # Perform the convolution at different rates. outputs = [] for i, rate in enumerate(rates): # Depthwise conv. net = nn.depthwise_conv2d(inputs, depthwise_weights, strides=[1, 1, 1, 1], padding='SAME', rate=utils.two_element_tuple(rate), data_format=data_format) # Add bias + abs. val. net = tf.abs(nn.bias_add(net, biases, data_format=data_format)) # Pooling... if pooling_sizes[i] > 1: net = tf.nn.pool(net, [pooling_sizes[i], pooling_sizes[i]], pooling_type, padding='SAME', data_format=data_format) outputs.append(net) # Fuse different rates/scales. net = None for i, o in enumerate(outputs): if net is None: # First in the list... net = o else: # MAX or AVG pooling... if pooling_type == 'MAX': net = tf.maximum(net, o) else: net += o * pooling_sizes[i]**2 # Pooling => for stride > 1 if stride_h > 1 or stride_w > 1: # net = tf.nn.pool(net, # [1, 1], # pooling_type, # padding='SAME', # strides=[stride_h, stride_w], # data_format=data_format) net = tf.nn.max_pool(net, [1, 1, 1, 1], padding='SAME', strides=strides, data_format=data_format) # (Batch normalization)... normalizer_params = normalizer_params or {} net = normalizer_fn(net, **normalizer_params) # Split into two parts: positive and negative extreme. net_p = slim.bias_add(net, data_format=data_format, scope='bias_positive') net_p = activation_fn(net_p) # net_p = slim.dropout(net_p, 0.5, is_training=True, scope='dropout_p') net_m = slim.bias_add(-net, data_format=data_format, scope='bias_negative') net_m = activation_fn(net_m) # net_m = slim.dropout(net_m, 0.5, is_training=True, scope='dropout_m') # Concat the final result... outputs = concat_channels([net_p, net_m], data_format=data_format) return utils.collect_named_outputs(outputs_collections, sc.original_name_scope, outputs)
def depthwise_convolution2d( inputs, kernel_size, depth_multiplier=1, stride=1, padding='SAME', rate=1, activation_fn=nn.relu, normalizer_fn=None, normalizer_params=None, weights_initializer=initializers.xavier_initializer(), weights_regularizer=None, biases_initializer=init_ops.zeros_initializer(), biases_regularizer=None, reuse=None, variables_collections=None, outputs_collections=None, trainable=True, data_format='NHWC', scope=None): """Adds a depthwise 2D convolution with optional batch_norm layer. This op performs a depthwise convolution that acts separately on channels, creating a variable called `depthwise_weights`. Then, if `normalizer_fn` is None, it adds bias to the result, creating a variable called 'biases', otherwise, the `normalizer_fn` is applied. It finally applies an activation function to produce the end result. Args: inputs: A tensor of size [batch_size, height, width, channels]. num_outputs: The number of pointwise convolution output filters. If is None, then we skip the pointwise convolution stage. kernel_size: A list of length 2: [kernel_height, kernel_width] of of the filters. Can be an int if both values are the same. depth_multiplier: The number of depthwise convolution output channels for each input channel. The total number of depthwise convolution output channels will be equal to `num_filters_in * depth_multiplier`. stride: A list of length 2: [stride_height, stride_width], specifying the depthwise convolution stride. Can be an int if both strides are the same. padding: One of 'VALID' or 'SAME'. rate: A list of length 2: [rate_height, rate_width], specifying the dilation rates for atrous convolution. Can be an int if both rates are the same. If any value is larger than one, then both stride values need to be one. activation_fn: Activation function. The default value is a ReLU function. Explicitly set it to None to skip it and maintain a linear activation. normalizer_fn: Normalization function to use instead of `biases`. If `normalizer_fn` is provided then `biases_initializer` and `biases_regularizer` are ignored and `biases` are not created nor added. default set to None for no normalizer function normalizer_params: Normalization function parameters. weights_initializer: An initializer for the weights. weights_regularizer: Optional regularizer for the weights. biases_initializer: An initializer for the biases. If None skip biases. biases_regularizer: Optional regularizer for the biases. reuse: Whether or not the layer and its variables should be reused. To be able to reuse the layer scope must be given. variables_collections: Optional list of collections for all the variables or a dictionary containing a different list of collection per variable. outputs_collections: Collection to add the outputs. trainable: Whether or not the variables should be trainable or not. scope: Optional scope for variable_scope. Returns: A `Tensor` representing the output of the operation. """ with variable_scope.variable_scope(scope, 'DepthwiseConv2d', [inputs], reuse=reuse) as sc: inputs = ops.convert_to_tensor(inputs) # Actually apply depthwise conv instead of separable conv. dtype = inputs.dtype.base_dtype kernel_h, kernel_w = utils.two_element_tuple(kernel_size) stride_h, stride_w = utils.two_element_tuple(stride) if data_format == 'NHWC': num_filters_in = utils.last_dimension(inputs.get_shape(), min_rank=4) strides = [1, stride_h, stride_w, 1] else: num_filters_in = inputs.get_shape().as_list()[1] strides = [1, 1, stride_h, stride_w] weights_collections = utils.get_variable_collections( variables_collections, 'weights') # Depthwise weights variable. depthwise_shape = [kernel_h, kernel_w, num_filters_in, depth_multiplier] depthwise_weights = variables.model_variable( 'depthwise_weights', shape=depthwise_shape, dtype=dtype, initializer=weights_initializer, regularizer=weights_regularizer, trainable=trainable, collections=weights_collections) outputs = nn.depthwise_conv2d(inputs, depthwise_weights, strides, padding, rate=utils.two_element_tuple(rate), data_format=data_format) num_outputs = depth_multiplier * num_filters_in if normalizer_fn is not None: normalizer_params = normalizer_params or {} outputs = normalizer_fn(outputs, **normalizer_params) else: if biases_initializer is not None: biases_collections = utils.get_variable_collections( variables_collections, 'biases') biases = variables.model_variable('biases', shape=[num_outputs,], dtype=dtype, initializer=biases_initializer, regularizer=biases_regularizer, trainable=trainable, collections=biases_collections) outputs = nn.bias_add(outputs, biases, data_format=data_format) if activation_fn is not None: outputs = activation_fn(outputs) return utils.collect_named_outputs(outputs_collections, sc.original_name_scope, outputs)
def _depthwise_conv(inputs, weights_shape, strides, padding='SAME'): weights = tf.Variable( tf.random_normal(shape=weights_shape, dtype=tf.float32)) return nn.depthwise_conv2d(inputs, weights, strides, padding=padding)
def _depthwise_conv2d(x, w): """Returns a 2d depthwise convolution layer with full stride.""" return nn.depthwise_conv2d(x, w, strides=[1, 1, 1, 1], padding='SAME')
def depthwise_convolution2d( inputs, kernel_size, depth_multiplier=1, stride=1, padding='SAME', rate=1, activation_fn=nn.relu, normalizer_fn=None, normalizer_params=None, weights_initializer=initializers.xavier_initializer(), weights_regularizer=None, biases_initializer=init_ops.zeros_initializer(), biases_regularizer=None, reuse=None, variables_collections=None, outputs_collections=None, trainable=True, data_format='NHWC', scope=None): """Adds a depthwise 2D convolution with optional batch_norm layer. This op performs a depthwise convolution that acts separately on channels, creating a variable called `depthwise_weights`. Then, if `normalizer_fn` is None, it adds bias to the result, creating a variable called 'biases', otherwise, the `normalizer_fn` is applied. It finally applies an activation function to produce the end result. Args: inputs: A tensor of size [batch_size, height, width, channels]. num_outputs: The number of pointwise convolution output filters. If is None, then we skip the pointwise convolution stage. kernel_size: A list of length 2: [kernel_height, kernel_width] of of the filters. Can be an int if both values are the same. depth_multiplier: The number of depthwise convolution output channels for each input channel. The total number of depthwise convolution output channels will be equal to `num_filters_in * depth_multiplier`. stride: A list of length 2: [stride_height, stride_width], specifying the depthwise convolution stride. Can be an int if both strides are the same. padding: One of 'VALID' or 'SAME'. rate: A list of length 2: [rate_height, rate_width], specifying the dilation rates for atrous convolution. Can be an int if both rates are the same. If any value is larger than one, then both stride values need to be one. activation_fn: Activation function. The default value is a ReLU function. Explicitly set it to None to skip it and maintain a linear activation. normalizer_fn: Normalization function to use instead of `biases`. If `normalizer_fn` is provided then `biases_initializer` and `biases_regularizer` are ignored and `biases` are not created nor added. default set to None for no normalizer function normalizer_params: Normalization function parameters. weights_initializer: An initializer for the weights. weights_regularizer: Optional regularizer for the weights. biases_initializer: An initializer for the biases. If None skip biases. biases_regularizer: Optional regularizer for the biases. reuse: Whether or not the layer and its variables should be reused. To be able to reuse the layer scope must be given. variables_collections: Optional list of collections for all the variables or a dictionary containing a different list of collection per variable. outputs_collections: Collection to add the outputs. trainable: Whether or not the variables should be trainable or not. scope: Optional scope for variable_scope. Returns: A `Tensor` representing the output of the operation. """ with variable_scope.variable_scope(scope, 'DepthwiseConv2d', [inputs], reuse=reuse) as sc: inputs = ops.convert_to_tensor(inputs) # Actually apply depthwise conv instead of separable conv. dtype = inputs.dtype.base_dtype kernel_h, kernel_w = utils.two_element_tuple(kernel_size) stride_h, stride_w = utils.two_element_tuple(stride) if data_format == 'NHWC': num_filters_in = utils.last_dimension(inputs.get_shape(), min_rank=4) strides = [1, stride_h, stride_w, 1] else: num_filters_in = inputs.get_shape().as_list()[1] strides = [1, 1, stride_h, stride_w] weights_collections = utils.get_variable_collections( variables_collections, 'weights') # Depthwise weights variable. depthwise_shape = [ kernel_h, kernel_w, num_filters_in, depth_multiplier ] depthwise_weights = variables.model_variable( 'depthwise_weights', shape=depthwise_shape, dtype=dtype, initializer=weights_initializer, regularizer=weights_regularizer, trainable=trainable, collections=weights_collections) outputs = nn.depthwise_conv2d(inputs, depthwise_weights, strides, padding, rate=utils.two_element_tuple(rate), data_format=data_format) num_outputs = depth_multiplier * num_filters_in if normalizer_fn is not None: normalizer_params = normalizer_params or {} outputs = normalizer_fn(outputs, **normalizer_params) else: if biases_initializer is not None: biases_collections = utils.get_variable_collections( variables_collections, 'biases') biases = variables.model_variable( 'biases', shape=[ num_outputs, ], dtype=dtype, initializer=biases_initializer, regularizer=biases_regularizer, trainable=trainable, collections=biases_collections) outputs = nn.bias_add(outputs, biases, data_format=data_format) if activation_fn is not None: outputs = activation_fn(outputs) return utils.collect_named_outputs(outputs_collections, sc.original_name_scope, outputs)
def separable_convolution2d_diffpad( inputs, num_outputs, kernel_size, depth_multiplier, stride=1, padding='SAME', activation_fn=nn.relu, normalizer_fn=None, normalizer_params=None, weights_initializer=initializers.xavier_initializer(), weights_regularizer=None, biases_initializer=init_ops.zeros_initializer, biases_regularizer=None, reuse=None, variables_collections=None, outputs_collections=None, trainable=True, scope=None): """Adds a depth-separable 2D convolution with optional batch_norm layer. This op first performs a depthwise convolution that acts separately on channels, creating a variable called `depthwise_weights`. If `num_outputs` is not None, it adds a pointwise convolution that mixes channels, creating a variable called `pointwise_weights`. Then, if `batch_norm_params` is None, it adds bias to the result, creating a variable called 'biases', otherwise it adds a batch normalization layer. It finally applies an activation function to produce the end result. Args: inputs: a tensor of size [batch_size, height, width, channels]. num_outputs: the number of pointwise convolution output filters. If is None, then we skip the pointwise convolution stage. kernel_size: a list of length 2: [kernel_height, kernel_width] of of the filters. Can be an int if both values are the same. depth_multiplier: the number of depthwise convolution output channels for each input channel. The total number of depthwise convolution output channels will be equal to `num_filters_in * depth_multiplier`. stride: a list of length 2: [stride_height, stride_width], specifying the depthwise convolution stride. Can be an int if both strides are the same. padding: one of 'VALID' or 'SAME'. activation_fn: activation function, set to None to skip it and maintain a linear activation. normalizer_fn: normalization function to use instead of `biases`. If `normalizer_fn` is provided then `biases_initializer` and `biases_regularizer` are ignored and `biases` are not created nor added. default set to None for no normalizer function normalizer_params: normalization function parameters. weights_initializer: An initializer for the weights. weights_regularizer: Optional regularizer for the weights. biases_initializer: An initializer for the biases. If None skip biases. biases_regularizer: Optional regularizer for the biases. reuse: whether or not the layer and its variables should be reused. To be able to reuse the layer scope must be given. variables_collections: optional list of collections for all the variables or a dictionay containing a different list of collection per variable. outputs_collections: collection to add the outputs. trainable: whether or not the variables should be trainable or not. scope: Optional scope for variable_scope. Returns: A `Tensor` representing the output of the operation. """ with variable_scope.variable_scope( scope, 'SeparableConv2dPad', [inputs], reuse=reuse) as sc: # TOFIX: Hard set padding and multiplier... padding = 'SAME' depth_multiplier = 1 dtype = inputs.dtype.base_dtype kernel_h, kernel_w = utils.two_element_tuple(kernel_size) stride_h, stride_w = utils.two_element_tuple(stride) num_filters_in = utils.last_dimension(inputs.get_shape(), min_rank=4) weights_collections = utils.get_variable_collections( variables_collections, 'weights') depthwise_shape = [kernel_h, kernel_w, num_filters_in, depth_multiplier] depthwise_weights = variables.model_variable( 'depthwise_weights', shape=depthwise_shape, dtype=dtype, initializer=weights_initializer, regularizer=weights_regularizer, trainable=trainable, collections=weights_collections) strides = [1, stride_h, stride_w, 1] # Classic depthwise_conv2d with SAME padding (i.e. zero padding). outputs = nn.depthwise_conv2d(inputs, depthwise_weights, strides, padding) # Fix padding according too the difference rule. Dirty shit! # diff_padding = variables.local_variable(outputs) diff_padding = variables.variable( 'diff_padding', shape=outputs.get_shape(), dtype=dtype, initializer=tf.constant_initializer(0.0), regularizer=None, trainable=False, collections=[ops.GraphKeys.LOCAL_VARIABLES]) # Bottom and top fixing... # print(diff_padding[:, 0, :, :].get_shape()) # print(depthwise_weights[0, 0, :, 0].get_shape()) op1 = diff_padding[:, 0, :, :].assign( outputs[:, 0, :, :] * (depthwise_weights[0, 0, :, 0] + depthwise_weights[0, 1, :, 0] + depthwise_weights[0, 2, :, 0])) op2 = diff_padding[:, -1, :, :].assign( outputs[:, -1, :, :] * (depthwise_weights[-1, 0, :, 0] + depthwise_weights[-1, 1, :, 0] + depthwise_weights[-1, 2, :, 0])) # Bottom and top fixing... op3 = diff_padding[:, :, 0, :].assign( outputs[:, :, 0, :] * (depthwise_weights[0, 0, :, 0] + depthwise_weights[1, 0, :, 0] + depthwise_weights[2, 0, :, 0])) op4 = diff_padding[:, :, -1, :].assign( outputs[:, :, -1, :] * (depthwise_weights[0, -1, :, 0] + depthwise_weights[1, -1, :, 0] + depthwise_weights[2, -1, :, 0])) diff_padding1 = control_flow_ops.with_dependencies([op1, op2, op3, op4], diff_padding) # Fix double addition in corners. op5 = diff_padding[:, 0, 0, :].assign( diff_padding1[:, 0, 0, :] - outputs[:, 0, 0, :] * depthwise_weights[0, 0, :, 0]) op6 = diff_padding[:, -1, 0, :].assign( diff_padding1[:, -1, 0, :] - outputs[:, -1, 0, :] * depthwise_weights[-1, 0, :, 0]) op7 = diff_padding[:, 0, -1, :].assign( diff_padding1[:, 0, -1, :] - outputs[:, 0, -1, :] * depthwise_weights[0, -1, :, 0]) op8 = diff_padding[:, -1, -1, :].assign( diff_padding1[:, -1, -1, :] - outputs[:, -1, -1, :] * depthwise_weights[-1, -1, :, 0]) diff_padding2 = control_flow_ops.with_dependencies([op5, op6, op7, op8], diff_padding) # # Update padding! outputs = outputs + diff_padding2 # Adding pointwise convolution. if num_outputs is not None: # Full separable convolution: Depthwise followed by pointwise convolution. pointwise_shape = [1, 1, depth_multiplier * num_filters_in, num_outputs] pointwise_weights = variables.model_variable( 'pointwise_weights', shape=pointwise_shape, dtype=dtype, initializer=weights_initializer, regularizer=weights_regularizer, trainable=trainable, collections=weights_collections) outputs = nn.conv2d(outputs, pointwise_weights, strides=(1, 1, 1, 1), padding='SAME') else: # Depthwise convolution only. num_outputs = depth_multiplier * num_filters_in if normalizer_fn is not None: normalizer_params = normalizer_params or {} outputs = normalizer_fn(outputs, **normalizer_params) else: if biases_initializer is not None: biases_collections = utils.get_variable_collections( variables_collections, 'biases') biases = variables.model_variable('biases', shape=[num_outputs,], dtype=dtype, initializer=biases_initializer, regularizer=biases_regularizer, collections=biases_collections) outputs = nn.bias_add(outputs, biases) if activation_fn is not None: outputs = activation_fn(outputs) return utils.collect_named_outputs(outputs_collections, sc.original_name_scope, outputs)
def depth_conv2d(inputs, kernel_size, stride=1, channel_multiplier=1, padding='SAME', data_format=DATA_FORMAT_NHWC, rate=1, activation_fn=nn.relu, normalizer_fn=None, normalizer_params=None, weights_initializer=initializers.xavier_initializer(), weights_regularizer=None, biases_initializer=init_ops.zeros_initializer(), biases_regularizer=None, reuse=None, variables_collections=None, outputs_collections=None, trainable=True, scope=None): if data_format not in (DATA_FORMAT_NCHW, DATA_FORMAT_NHWC): raise ValueError('data_format has to be either NCHW or NHWC.') layer_variable_getter = _build_variable_getter({ 'bias': 'biases', 'depthwise_kernel': 'depthwise_weights' }) with variable_scope.variable_scope( scope, 'SeparableConv2d', [inputs], reuse=reuse, custom_getter=layer_variable_getter) as sc: inputs = ops.convert_to_tensor(inputs) df = ('channels_first' if data_format and data_format.startswith('NC') else 'channels_last') # Actually apply depthwise conv instead of separable conv. dtype = inputs.dtype.base_dtype kernel_h, kernel_w = utils.two_element_tuple(kernel_size) stride_h, stride_w = utils.two_element_tuple(stride) num_filters_in = utils.channel_dimension(inputs.get_shape(), df, min_rank=4) weights_collections = utils.get_variable_collections( variables_collections, 'weights') depthwise_shape = [ kernel_h, kernel_w, num_filters_in, channel_multiplier ] depthwise_weights = variables.model_variable( 'depthwise_weights', shape=depthwise_shape, dtype=dtype, initializer=weights_initializer, regularizer=weights_regularizer, trainable=trainable, collections=weights_collections) strides = [ 1, 1, stride_h, stride_w ] if data_format.startswith('NC') else [1, stride_h, stride_w, 1] outputs = nn.depthwise_conv2d(inputs, depthwise_weights, strides, padding, rate=utils.two_element_tuple(rate), data_format=data_format) num_outputs = num_filters_in if normalizer_fn is not None: normalizer_params = normalizer_params or {} outputs = normalizer_fn(outputs, **normalizer_params) else: if biases_initializer is not None: biases_collections = utils.get_variable_collections( variables_collections, 'biases') biases = variables.model_variable( 'biases', shape=[ num_outputs, ], dtype=dtype, initializer=biases_initializer, regularizer=biases_regularizer, trainable=trainable, collections=biases_collections) outputs = nn.bias_add(outputs, biases, data_format=data_format) if activation_fn is not None: outputs = activation_fn(outputs) return utils.collect_named_outputs(outputs_collections, sc.name, outputs)
def separable_convolution2d( inputs, num_outputs, kernel_size, depth_multiplier, stride=1, padding='SAME', data_format=DATA_FORMAT_NHWC, rate=1, activation_fn=nn.relu, normalizer_fn=None, normalizer_params=None, weights_initializer=initializers.xavier_initializer(), weights_regularizer=None, biases_initializer=init_ops.zeros_initializer(), biases_regularizer=None, reuse=None, variables_collections=None, outputs_collections=None, trainable=True, scope=None, mask_type=None, mask_init_value=None, mask_bern_sample=None): """Adds a depth-separable 2D convolution with optional batch_norm layer. This op first performs a depthwise convolution that acts separately on channels, creating a variable called `depthwise_weights`. If `num_outputs` is not None, it adds a pointwise convolution that mixes channels, creating a variable called `pointwise_weights`. Then, if `normalizer_fn` is None, it adds bias to the result, creating a variable called 'biases', otherwise, the `normalizer_fn` is applied. It finally applies an activation function to produce the end result. Args: inputs: A tensor of size [batch_size, height, width, channels]. num_outputs: The number of pointwise convolution output filters. If is None, then we skip the pointwise convolution stage. kernel_size: A list of length 2: [kernel_height, kernel_width] of of the filters. Can be an int if both values are the same. depth_multiplier: The number of depthwise convolution output channels for each input channel. The total number of depthwise convolution output channels will be equal to `num_filters_in * depth_multiplier`. stride: A list of length 2: [stride_height, stride_width], specifying the depthwise convolution stride. Can be an int if both strides are the same. padding: One of 'VALID' or 'SAME'. data_format: A string. `NHWC` (default) and `NCHW` are supported. rate: A list of length 2: [rate_height, rate_width], specifying the dilation rates for atrous convolution. Can be an int if both rates are the same. If any value is larger than one, then both stride values need to be one. activation_fn: Activation function. The default value is a ReLU function. Explicitly set it to None to skip it and maintain a linear activation. normalizer_fn: Normalization function to use instead of `biases`. If `normalizer_fn` is provided then `biases_initializer` and `biases_regularizer` are ignored and `biases` are not created nor added. default set to None for no normalizer function normalizer_params: Normalization function parameters. weights_initializer: An initializer for the weights. weights_regularizer: Optional regularizer for the weights. biases_initializer: An initializer for the biases. If None skip biases. biases_regularizer: Optional regularizer for the biases. reuse: Whether or not the layer and its variables should be reused. To be able to reuse the layer scope must be given. variables_collections: Optional list of collections for all the variables or a dictionary containing a different list of collection per variable. outputs_collections: Collection to add the outputs. trainable: Whether or not the variables should be trainable or not. scope: Optional scope for variable_scope. Returns: A `Tensor` representing the output of the operation. Raises: ValueError: If `data_format` is invalid. """ if data_format not in (DATA_FORMAT_NCHW, DATA_FORMAT_NHWC): raise ValueError('data_format has to be either NCHW or NHWC.') layer_variable_getter = _build_variable_getter({ 'bias': 'biases', 'depthwise_kernel': 'depthwise_weights', 'pointwise_kernel': 'pointwise_weights' }) gen_mask_kwargs = dict(mask_bern_sample=mask_bern_sample, mask_type=mask_type, mask_shape=None, mask_init_value=mask_init_value, dtype=inputs.dtype.base_dtype, get_var_fn=None) with variable_scope.variable_scope( scope, 'SeparableConv2d', [inputs], reuse=reuse, custom_getter=layer_variable_getter) as sc: inputs = ops.convert_to_tensor(inputs) df = ('channels_first' if data_format and data_format.startswith('NC') else 'channels_last') if num_outputs is not None: # Apply separable conv using the SeparableConvolution2D layer. layer = convolutional_layers.SeparableConvolution2D( filters=num_outputs, kernel_size=kernel_size, strides=stride, padding=padding, data_format=df, dilation_rate=utils.two_element_tuple(rate), activation=None, depth_multiplier=depth_multiplier, use_bias=not normalizer_fn and biases_initializer, depthwise_initializer=weights_initializer, pointwise_initializer=weights_initializer, bias_initializer=biases_initializer, depthwise_regularizer=weights_regularizer, pointwise_regularizer=weights_regularizer, bias_regularizer=biases_regularizer, activity_regularizer=None, trainable=trainable, name=sc.name, dtype=inputs.dtype.base_dtype, _scope=sc, _reuse=reuse) # Insert masks for pruning layer.build(inputs.get_shape()) masked_depthwise_kernel, masked_bias = generate_masks( kernel=layer.depthwise_kernel, bias=layer.bias, **gen_mask_kwargs) masked_pointwise_kernel, _ = generate_masks( kernel=layer.pointwise_kernel, bias=None, **gen_mask_kwargs) layer.depthwise_kernel_copy = layer.depthwise_kernel layer.pointwise_kernel_copy = layer.pointwise_kernel layer.bias_copy = layer.bias layer.depthwise_kernel = masked_depthwise_kernel layer.pointwise_kernel = masked_pointwise_kernel layer.bias = masked_bias outputs = layer.apply(inputs) # Compute layer.depthwise_kernel = layer.depthwise_kernel_copy layer.pointwise_kernel = layer.pointwise_kernel_copy layer.bias = layer.bias_copy # Add variables to collections. _add_variable_to_collections(layer.depthwise_kernel, variables_collections, 'weights') _add_variable_to_collections(layer.pointwise_kernel, variables_collections, 'weights') if layer.bias is not None: _add_variable_to_collections(layer.bias, variables_collections, 'biases') if normalizer_fn is not None: normalizer_params = normalizer_params or {} outputs = normalizer_fn(outputs, **normalizer_params) else: # Actually apply depthwise conv instead of separable conv. dtype = inputs.dtype.base_dtype kernel_h, kernel_w = utils.two_element_tuple(kernel_size) stride_h, stride_w = utils.two_element_tuple(stride) num_filters_in = utils.channel_dimension(inputs.get_shape(), df, min_rank=4) weights_collections = utils.get_variable_collections( variables_collections, 'weights') depthwise_shape = [ kernel_h, kernel_w, num_filters_in, depth_multiplier ] depthwise_weights = variables.model_variable( 'depthwise_weights', shape=depthwise_shape, dtype=dtype, initializer=weights_initializer, regularizer=weights_regularizer, trainable=trainable, collections=weights_collections) masked_depthwise_weights, _ = generate_masks( kernel=depthwise_weights, bias=None, **gen_mask_kwargs) strides = [ 1, 1, stride_h, stride_w ] if data_format.startswith('NC') else [1, stride_h, stride_w, 1] outputs = nn.depthwise_conv2d(inputs, masked_depthwise_weights, strides, padding, rate=utils.two_element_tuple(rate), data_format=data_format) num_outputs = depth_multiplier * num_filters_in if normalizer_fn is not None: normalizer_params = normalizer_params or {} outputs = normalizer_fn(outputs, **normalizer_params) else: if biases_initializer is not None: biases_collections = utils.get_variable_collections( variables_collections, 'biases') biases = variables.model_variable( 'biases', shape=[ num_outputs, ], dtype=dtype, initializer=biases_initializer, regularizer=biases_regularizer, trainable=trainable, collections=biases_collections) # TODO: bias is not masked currently outputs = nn.bias_add(outputs, biases, data_format=data_format) if activation_fn is not None: outputs = activation_fn(outputs) return utils.collect_named_outputs(outputs_collections, sc.name, outputs)