def _get_diff_for_monotonic_comparison(x): """Gets the difference x[1:] - x[:-1].""" x = array_ops.reshape(x, [-1]) if not is_numeric_tensor(x): raise TypeError('Expected x to be numeric, instead found: %s' % x) # If x has less than 2 elements, there is nothing to compare. So return []. is_shorter_than_two = math_ops.less(array_ops.size(x), 2) short_result = lambda: ops.convert_to_tensor([], dtype=x.dtype) # With 2 or more elements, return x[1:] - x[:-1] s_len = array_ops.shape(x) - 1 diff = lambda: array_ops.strided_slice(x, [1], [1] + s_len)- array_ops.strided_slice(x, [0], s_len) return control_flow_ops.cond(is_shorter_than_two, short_result, diff)
def _inverse(self, y): # To derive the inverse mapping note that: # y[i] = exp(x[i]) / normalization # and # y[end] = 1 / normalization. # Thus: # x[i] = log(exp(x[i])) - log(y[end]) - log(normalization) # = log(exp(x[i])/normalization) - log(y[end]) # = log(y[i]) - log(y[end]) shape = (np.asarray(y.shape.as_list(), dtype=np.int32) if y.shape.is_fully_defined() else array_ops.shape(y, name="shape")) ndims = distribution_util.prefer_static_rank(y) # Do this first to make sure CSE catches that it'll happen again in # _inverse_log_det_jacobian. x = math_ops.log(y) # We now extract the last coordinate of the rightmost dimension. # Our trick is to slice from [0,0,...,shape[-1]-1] to shape[:-1]+[1]. begin = array_ops.one_hot(indices=ndims-1, depth=ndims, on_value=shape[-1]-np.array(1, dtype=shape.dtype), dtype=shape.dtype) size = array_ops.concat([shape[:-1], np.asarray([1], dtype=shape.dtype)], 0) log_normalization = -array_ops.strided_slice(x, begin, begin + size) # Here we slice out all but the last coordinate; see above for idea. begin = array_ops.zeros_like(shape) size = array_ops.concat([shape[:-1], [shape[-1] - 1]], 0) x = array_ops.strided_slice(x, begin, begin + size) x += log_normalization if self._static_event_ndims == 0: x = array_ops.squeeze(x, squeeze_dims=[ndims-1]) # Set shape hints. if y.shape.ndims is not None: shape = y.shape.as_list() if self._static_event_ndims == 0: shape = shape[:-1] elif shape[-1] is not None: shape[-1] -= 1 shape = tensor_shape.TensorShape(shape) x.shape.assert_is_compatible_with(shape) x.set_shape(shape) return x
def test3DNegativeStride(self): for dtype in self.numeric_types: with self.test_session(): i = array_ops.placeholder(dtype, shape=[3, 4, 10]) with self.test_scope(): o = array_ops.strided_slice(i, [2, 2, 6], [0, 0, 2], [-1, -1, -2]) params = { i: [[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [5, 3, 1, 7, 9, 2, 4, 6, 8, 0], [4, 5, 2, 4, 3, 7, 6, 8, 9, 4]], [[5, 5, 5, 5, 5, 5, 5, 5, 5, 5], [4, 3, 4, 5, 7, 6, 5, 3, 4, 5], [8, 7, 6, 5, 4, 3, 2, 1, 8, 7], [7, 1, 7, 1, 8, 1, 8, 1, 3, 1]], [[7, 5, 7, 5, 7, 5, 7, 5, 7, 5], [1, 2, 1, 2, 1, 2, 1, 2, 1, 2], [9, 8, 7, 9, 8, 7, 9, 8, 7, 9], [9, 9, 5, 5, 6, 6, 3, 3, 6, 6]]] } result = o.eval(feed_dict=params) self.assertAllEqual([[[9, 8], [1, 1]], [[2, 4], [5, 7]]], result)
def _flip_vector_to_matrix_dynamic(vec, batch_shape): """flip_vector_to_matrix with dynamic shapes.""" # Shapes associated with batch_shape batch_rank = array_ops.size(batch_shape) # Shapes associated with vec. vec = ops.convert_to_tensor(vec, name="vec") vec_shape = array_ops.shape(vec) vec_rank = array_ops.rank(vec) vec_batch_rank = vec_rank - 1 m = vec_batch_rank - batch_rank # vec_shape_left = [M1,...,Mm] or []. vec_shape_left = array_ops.strided_slice(vec_shape, [0], [m]) # If vec_shape_left = [], then condensed_shape = [1] since reduce_prod([]) = 1 # If vec_shape_left = [M1,...,Mm], condensed_shape = [M1*...*Mm] condensed_shape = [math_ops.reduce_prod(vec_shape_left)] k = array_ops.gather(vec_shape, vec_rank - 1) new_shape = array_ops.concat(0, (batch_shape, [k], condensed_shape)) def _flip_front_dims_to_back(): # Permutation corresponding to [N1,...,Nn] + [k, M1,...,Mm] perm = array_ops.concat( 0, (math_ops.range(m, vec_rank), math_ops.range(0, m))) return array_ops.transpose(vec, perm=perm) x_flipped = control_flow_ops.cond( math_ops.less(0, m), _flip_front_dims_to_back, lambda: array_ops.expand_dims(vec, -1)) return array_ops.reshape(x_flipped, new_shape)
def _my_metric_op(predictions, labels): # For the case of binary classification, the 2nd column of "predictions" # denotes the model predictions. labels = math_ops.to_float(labels) predictions = array_ops.strided_slice( predictions, [0, 1], [-1, 2], end_mask=1) labels = math_ops.cast(labels, predictions.dtype) return math_ops.reduce_sum(math_ops.multiply(predictions, labels))
def testConcatSlice(self): r1 = test_ops.stub_resource_handle_op(container="a", shared_name="b") r2 = test_ops.stub_resource_handle_op(container="a", shared_name="c") c = array_ops.stack([r1, r2]) s = array_ops.strided_slice(c, [1], [2]) self.evaluate(test_ops.resource_create_op(s)) with self.assertRaises(errors.AlreadyExistsError): self.evaluate(test_ops.resource_create_op(r2))
def testInt64GPU(self): if not test_util.is_gpu_available(): self.skipTest("No GPU available") with self.test_session(use_gpu=True, force_gpu=True): x = constant_op.constant([1., 2., 3.]) begin = constant_op.constant([2], dtype=dtypes.int64) end = constant_op.constant([3], dtype=dtypes.int64) strides = constant_op.constant([1], dtype=dtypes.int64) s = array_ops.strided_slice(x, begin, end, strides) self.assertAllEqual([3.], self.evaluate(s))
def testStridedSlice(self): self._testNAry(lambda x: array_ops.strided_slice(*x), [np.array([[], [], []], dtype=np.float32), np.array([1, 0], dtype=np.int32), np.array([3, 0], dtype=np.int32), np.array([1, 1], dtype=np.int32)], expected=np.array([[], []], dtype=np.float32)) if np.int64 in self.int_types: self._testNAry( lambda x: array_ops.strided_slice(*x), [ np.array([[], [], []], dtype=np.float32), np.array( [1, 0], dtype=np.int64), np.array([3, 0], dtype=np.int64), np.array([1, 1], dtype=np.int64) ], expected=np.array([[], []], dtype=np.float32)) self._testNAry(lambda x: array_ops.strided_slice(*x), [np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.float32), np.array([1, 1], dtype=np.int32), np.array([3, 3], dtype=np.int32), np.array([1, 1], dtype=np.int32)], expected=np.array([[5, 6], [8, 9]], dtype=np.float32)) self._testNAry(lambda x: array_ops.strided_slice(*x), [np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.float32), np.array([0, 2], dtype=np.int32), np.array([2, 0], dtype=np.int32), np.array([1, -1], dtype=np.int32)], expected=np.array([[3, 2], [6, 5]], dtype=np.float32)) self._testNAry(lambda x: x[0][0:2, array_ops.newaxis, ::-1], [np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.float32)], expected=np.array([[[3, 2, 1]], [[6, 5, 4]]], dtype=np.float32)) self._testNAry(lambda x: x[0][1, :, array_ops.newaxis], [np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.float32)], expected=np.array([[4], [5], [6]], dtype=np.float32))
def _objective(self, x): """Rosenbrock function. (Carl Edward Rasmussen, 2001-07-21). f(x) = sum_{i=1:D-1} 100*(x(i+1) - x(i)^2)^2 + (1-x(i))^2 Args: x: a Variable Returns: f: a tensor (objective value) """ d = array_ops.size(x) s = math_ops.add( 100 * math_ops.square( math_ops.subtract( array_ops.strided_slice(x, [1], [d]), math_ops.square(array_ops.strided_slice(x, [0], [d - 1])))), math_ops.square( math_ops.subtract(1.0, array_ops.strided_slice(x, [0], [d - 1])))) return math_ops.reduce_sum(s)
def training_graph(self, input_data, input_labels, data_spec=None, **tree_kwargs): """Constructs a TF graph for training a random forest. Args: input_data: A tensor or SparseTensor or placeholder for input data. input_labels: A tensor or placeholder for labels associated with input_data. data_spec: A list of tf.dtype values specifying the original types of each column. **tree_kwargs: Keyword arguments passed to each tree's training_graph. Returns: The last op in the random forest training graph. """ data_spec = [constants.DATA_FLOAT] if data_spec is None else data_spec tree_graphs = [] for i in range(self.params.num_trees): with ops.device(self.device_assigner.get_device(i)): seed = self.params.base_random_seed if seed != 0: seed += i # If using bagging, randomly select some of the input. tree_data = input_data tree_labels = input_labels if self.params.bagging_fraction < 1.0: # TODO(thomaswc): This does sampling without replacment. Consider # also allowing sampling with replacement as an option. batch_size = array_ops.strided_slice( array_ops.shape(input_data), [0], [1]) r = random_ops.random_uniform(batch_size, seed=seed) mask = math_ops.less( r, array_ops.ones_like(r) * self.params.bagging_fraction) gather_indices = array_ops.squeeze( array_ops.where(mask), squeeze_dims=[1]) # TODO(thomaswc): Calculate out-of-bag data and labels, and store # them for use in calculating statistics later. tree_data = array_ops.gather(input_data, gather_indices) tree_labels = array_ops.gather(input_labels, gather_indices) if self.params.bagged_features: tree_data = self._bag_features(i, tree_data) initialization = self.trees[i].tree_initialization() with ops.control_dependencies([initialization]): tree_graphs.append( self.trees[i].training_graph( tree_data, tree_labels, seed, data_spec=data_spec, **tree_kwargs)) return control_flow_ops.group(*tree_graphs, name='train')
def _check_shapes_dynamic(self, operator, v, diag): """Return (v, diag) with Assert dependencies, which check shape.""" checks = [] with ops.name_scope("check_shapes", values=[operator, v, diag]): s_v = array_ops.shape(v) r_op = operator.rank() r_v = array_ops.rank(v) if diag is not None: s_d = array_ops.shape(diag) r_d = array_ops.rank(diag) # Check tensor rank. checks.append(check_ops.assert_rank( v, r_op, message="v is not the same rank as operator.")) if diag is not None: checks.append(check_ops.assert_rank( diag, r_op - 1, message="diag is not the same rank as operator.")) # Check batch shape checks.append(check_ops.assert_equal( operator.batch_shape(), array_ops.strided_slice(s_v, [0], [r_v - 2]), message="v does not have same batch shape as operator.")) if diag is not None: checks.append(check_ops.assert_equal( operator.batch_shape(), array_ops.strided_slice( s_d, [0], [r_d - 1]), message="diag does not have same batch shape as operator.")) # Check event shape checks.append(check_ops.assert_equal( operator.vector_space_dimension(), array_ops.gather(s_v, r_v - 2), message="v does not have same event shape as operator.")) if diag is not None: checks.append(check_ops.assert_equal( array_ops.gather(s_v, r_v - 1), array_ops.gather(s_d, r_d - 1), message="diag does not have same event shape as v.")) v = control_flow_ops.with_dependencies(checks, v) if diag is not None: diag = control_flow_ops.with_dependencies(checks, diag) return v, diag
def test1DNegtiveStride(self): for dtype in self.numeric_types: with self.test_session(): i = array_ops.placeholder(dtype, shape=[10]) with self.test_scope(): o = array_ops.strided_slice(i, [6], [2], [-2]) params = { i: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], } result = o.eval(feed_dict=params) self.assertAllEqual([6, 4], result)
def tree_initialization(self): def _init_tree(): return state_ops.scatter_update(self.variables.tree, [0], [[-1, -1]]).op def _nothing(): return control_flow_ops.no_op() return control_flow_ops.cond( math_ops.equal( array_ops.squeeze( array_ops.strided_slice(self.variables.tree, [0, 0], [1, 1])), -2), _init_tree, _nothing)
def test2DDegenerateNegativeStride(self): for dtype in self.numeric_types: with self.test_session(): i = array_ops.placeholder(dtype, shape=[2, 3]) with self.test_scope(): o = array_ops.strided_slice(i, [0, 0], [-1, 3], [-1, 1]) params = { i: [[0, 1, 2], [3, 4, 5]] } result = o.eval(feed_dict=params) self.assertEqual(tensor_shape.TensorShape((0, 3)), result.shape)
def _get_identity_operator(self, v): """Get an `OperatorPDIdentity` to play the role of `D` in `VDV^T`.""" with ops.name_scope("get_identity_operator", values=[v]): if v.get_shape().is_fully_defined(): v_shape = v.get_shape().as_list() v_batch_shape = v_shape[:-2] r = v_shape[-1] id_shape = v_batch_shape + [r, r] else: v_shape = array_ops.shape(v) v_rank = array_ops.rank(v) v_batch_shape = array_ops.strided_slice(v_shape, [0], [v_rank - 2]) r = array_ops.gather(v_shape, v_rank - 1) # Last dim of v id_shape = array_ops.concat_v2((v_batch_shape, [r, r]), 0) return operator_pd_identity.OperatorPDIdentity( id_shape, v.dtype, verify_pd=self._verify_pd)
def batch_shape(self, name="batch_shape"): """Shape of batches associated with this operator. If this operator represents the batch matrix `A` with `A.shape = [N1,...,Nn, k, k]`, the `batch_shape` is `[N1,...,Nn]`. Args: name: A name scope to use for ops added by this method. Returns: `int32` `Tensor` """ # Derived classes get this "for free" once .shape() is implemented. with ops.name_scope(self.name): with ops.name_scope(name, values=self.inputs): return array_ops.strided_slice(self.shape(), [0], [self.rank() - 2])
def _StridedSliceGradGrad(op, grad): """Gradient for StridedSliceGrad op.""" begin = op.inputs[1] end = op.inputs[2] strides = op.inputs[3] return None, None, None, None, array_ops.strided_slice( grad, begin, end, strides, begin_mask=op.get_attr("begin_mask"), end_mask=op.get_attr("end_mask"), ellipsis_mask=op.get_attr("ellipsis_mask"), new_axis_mask=op.get_attr("new_axis_mask"), shrink_axis_mask=op.get_attr("shrink_axis_mask"))
def _StridedSliceGradGrad(op, grad): """Gradient for StridedSliceGrad op.""" begin = op.inputs[1] end = op.inputs[2] strides = op.inputs[3] return None, None, None, None, array_ops.strided_slice( grad, begin, end, strides, begin_mask=op.get_attr("begin_mask"), end_mask=op.get_attr("end_mask"), ellipsis_mask=op.get_attr("ellipsis_mask"), new_axis_mask=op.get_attr("new_axis_mask"), shrink_axis_mask=op.get_attr("shrink_axis_mask"))
def batch_shape(self, name="batch_shape"): """Shape of batches associated with this operator. If this operator represents the batch matrix `A` with `A.shape = [N1,...,Nn, k, k]`, the `batch_shape` is `[N1,...,Nn]`. Args: name: A name scope to use for ops added by this method. Returns: `int32` `Tensor` """ # Derived classes get this "for free" once .shape() is implemented. with ops.name_scope(self.name): with ops.name_scope(name, values=self.inputs): return array_ops.strided_slice(self.shape(), [0], [self.rank() - 2])
def _get_identity_operator(self, v): """Get an `OperatorPDIdentity` to play the role of `D` in `VDV^T`.""" with ops.name_scope("get_identity_operator", values=[v]): if v.get_shape().is_fully_defined(): v_shape = v.get_shape().as_list() v_batch_shape = v_shape[:-2] r = v_shape[-1] id_shape = v_batch_shape + [r, r] else: v_shape = array_ops.shape(v) v_rank = array_ops.rank(v) v_batch_shape = array_ops.strided_slice( v_shape, [0], [v_rank - 2]) r = array_ops.gather(v_shape, v_rank - 1) # Last dim of v id_shape = array_ops.concat(0, (v_batch_shape, [r, r])) return operator_pd_identity.OperatorPDIdentity( id_shape, v.dtype, verify_pd=self._verify_pd)
def testStridedSliceGradWithNonConstAxis(self): if test.is_gpu_available(cuda_only=True): random_seed.set_random_seed(0) x = random_ops.truncated_normal([1, 784], seed=0) conv = _two_layer_model(x) end = array_ops.placeholder(dtype='int32') shape = array_ops.shape(conv) end_val = [1, 2, 3, 4] s = array_ops.strided_slice( conv, [0, 0, 0, 0], end_val, strides=[1, 2, 3, 1]) s_grad = array_ops.strided_slice_grad(shape, [0, 0, 0, 0], end, [1, 2, 3, 1], s) output = array_ops.identity(s_grad) with session.Session() as sess: output_val_ref = sess.run(output, feed_dict={end: end_val}) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() output_val = sess.run( output, run_metadata=metadata, feed_dict={ end: end_val }) nodes = [] num_transposes = 0 for node in metadata.cost_graph.node: if node.name.startswith('LayoutOptimizerTranspose'): num_transposes += 1 nodes.append(node.name) # Four transposes were initially added in the Expand phase of # LayoutOptimizer; two of them are cancelled out in the Collapse phase. expected_num_transposes = 2 self.assertEqual(expected_num_transposes, num_transposes) self.assertIn('LayoutOptimizerTransposeNHWCToNCHW-Conv2D-0', nodes) self.assertIn('LayoutOptimizerTransposeNCHWToNHWC-StridedSliceGrad-0-0', nodes) self.assertIn('LayoutOptimizerVecPermuteNHWCToNCHW_StridedSliceGrad_2', nodes) self.assertIn('LayoutOptimizer-StridedSlice-StridedSliceGrad/begin', nodes) self.assertIn('LayoutOptimizer-StridedSlice-StridedSliceGrad/strides', nodes) self.assertAllClose(output_val_ref, output_val, atol=1e-3)
def extract_batch_shape(x, num_event_dims, name="extract_batch_shape"): """Extract the batch shape from `x`. Assuming `x.shape = batch_shape + event_shape`, when `event_shape` has `num_event_dims` dimensions. This `Op` returns the batch shape `Tensor`. Args: x: `Tensor` with rank at least `num_event_dims`. If rank is not high enough this `Op` will fail. num_event_dims: `int32` scalar `Tensor`. The number of trailing dimensions in `x` to be considered as part of `event_shape`. name: A name to prepend to created `Ops`. Returns: batch_shape: `1-D` `int32` `Tensor` """ with ops.name_scope(name, values=[x]): x = ops.convert_to_tensor(x, name="x") return array_ops.strided_slice(array_ops.shape(x), [0], [array_ops.rank(x) - num_event_dims])
def extract_batch_shape(x, num_event_dims, name="extract_batch_shape"): """Extract the batch shape from `x`. Assuming `x.shape = batch_shape + event_shape`, when `event_shape` has `num_event_dims` dimensions. This `Op` returns the batch shape `Tensor`. Args: x: `Tensor` with rank at least `num_event_dims`. If rank is not high enough this `Op` will fail. num_event_dims: `int32` scalar `Tensor`. The number of trailing dimensions in `x` to be considered as part of `event_shape`. name: A name to prepend to created `Ops`. Returns: batch_shape: `1-D` `int32` `Tensor` """ with ops.name_scope(name, values=[x]): x = ops.convert_to_tensor(x, name="x") return array_ops.strided_slice( array_ops.shape(x), [0], [array_ops.rank(x) - num_event_dims])
def test3D(self): for dtype in self.numeric_types: with self.test_session(): i = array_ops.placeholder(dtype, shape=[3, 3, 10]) with self.test_scope(): o = array_ops.strided_slice(i, [0, 2, 2], [2, 3, 6], [1, 1, 2]) params = { i: [[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [5, 3, 1, 7, 9, 2, 4, 6, 8, 0]], [[5, 5, 5, 5, 5, 5, 5, 5, 5, 5], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [8, 7, 6, 5, 4, 3, 2, 1, 8, 7]], [[7, 5, 7, 5, 7, 5, 7, 5, 7, 5], [1, 2, 1, 2, 1, 2, 1, 2, 1, 2], [9, 8, 7, 9, 8, 7, 9, 8, 7, 9]]] } result = o.eval(feed_dict=params) self.assertAllEqual([[[1, 9]], [[6, 4]]], result)
def testStridedSliceWithNonConstAxis(self): if test.is_gpu_available(cuda_only=True): random_seed.set_random_seed(0) x = random_ops.truncated_normal([1, 784], seed=0) conv = _two_layer_model(x) end = array_ops.placeholder(dtype='int32') s = array_ops.strided_slice(conv, [0, 0, 0, 0], end, strides=[1, 2, 3, 1]) output = array_ops.identity(s) end_val = [1, 2, 3, 4] with session.Session() as sess: output_val_ref = sess.run(output, feed_dict={end: end_val}) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() output_val = sess.run( output, run_metadata=metadata, feed_dict={ end: end_val }) nodes = [] num_transposes = 0 for node in metadata.cost_graph.node: if _is_transpose(node.name): num_transposes += 1 nodes.append(node.name) # Four transposes were initially added in the Expand phase of # LayoutOptimizer; two of them are cancelled out in the Collapse phase. expected_num_transposes = 2 self.assertEqual(expected_num_transposes, num_transposes) self._assert_trans_nhwc_to_nchw('Conv2D-0', nodes) self._assert_trans_nchw_to_nhwc('StridedSlice-0-0', nodes) self._assert_vec_nhwc_to_nchw('StridedSlice-2', nodes) self.assertIn('StridedSlice-1-LayoutOptimizer', nodes) self.assertIn('StridedSlice-3-LayoutOptimizer', nodes) self.assertAllClose(output_val_ref, output_val, atol=1e-3)
def testStridedSliceWithNonConstAxis(self): if test.is_gpu_available(cuda_only=True): random_seed.set_random_seed(0) x = random_ops.truncated_normal([1, 784], seed=0) conv = _two_layer_model(x) end = array_ops.placeholder(dtype='int32') s = array_ops.strided_slice(conv, [0, 0, 0, 0], end, strides=[1, 2, 3, 1]) output = array_ops.identity(s) end_val = [1, 2, 3, 4] with session.Session() as sess: output_val_ref = sess.run(output, feed_dict={end: end_val}) with session.Session(config=_get_config()) as sess: metadata = config_pb2.RunMetadata() output_val = sess.run( output, run_metadata=metadata, feed_dict={ end: end_val }) nodes = [] num_transposes = 0 for node in metadata.cost_graph.node: if _is_transpose(node.name): num_transposes += 1 nodes.append(node.name) # Four transposes were initially added in the Expand phase of # LayoutOptimizer; two of them are cancelled out in the Collapse phase. expected_num_transposes = 2 self.assertEqual(expected_num_transposes, num_transposes) self._assert_trans_nhwc_to_nchw('Conv2D-0', nodes) self._assert_trans_nchw_to_nhwc('StridedSlice-0-0', nodes) self._assert_vec_nhwc_to_nchw('StridedSlice-2', nodes) self.assertIn('StridedSlice-1-LayoutOptimizer', nodes) self.assertIn('StridedSlice-3-LayoutOptimizer', nodes) self.assertAllClose(output_val_ref, output_val, atol=1e-3)
def training_graph(self, input_data, input_labels, num_trainers=1, trainer_id=0, **tree_kwargs): """Constructs a TF graph for training a random forest. Args: input_data: A tensor or dict of string->Tensor for input data. input_labels: A tensor or placeholder for labels associated with input_data. num_trainers: Number of parallel trainers to split trees among. trainer_id: Which trainer this instance is. **tree_kwargs: Keyword arguments passed to each tree's training_graph. Returns: The last op in the random forest training graph. Raises: NotImplementedError: If trying to use bagging with sparse features. """ processed_dense_features, processed_sparse_features, data_spec = ( data_ops.ParseDataTensorOrDict(input_data)) if input_labels is not None: labels = data_ops.ParseLabelTensorOrDict(input_labels) data_spec = data_spec or self.get_default_data_spec(input_data) tree_graphs = [] trees_per_trainer = self.params.num_trees / num_trainers tree_start = int(trainer_id * trees_per_trainer) tree_end = int((trainer_id + 1) * trees_per_trainer) for i in range(tree_start, tree_end): with ops.device(self.variables.device_dummies[i].device): seed = self.params.base_random_seed if seed != 0: seed += i # If using bagging, randomly select some of the input. tree_data = processed_dense_features tree_labels = labels if self.params.bagging_fraction < 1.0: # TODO(gilberth): Support bagging for sparse features. if processed_sparse_features is not None: raise NotImplementedError( 'Bagging not supported with sparse features.') # TODO(thomaswc): This does sampling without replacement. Consider # also allowing sampling with replacement as an option. batch_size = array_ops.strided_slice( array_ops.shape(processed_dense_features), [0], [1]) r = random_ops.random_uniform(batch_size, seed=seed) mask = math_ops.less( r, array_ops.ones_like(r) * self.params.bagging_fraction) gather_indices = array_ops.squeeze(array_ops.where(mask), squeeze_dims=[1]) # TODO(thomaswc): Calculate out-of-bag data and labels, and store # them for use in calculating statistics later. tree_data = array_ops.gather(processed_dense_features, gather_indices) tree_labels = array_ops.gather(labels, gather_indices) if self.params.bagged_features: if processed_sparse_features is not None: raise NotImplementedError( 'Feature bagging not supported with sparse features.' ) tree_data = self._bag_features(i, tree_data) tree_graphs.append(self.trees[i].training_graph( tree_data, tree_labels, seed, data_spec=data_spec, sparse_features=processed_sparse_features, **tree_kwargs)) return control_flow_ops.group(*tree_graphs, name='train')
def testSlice(self): tf_val = array_ops.placeholder(dtypes.int32, shape=(4,))[0:2] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([None, None], c_val.as_list()) # begin:end tf_val = constant_op.constant([10, 20, 30])[1:3] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([20, 30], c_val.as_list()) # begin:end:stride tf_val = array_ops.strided_slice( constant_op.constant([10, 20, 30]), [1], [3], strides=[2]) c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([20], c_val.as_list()) # [1, 2, 16, 37, None, 48] tf_val_orig = array_ops.concat( [[1, 2, 16, 37], array_ops.placeholder( dtypes.int32, shape=(1,)), [48]], 0) # begin: no end tf_val = tf_val_orig[2:] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([16, 37, None, 48], c_val.as_list()) # begin::negative slice tf_val = tf_val_orig[2::-1] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([16, 2, 1], c_val.as_list()) # :end:negative slice tf_val = tf_val_orig[:1:-2] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([48, 37], c_val.as_list()) # begin:end:negative slice tf_val = tf_val_orig[3:1:-1] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([37, 16], c_val.as_list()) # begin:negative end:slice tf_val = tf_val_orig[1:-3:1] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([2, 16], c_val.as_list()) # negative begin::slice tf_val = tf_val_orig[-3::1] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([37, None, 48], c_val.as_list()) # negative begin::negative slice tf_val = tf_val_orig[-3::-1] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([37, 16, 2, 1], c_val.as_list()) # negative begin:negative end:negative slice tf_val = tf_val_orig[-3:-5:-1] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([37, 16], c_val.as_list()) # Do not support shape inference for additional arguments tf_val = constant_op.constant([10, 20, 30])[...] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([None, None, None], c_val.as_list()) # Do not support shape inference for tensor slices. tf_val = constant_op.constant([10, 20, 30])[ array_ops.placeholder(dtypes.int32, shape=()):] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual(tensor_shape.unknown_shape(), c_val) # Do not support shape inference for higher rank with self.assertRaises(ValueError): tf_val = constant_op.constant([[10], [20], [30]])[:, 0:] c_val = tensor_util.constant_value_as_shape(tf_val)
def frame(signal, frame_length, frame_step, pad_end=False, pad_value=0, axis=-1, name=None): """Expands `signal`'s `axis` dimension into frames of `frame_length`. Slides a window of size `frame_length` over `signal`'s `axis` dimension with a stride of `frame_step`, replacing the `axis` dimension with `[frames, frame_length]` frames. If `pad_end` is True, window positions that are past the end of the `axis` dimension are padded with `pad_value` until the window moves fully past the end of the dimension. Otherwise, only window positions that fully overlap the `axis` dimension are produced. For example: ```python # A batch size 3 tensor of 9152 audio samples. audio = tf.random.normal([3, 9152]) # Compute overlapping frames of length 512 with a step of 180 (frames overlap # by 332 samples). By default, only 50 frames are generated since the last # 152 samples do not form a full frame. frames = tf.signal.frame(audio, 512, 180) frames.shape.assert_is_compatible_with([3, 50, 512]) # When pad_end is enabled, the final frame is kept (padded with zeros). frames = tf.signal.frame(audio, 512, 180, pad_end=True) frames.shape.assert_is_compatible_with([3, 51, 512]) ``` Args: signal: A `[..., samples, ...]` `Tensor`. The rank and dimensions may be unknown. Rank must be at least 1. frame_length: The frame length in samples. An integer or scalar `Tensor`. frame_step: The frame hop size in samples. An integer or scalar `Tensor`. pad_end: Whether to pad the end of `signal` with `pad_value`. pad_value: An optional scalar `Tensor` to use where the input signal does not exist when `pad_end` is True. axis: A scalar integer `Tensor` indicating the axis to frame. Defaults to the last axis. Supports negative values for indexing from the end. name: An optional name for the operation. Returns: A `Tensor` of frames with shape `[..., frames, frame_length, ...]`. Raises: ValueError: If `frame_length`, `frame_step`, `pad_value`, or `axis` are not scalar. """ with ops.name_scope(name, "frame", [signal, frame_length, frame_step, pad_value]): signal = ops.convert_to_tensor(signal, name="signal") frame_length = ops.convert_to_tensor(frame_length, name="frame_length") frame_step = ops.convert_to_tensor(frame_step, name="frame_step") axis = ops.convert_to_tensor(axis, name="axis") signal.shape.with_rank_at_least(1) frame_length.shape.assert_has_rank(0) frame_step.shape.assert_has_rank(0) axis.shape.assert_has_rank(0) result_shape = _infer_frame_shape(signal, frame_length, frame_step, pad_end, axis) # Axis can be negative. Convert it to positive. signal_rank = array_ops.rank(signal) axis = math_ops.range(signal_rank)[axis] signal_shape = array_ops.shape(signal) outer_dimensions, length_samples, inner_dimensions = array_ops.split( signal_shape, [axis, 1, signal_rank - 1 - axis]) length_samples = array_ops.reshape(length_samples, []) num_outer_dimensions = array_ops.size(outer_dimensions) num_inner_dimensions = array_ops.size(inner_dimensions) # If padding is requested, pad the input signal tensor with pad_value. if pad_end: pad_value = ops.convert_to_tensor(pad_value, signal.dtype) pad_value.shape.assert_has_rank(0) # Calculate number of frames, using double negatives to round up. num_frames = -(-length_samples // frame_step) # Pad the signal by up to frame_length samples based on how many samples # are remaining starting from last_frame_position. pad_samples = math_ops.maximum( 0, frame_length + frame_step * (num_frames - 1) - length_samples) # Pad the inner dimension of signal by pad_samples. paddings = array_ops.concat([ array_ops.zeros([num_outer_dimensions, 2], dtype=pad_samples.dtype), [[0, pad_samples]], array_ops.zeros([num_inner_dimensions, 2], dtype=pad_samples.dtype) ], 0) signal = array_ops.pad(signal, paddings, constant_values=pad_value) signal_shape = array_ops.shape(signal) length_samples = signal_shape[axis] else: num_frames = math_ops.maximum( 0, 1 + (length_samples - frame_length) // frame_step) subframe_length = util_ops.gcd(frame_length, frame_step) subframes_per_frame = frame_length // subframe_length subframes_per_hop = frame_step // subframe_length num_subframes = length_samples // subframe_length slice_shape = array_ops.concat([ outer_dimensions, [num_subframes * subframe_length], inner_dimensions ], 0) subframe_shape = array_ops.concat([ outer_dimensions, [num_subframes, subframe_length], inner_dimensions ], 0) subframes = array_ops.reshape( array_ops.strided_slice(signal, array_ops.zeros_like(signal_shape), slice_shape), subframe_shape) # frame_selector is a [num_frames, subframes_per_frame] tensor # that indexes into the appropriate frame in subframes. For example: # [[0, 0, 0, 0], [2, 2, 2, 2], [4, 4, 4, 4]] frame_selector = array_ops.reshape( math_ops.range(num_frames) * subframes_per_hop, [num_frames, 1]) # subframe_selector is a [num_frames, subframes_per_frame] tensor # that indexes into the appropriate subframe within a frame. For example: # [[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]] subframe_selector = array_ops.reshape( math_ops.range(subframes_per_frame), [1, subframes_per_frame]) # Adding the 2 selector tensors together produces a [num_frames, # subframes_per_frame] tensor of indices to use with tf.gather to select # subframes from subframes. We then reshape the inner-most # subframes_per_frame dimension to stitch the subframes together into # frames. For example: [[0, 1, 2, 3], [2, 3, 4, 5], [4, 5, 6, 7]]. selector = frame_selector + subframe_selector frames = array_ops.reshape( array_ops.gather(subframes, selector, axis=axis), array_ops.concat([ outer_dimensions, [num_frames, frame_length], inner_dimensions ], 0)) if result_shape: frames.set_shape(result_shape) return frames
def training_graph(self, input_data, input_labels, num_trainers=1, trainer_id=0, **tree_kwargs): """Constructs a TF graph for training a random forest. Args: input_data: A tensor or dict of string->Tensor for input data. input_labels: A tensor or placeholder for labels associated with input_data. num_trainers: Number of parallel trainers to split trees among. trainer_id: Which trainer this instance is. **tree_kwargs: Keyword arguments passed to each tree's training_graph. Returns: The last op in the random forest training graph. Raises: NotImplementedError: If trying to use bagging with sparse features. """ processed_dense_features, processed_sparse_features, data_spec = ( data_ops.ParseDataTensorOrDict(input_data)) if input_labels is not None: labels = data_ops.ParseLabelTensorOrDict(input_labels) data_spec = data_spec or self.get_default_data_spec(input_data) tree_graphs = [] trees_per_trainer = self.params.num_trees / num_trainers tree_start = int(trainer_id * trees_per_trainer) tree_end = int((trainer_id + 1) * trees_per_trainer) for i in range(tree_start, tree_end): with ops.device(self.variables.device_dummies[i].device): seed = self.params.base_random_seed if seed != 0: seed += i # If using bagging, randomly select some of the input. tree_data = processed_dense_features tree_labels = labels if self.params.bagging_fraction < 1.0: # TODO(gilberth): Support bagging for sparse features. if processed_sparse_features is not None: raise NotImplementedError( 'Bagging not supported with sparse features.') # TODO(thomaswc): This does sampling without replacement. Consider # also allowing sampling with replacement as an option. batch_size = array_ops.strided_slice( array_ops.shape(processed_dense_features), [0], [1]) r = random_ops.random_uniform(batch_size, seed=seed) mask = math_ops.less( r, array_ops.ones_like(r) * self.params.bagging_fraction) gather_indices = array_ops.squeeze( array_ops.where(mask), squeeze_dims=[1]) # TODO(thomaswc): Calculate out-of-bag data and labels, and store # them for use in calculating statistics later. tree_data = array_ops.gather(processed_dense_features, gather_indices) tree_labels = array_ops.gather(labels, gather_indices) if self.params.bagged_features: if processed_sparse_features is not None: raise NotImplementedError( 'Feature bagging not supported with sparse features.') tree_data = self._bag_features(i, tree_data) tree_graphs.append(self.trees[i].training_graph( tree_data, tree_labels, seed, data_spec=data_spec, sparse_features=processed_sparse_features, **tree_kwargs)) return control_flow_ops.group(*tree_graphs, name='train')
def _log_prob(self, x): if self.cholesky_input_output_matrices: x_sqrt = x else: # Complexity: O(nbk^3) x_sqrt = linalg_ops.cholesky(x) batch_shape = self.batch_shape() event_shape = self.event_shape() ndims = array_ops.rank(x_sqrt) # sample_ndims = ndims - batch_ndims - event_ndims sample_ndims = ndims - array_ops.shape(batch_shape)[0] - 2 sample_shape = array_ops.strided_slice( array_ops.shape(x_sqrt), [0], [sample_ndims]) # We need to be able to pre-multiply each matrix by its corresponding # batch scale matrix. Since a Distribution Tensor supports multiple # samples per batch, this means we need to reshape the input matrix `x` # so that the first b dimensions are batch dimensions and the last two # are of shape [dimension, dimensions*number_of_samples]. Doing these # gymnastics allows us to do a batch_solve. # # After we're done with sqrt_solve (the batch operation) we need to undo # this reshaping so what we're left with is a Tensor partitionable by # sample, batch, event dimensions. # Complexity: O(nbk^2) since transpose must access every element. scale_sqrt_inv_x_sqrt = x_sqrt perm = array_ops.concat((math_ops.range(sample_ndims, ndims), math_ops.range(0, sample_ndims)), 0) scale_sqrt_inv_x_sqrt = array_ops.transpose(scale_sqrt_inv_x_sqrt, perm) shape = array_ops.concat( (batch_shape, (math_ops.cast( self.dimension, dtype=dtypes.int32), -1)), 0) scale_sqrt_inv_x_sqrt = array_ops.reshape(scale_sqrt_inv_x_sqrt, shape) # Complexity: O(nbM*k) where M is the complexity of the operator solving # a vector system. E.g., for OperatorPDDiag, each solve is O(k), so # this complexity is O(nbk^2). For OperatorPDCholesky, each solve is # O(k^2) so this step has complexity O(nbk^3). scale_sqrt_inv_x_sqrt = self.scale_operator_pd.sqrt_solve( scale_sqrt_inv_x_sqrt) # Undo make batch-op ready. # Complexity: O(nbk^2) shape = array_ops.concat((batch_shape, event_shape, sample_shape), 0) scale_sqrt_inv_x_sqrt = array_ops.reshape(scale_sqrt_inv_x_sqrt, shape) perm = array_ops.concat((math_ops.range(ndims - sample_ndims, ndims), math_ops.range(0, ndims - sample_ndims)), 0) scale_sqrt_inv_x_sqrt = array_ops.transpose(scale_sqrt_inv_x_sqrt, perm) # Write V = SS', X = LL'. Then: # tr[inv(V) X] = tr[inv(S)' inv(S) L L'] # = tr[inv(S) L L' inv(S)'] # = tr[(inv(S) L) (inv(S) L)'] # = sum_{ik} (inv(S) L)_{ik}^2 # The second equality follows from the cyclic permutation property. # Complexity: O(nbk^2) trace_scale_inv_x = math_ops.reduce_sum( math_ops.square(scale_sqrt_inv_x_sqrt), reduction_indices=[-2, -1]) # Complexity: O(nbk) half_log_det_x = math_ops.reduce_sum( math_ops.log(array_ops.matrix_diag_part(x_sqrt)), reduction_indices=[-1]) # Complexity: O(nbk^2) log_prob = ((self.df - self.dimension - 1.) * half_log_det_x - 0.5 * trace_scale_inv_x - self.log_normalizing_constant()) # Set shape hints. # Try to merge what we know from the input then what we know from the # parameters of this distribution. if x.get_shape().ndims is not None: log_prob.set_shape(x.get_shape()[:-2]) if (log_prob.get_shape().ndims is not None and self.get_batch_shape().ndims is not None and self.get_batch_shape().ndims > 0): log_prob.get_shape()[-self.get_batch_shape().ndims:].merge_with( self.get_batch_shape()) return log_prob
def _event_shape_tensor(self): s = self.scale_operator_pd.shape() return array_ops.strided_slice(s, array_ops.shape(s) - 2, array_ops.shape(s))
def _slice_helper(tensor, slice_spec, var=None): """Copied from array_ops._slice_helper, will be merged back later.""" if isinstance(slice_spec, bool) or \ (isinstance(slice_spec, ops.Tensor) and slice_spec.dtype == dtypes.bool) or \ (isinstance(slice_spec, np.ndarray) and slice_spec.dtype == bool): return array_ops.boolean_mask(tensor=tensor, mask=slice_spec) if not isinstance(slice_spec, (list, tuple)): slice_spec = [slice_spec] begin, end, strides = [], [], [] index = 0 new_axis_mask, shrink_axis_mask = 0, 0 begin_mask, end_mask = 0, 0 ellipsis_mask = 0 for s in slice_spec: if isinstance(s, slice): if s.start is not None and not _is_undefined_dimension(s.start): _check_index(s.start) begin.append(s.start) else: begin.append(0) begin_mask |= (1 << index) if s.stop is not None and not _is_undefined_dimension(s.stop): _check_index(s.stop) end.append(s.stop) else: end.append(0) end_mask |= (1 << index) if s.step is not None and not _is_undefined_dimension(s.step): _check_index(s.step) strides.append(s.step) else: strides.append(1) elif s is Ellipsis: begin.append(0) end.append(0) strides.append(1) ellipsis_mask |= (1 << index) elif s is array_ops.newaxis: begin.append(0) end.append(0) strides.append(1) new_axis_mask |= (1 << index) else: _check_index(s) begin.append(s) end.append(s + 1) strides.append(1) shrink_axis_mask |= (1 << index) index += 1 # stack possibly involves no tensors, so we must use op_scope correct graph. with ops.name_scope(None, 'strided_slice', [tensor] + begin + end + strides, skip_on_eager=False) as name: if begin: packed_begin, packed_end, packed_strides = ( array_ops.stack(begin), array_ops.stack(end), array_ops.stack(strides)) if (packed_begin.dtype == dtypes.int64 or packed_end.dtype == dtypes.int64 or packed_strides.dtype == dtypes.int64): if packed_begin.dtype != dtypes.int64: packed_begin = math_ops.cast(packed_begin, dtypes.int64) if packed_end.dtype != dtypes.int64: packed_end = math_ops.cast(packed_end, dtypes.int64) if packed_strides.dtype != dtypes.int64: packed_strides = math_ops.cast(packed_strides, dtypes.int64) else: var_empty = constant_op.constant([], dtype=dtypes.int32) packed_begin = packed_end = packed_strides = var_empty return array_ops.strided_slice(tensor, packed_begin, packed_end, packed_strides, begin_mask=begin_mask, end_mask=end_mask, shrink_axis_mask=shrink_axis_mask, new_axis_mask=new_axis_mask, ellipsis_mask=ellipsis_mask, var=var, name=name)
def my_net(a, b, c): a = array_ops.broadcast_to(a, shape=[1024]) b = array_ops.strided_slice(b, [0], [8192], [8]) c = array_ops.pad(c, paddings=[[256, 256]]) out = a + b + c return [out]
def testSlice(self): tf_val = array_ops.placeholder(dtypes.int32, shape=(4, ))[0:2] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([None, None], c_val.as_list()) # begin:end tf_val = constant_op.constant([10, 20, 30])[1:3] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([20, 30], c_val.as_list()) # begin:end:stride tf_val = array_ops.strided_slice(constant_op.constant([10, 20, 30]), [1], [3], strides=[2]) c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([20], c_val.as_list()) # [1, 2, 16, 37, None, 48] tf_val_orig = array_ops.concat( [[1, 2, 16, 37], array_ops.placeholder(dtypes.int32, shape=(1, )), [48]], 0) # begin: no end tf_val = tf_val_orig[2:] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([16, 37, None, 48], c_val.as_list()) # begin::negative slice tf_val = tf_val_orig[2::-1] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([16, 2, 1], c_val.as_list()) # :end:negative slice tf_val = tf_val_orig[:1:-2] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([48, 37], c_val.as_list()) # begin:end:negative slice tf_val = tf_val_orig[3:1:-1] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([37, 16], c_val.as_list()) # begin:negative end:slice tf_val = tf_val_orig[1:-3:1] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([2, 16], c_val.as_list()) # negative begin::slice tf_val = tf_val_orig[-3::1] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([37, None, 48], c_val.as_list()) # negative begin::negative slice tf_val = tf_val_orig[-3::-1] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([37, 16, 2, 1], c_val.as_list()) # negative begin:negative end:negative slice tf_val = tf_val_orig[-3:-5:-1] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([37, 16], c_val.as_list()) # Do not support shape inference for additional arguments tf_val = constant_op.constant([10, 20, 30])[...] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual([None, None, None], c_val.as_list()) # Do not support shape inference for tensor slices. tf_val = constant_op.constant( [10, 20, 30])[array_ops.placeholder(dtypes.int32, shape=()):] c_val = tensor_util.constant_value_as_shape(tf_val) self.assertEqual(tensor_shape.unknown_shape(), c_val) # Do not support shape inference for higher rank with self.assertRaises(ValueError): tf_val = constant_op.constant([[10], [20], [30]])[:, 0:] c_val = tensor_util.constant_value_as_shape(tf_val)
def _event_shape(self): s = self.scale_operator_pd.shape() return array_ops.strided_slice(s, array_ops.shape(s) - 2, array_ops.shape(s))
def _log_prob(self, x): if self.cholesky_input_output_matrices: x_sqrt = x else: # Complexity: O(nbk^3) x_sqrt = linalg_ops.cholesky(x) batch_shape = self.batch_shape_tensor() event_shape = self.event_shape_tensor() ndims = array_ops.rank(x_sqrt) # sample_ndims = ndims - batch_ndims - event_ndims sample_ndims = ndims - array_ops.shape(batch_shape)[0] - 2 sample_shape = array_ops.strided_slice(array_ops.shape(x_sqrt), [0], [sample_ndims]) # We need to be able to pre-multiply each matrix by its corresponding # batch scale matrix. Since a Distribution Tensor supports multiple # samples per batch, this means we need to reshape the input matrix `x` # so that the first b dimensions are batch dimensions and the last two # are of shape [dimension, dimensions*number_of_samples]. Doing these # gymnastics allows us to do a batch_solve. # # After we're done with sqrt_solve (the batch operation) we need to undo # this reshaping so what we're left with is a Tensor partitionable by # sample, batch, event dimensions. # Complexity: O(nbk**2) since transpose must access every element. scale_sqrt_inv_x_sqrt = x_sqrt perm = array_ops.concat([ math_ops.range(sample_ndims, ndims), math_ops.range(0, sample_ndims) ], 0) scale_sqrt_inv_x_sqrt = array_ops.transpose(scale_sqrt_inv_x_sqrt, perm) shape = array_ops.concat( (batch_shape, (math_ops.cast(self.dimension, dtype=dtypes.int32), -1)), 0) scale_sqrt_inv_x_sqrt = array_ops.reshape(scale_sqrt_inv_x_sqrt, shape) # Complexity: O(nbM*k) where M is the complexity of the operator solving # a vector system. E.g., for OperatorPDDiag, each solve is O(k), so # this complexity is O(nbk**2). For OperatorPDCholesky, each solve is # O(k**2) so this step has complexity O(nbk^3). scale_sqrt_inv_x_sqrt = self.scale_operator_pd.sqrt_solve( scale_sqrt_inv_x_sqrt) # Undo make batch-op ready. # Complexity: O(nbk**2) shape = array_ops.concat([batch_shape, event_shape, sample_shape], 0) scale_sqrt_inv_x_sqrt = array_ops.reshape(scale_sqrt_inv_x_sqrt, shape) perm = array_ops.concat([ math_ops.range(ndims - sample_ndims, ndims), math_ops.range(0, ndims - sample_ndims) ], 0) scale_sqrt_inv_x_sqrt = array_ops.transpose(scale_sqrt_inv_x_sqrt, perm) # Write V = SS', X = LL'. Then: # tr[inv(V) X] = tr[inv(S)' inv(S) L L'] # = tr[inv(S) L L' inv(S)'] # = tr[(inv(S) L) (inv(S) L)'] # = sum_{ik} (inv(S) L)_{ik}**2 # The second equality follows from the cyclic permutation property. # Complexity: O(nbk**2) trace_scale_inv_x = math_ops.reduce_sum( math_ops.square(scale_sqrt_inv_x_sqrt), axis=[-2, -1]) # Complexity: O(nbk) half_log_det_x = math_ops.reduce_sum(math_ops.log( array_ops.matrix_diag_part(x_sqrt)), axis=[-1]) # Complexity: O(nbk**2) log_prob = ((self.df - self.dimension - 1.) * half_log_det_x - 0.5 * trace_scale_inv_x - self.log_normalization()) # Set shape hints. # Try to merge what we know from the input then what we know from the # parameters of this distribution. if x.get_shape().ndims is not None: log_prob.set_shape(x.get_shape()[:-2]) if (log_prob.get_shape().ndims is not None and self.batch_shape.ndims is not None and self.batch_shape.ndims > 0): log_prob.get_shape()[-self.batch_shape.ndims:].merge_with( self.batch_shape) return log_prob
def frame(signal, frame_length, frame_step, pad_end=False, pad_value=0, axis=-1, name=None): """Expands `signal`'s `axis` dimension into frames of `frame_length`. Slides a window of size `frame_length` over `signal`'s `axis` dimension with a stride of `frame_step`, replacing the `axis` dimension with `[frames, frame_length]` frames. If `pad_end` is True, window positions that are past the end of the `axis` dimension are padded with `pad_value` until the window moves fully past the end of the dimension. Otherwise, only window positions that fully overlap the `axis` dimension are produced. For example: ```python pcm = tf.placeholder(tf.float32, [None, 9152]) frames = tf.signal.frame(pcm, 512, 180) magspec = tf.abs(tf.spectral.rfft(frames, [512])) image = tf.expand_dims(magspec, 3) ``` Args: signal: A `[..., samples, ...]` `Tensor`. The rank and dimensions may be unknown. Rank must be at least 1. frame_length: The frame length in samples. An integer or scalar `Tensor`. frame_step: The frame hop size in samples. An integer or scalar `Tensor`. pad_end: Whether to pad the end of `signal` with `pad_value`. pad_value: An optional scalar `Tensor` to use where the input signal does not exist when `pad_end` is True. axis: A scalar integer `Tensor` indicating the axis to frame. Defaults to the last axis. Supports negative values for indexing from the end. name: An optional name for the operation. Returns: A `Tensor` of frames with shape `[..., frames, frame_length, ...]`. Raises: ValueError: If `frame_length`, `frame_step`, `pad_value`, or `axis` are not scalar. """ with ops.name_scope(name, "frame", [signal, frame_length, frame_step, pad_value]): signal = ops.convert_to_tensor(signal, name="signal") frame_length = ops.convert_to_tensor(frame_length, name="frame_length") frame_step = ops.convert_to_tensor(frame_step, name="frame_step") axis = ops.convert_to_tensor(axis, name="axis") signal.shape.with_rank_at_least(1) frame_length.shape.assert_has_rank(0) frame_step.shape.assert_has_rank(0) axis.shape.assert_has_rank(0) result_shape = _infer_frame_shape(signal, frame_length, frame_step, pad_end, axis) # Axis can be negative. Convert it to positive. signal_rank = array_ops.rank(signal) axis = math_ops.range(signal_rank)[axis] signal_shape = array_ops.shape(signal) outer_dimensions, length_samples, inner_dimensions = array_ops.split( signal_shape, [axis, 1, signal_rank - 1 - axis]) length_samples = array_ops.reshape(length_samples, []) num_outer_dimensions = array_ops.size(outer_dimensions) num_inner_dimensions = array_ops.size(inner_dimensions) # If padding is requested, pad the input signal tensor with pad_value. if pad_end: pad_value = ops.convert_to_tensor(pad_value, signal.dtype) pad_value.shape.assert_has_rank(0) # Calculate number of frames, using double negatives to round up. num_frames = -(-length_samples // frame_step) # Pad the signal by up to frame_length samples based on how many samples # are remaining starting from last_frame_position. pad_samples = math_ops.maximum( 0, frame_length + frame_step * (num_frames - 1) - length_samples) # Pad the inner dimension of signal by pad_samples. paddings = array_ops.concat( [array_ops.zeros([num_outer_dimensions, 2], dtype=pad_samples.dtype), [[0, pad_samples]], array_ops.zeros([num_inner_dimensions, 2], dtype=pad_samples.dtype)], 0) signal = array_ops.pad(signal, paddings, constant_values=pad_value) signal_shape = array_ops.shape(signal) length_samples = signal_shape[axis] else: num_frames = math_ops.maximum( 0, 1 + (length_samples - frame_length) // frame_step) subframe_length = util_ops.gcd(frame_length, frame_step) subframes_per_frame = frame_length // subframe_length subframes_per_hop = frame_step // subframe_length num_subframes = length_samples // subframe_length slice_shape = array_ops.concat([outer_dimensions, [num_subframes * subframe_length], inner_dimensions], 0) subframe_shape = array_ops.concat([outer_dimensions, [num_subframes, subframe_length], inner_dimensions], 0) subframes = array_ops.reshape(array_ops.strided_slice( signal, array_ops.zeros_like(signal_shape), slice_shape), subframe_shape) # frame_selector is a [num_frames, subframes_per_frame] tensor # that indexes into the appropriate frame in subframes. For example: # [[0, 0, 0, 0], [2, 2, 2, 2], [4, 4, 4, 4]] frame_selector = array_ops.reshape( math_ops.range(num_frames) * subframes_per_hop, [num_frames, 1]) # subframe_selector is a [num_frames, subframes_per_frame] tensor # that indexes into the appropriate subframe within a frame. For example: # [[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]] subframe_selector = array_ops.reshape( math_ops.range(subframes_per_frame), [1, subframes_per_frame]) # Adding the 2 selector tensors together produces a [num_frames, # subframes_per_frame] tensor of indices to use with tf.gather to select # subframes from subframes. We then reshape the inner-most # subframes_per_frame dimension to stitch the subframes together into # frames. For example: [[0, 1, 2, 3], [2, 3, 4, 5], [4, 5, 6, 7]]. selector = frame_selector + subframe_selector frames = array_ops.reshape( array_ops.gather(subframes, selector, axis=axis), array_ops.concat([outer_dimensions, [num_frames, frame_length], inner_dimensions], 0)) if result_shape: frames.set_shape(result_shape) return frames