def testFloorDivGrad(self): a = variables.Variable(2.) b = variables.Variable(4.) input_vars = [a, b] self.evaluate(variables.global_variables_initializer()) if context.executing_eagerly(): # TDOO(rmlarsen): Is there a more compact way of # writing this for multiple expressions? with backprop.GradientTape() as tape: tape.watch(input_vars) c_grad0 = tape.gradient(math_ops.divide(a, b), input_vars) with backprop.GradientTape() as tape: tape.watch(input_vars) c_grad1 = tape.gradient(math_ops.div(a, b), input_vars) with backprop.GradientTape() as tape: tape.watch(input_vars) c_grad2 = tape.gradient(math_ops.floordiv(a, b), input_vars) else: c_grad0 = gradients.gradients(math_ops.divide(a, b), input_vars) c_grad1 = gradients.gradients(math_ops.div(a, b), input_vars) c_grad2 = gradients.gradients(math_ops.floordiv(a, b), input_vars) self.assertAllEqual([self.evaluate(x) for x in c_grad0], [.25, -.125]) self.assertAllEqual([self.evaluate(x) for x in c_grad1], [.25, -.125]) self.assertAllEqual( [None if x is None else self.evaluate(x) for x in c_grad2], [None, None])
def testFloorDivGrad(self): a = variables.Variable(2.) b = variables.Variable(4.) self.evaluate(variables.global_variables_initializer()) c_grad = gradients.gradients(math_ops.divide(a, b), [a, b]) self.assertAllEqual([self.evaluate(x) for x in c_grad], [.25, -.125]) c_grad = gradients.gradients(math_ops.div(a, b), [a, b]) self.assertAllEqual([self.evaluate(x) for x in c_grad], [.25, -.125]) c_grad = gradients.gradients(math_ops.floordiv(a, b), [a, b]) self.assertAllEqual( [None if x is None else self.evaluate(x) for x in c_grad], [None, None])
def testFloorDivGrad(self): with self.test_session(): a = variables.Variable(2.0) b = variables.Variable(4.0) with self.test_session() as sess: sess.run(variables.initialize_all_variables()) c_grad = gradients.gradients(math_ops.div_deprecated(a, b), [a, b]) self.assertAllEqual([x.eval() for x in c_grad], [0.25, -0.125]) c_grad = gradients.gradients(math_ops.div(a, b), [a, b]) self.assertAllEqual([x.eval() for x in c_grad], [0.25, -0.125]) c_grad = gradients.gradients(math_ops.floordiv(a, b), [a, b]) self.assertAllEqual([None if x is None else x.eval() for x in c_grad], [None, None])
def testFloorDivGrad(self): with self.test_session(): a = variables.Variable(2.) b = variables.Variable(4.) with self.test_session() as sess: sess.run(variables.global_variables_initializer()) c_grad = gradients.gradients(math_ops.divide(a, b), [a, b]) self.assertAllEqual([x.eval() for x in c_grad], [.25, -.125]) c_grad = gradients.gradients(math_ops.div(a, b), [a, b]) self.assertAllEqual([x.eval() for x in c_grad], [.25, -.125]) c_grad = gradients.gradients(math_ops.floordiv(a, b), [a, b]) self.assertAllEqual([None if x is None else x.eval() for x in c_grad], [None, None])
def _look_ahead(self, coefficients, train_op, var): """lookahead at the param level instead of group level""" with ops.control_dependencies([train_op]): slow_var = self.get_slot(var, 'slow') step_back = slow_var + coefficients['beta_lookahead'] * (var - slow_var) sync_cond = math_ops.equal( math_ops.floordiv(coefficients['local_step'], coefficients['lookahead_every_nth_iter']) * coefficients['lookahead_every_nth_iter'], coefficients['local_step']) with ops.control_dependencies([step_back]): slow_update = state_ops.assign(slow_var, array_ops.where( sync_cond, step_back, slow_var), use_locking=self._use_locking) var_update = state_ops.assign(var, array_ops.where( sync_cond, step_back, var), use_locking=self._use_locking) look_ahead_op = control_flow_ops.group(slow_update, var_update) return look_ahead_op
def __rfloordiv__(self, other): return math_ops.floordiv(other, self)
def __floordiv__(self, other): return math_ops.floordiv(self, other)
def split(value: ragged_tensor.Ragged, num_or_size_splits, axis=0, num=None, name=None): """Splits a RaggedTensor `value` into a list of sub RaggedTensors. If `num_or_size_splits` is an `int`, then it splits `value` along the dimension `axis` into `num_or_size_splits` smaller RaggedTensors. This requires that `value.shape[axis]` is divisible by `num_or_size_splits`. If `num_or_size_splits` is a 1-D Tensor (or list), then `value` is split into `len(num_or_size_splits)` elements. The shape of the `i`-th element has the same size as the `value` except along dimension `axis` where the size is `num_or_size_splits[i]`. Splits along a ragged dimension is not allowed. For example: >>> rt = tf.RaggedTensor.from_row_lengths( ... np.arange(6 * 3).reshape(6, 3), row_lengths=[1, 2, 2, 1]) >>> rt.shape TensorShape([4, None, 3]) >>> >>> rt1, rt2 = tf.split(rt, 2) # uniform splits >>> rt1.shape TensorShape([2, None, 3]) >>> rt2.shape TensorShape([2, None, 3]) >>> >>> rt3, rt4, rt5 = tf.split(rt, [1, 2, 1]) # ragged splits >>> rt3.shape TensorShape([1, None, 3]) >>> rt4.shape TensorShape([2, None, 3]) >>> rt5.shape TensorShape([1, None, 3]) >>> >>> rt6, rt7 = tf.split(rt, [1, 2], axis=2) # splits along axis 2 >>> rt6.shape TensorShape([4, None, 1]) >>> rt7.shape TensorShape([4, None, 2]) Args: value: The `RaggedTensor` to split. num_or_size_splits: Either an `int` indicating the number of splits along `axis` or a 1-D integer `Tensor` or Python list containing the sizes of each output tensor along `axis`. If a Python int, then it must evenly divide `value.shape[axis]`; otherwise the sum of sizes along the split axis must match that of the `value`. axis: An `int` or scalar `int32` `Tensor`. The dimension along which to split. Must be in the range `[-rank(value), rank(value))`. Defaults to 0. num: An `int` used to specify the number of outputs when `num_or_size_splits` is a 1-D list or `Tensor` and its length is statically unknown, e.g., specifying `tf.TensorSepc(None)` with the `input_signature` argument of `tf.function` (optional). name: A name for the operation (optional). Returns: if `num_or_size_splits` is an `int` returns a list of `num_or_size_splits` `RaggedTensor` objects; if `num_or_size_splits` is a 1-D Tensor returns `num_or_size_splits.get_shape[0]` `RaggedTensor` objects resulting from splitting `value`. Raises: ValueError: If the dimension `axis` of `value` is a ragged dimension. ValueError: If `num` is unspecified and cannot be inferred. ValueError: If `num` is specified but doesn't match the length of `num_or_size_splits`. ValueError: If `num_or_size_splits` is an `int` and less than 1. TypeError: If `num_or_size_splits` is not an `int` or 1-D list or 1-D `Tensor`. InvalidArgumentError: If the `axis` of `value` cannot be exactly splitted by `num_or_size_splits`. InvalidArgumentError: If `num_or_size_splits` is contains negative integers. InvalidArgumentError: If `num_or_size_splits`'s static shape is unknown and its dynamic shape is inconsistent `num`. InvalidArgumentError: If `num_or_size_splits`'s static rank is unknown and `axis` is a negative integer. """ with ops.name_scope(name, 'RaggedSplit'): value = ragged_tensor.convert_to_tensor_or_ragged_tensor( value, name='value') if isinstance(num_or_size_splits, int) and num_or_size_splits == 1: return [value] # static assert check_ops.assert_integer_v2( num_or_size_splits, message=('`num_or_size_splits` must be an `int` or 1-D list or ' '`Tensor` of integers.')) value_shape = ragged_shape.RaggedShape.from_tensor(value) axis = array_ops.get_positive_axis(axis, value_shape.rank) try: dim_size = value_shape[axis] except ValueError: raise ValueError('Cannot split a ragged dimension. Got `value` with ' f'shape {value_shape} and `axis` {axis}.') if isinstance(num_or_size_splits, int): # Uniform split num_splits = num_or_size_splits if num_splits < 1: raise ValueError('`num_or_size_splits` must be >=1 if it is an `int`.' f'Received {num_or_size_splits}.') split_length = math_ops.floordiv(dim_size, num_splits) split_lengths = array_ops.repeat(split_length, num_splits) else: # Ragged split num_splits = None split_lengths = ops.convert_to_tensor(num_or_size_splits) if split_lengths.shape.ndims is not None: if split_lengths.shape.ndims != 1: raise TypeError('`num_or_size_splits` must be an `int` or 1-D list ' f'or `Tensor`. Received {num_or_size_splits}.') num_splits = tensor_shape.dimension_value(split_lengths.shape[0]) if num_splits is None: if num is None: raise ValueError('`num` must be specified as an `int` when the ' 'size of `num_or_size_split` is statically ' f'unknown. Received `num`: {num} and ' f'`num_or_size_split`: {num_or_size_splits}.') num_splits = num else: if num is not None and num != num_splits: raise ValueError('`num` does not match the size of ' f'`num_or_size_split`. Received `num`: {num} and ' f'size of `num_or_size_split`: {num_splits}.') splits = array_ops.concat([[0], math_ops.cumsum(split_lengths)], axis=0) checks = [] checks.append( check_ops.assert_non_negative_v2( num_or_size_splits, message='`num_or_size_splits` must be non-negative.')) checks.append( check_ops.assert_equal_v2( num_splits, array_ops.shape(split_lengths)[0], message='`num` is inconsistent with `num_or_size_split.shape[0]`.')) checks.append( check_ops.assert_equal_v2( math_ops.cast(dim_size, splits.dtype), splits[-1], message=('Cannot exactly split the `axis` dimension of `value` ' 'with the given `num_or_size_split`.'))) splits = control_flow_ops.with_dependencies(checks, splits) splited_rts = [] slices = [slice(None)] * (axis + 1) for i in range(num_splits): slices[-1] = slice(splits[i], splits[i + 1]) splited_rts.append(value[tuple(slices)]) return splited_rts
def f(x1, x2): if x1.dtype == dtypes.bool: assert x2.dtype == dtypes.bool x1 = math_ops.cast(x1, dtypes.int8) x2 = math_ops.cast(x2, dtypes.int8) return math_ops.floordiv(x1, x2)
def atrous_conv2d(value, filters, rate, padding, name=None): """Atrous convolution (a.k.a. convolution with holes or dilated convolution). Computes a 2-D atrous convolution, also known as convolution with holes or dilated convolution, given 4-D `value` and `filters` tensors. If the `rate` parameter is equal to one, it performs regular 2-D convolution. If the `rate` parameter is greater than one, it performs convolution with holes, sampling the input values every `rate` pixels in the `height` and `width` dimensions. This is equivalent to convolving the input with a set of upsampled filters, produced by inserting `rate - 1` zeros between two consecutive values of the filters along the `height` and `width` dimensions, hence the name atrous convolution or convolution with holes (the French word trous means holes in English). More specifically: output[b, i, j, k] = sum_{di, dj, q} filters[di, dj, q, k] * value[b, i + rate * di, j + rate * dj, q] Atrous convolution allows us to explicitly control how densely to compute feature responses in fully convolutional networks. Used in conjunction with bilinear interpolation, it offers an alternative to `conv2d_transpose` in dense prediction tasks such as semantic image segmentation, optical flow computation, or depth estimation. It also allows us to effectively enlarge the field of view of filters without increasing the number of parameters or the amount of computation. For a description of atrous convolution and how it can be used for dense feature extraction, please see: [Semantic Image Segmentation with Deep Convolutional Nets and Fully Connected CRFs](http://arxiv.org/abs/1412.7062). The same operation is investigated further in [Multi-Scale Context Aggregation by Dilated Convolutions](http://arxiv.org/abs/1511.07122). Previous works that effectively use atrous convolution in different ways are, among others, [OverFeat: Integrated Recognition, Localization and Detection using Convolutional Networks](http://arxiv.org/abs/1312.6229) and [Fast Image Scanning with Deep Max-Pooling Convolutional Neural Networks] (http://arxiv.org/abs/1302.1700). Atrous convolution is also closely related to the so-called noble identities in multi-rate signal processing. There are many different ways to implement atrous convolution (see the refs above). The implementation here reduces atrous_conv2d(value, filters, rate, padding=padding) to the following three operations: paddings = ... net = space_to_batch(value, paddings, block_size=rate) net = conv2d(net, filters, strides=[1, 1, 1, 1], padding="VALID") crops = ... net = batch_to_space(net, crops, block_size=rate) Advanced usage. Note the following optimization: A sequence of `atrous_conv2d` operations with identical `rate` parameters, 'SAME' `padding`, and filters with odd heights/ widths: net = atrous_conv2d(net, filters1, rate, padding="SAME") net = atrous_conv2d(net, filters2, rate, padding="SAME") ... net = atrous_conv2d(net, filtersK, rate, padding="SAME") can be equivalently performed cheaper in terms of computation and memory as: pad = ... # padding so that the input dims are multiples of rate net = space_to_batch(net, paddings=pad, block_size=rate) net = conv2d(net, filters1, strides=[1, 1, 1, 1], padding="SAME") net = conv2d(net, filters2, strides=[1, 1, 1, 1], padding="SAME") ... net = conv2d(net, filtersK, strides=[1, 1, 1, 1], padding="SAME") net = batch_to_space(net, crops=pad, block_size=rate) because a pair of consecutive `space_to_batch` and `batch_to_space` ops with the same `block_size` cancel out when their respective `paddings` and `crops` inputs are identical. Args: value: A 4-D `Tensor` of type `float`. It needs to be in the default "NHWC" format. Its shape is `[batch, in_height, in_width, in_channels]`. filters: A 4-D `Tensor` with the same type as `value` and shape `[filter_height, filter_width, in_channels, out_channels]`. `filters`' `in_channels` dimension must match that of `value`. Atrous convolution is equivalent to standard convolution with upsampled filters with effective height `filter_height + (filter_height - 1) * (rate - 1)` and effective width `filter_width + (filter_width - 1) * (rate - 1)`, produced by inserting `rate - 1` zeros along consecutive elements across the `filters`' spatial dimensions. rate: A positive int32. The stride with which we sample input values across the `height` and `width` dimensions. Equivalently, the rate by which we upsample the filter values by inserting zeros across the `height` and `width` dimensions. In the literature, the same parameter is sometimes called `input stride` or `dilation`. padding: A string, either `'VALID'` or `'SAME'`. The padding algorithm. name: Optional name for the returned tensor. Returns: A `Tensor` with the same type as `value`. Raises: ValueError: If input/output depth does not match `filters`' shape, or if padding is other than `'VALID'` or `'SAME'`. """ with ops.op_scope([value, filters], name, "atrous_conv2d") as name: value = ops.convert_to_tensor(value, name="value") filters = ops.convert_to_tensor(filters, name="filters") value_shape = value.get_shape() filter_shape = filters.get_shape() if not value_shape[3].is_compatible_with(filter_shape[2]): raise ValueError( "value's input channels does not match filters' input channels, " "{} != {}".format(value_shape[3], filter_shape[2])) if rate < 1: raise ValueError("rate {} cannot be less than one".format(rate)) if rate == 1: value = gen_nn_ops.conv2d(input=value, filter=filters, strides=[1, 1, 1, 1], padding=padding) return value # We have two padding contributions. The first is used for converting "SAME" # to "VALID". The second is required so that the height and width of the # zero-padded value tensor are multiples of rate. # Spatial dimensions of original input value_shape = array_ops.shape(value) in_height = value_shape[1] in_width = value_shape[2] # Spatial dimensions of the filters and the upsampled filters in which we # introduce (rate - 1) zeros between consecutive filter values. filter_height = int(filter_shape[0]) filter_width = int(filter_shape[1]) filter_height_up = filter_height + (filter_height - 1) * (rate - 1) filter_width_up = filter_width + (filter_width - 1) * (rate - 1) # Padding required to reduce to "VALID" convolution if padding == "SAME": pad_height = filter_height_up - 1 pad_width = filter_width_up - 1 elif padding == "VALID": pad_height = 0 pad_width = 0 else: raise ValueError("Invalid padding") # When padding is "SAME" and the pad_height (pad_width) is odd, we pad more # to bottom (right), following the same convention as conv2d(). pad_top = math_ops.floordiv(pad_height, 2) pad_bottom = pad_height - pad_top pad_left = math_ops.floordiv(pad_width, 2) pad_right = pad_width - pad_left # More padding so that rate divides the height and width of the input value in_height = in_height + pad_top + pad_bottom in_width = in_width + pad_left + pad_right mod_height = math_ops.mod(in_height, rate) mod_width = math_ops.mod(in_width, rate) null = constant_op.constant(0) pad_bottom_extra = control_flow_ops.cond(gen_math_ops.equal(mod_height, 0), lambda: null, lambda: rate - mod_height) pad_right_extra = control_flow_ops.cond(gen_math_ops.equal(mod_width, 0), lambda: null, lambda: rate - mod_width) # The paddings argument to space_to_batch includes both padding components pad_bottom = pad_bottom + pad_bottom_extra pad_right = pad_right + pad_right_extra space_to_batch_pad = [[pad_top, pad_bottom], [pad_left, pad_right]] value = array_ops.space_to_batch(input=value, paddings=space_to_batch_pad, block_size=rate) value = gen_nn_ops.conv2d(input=value, filter=filters, strides=[1, 1, 1, 1], padding="VALID", name=name) # The crops argument to batch_to_space is just the extra padding component batch_to_space_crop = [[0, pad_bottom_extra], [0, pad_right_extra]] value = array_ops.batch_to_space(input=value, crops=batch_to_space_crop, block_size=rate) return value
def get_updates(self, loss, params): grads = self.get_gradients(loss, params) self.updates = [] lr = self.lr completed_updates = K.cast( math_ops.floordiv(self.iterations, self.accum_iters), K.floatx()) if self.initial_decay > 0: lr = lr * ( # pylint: disable=g-no-augmented-assignment 1. / (1. + self.decay * completed_updates)) with ops.control_dependencies( [state_ops.assign_add(self.iterations, 1)]): t = math_ops.cast(completed_updates + 1, K.floatx()) lr_t = lr * (K.sqrt(1. - math_ops.pow(self.beta_2, t)) / (1. - math_ops.pow(self.beta_1, t))) # self.iterations incremented after processing a batch # batch: 1 2 3 4 5 6 7 8 9 # self.iterations: 0 1 2 3 4 5 6 7 8 # update_switch = 1: x x (if accum_iters=4) update_switch = K.equal((self.iterations + 1) % self.accum_iters, 0) update_switch = K.cast(update_switch, K.floatx()) ms = [K.zeros(K.int_shape(p), dtype=K.dtype(p)) for p in params] vs = [K.zeros(K.int_shape(p), dtype=K.dtype(p)) for p in params] gs = [K.zeros(K.int_shape(p), dtype=K.dtype(p)) for p in params] if self.amsgrad: vhats = [K.zeros(K.int_shape(p), dtype=K.dtype(p)) for p in params] else: vhats = [K.zeros(1) for _ in params] self.weights = [self.iterations] + ms + vs + vhats for p, g, m, v, vhat, tg in zip(params, grads, ms, vs, vhats, gs): sum_grad = tg + g avg_grad = sum_grad / self.accum_iters_float m_t = (self.beta_1 * m) + (1. - self.beta_1) * avg_grad v_t = (self.beta_2 * v) + (1. - self.beta_2) * math_ops.square(avg_grad) if self.amsgrad: vhat_t = math_ops.maximum(vhat, v_t) p_t = p - lr_t * m_t / (K.sqrt(vhat_t) + self.epsilon) self.updates.append(state_ops.assign(vhat, vhat_t)) else: p_t = p - lr_t * m_t / (K.sqrt(v_t) + self.epsilon) self.updates.append( state_ops.assign(m, (1 - update_switch) * m + update_switch * m_t)) self.updates.append( state_ops.assign(v, (1 - update_switch) * v + update_switch * v_t)) self.updates.append( state_ops.assign(tg, (1 - update_switch) * sum_grad)) new_p = p_t # Apply constraints. if getattr(p, 'constraint', None) is not None: new_p = p.constraint(new_p) self.updates.append( state_ops.assign(p, (1 - update_switch) * p + update_switch * new_p)) return self.updates
def atrous_conv2d(value, filters, rate, padding, name=None): """Atrous convolution (a.k.a. convolution with holes or dilated convolution). Computes a 2-D atrous convolution, also known as convolution with holes or dilated convolution, given 4-D `value` and `filters` tensors. If the `rate` parameter is equal to one, it performs regular 2-D convolution. If the `rate` parameter is greater than one, it performs convolution with holes, sampling the input values every `rate` pixels in the `height` and `width` dimensions. This is equivalent to convolving the input with a set of upsampled filters, produced by inserting `rate - 1` zeros between two consecutive values of the filters along the `height` and `width` dimensions, hence the name atrous convolution or convolution with holes (the French word trous means holes in English). More specifically: output[b, i, j, k] = sum_{di, dj, q} filters[di, dj, q, k] * value[b, i + rate * di, j + rate * dj, q] Atrous convolution allows us to explicitly control how densely to compute feature responses in fully convolutional networks. Used in conjunction with bilinear interpolation, it offers an alternative to `conv2d_transpose` in dense prediction tasks such as semantic image segmentation, optical flow computation, or depth estimation. It also allows us to effectively enlarge the field of view of filters without increasing the number of parameters or the amount of computation. For a description of atrous convolution and how it can be used for dense feature extraction, please see: [Semantic Image Segmentation with Deep Convolutional Nets and Fully Connected CRFs](http://arxiv.org/abs/1412.7062). The same operation is investigated further in [Multi-Scale Context Aggregation by Dilated Convolutions](http://arxiv.org/abs/1511.07122). Previous works that effectively use atrous convolution in different ways are, among others, [OverFeat: Integrated Recognition, Localization and Detection using Convolutional Networks](http://arxiv.org/abs/1312.6229) and [Fast Image Scanning with Deep Max-Pooling Convolutional Neural Networks] (http://arxiv.org/abs/1302.1700). Atrous convolution is also closely related to the so-called noble identities in multi-rate signal processing. There are many different ways to implement atrous convolution (see the refs above). The implementation here reduces atrous_conv2d(value, filters, rate, padding=padding) to the following three operations: paddings = ... net = space_to_batch(value, paddings, block_size=rate) net = conv2d(net, filters, strides=[1, 1, 1, 1], padding="VALID") crops = ... net = batch_to_space(net, crops, block_size=rate) Advanced usage. Note the following optimization: A sequence of `atrous_conv2d` operations with identical `rate` parameters, 'SAME' `padding`, and filters with odd heights/ widths: net = atrous_conv2d(net, filters1, rate, padding="SAME") net = atrous_conv2d(net, filters2, rate, padding="SAME") ... net = atrous_conv2d(net, filtersK, rate, padding="SAME") can be equivalently performed cheaper in terms of computation and memory as: pad = ... # padding so that the input dims are multiples of rate net = space_to_batch(net, paddings=pad, block_size=rate) net = conv2d(net, filters1, strides=[1, 1, 1, 1], padding="SAME") net = conv2d(net, filters2, strides=[1, 1, 1, 1], padding="SAME") ... net = conv2d(net, filtersK, strides=[1, 1, 1, 1], padding="SAME") net = batch_to_space(net, crops=pad, block_size=rate) because a pair of consecutive `space_to_batch` and `batch_to_space` ops with the same `block_size` cancel out when their respective `paddings` and `crops` inputs are identical. Args: value: A 4-D `Tensor` of type `float`. It needs to be in the default "NHWC" format. Its shape is `[batch, in_height, in_width, in_channels]`. filters: A 4-D `Tensor` with the same type as `value` and shape `[filter_height, filter_width, in_channels, out_channels]`. `filters`' `in_channels` dimension must match that of `value`. Atrous convolution is equivalent to standard convolution with upsampled filters with effective height `filter_height + (filter_height - 1) * (rate - 1)` and effective width `filter_width + (filter_width - 1) * (rate - 1)`, produced by inserting `rate - 1` zeros along consecutive elements across the `filters`' spatial dimensions. rate: A positive int32. The stride with which we sample input values across the `height` and `width` dimensions. Equivalently, the rate by which we upsample the filter values by inserting zeros across the `height` and `width` dimensions. In the literature, the same parameter is sometimes called `input stride` or `dilation`. padding: A string, either `'VALID'` or `'SAME'`. The padding algorithm. name: Optional name for the returned tensor. Returns: A `Tensor` with the same type as `value`. Raises: ValueError: If input/output depth does not match `filters`' shape, or if padding is other than `'VALID'` or `'SAME'`. """ with ops.op_scope([value, filters], name, "atrous_conv2d") as name: value = ops.convert_to_tensor(value, name="value") filters = ops.convert_to_tensor(filters, name="filters") value_shape = value.get_shape() filter_shape = filters.get_shape() if not value_shape[3].is_compatible_with(filter_shape[2]): raise ValueError( "value's input channels does not match filters' input channels, " "{} != {}".format(value_shape[3], filter_shape[2])) if rate < 1: raise ValueError("rate {} cannot be less than one".format(rate)) if rate == 1: value = gen_nn_ops.conv2d(input=value, filter=filters, strides=[1, 1, 1, 1], padding=padding) return value # We have two padding contributions. The first is used for converting "SAME" # to "VALID". The second is required so that the height and width of the # zero-padded value tensor are multiples of rate. # Spatial dimensions of original input value_shape = array_ops.shape(value) in_height = value_shape[1] in_width = value_shape[2] # Spatial dimensions of the filters and the upsampled filters in which we # introduce (rate - 1) zeros between consecutive filter values. filter_height = int(filter_shape[0]) filter_width = int(filter_shape[1]) filter_height_up = filter_height + (filter_height - 1) * (rate - 1) filter_width_up = filter_width + (filter_width - 1) * (rate - 1) # Padding required to reduce to "VALID" convolution if padding == "SAME": pad_height = filter_height_up - 1 pad_width = filter_width_up - 1 elif padding == "VALID": pad_height = 0 pad_width = 0 else: raise ValueError("Invalid padding") # When padding is "SAME" and the pad_height (pad_width) is odd, we pad more # to bottom (right), following the same convention as conv2d(). pad_top = math_ops.floordiv(pad_height, 2) pad_bottom = pad_height - pad_top pad_left = math_ops.floordiv(pad_width, 2) pad_right = pad_width - pad_left # More padding so that rate divides the height and width of the input value in_height = in_height + pad_top + pad_bottom in_width = in_width + pad_left + pad_right mod_height = math_ops.mod(in_height, rate) mod_width = math_ops.mod(in_width, rate) null = constant_op.constant(0) pad_bottom_extra = control_flow_ops.cond( gen_math_ops.equal(mod_height, 0), lambda: null, lambda: rate - mod_height) pad_right_extra = control_flow_ops.cond( gen_math_ops.equal(mod_width, 0), lambda: null, lambda: rate - mod_width) # The paddings argument to space_to_batch includes both padding components pad_bottom = pad_bottom + pad_bottom_extra pad_right = pad_right + pad_right_extra space_to_batch_pad = [[pad_top, pad_bottom], [pad_left, pad_right]] value = array_ops.space_to_batch(input=value, paddings=space_to_batch_pad, block_size=rate) value = gen_nn_ops.conv2d(input=value, filter=filters, strides=[1, 1, 1, 1], padding="VALID", name=name) # The crops argument to batch_to_space is just the extra padding component batch_to_space_crop = [[0, pad_bottom_extra], [0, pad_right_extra]] value = array_ops.batch_to_space(input=value, crops=batch_to_space_crop, block_size=rate) return value
def atrous_conv2d(value, filters, rate, padding, name=None): with ops.op_scope([value, filters], name, "atrous_conv2d") as name: value = ops.convert_to_tensor(value, name="value") filters = ops.convert_to_tensor(filters, name="filters") value_shape = value.get_shape() filter_shape = filters.get_shape() if not value_shape[3].is_compatible_with(filter_shape[2]): raise ValueError( "value's input channels does not match filters' input channels, " "{} != {}".format(value_shape[3], filter_shape[2])) if rate < 1: raise ValueError("rate {} cannot be less than one".format(rate)) if rate == 1: value = gen_nn_ops.conv2d(input=value, filter=filters, strides=[1, 1, 1, 1], padding=padding) return value # We have two padding contributions. The first is used for converting "SAME" # to "VALID". The second is required so that the height and width of the # zero-padded value tensor are multiples of rate. # Spatial dimensions of original input value_shape = array_ops.shape(value) in_height = value_shape[1] in_width = value_shape[2] # Spatial dimensions of the filters and the upsampled filters in which we # introduce (rate - 1) zeros between consecutive filter values. filter_shape = array_ops.shape(filters) filter_height = filter_shape[0] filter_width = filter_shape[1] filter_height_up = filter_height + (filter_height - 1) * (rate - 1) filter_width_up = filter_width + (filter_width - 1) * (rate - 1) # Padding required to reduce to "VALID" convolution if padding == "SAME": pad_height = filter_height_up - 1 pad_width = filter_width_up - 1 elif padding == "VALID": pad_height = 0 pad_width = 0 else: raise ValueError("Invalid padding") # When padding is "SAME" and the pad_height (pad_width) is odd, we pad more # to bottom (right), following the same convention as conv2d(). pad_top = math_ops.floordiv(pad_height, 2) pad_bottom = pad_height - pad_top pad_left = math_ops.floordiv(pad_width, 2) pad_right = pad_width - pad_left # More padding so that rate divides the height and width of the input value in_height = in_height + pad_top + pad_bottom in_width = in_width + pad_left + pad_right mod_height = math_ops.mod(in_height, rate) mod_width = math_ops.mod(in_width, rate) null = constant_op.constant(0) pad_bottom_extra = control_flow_ops.cond(gen_math_ops.equal(mod_height, 0), lambda: null, lambda: rate - mod_height) pad_right_extra = control_flow_ops.cond(gen_math_ops.equal(mod_width, 0), lambda: null, lambda: rate - mod_width) # The paddings argument to space_to_batch includes both padding components pad_bottom = pad_bottom + pad_bottom_extra pad_right = pad_right + pad_right_extra print 'hahahaha' v = array_ops.expand_dims(array_ops.pack([pad_top, pad_bottom]),1) h = array_ops.expand_dims(array_ops.pack([pad_left, pad_right]),1) space_to_batch_pad = array_ops.concat(1, [v,h]) space_to_batch_pad = [[pad_top, pad_bottom], [pad_left, pad_right]] value = array_ops.space_to_batch(input=value, paddings=space_to_batch_pad, block_size=rate) value = gen_nn_ops.conv2d(input=value, filter=filters, strides=[1, 1, 1, 1], padding="VALID", name=name) # The crops argument to batch_to_space is just the extra padding component v = array_ops.expand_dims(array_ops.pack([0, pad_bottom_extra]),1) h = array_ops.expand_dims(array_ops.pack([0, pad_right_extra]),1) batch_to_space_crop = array_ops.concat(1, [v,h]) batch_to_space_crop = [[0, pad_bottom_extra], [0, pad_right_extra]] value = array_ops.batch_to_space(input=value, crops=batch_to_space_crop, block_size=rate) return value