def testMakeDenseSplitEmptyInputs(self): """Tests empty inputs op.""" with self.test_session() as sess: partition_ids = array_ops.constant([], dtype=dtypes.int32) bucket_ids = array_ops.constant([[]], dtype=dtypes.int64) gradients = array_ops.constant([]) hessians = array_ops.constant([]) bucket_boundaries = [0.3, 0.52] partitions, gains, splits = ( split_handler_ops.build_dense_inequality_splits( num_minibatches=0, partition_ids=partition_ids, bucket_ids=bucket_ids, gradients=gradients, hessians=hessians, bucket_boundaries=bucket_boundaries, l1_regularization=0.1, l2_regularization=1, tree_complexity_regularization=0, min_node_weight=0, class_id=-1, feature_column_group_id=0, multiclass_strategy=learner_pb2.LearnerConfig.TREE_PER_CLASS)) partitions, gains, splits = sess.run([partitions, gains, splits]) # .assertEmpty doesn't exist on ubuntu-contrib self.assertEqual(0, len(partitions)) self.assertEqual(0, len(gains)) self.assertEqual(0, len(splits))
def testMakeDenseSplitEmptyInputs(self): """Tests empty inputs op.""" with self.test_session() as sess: partition_ids = array_ops.constant([], dtype=dtypes.int32) bucket_ids = array_ops.constant([[]], dtype=dtypes.int64) gradients = array_ops.constant([]) hessians = array_ops.constant([]) bucket_boundaries = [0.3, 0.52] partitions, gains, splits = ( split_handler_ops.build_dense_inequality_splits( num_minibatches=0, partition_ids=partition_ids, bucket_ids=bucket_ids, gradients=gradients, hessians=hessians, bucket_boundaries=bucket_boundaries, l1_regularization=0.1, l2_regularization=1, tree_complexity_regularization=0, min_node_weight=0, class_id=-1, feature_column_group_id=0, multiclass_strategy=learner_pb2.LearnerConfig. TREE_PER_CLASS)) partitions, gains, splits = sess.run([partitions, gains, splits]) # .assertEmpty doesn't exist on ubuntu-contrib self.assertEqual(0, len(partitions)) self.assertEqual(0, len(gains)) self.assertEqual(0, len(splits))
def _make_dense_split(quantile_accumulator_handle, stats_accumulator_handle, stamp_token, next_stamp_token, multiclass_strategy, class_id, feature_column_id, l1_regularization, l2_regularization, tree_complexity_regularization, min_node_weight, is_multi_dimentional, loss_uses_sum_reduction, weak_learner_type): """Function that builds splits for a dense feature column.""" # Get the bucket boundaries are_splits_ready, buckets = ( gen_quantile_ops.quantile_accumulator_get_buckets( quantile_accumulator_handles=[quantile_accumulator_handle], stamp_token=stamp_token)) # quantile_accumulator_get_buckets returns a list of results per handle that # we pass to it. In this case we're getting results just for one resource. are_splits_ready = are_splits_ready[0] buckets = buckets[0] # After we receive the boundaries from previous iteration we can flush # the quantile accumulator. with ops.control_dependencies([buckets]): flush_quantiles = gen_quantile_ops.quantile_accumulator_flush( quantile_accumulator_handle=quantile_accumulator_handle, stamp_token=stamp_token, next_stamp_token=next_stamp_token) if is_multi_dimentional: num_minibatches, partition_ids, bucket_ids, gradients, hessians = ( gen_stats_accumulator_ops.stats_accumulator_tensor_flush( stats_accumulator_handle, stamp_token, next_stamp_token)) else: num_minibatches, partition_ids, bucket_ids, gradients, hessians = ( gen_stats_accumulator_ops.stats_accumulator_scalar_flush( stats_accumulator_handle, stamp_token, next_stamp_token)) # For sum_reduction, we don't need to divide by number of minibatches. num_minibatches = control_flow_ops.cond( loss_uses_sum_reduction, lambda: math_ops.cast(1, dtypes.int64), lambda: num_minibatches) # Put quantile and stats accumulator flushing in the dependency path. with ops.control_dependencies([flush_quantiles, partition_ids]): are_splits_ready = array_ops.identity(are_splits_ready) partition_ids, gains, split_infos = ( split_handler_ops.build_dense_inequality_splits( num_minibatches=num_minibatches, bucket_boundaries=buckets, partition_ids=partition_ids, bucket_ids=bucket_ids, gradients=gradients, hessians=hessians, class_id=class_id, feature_column_group_id=feature_column_id, l1_regularization=l1_regularization, l2_regularization=l2_regularization, tree_complexity_regularization=tree_complexity_regularization, min_node_weight=min_node_weight, multiclass_strategy=multiclass_strategy, weak_learner_type=weak_learner_type)) return are_splits_ready, partition_ids, gains, split_infos
def _make_dense_split(quantile_accumulator_handle, stats_accumulator_handle, stamp_token, next_stamp_token, multiclass_strategy, class_id, feature_column_id, l1_regularization, l2_regularization, tree_complexity_regularization, min_node_weight, is_multi_dimentional, loss_uses_sum_reduction, weak_learner_type): """Function that builds splits for a dense feature column.""" # Get the bucket boundaries are_splits_ready, buckets = ( gen_quantile_ops.quantile_accumulator_get_buckets( quantile_accumulator_handles=[quantile_accumulator_handle], stamp_token=stamp_token)) # quantile_accumulator_get_buckets returns a list of results per handle that # we pass to it. In this case we're getting results just for one resource. are_splits_ready = are_splits_ready[0] buckets = buckets[0] # After we receive the boundaries from previous iteration we can flush # the quantile accumulator. with ops.control_dependencies([buckets]): flush_quantiles = gen_quantile_ops.quantile_accumulator_flush( quantile_accumulator_handle=quantile_accumulator_handle, stamp_token=stamp_token, next_stamp_token=next_stamp_token) if is_multi_dimentional: num_minibatches, partition_ids, bucket_ids, gradients, hessians = ( gen_stats_accumulator_ops.stats_accumulator_tensor_flush( stats_accumulator_handle, stamp_token, next_stamp_token)) else: num_minibatches, partition_ids, bucket_ids, gradients, hessians = ( gen_stats_accumulator_ops.stats_accumulator_scalar_flush( stats_accumulator_handle, stamp_token, next_stamp_token)) # For sum_reduction, we don't need to divide by number of minibatches. num_minibatches = control_flow_ops.cond(loss_uses_sum_reduction, lambda: math_ops.to_int64(1), lambda: num_minibatches) # Put quantile and stats accumulator flushing in the dependency path. with ops.control_dependencies([flush_quantiles, partition_ids]): are_splits_ready = array_ops.identity(are_splits_ready) partition_ids, gains, split_infos = ( split_handler_ops.build_dense_inequality_splits( num_minibatches=num_minibatches, bucket_boundaries=buckets, partition_ids=partition_ids, bucket_ids=bucket_ids, gradients=gradients, hessians=hessians, class_id=class_id, feature_column_group_id=feature_column_id, l1_regularization=l1_regularization, l2_regularization=l2_regularization, tree_complexity_regularization=tree_complexity_regularization, min_node_weight=min_node_weight, multiclass_strategy=multiclass_strategy, weak_learner_type=weak_learner_type)) return are_splits_ready, partition_ids, gains, split_infos
def testMakeMulticlassDenseSplit(self): """Tests split handler op.""" with self.cached_session() as sess: partition_ids = array_ops.constant([0, 0, 1], dtype=dtypes.int32) bucket_ids = array_ops.constant([[0, 0], [1, 0], [1, 0]], dtype=dtypes.int64) gradients = array_ops.constant([[2.4, 3.0], [-0.6, 0.1], [8.0, 1.0]]) hessians = array_ops.constant([[[0.4, 1], [1, 1]], [[0.38, 1], [1, 1]], [[0.26, 1], [1, 1]]]) bucket_boundaries = [0.3, 0.52] partitions, gains, splits = ( split_handler_ops.build_dense_inequality_splits( num_minibatches=2, partition_ids=partition_ids, bucket_ids=bucket_ids, gradients=gradients, hessians=hessians, bucket_boundaries=bucket_boundaries, l1_regularization=0, l2_regularization=1, tree_complexity_regularization=0, min_node_weight=0, class_id=-1, feature_column_group_id=0, multiclass_strategy=learner_pb2.LearnerConfig.FULL_HESSIAN, weak_learner_type=learner_pb2.LearnerConfig. NORMAL_DECISION_TREE)) partitions, gains, splits = sess.run([partitions, gains, splits]) self.assertAllEqual([0, 1], partitions) split_info = split_info_pb2.SplitInfo() split_info.ParseFromString(splits[0]) left_child = split_info.left_child.vector right_child = split_info.right_child.vector split_node = split_info.split_node.dense_float_binary_split # Each leaf has 2 element vector. self.assertEqual(2, len(left_child.value)) self.assertEqual(2, len(right_child.value)) self.assertEqual(0, split_node.feature_column) self.assertAllClose(0.3, split_node.threshold, 1e-6)
def make_splits(self, stamp_token, next_stamp_token, class_id): """Create the best split using the accumulated stats and flush the state.""" # Get the bucket boundaries are_splits_ready, buckets = ( self._quantile_accumulator.get_buckets(stamp_token)) # After we receive the boundaries from previous iteration we can flush # the quantile accumulator. with ops.control_dependencies([buckets]): flush_quantiles = self._quantile_accumulator.flush( stamp_token=stamp_token, next_stamp_token=next_stamp_token) # Get the aggregated gradients and hessians per <partition_id, feature_id> # pair. # In order to distribute the computation on all the PSs we use the PS that # had the stats accumulator on. with ops.device(None): with ops.device(self._stats_accumulator.resource().device): num_minibatches, partition_ids, bucket_ids, gradients, hessians = ( self._stats_accumulator.flush(stamp_token, next_stamp_token)) # Put quantile and stats accumulator flushing in the dependency path. are_splits_ready = control_flow_ops.with_dependencies( [flush_quantiles, partition_ids], are_splits_ready) partition_ids, gains, split_infos = ( split_handler_ops.build_dense_inequality_splits( num_minibatches=num_minibatches, bucket_boundaries=buckets, partition_ids=partition_ids, bucket_ids=bucket_ids, gradients=gradients, hessians=hessians, class_id=class_id, feature_column_group_id=self._feature_column_group_id, l1_regularization=self._l1_regularization, l2_regularization=self._l2_regularization, tree_complexity_regularization=self. _tree_complexity_regularization, min_node_weight=self._min_node_weight, multiclass_strategy=self._multiclass_strategy)) return (are_splits_ready, partition_ids, gains, split_infos)
def make_splits(self, stamp_token, next_stamp_token, class_id): """Create the best split using the accumulated stats and flush the state.""" # Get the bucket boundaries are_splits_ready, buckets = ( self._quantile_accumulator.get_buckets(stamp_token)) # After we receive the boundaries from previous iteration we can flush # the quantile accumulator. with ops.control_dependencies([buckets]): flush_quantiles = self._quantile_accumulator.flush( stamp_token=stamp_token, next_stamp_token=next_stamp_token) # Get the aggregated gradients and hessians per <partition_id, feature_id> # pair. # In order to distribute the computation on all the PSs we use the PS that # had the stats accumulator on. with ops.device(None): with ops.device(self._stats_accumulator.resource().device): num_minibatches, partition_ids, bucket_ids, gradients, hessians = ( self._stats_accumulator.flush(stamp_token, next_stamp_token)) # Put quantile and stats accumulator flushing in the dependency path. are_splits_ready = control_flow_ops.with_dependencies( [flush_quantiles, partition_ids], are_splits_ready) partition_ids, gains, split_infos = ( split_handler_ops.build_dense_inequality_splits( num_minibatches=num_minibatches, bucket_boundaries=buckets, partition_ids=partition_ids, bucket_ids=bucket_ids, gradients=gradients, hessians=hessians, class_id=class_id, feature_column_group_id=self._feature_column_group_id, l1_regularization=self._l1_regularization, l2_regularization=self._l2_regularization, tree_complexity_regularization=self. _tree_complexity_regularization, min_node_weight=self._min_node_weight, multiclass_strategy=self._multiclass_strategy)) return (are_splits_ready, partition_ids, gains, split_infos)
def testMakeMulticlassDenseSplit(self): """Tests split handler op.""" with self.test_session() as sess: partition_ids = array_ops.constant([0, 0, 1], dtype=dtypes.int32) bucket_ids = array_ops.constant( [[0, 0], [1, 0], [1, 0]], dtype=dtypes.int64) gradients = array_ops.constant([[2.4, 3.0], [-0.6, 0.1], [8.0, 1.0]]) hessians = array_ops.constant([[[0.4, 1], [1, 1]], [[0.38, 1], [1, 1]], [[0.26, 1], [1, 1]]]) bucket_boundaries = [0.3, 0.52] partitions, gains, splits = ( split_handler_ops.build_dense_inequality_splits( num_minibatches=2, partition_ids=partition_ids, bucket_ids=bucket_ids, gradients=gradients, hessians=hessians, bucket_boundaries=bucket_boundaries, l1_regularization=0, l2_regularization=1, tree_complexity_regularization=0, min_node_weight=0, class_id=-1, feature_column_group_id=0, multiclass_strategy=learner_pb2.LearnerConfig.FULL_HESSIAN, weak_learner_type=learner_pb2.LearnerConfig.NORMAL_DECISION_TREE)) partitions, gains, splits = sess.run([partitions, gains, splits]) self.assertAllEqual([0, 1], partitions) split_info = split_info_pb2.SplitInfo() split_info.ParseFromString(splits[0]) left_child = split_info.left_child.vector right_child = split_info.right_child.vector split_node = split_info.split_node.dense_float_binary_split # Each leaf has 2 element vector. self.assertEqual(2, len(left_child.value)) self.assertEqual(2, len(right_child.value)) self.assertEqual(0, split_node.feature_column) self.assertAllClose(0.3, split_node.threshold, 1e-6)
def testMakeDenseSplit(self): """Tests split handler op.""" with self.test_session() as sess: # The data looks like the following after dividing by number of steps (2). # Gradients | Partition | Dense Quantile | # (1.2, 0.2) | 0 | 0 | # (-0.3, 0.19) | 0 | 1 | # (4.0, 0.13) | 1 | 1 | partition_ids = array_ops.constant([0, 0, 1], dtype=dtypes.int32) bucket_ids = array_ops.constant([[0, 0], [1, 0], [1, 0]], dtype=dtypes.int64) gradients = array_ops.constant([2.4, -0.6, 8.0]) hessians = array_ops.constant([0.4, 0.38, 0.26]) bucket_boundaries = [0.3, 0.52] partitions, gains, splits = ( split_handler_ops.build_dense_inequality_splits( num_minibatches=2, partition_ids=partition_ids, bucket_ids=bucket_ids, gradients=gradients, hessians=hessians, bucket_boundaries=bucket_boundaries, l1_regularization=0.1, l2_regularization=1, tree_complexity_regularization=0, min_node_weight=0, class_id=-1, feature_column_group_id=0, multiclass_strategy=learner_pb2.LearnerConfig. TREE_PER_CLASS)) partitions, gains, splits = sess.run([partitions, gains, splits]) self.assertAllEqual([0, 1], partitions) # Check the split on partition 0. # -(1.2 - 0.1) / (0.2 + 1) expected_left_weight = -0.91666 # expected_left_weight * -(1.2 - 0.1) expected_left_gain = 1.0083333333333331 # (-0.3 + 0.1) / (0.19 + 1) expected_right_weight = 0.1680672 # expected_right_weight * -(-0.3 + 0.1) expected_right_gain = 0.033613445378151252 # (-0.3 + 1.2 - 0.1) ** 2 / (0.19 + 0.2 + 1) expected_bias_gain = 0.46043165467625885 split_info = split_info_pb2.SplitInfo() split_info.ParseFromString(splits[0]) left_child = split_info.left_child.vector right_child = split_info.right_child.vector split_node = split_info.split_node.dense_float_binary_split self.assertAllClose( expected_left_gain + expected_right_gain - expected_bias_gain, gains[0], 0.00001) self.assertAllClose([expected_left_weight], left_child.value, 0.00001) self.assertAllClose([expected_right_weight], right_child.value, 0.00001) self.assertEqual(0, split_node.feature_column) self.assertAllClose(0.3, split_node.threshold, 0.00001) # Check the split on partition 1. # (-4 + 0.1) / (0.13 + 1) expected_left_weight = -3.4513274336283186 expected_right_weight = 0 split_info = split_info_pb2.SplitInfo() split_info.ParseFromString(splits[1]) left_child = split_info.left_child.vector right_child = split_info.right_child.vector split_node = split_info.split_node.dense_float_binary_split # There's only one active bucket here so zero gain is expected. self.assertAllClose(0.0, gains[1], 0.00001) self.assertAllClose([expected_left_weight], left_child.value, 0.00001) self.assertAllClose([expected_right_weight], right_child.value, 0.00001) self.assertEqual(0, split_node.feature_column) self.assertAllClose(0.52, split_node.threshold, 0.00001)
def testMakeDenseSplit(self): """Tests split handler op.""" with self.test_session() as sess: # The data looks like the following after dividing by number of steps (2). # Gradients | Partition | Dense Quantile | # (1.2, 0.2) | 0 | 0 | # (-0.3, 0.19) | 0 | 1 | # (4.0, 0.13) | 1 | 1 | partition_ids = array_ops.constant([0, 0, 1], dtype=dtypes.int32) bucket_ids = array_ops.constant( [[0, 0], [1, 0], [1, 0]], dtype=dtypes.int64) gradients = array_ops.constant([2.4, -0.6, 8.0]) hessians = array_ops.constant([0.4, 0.38, 0.26]) bucket_boundaries = [0.3, 0.52] partitions, gains, splits = ( split_handler_ops.build_dense_inequality_splits( num_minibatches=2, partition_ids=partition_ids, bucket_ids=bucket_ids, gradients=gradients, hessians=hessians, bucket_boundaries=bucket_boundaries, l1_regularization=0.1, l2_regularization=1, tree_complexity_regularization=0, min_node_weight=0, class_id=-1, feature_column_group_id=0, multiclass_strategy=learner_pb2.LearnerConfig.TREE_PER_CLASS)) partitions, gains, splits = sess.run([partitions, gains, splits]) self.assertAllEqual([0, 1], partitions) # Check the split on partition 0. # -(1.2 - 0.1) / (0.2 + 1) expected_left_weight = -0.91666 # expected_left_weight * -(1.2 - 0.1) expected_left_gain = 1.0083333333333331 # (-0.3 + 0.1) / (0.19 + 1) expected_right_weight = 0.1680672 # expected_right_weight * -(-0.3 + 0.1) expected_right_gain = 0.033613445378151252 # (-0.3 + 1.2 - 0.1) ** 2 / (0.19 + 0.2 + 1) expected_bias_gain = 0.46043165467625885 split_info = split_info_pb2.SplitInfo() split_info.ParseFromString(splits[0]) left_child = split_info.left_child.vector right_child = split_info.right_child.vector split_node = split_info.split_node.dense_float_binary_split self.assertAllClose( expected_left_gain + expected_right_gain - expected_bias_gain, gains[0], 0.00001) self.assertAllClose([expected_left_weight], left_child.value, 0.00001) self.assertAllClose([expected_right_weight], right_child.value, 0.00001) self.assertEqual(0, split_node.feature_column) self.assertAllClose(0.3, split_node.threshold, 0.00001) # Check the split on partition 1. # (-4 + 0.1) / (0.13 + 1) expected_left_weight = -3.4513274336283186 expected_right_weight = 0 split_info = split_info_pb2.SplitInfo() split_info.ParseFromString(splits[1]) left_child = split_info.left_child.vector right_child = split_info.right_child.vector split_node = split_info.split_node.dense_float_binary_split # There's only one active bucket here so zero gain is expected. self.assertAllClose(0.0, gains[1], 0.00001) self.assertAllClose([expected_left_weight], left_child.value, 0.00001) self.assertAllClose([expected_right_weight], right_child.value, 0.00001) self.assertEqual(0, split_node.feature_column) self.assertAllClose(0.52, split_node.threshold, 0.00001)