def test_loss_fn_arg_invalid(self): def _loss_fn(labels, logits, name=None): del labels, logits, name # Unused with self.assertRaisesRegexp( ValueError, r'loss_fn has unexpected args: \[\'name\'\]'): head_lib.multi_label_head(n_classes=3, loss_fn=_loss_fn)
def test_loss_fn_arg_logits_missing(self): def _loss_fn(labels): del labels # unused with self.assertRaisesRegexp( ValueError, r'loss_fn must contain argument: logits\. ' r'Given arguments: \(\'labels\',\)'): head_lib.multi_label_head(n_classes=3, loss_fn=_loss_fn)
def test_head_weights_wrong_size(self): head1 = head_lib.multi_label_head(n_classes=2, name='head1') head2 = head_lib.multi_label_head(n_classes=3, name='head2') with self.assertRaisesRegexp( ValueError, r'heads and head_weights must have the same size\. ' r'Given len\(heads\): 2. Given len\(head_weights\): 1\.'): multi_head_lib.multi_head([head1, head2], head_weights=[1.])
def test_invalid_loss_reduction(self): with self.assertRaisesRegexp( ValueError, r'Invalid loss_reduction: invalid_loss_reduction'): head_lib.multi_label_head( n_classes=3, loss_reduction='invalid_loss_reduction') with self.assertRaisesRegexp( ValueError, r'Invalid loss_reduction: none'): head_lib.multi_label_head( n_classes=3, loss_reduction=losses.Reduction.NONE)
def test_predict_two_heads_logits_tensor(self): """Tests predict with logits as Tensor.""" head1 = head_lib.multi_label_head(n_classes=2, name='head1') head2 = head_lib.multi_label_head(n_classes=3, name='head2') multi_head = multi_head_lib.multi_head([head1, head2]) logits = np.array( [[-1., 1., 2., -2., 2.], [-1.5, 1., -3., 2., -2.]], dtype=np.float32) expected_logits1 = np.array([[-1., 1.], [-1.5, 1.]], dtype=np.float32) expected_logits2 = np.array([[2., -2., 2.], [-3., 2., -2.]], dtype=np.float32) expected_probabilities = { 'head1': _sigmoid(expected_logits1), 'head2': _sigmoid(expected_logits2), } spec = multi_head.create_estimator_spec( features={'x': np.array(((42,),), dtype=np.int32)}, mode=model_fn.ModeKeys.PREDICT, logits=logits) self.assertItemsEqual( (_DEFAULT_SERVING_KEY, 'head1', 'classification/head1', 'predict/head1', 'head2', 'classification/head2', 'predict/head2'), spec.export_outputs.keys()) # Assert predictions and export_outputs. with self.test_session() as sess: _initialize_variables(self, spec.scaffold) self.assertIsNone(spec.scaffold.summary_op) predictions = sess.run(spec.predictions) self.assertAllClose( expected_logits1, predictions[('head1', prediction_keys.PredictionKeys.LOGITS)]) self.assertAllClose( expected_logits2, predictions[('head2', prediction_keys.PredictionKeys.LOGITS)]) self.assertAllClose( expected_probabilities['head1'], predictions[('head1', prediction_keys.PredictionKeys.PROBABILITIES)]) self.assertAllClose( expected_probabilities['head2'], predictions[('head2', prediction_keys.PredictionKeys.PROBABILITIES)]) self.assertAllClose( expected_probabilities['head1'], sess.run(spec.export_outputs[_DEFAULT_SERVING_KEY].scores)) self.assertAllClose( expected_probabilities['head1'], sess.run(spec.export_outputs['head1'].scores)) self.assertAllClose( expected_probabilities['head2'], sess.run(spec.export_outputs['head2'].scores))
def test_predict_two_heads_logits_tensor(self): """Tests predict with logits as Tensor.""" head1 = head_lib.multi_label_head(n_classes=2, name='head1') head2 = head_lib.multi_label_head(n_classes=3, name='head2') multi_head = multi_head_lib.multi_head([head1, head2]) logits = np.array( [[-1., 1., 2., -2., 2.], [-1.5, 1., -3., 2., -2.]], dtype=np.float32) expected_logits1 = np.array([[-1., 1.], [-1.5, 1.]], dtype=np.float32) expected_logits2 = np.array([[2., -2., 2.], [-3., 2., -2.]], dtype=np.float32) expected_probabilities = { 'head1': _sigmoid(expected_logits1), 'head2': _sigmoid(expected_logits2), } spec = multi_head.create_estimator_spec( features={'x': np.array(((42,),), dtype=np.int32)}, mode=model_fn.ModeKeys.PREDICT, logits=logits) self.assertItemsEqual( (_DEFAULT_SERVING_KEY, 'predict', 'head1', 'classification/head1', 'predict/head1', 'head2', 'classification/head2', 'predict/head2'), spec.export_outputs.keys()) # Assert predictions and export_outputs. with self.test_session() as sess: _initialize_variables(self, spec.scaffold) self.assertIsNone(spec.scaffold.summary_op) predictions = sess.run(spec.predictions) self.assertAllClose( expected_logits1, predictions[('head1', prediction_keys.PredictionKeys.LOGITS)]) self.assertAllClose( expected_logits2, predictions[('head2', prediction_keys.PredictionKeys.LOGITS)]) self.assertAllClose( expected_probabilities['head1'], predictions[('head1', prediction_keys.PredictionKeys.PROBABILITIES)]) self.assertAllClose( expected_probabilities['head2'], predictions[('head2', prediction_keys.PredictionKeys.PROBABILITIES)]) self.assertAllClose( expected_probabilities['head1'], sess.run(spec.export_outputs[_DEFAULT_SERVING_KEY].scores)) self.assertAllClose( expected_probabilities['head1'], sess.run(spec.export_outputs['head1'].scores)) self.assertAllClose( expected_probabilities['head2'], sess.run(spec.export_outputs['head2'].scores))
def test_train_create_loss_two_heads_with_weights(self): # Use different example weighting for each head weighting. weights1 = np.array([[1.], [2.]], dtype=np.float32) weights2 = np.array([[2.], [3.]]) head1 = head_lib.multi_label_head(n_classes=2, name='head1', weight_column='weights1') head2 = head_lib.multi_label_head(n_classes=3, name='head2', weight_column='weights2') multi_head = multi_head_lib.multi_head([head1, head2], head_weights=[1., 2.]) logits = { 'head1': np.array([[-10., 10.], [-15., 10.]], dtype=np.float32), 'head2': np.array([[20., -20., 20.], [-30., 20., -20.]], dtype=np.float32), } labels = { 'head1': np.array([[1, 0], [1, 1]], dtype=np.int64), 'head2': np.array([[0, 1, 0], [1, 1, 0]], dtype=np.int64), } weighted_sum_loss, example_weight_sum, _ = multi_head.create_loss( features={ 'x': np.array(((42, ), ), dtype=np.int32), 'weights1': weights1, 'weights2': weights2 }, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels) tol = 1e-3 with self.test_session(): # loss of the first head is [[(10 + 10) / 2], [(15 + 0) / 2]] # = [10, 7.5] # weighted_sum_loss = 1 * 10 + 2 * 7.5 = 25 # loss of the second head is [[(20 + 20 + 20) / 3], [(30 + 0 + 0) / 3]] # = [20, 10] # weighted_sum_loss = 2 * 20 + 3 * 10 = 70 # head-weighted merge = 1 * 25 + 2 * 70 = 165 self.assertAllClose(165, weighted_sum_loss.eval(), rtol=tol, atol=tol) # example_weight_sum = 1 * (1 + 2) + 2 * (2 + 3) = 13 self.assertAllClose(13., example_weight_sum.eval(), rtol=tol, atol=tol)
def test_train_create_loss_two_heads_with_weights(self): # Use different example weighting for each head weighting. weights1 = np.array([[1.], [2.]], dtype=np.float32) weights2 = np.array([[2.], [3.]]) head1 = head_lib.multi_label_head(n_classes=2, name='head1', weight_column='weights1') head2 = head_lib.multi_label_head(n_classes=3, name='head2', weight_column='weights2') multi_head = multi_head_lib.multi_head( [head1, head2], head_weights=[1., 2.]) logits = { 'head1': np.array([[-10., 10.], [-15., 10.]], dtype=np.float32), 'head2': np.array([[20., -20., 20.], [-30., 20., -20.]], dtype=np.float32), } labels = { 'head1': np.array([[1, 0], [1, 1]], dtype=np.int64), 'head2': np.array([[0, 1, 0], [1, 1, 0]], dtype=np.int64), } training_loss, unreduced_losses, weights, _ = multi_head.create_loss( features={ 'x': np.array(((42,),), dtype=np.int32), 'weights1': weights1, 'weights2': weights2 }, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels) tol = 1e-3 with self.cached_session(): # loss of the first head is [[(10 + 10) / 2], [(15 + 0) / 2]] # = [10, 7.5] # training_loss = (1 * 10 + 2 * 7.5) / 2 = 12.5 # head-weighted unreduced_loss = 1 * [10, 7.5] self.assertAllClose( [[10.], [7.5]], unreduced_losses['head1'].eval(), rtol=tol, atol=tol) # loss of the second head is [[(20 + 20 + 20) / 3], [(30 + 0 + 0) / 3]] # = [20, 10] # training_loss = (2 * 20 + 3 * 10) / 2 = 35 # head-weighted unreduced_loss = 2 * [20, 10] self.assertAllClose( [[40.], [20.]], unreduced_losses['head2'].eval(), rtol=tol, atol=tol) # head-weighted training_loss = 1 * 12.5 + 2 * 35 = 82.5 self.assertAllClose(82.5, training_loss.eval(), rtol=tol, atol=tol) # head-weighted example weights self.assertAllClose( [[1.], [2.]], weights['head1'].eval(), rtol=tol, atol=tol) self.assertAllClose( [[4.], [6.]], weights['head2'].eval(), rtol=tol, atol=tol)
def test_train_create_loss_two_heads_with_weights(self): # Use different example weighting for each head weighting. weights1 = np.array([[1.], [2.]], dtype=np.float32) weights2 = np.array([[2.], [3.]]) head1 = head_lib.multi_label_head(n_classes=2, name='head1', weight_column='weights1') head2 = head_lib.multi_label_head(n_classes=3, name='head2', weight_column='weights2') multi_head = multi_head_lib.multi_head( [head1, head2], head_weights=[1., 2.]) logits = { 'head1': np.array([[-10., 10.], [-15., 10.]], dtype=np.float32), 'head2': np.array([[20., -20., 20.], [-30., 20., -20.]], dtype=np.float32), } labels = { 'head1': np.array([[1, 0], [1, 1]], dtype=np.int64), 'head2': np.array([[0, 1, 0], [1, 1, 0]], dtype=np.int64), } training_loss, unreduced_losses, weights, _ = multi_head.create_loss( features={ 'x': np.array(((42,),), dtype=np.int32), 'weights1': weights1, 'weights2': weights2 }, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels) tol = 1e-3 with self.test_session(): # loss of the first head is [[(10 + 10) / 2], [(15 + 0) / 2]] # = [10, 7.5] # training_loss = (1 * 10 + 2 * 7.5) / 2 = 12.5 # head-weighted unreduced_loss = 1 * [10, 7.5] self.assertAllClose( [[10.], [7.5]], unreduced_losses['head1'].eval(), rtol=tol, atol=tol) # loss of the second head is [[(20 + 20 + 20) / 3], [(30 + 0 + 0) / 3]] # = [20, 10] # training_loss = (2 * 20 + 3 * 10) / 2 = 35 # head-weighted unreduced_loss = 2 * [20, 10] self.assertAllClose( [[40.], [20.]], unreduced_losses['head2'].eval(), rtol=tol, atol=tol) # head-weighted training_loss = 1 * 12.5 + 2 * 35 = 82.5 self.assertAllClose(82.5, training_loss.eval(), rtol=tol, atol=tol) # head-weighted example weights self.assertAllClose( [[1.], [2.]], weights['head1'].eval(), rtol=tol, atol=tol) self.assertAllClose( [[4.], [6.]], weights['head2'].eval(), rtol=tol, atol=tol)
def test_multi_dim_weights_wrong_outer_dim(self): """Logits and labels of shape [2, 2, 3], weights [2, 2, 3].""" head = head_lib.multi_label_head(n_classes=3, weight_column='weights') logits = np.array([[[-10., 10., -10.], [10., -10., 10.]], [[-12., 12., -12.], [12., -12., 12.]]], dtype=np.float32) labels = np.array([[[1, 0, 0], [1, 0, 0]], [[0, 1, 1], [0, 1, 1]]], dtype=np.int64) weights = np.array([[[1., 1., 1.], [1.5, 1.5, 1.5]], [[2., 2., 2.], [2.5, 2.5, 2.5]]], dtype=np.float32) weights_placeholder = array_ops.placeholder(dtype=dtypes.float32) def _train_op_fn(loss): del loss return control_flow_ops.no_op() spec = head.create_estimator_spec( features={'weights': weights_placeholder}, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels, train_op_fn=_train_op_fn) with self.test_session(): _initialize_variables(self, monitored_session.Scaffold()) with self.assertRaisesRegexp( errors.InvalidArgumentError, r'\[logits_shape: \] \[2 2 3\] \[weights_shape: \] \[2 2 3\]'): spec.loss.eval({weights_placeholder: weights})
def test_multi_dim_weighted_train(self): """Logits and labels of shape [2, 2, 3], weights [2, 2].""" head = head_lib.multi_label_head(n_classes=3, weight_column='weights') logits = np.array([[[-10., 10., -10.], [10., -10., 10.]], [[-12., 12., -12.], [12., -12., 12.]]], dtype=np.float32) labels = np.array([[[1, 0, 0], [1, 0, 0]], [[0, 1, 1], [0, 1, 1]]], dtype=np.int64) weights = np.array([[1., 1.5], [2., 2.5]], dtype=np.float32) # loss = [[10 + 10 + 0, 0 + 0 + 10], [0 + 0 + 12, 12 + 12 + 0]] / 3 # = [[20/3, 10/3], [4, 8]] # weighted_sum_loss = 1*20/3 + 1.5*10/3 + 2*4 + 2.5*8 = 39.6667 expected_loss = 39.6667 expected_train_result = 'my_train_op' def _train_op_fn(loss): return string_ops.string_join( [constant_op.constant(expected_train_result), string_ops.as_string(loss, precision=3)]) spec = head.create_estimator_spec( features={'weights': weights}, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels, train_op_fn=_train_op_fn) atol = 1.e-3 with self.test_session() as sess: _initialize_variables(self, monitored_session.Scaffold()) loss, train_result = sess.run((spec.loss, spec.train_op)) self.assertAllClose(expected_loss, loss, atol=atol) self.assertEqual( six.b('{0:s}{1:.3f}'.format(expected_train_result, expected_loss)), train_result)
def test_multi_dim_weighted_train_create_loss(self): """Logits and labels of shape [2, 2, 3], weights [2, 2].""" head = head_lib.multi_label_head(n_classes=3, weight_column='weights') logits = np.array([[[-10., 10., -10.], [10., -10., 10.]], [[-12., 12., -12.], [12., -12., 12.]]], dtype=np.float32) labels = np.array([[[1, 0, 0], [1, 0, 0]], [[0, 1, 1], [0, 1, 1]]], dtype=np.int64) weights = np.array([[1., 1.5], [2., 2.5]], dtype=np.float32) # unreduced_loss = # [[10 + 10 + 0, 0 + 0 + 10], [0 + 0 + 12, 12 + 12 + 0]] / 3 # = [[20/3, 10/3], [4, 8]] expected_unreduced_loss = [[[20./3.], [10./3.]], [[4.], [8.]]] # weights are reshaped to [2, 2, 1] to match logits. expected_weights = [[[1.], [1.5]], [[2.], [2.5]]] # weighted_sum_loss = 1*20/3 + 1.5*10/3 + 2*4 + 2.5*8 = 39.6667 expected_training_loss = 39.6667 training_loss, unreduced_loss, actual_weights, _ = head.create_loss( features={'weights': weights}, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels) atol = 1.e-3 with self.test_session(): _initialize_variables(self, monitored_session.Scaffold()) self.assertAllClose( expected_training_loss, training_loss.eval(), atol=atol) self.assertAllClose( expected_unreduced_loss, unreduced_loss.eval(), atol=atol) self.assertAllClose(expected_weights, actual_weights.eval())
def test_train_create_loss_loss_reduction(self): """Tests head.create_loss with loss_reduction.""" n_classes = 2 head = head_lib.multi_label_head( n_classes, weight_column='example_weights', loss_reduction=losses.Reduction.SUM_BY_NONZERO_WEIGHTS) logits = np.array([[-10., 10.], [-15., 10.]], dtype=np.float32) labels = np.array([[1, 0], [1, 1]], dtype=np.int64) weights = np.array([[1.], [2.]], dtype=np.float32) # loss = labels * -log(sigmoid(logits)) + # (1 - labels) * -log(1 - sigmoid(logits)) # For large logits, this is approximated as: # loss = labels * (logits < 0) * (-logits) + # (1 - labels) * (logits > 0) * logits expected_unreduced_loss = [[(10. + 10.) / 2.], [(15. + 0.) / 2.]] expected_weights = [[1.], [2.]] expected_training_loss = (1. * (10. + 10.) / 2. + 2. * (15. + 0.) / 2.) / 2. training_loss, unreduced_loss, actual_weights, _ = head.create_loss( features={ 'x': np.array(((42,),), dtype=np.int32), 'example_weights': weights }, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels) with self.test_session(): _initialize_variables(self, monitored_session.Scaffold()) self.assertAllClose( expected_training_loss, training_loss.eval(), atol=1e-4) self.assertAllClose( expected_unreduced_loss, unreduced_loss.eval(), atol=1e-4) self.assertAllClose(expected_weights, actual_weights.eval())
def test_eval_with_regularization_losses(self): n_classes = 2 head = head_lib.multi_label_head( n_classes, loss_reduction=losses.Reduction.SUM_OVER_BATCH_SIZE) logits = np.array([[-1., 1.], [-1.5, 1.5]], dtype=np.float32) labels = np.array([[1, 0], [1, 1]], dtype=np.int64) regularization_losses = [1.5, 0.5] expected_regularization_loss = 2. # unregularized_loss = sum( # labels * -log(sigmoid(logits)) + # (1 - labels) * -log(1 - sigmoid(logits))) / batch_size expected_unregularized_loss = np.sum( _sigmoid_cross_entropy(labels=labels, logits=logits)) / 2. expected_regularized_loss = ( expected_unregularized_loss + expected_regularization_loss) keys = metric_keys.MetricKeys expected_metrics = { keys.LOSS_MEAN: expected_unregularized_loss, keys.LOSS_REGULARIZATION: expected_regularization_loss, # auc and auc_pr cannot be reliably calculated for only 4 samples, but # this assert tests that the algorithm remains consistent. keys.AUC: 0.3333, keys.AUC_PR: 0.7639, } self._test_eval( head=head, logits=logits, labels=labels, expected_loss=expected_regularized_loss, expected_metrics=expected_metrics, regularization_losses=regularization_losses)
def test_multi_dim_weighted_eval(self): """Logits and labels of shape [2, 2, 3], weights [2, 2].""" head = head_lib.multi_label_head(n_classes=3, weight_column='weights') logits = np.array([[[-10., 10., -10.], [10., -10., 10.]], [[-12., 12., -12.], [12., -12., 12.]]], dtype=np.float32) labels = np.array([[[1, 0, 0], [1, 0, 0]], [[0, 1, 1], [0, 1, 1]]], dtype=np.int64) weights = np.array([[1., 1.5], [2., 2.5]], dtype=np.float32) # loss = [[10 + 10 + 0, 0 + 0 + 10], [0 + 0 + 12, 12 + 12 + 0]] / 3 # = [[20/3, 10/3], [4, 8]] # weighted_sum_loss = 1*20/3 + 1.5*10/3 + 2*4 + 2.5*8 = 39.6667 expected_loss = 39.6667 keys = metric_keys.MetricKeys expected_metrics = { keys.LOSS_MEAN: expected_loss / np.sum(weights), # auc and auc_pr cannot be reliably calculated for only 4 samples, but # this assert tests that the algorithm remains consistent. keys.AUC: 0.4977, keys.AUC_PR: 0.6645, } self._test_eval( head=head, features={'weights': weights}, logits=logits, labels=labels, expected_loss=expected_loss, expected_metrics=expected_metrics)
def test_predict(self): n_classes = 4 head = head_lib.multi_label_head(n_classes) self.assertEqual(n_classes, head.logits_dimension) logits = np.array( [[0., 1., 2., -1.], [-1., -2., -3., 1.]], dtype=np.float32) expected_probabilities = _sigmoid(logits) spec = head.create_estimator_spec( features={'x': np.array(((42,),), dtype=np.int32)}, mode=model_fn.ModeKeys.PREDICT, logits=logits) self.assertItemsEqual( ('', _DEFAULT_SERVING_KEY), spec.export_outputs.keys()) # Assert predictions and export_outputs. with self.test_session() as sess: _initialize_variables(self, spec.scaffold) self.assertIsNone(spec.scaffold.summary_op) predictions = sess.run(spec.predictions) self.assertAllClose(logits, predictions[prediction_keys.PredictionKeys.LOGITS]) self.assertAllClose( expected_probabilities, predictions[prediction_keys.PredictionKeys.PROBABILITIES]) self.assertAllClose( expected_probabilities, sess.run(spec.export_outputs[_DEFAULT_SERVING_KEY].scores))
def test_eval(self): n_classes = 2 head = head_lib.multi_label_head(n_classes) logits = np.array([[-1., 1.], [-1.5, 1.5]], dtype=np.float32) labels = np.array([[1, 0], [1, 1]], dtype=np.int64) # loss = labels * -log(sigmoid(logits)) + # (1 - labels) * -log(1 - sigmoid(logits)) # Sum over examples, divide by batch_size. expected_loss = 0.5 * np.sum( _sigmoid_cross_entropy(labels=labels, logits=logits)) keys = metric_keys.MetricKeys expected_metrics = { # Average loss over examples. keys.LOSS_MEAN: expected_loss, # auc and auc_pr cannot be reliably calculated for only 4 samples, but # this assert tests that the algorithm remains consistent. keys.AUC: 0.3333, keys.AUC_PR: 0.7639, } self._test_eval( head=head, logits=logits, labels=labels, expected_loss=expected_loss, expected_metrics=expected_metrics)
def test_predict(self): n_classes = 4 head = head_lib.multi_label_head(n_classes) self.assertEqual(n_classes, head.logits_dimension) logits = np.array( [[0., 1., 2., -1.], [-1., -2., -3., 1.]], dtype=np.float32) expected_probabilities = _sigmoid(logits) spec = head.create_estimator_spec( features={'x': np.array(((42,),), dtype=np.int32)}, mode=model_fn.ModeKeys.PREDICT, logits=logits) self.assertItemsEqual( ('', _DEFAULT_SERVING_KEY), spec.export_outputs.keys()) # Assert predictions and export_outputs. with self.test_session() as sess: _initialize_variables(self, spec.scaffold) self.assertIsNone(spec.scaffold.summary_op) predictions = sess.run(spec.predictions) self.assertAllClose(logits, predictions[prediction_keys.PredictionKeys.LOGITS]) self.assertAllClose( expected_probabilities, predictions[prediction_keys.PredictionKeys.PROBABILITIES]) self.assertAllClose( expected_probabilities, sess.run(spec.export_outputs[_DEFAULT_SERVING_KEY].scores))
def test_train_with_optimizer(self): head = head_lib.multi_label_head(n_classes=2) logits = np.array([[-10., 10.], [-15., 10.]], dtype=np.float32) labels = np.array([[1, 0], [1, 1]], dtype=np.int64) # For large logits, sigmoid cross entropy loss is approximated as: # loss = labels * (logits < 0) * (-logits) + # (1 - labels) * (logits > 0) * logits => # expected_unweighted_loss = [[10., 10.], [15., 0.]] # Average over classes, sum over weights. expected_loss = 17.5 expected_train_result = 'my_train_op' class _Optimizer(object): def minimize(self, loss, global_step): del global_step return string_ops.string_join( [constant_op.constant(expected_train_result), string_ops.as_string(loss, precision=3)]) spec = head.create_estimator_spec( features={'x': np.array(((42,),), dtype=np.int32)}, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels, optimizer=_Optimizer()) tol = 1e-3 with self.test_session() as sess: _initialize_variables(self, spec.scaffold) loss, train_result = sess.run((spec.loss, spec.train_op)) self.assertAllClose(expected_loss, loss, rtol=tol, atol=tol) self.assertEqual( six.b('{0:s}{1:.3f}'.format(expected_train_result, expected_loss)), train_result)
def test_eval_create_loss_large_logits(self): """Tests head.create_loss for eval mode and large logits.""" n_classes = 2 head = head_lib.multi_label_head(n_classes) logits = np.array([[-10., 10.], [-15., 10.]], dtype=np.float32) labels = np.array([[1, 0], [1, 1]], dtype=np.int64) # loss = labels * -log(sigmoid(logits)) + # (1 - labels) * -log(1 - sigmoid(logits)) # For large logits, this is approximated as: # loss = labels * (logits < 0) * (-logits) + # (1 - labels) * (logits > 0) * logits expected_weighted_sum_loss = np.sum( np.array([[(10. + 10.) / 2.], [(15. + 0.) / 2.]], dtype=np.float32)) actual_weighted_sum_loss = head.create_loss( features={'x': np.array(((42,),), dtype=np.int32)}, mode=model_fn.ModeKeys.EVAL, logits=logits, labels=labels)[0] with self.test_session(): _initialize_variables(self, monitored_session.Scaffold()) self.assertAllClose( expected_weighted_sum_loss, actual_weighted_sum_loss.eval(), atol=1e-4)
def test_train_with_weights(self): n_classes = 2 head = head_lib.multi_label_head(n_classes, weight_column='label_weights') logits = np.array([[-10., 10.], [-15., 10.]], dtype=np.float32) labels = np.array([[1, 0], [1, 1]], dtype=np.int64) # For large logits, sigmoid cross entropy loss is approximated as: # loss = labels * (logits < 0) * (-logits) + # (1 - labels) * (logits > 0) * logits => # expected_unweighted_loss = [[10., 10.], [15., 0.]] # Average over classes, weighted sum over examples. expected_loss = 25. expected_train_result = 'my_train_op' def _train_op_fn(loss): return string_ops.string_join([ constant_op.constant(expected_train_result), string_ops.as_string(loss, precision=3) ]) spec = head.create_estimator_spec(features={ 'x': np.array([[41], [42]], dtype=np.int32), 'label_weights': np.array([[1.], [2.]], dtype=np.float32), }, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels, train_op_fn=_train_op_fn) self.assertIsNotNone(spec.loss) self.assertEqual({}, spec.eval_metric_ops) self.assertIsNotNone(spec.train_op) self.assertIsNone(spec.export_outputs) _assert_no_hooks(self, spec) # Assert predictions, loss, train_op, and summaries. tol = 1e-3 with self.test_session() as sess: _initialize_variables(self, spec.scaffold) self.assertIsNotNone(spec.scaffold.summary_op) loss, train_result, summary_str = sess.run( (spec.loss, spec.train_op, spec.scaffold.summary_op)) self.assertAllClose(expected_loss, loss, rtol=tol, atol=tol) self.assertEqual( six.b('{0:s}{1:.3f}'.format(expected_train_result, expected_loss)), train_result) _assert_simple_summaries( self, { metric_keys.MetricKeys.LOSS: expected_loss, # Average loss over weighted examples. metric_keys.MetricKeys.LOSS_MEAN: expected_loss / 3, }, summary_str, tol)
def test_eval_create_loss_sparse_labels(self): """Tests head.create_loss for eval mode and sparse labels.""" n_classes = 2 head = head_lib.multi_label_head(n_classes) logits = np.array([[-10., 10.], [-15., 10.]], dtype=np.float32) labels = sparse_tensor.SparseTensor( values=[0, 0, 1], indices=[[0, 0], [1, 0], [1, 1]], dense_shape=[2, 2]) expected_labels = np.array([[1, 0], [1, 1]], dtype=np.int64) # loss = labels * -log(sigmoid(logits)) + # (1 - labels) * -log(1 - sigmoid(logits)) # For large logits, this is approximated as: # loss = labels * (logits < 0) * (-logits) + # (1 - labels) * (logits > 0) * logits expected_unweighted_loss = np.array( [[10., 10.], [15., 0.]], dtype=np.float32) actual_unweighted_loss, actual_labels = head.create_loss( features={'x': np.array(((42,),), dtype=np.int32)}, mode=model_fn.ModeKeys.EVAL, logits=logits, labels=labels) with self.test_session(): _initialize_variables(self, monitored_session.Scaffold()) self.assertAllEqual(expected_labels, actual_labels.eval()) self.assertAllClose( expected_unweighted_loss, actual_unweighted_loss.eval(), atol=1e-4)
def test_multi_dim_weighted_eval(self): """Logits and labels of shape [2, 2, 3], weights [2, 2].""" head = head_lib.multi_label_head(n_classes=3, weight_column='weights') logits = np.array([[[-10., 10., -10.], [10., -10., 10.]], [[-12., 12., -12.], [12., -12., 12.]]], dtype=np.float32) labels = np.array([[[1, 0, 0], [1, 0, 0]], [[0, 1, 1], [0, 1, 1]]], dtype=np.int64) weights = np.array([[1., 1.5], [2., 2.5]], dtype=np.float32) # loss = [[10 + 10 + 0, 0 + 0 + 10], [0 + 0 + 12, 12 + 12 + 0]] / 3 # = [[20/3, 10/3], [4, 8]] # weighted_sum_loss = 1*20/3 + 1.5*10/3 + 2*4 + 2.5*8 = 39.6667 expected_loss = 39.6667 keys = metric_keys.MetricKeys expected_metrics = { keys.LOSS_MEAN: expected_loss / np.sum(weights), # auc and auc_pr cannot be reliably calculated for only 4 samples, but # this assert tests that the algorithm remains consistent. keys.AUC: 0.4977, keys.AUC_PR: 0.6645, } self._test_eval( head=head, features={'weights': weights}, logits=logits, labels=labels, expected_loss=expected_loss, expected_metrics=expected_metrics)
def test_eval_create_loss_large_logits(self): """Tests head.create_loss for eval mode and large logits.""" n_classes = 2 head = head_lib.multi_label_head(n_classes) logits = np.array([[-10., 10.], [-15., 10.]], dtype=np.float32) labels = np.array([[1, 0], [1, 1]], dtype=np.int64) # loss = labels * -log(sigmoid(logits)) + # (1 - labels) * -log(1 - sigmoid(logits)) # For large logits, this is approximated as: # loss = labels * (logits < 0) * (-logits) + # (1 - labels) * (logits > 0) * logits expected_weighted_sum_loss = np.sum( np.array([[(10. + 10.) / 2.], [(15. + 0.) / 2.]], dtype=np.float32)) actual_weighted_sum_loss = head.create_loss( features={'x': np.array(((42, ), ), dtype=np.int32)}, mode=model_fn.ModeKeys.EVAL, logits=logits, labels=labels)[0] with self.test_session(): _initialize_variables(self, monitored_session.Scaffold()) self.assertAllClose(expected_weighted_sum_loss, actual_weighted_sum_loss.eval(), atol=1e-4)
def test_multi_dim_weighted_train_create_loss(self): """Logits and labels of shape [2, 2, 3], weights [2, 2].""" head = head_lib.multi_label_head(n_classes=3, weight_column='weights') logits = np.array([[[-10., 10., -10.], [10., -10., 10.]], [[-12., 12., -12.], [12., -12., 12.]]], dtype=np.float32) labels = np.array([[[1, 0, 0], [1, 0, 0]], [[0, 1, 1], [0, 1, 1]]], dtype=np.int64) weights = np.array([[1., 1.5], [2., 2.5]], dtype=np.float32) # unreduced_loss = # [[10 + 10 + 0, 0 + 0 + 10], [0 + 0 + 12, 12 + 12 + 0]] / 3 # = [[20/3, 10/3], [4, 8]] expected_unreduced_loss = [[[20./3.], [10./3.]], [[4.], [8.]]] # weights are reshaped to [2, 2, 1] to match logits. expected_weights = [[[1.], [1.5]], [[2.], [2.5]]] # weighted_sum_loss = 1*20/3 + 1.5*10/3 + 2*4 + 2.5*8 = 39.6667 expected_training_loss = 39.6667 training_loss, unreduced_loss, actual_weights, _ = head.create_loss( features={'weights': weights}, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels) atol = 1.e-3 with self.test_session(): _initialize_variables(self, monitored_session.Scaffold()) self.assertAllClose( expected_training_loss, training_loss.eval(), atol=atol) self.assertAllClose( expected_unreduced_loss, unreduced_loss.eval(), atol=atol) self.assertAllClose(expected_weights, actual_weights.eval())
def test_eval_create_loss_labels_wrong_shape(self): """Tests head.create_loss for eval mode when labels has the wrong shape.""" n_classes = 2 head = head_lib.multi_label_head(n_classes) logits = np.array([[-1., 1.], [-1.5, 1.]], dtype=np.float32) labels_placeholder = array_ops.placeholder(dtype=dtypes.int64) actual_weighted_sum_loss = head.create_loss( features={'x': np.array(((42, ), ), dtype=np.int32)}, mode=model_fn.ModeKeys.EVAL, logits=logits, labels=labels_placeholder)[0] with self.test_session(): _initialize_variables(self, monitored_session.Scaffold()) with self.assertRaisesRegexp( errors.InvalidArgumentError, r'labels shape must be \[batch_size, 2\]\. Given: \] \[2 1\]' ): actual_weighted_sum_loss.eval( {labels_placeholder: np.array([[1], [1]], dtype=np.int64)}) with self.assertRaisesRegexp( errors.InvalidArgumentError, r'labels shape must be \[batch_size, 2\]\. Given: \] \[2\]' ): actual_weighted_sum_loss.eval( {labels_placeholder: np.array([1, 1], dtype=np.int64)})
def test_multi_dim_weighted_train(self): """Logits and labels of shape [2, 2, 3], weights [2, 2].""" head = head_lib.multi_label_head(n_classes=3, weight_column='weights') logits = np.array([[[-10., 10., -10.], [10., -10., 10.]], [[-12., 12., -12.], [12., -12., 12.]]], dtype=np.float32) labels = np.array([[[1, 0, 0], [1, 0, 0]], [[0, 1, 1], [0, 1, 1]]], dtype=np.int64) weights = np.array([[1., 1.5], [2., 2.5]], dtype=np.float32) # loss = [[10 + 10 + 0, 0 + 0 + 10], [0 + 0 + 12, 12 + 12 + 0]] / 3 # = [[20/3, 10/3], [4, 8]] # weighted_sum_loss = 1*20/3 + 1.5*10/3 + 2*4 + 2.5*8 = 39.6667 expected_loss = 39.6667 expected_train_result = 'my_train_op' def _train_op_fn(loss): return string_ops.string_join( [constant_op.constant(expected_train_result), string_ops.as_string(loss, precision=3)]) spec = head.create_estimator_spec( features={'weights': weights}, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels, train_op_fn=_train_op_fn) atol = 1.e-3 with self.test_session() as sess: _initialize_variables(self, monitored_session.Scaffold()) loss, train_result = sess.run((spec.loss, spec.train_op)) self.assertAllClose(expected_loss, loss, atol=atol) self.assertEqual( six.b('{0:s}{1:.3f}'.format(expected_train_result, expected_loss)), train_result)
def test_eval_with_regularization_losses(self): n_classes = 2 head = head_lib.multi_label_head( n_classes, loss_reduction=losses.Reduction.SUM_OVER_BATCH_SIZE) logits = np.array([[-1., 1.], [-1.5, 1.5]], dtype=np.float32) labels = np.array([[1, 0], [1, 1]], dtype=np.int64) regularization_losses = [1.5, 0.5] expected_regularization_loss = 2. # unregularized_loss = sum( # labels * -log(sigmoid(logits)) + # (1 - labels) * -log(1 - sigmoid(logits))) / batch_size expected_unregularized_loss = np.sum( _sigmoid_cross_entropy(labels=labels, logits=logits)) / 2. expected_regularized_loss = ( expected_unregularized_loss + expected_regularization_loss) keys = metric_keys.MetricKeys expected_metrics = { keys.LOSS_MEAN: expected_unregularized_loss, keys.LOSS_REGULARIZATION: expected_regularization_loss, # auc and auc_pr cannot be reliably calculated for only 4 samples, but # this assert tests that the algorithm remains consistent. keys.AUC: 0.3333, keys.AUC_PR: 0.5972, } self._test_eval( head=head, logits=logits, labels=labels, expected_loss=expected_regularized_loss, expected_metrics=expected_metrics, regularization_losses=regularization_losses)
def test_eval_create_loss_loss_fn(self): """Tests head.create_loss for eval mode and custom loss_fn.""" loss = np.array([[1.], [2.]], dtype=np.float32) logits_input = np.array([[-10., 10.], [-15., 10.]], dtype=np.float32) labels_input = np.array([[1, 0], [1, 1]], dtype=np.int64) def _loss_fn(labels, logits): check_labels = control_flow_ops.Assert(math_ops.reduce_all( math_ops.equal(labels, labels_input)), data=[labels]) check_logits = control_flow_ops.Assert(math_ops.reduce_all( math_ops.equal(logits, logits_input)), data=[logits]) with ops.control_dependencies([check_labels, check_logits]): return constant_op.constant(loss) head = head_lib.multi_label_head(n_classes=2, loss_fn=_loss_fn) actual_weighted_sum_loss = head.create_loss( features={'x': np.array(((42, ), ), dtype=np.int32)}, mode=model_fn.ModeKeys.EVAL, logits=logits_input, labels=labels_input)[0] with self.test_session(): _initialize_variables(self, monitored_session.Scaffold()) self.assertAllClose(np.sum(loss), actual_weighted_sum_loss.eval())
def test_eval(self): n_classes = 2 head = head_lib.multi_label_head(n_classes) logits = np.array([[-1., 1.], [-1.5, 1.5]], dtype=np.float32) labels = np.array([[1, 0], [1, 1]], dtype=np.int64) # loss = labels * -log(sigmoid(logits)) + # (1 - labels) * -log(1 - sigmoid(logits)) # Sum over examples. expected_loss = np.sum( _sigmoid_cross_entropy(labels=labels, logits=logits)) keys = metric_keys.MetricKeys expected_metrics = { # Average loss over examples. keys.LOSS_MEAN: expected_loss / 2, # auc and auc_pr cannot be reliably calculated for only 4 samples, but # this assert tests that the algorithm remains consistent. keys.AUC: 0.3333, keys.AUC_PR: 0.7639, } self._test_eval(head=head, logits=logits, labels=labels, expected_loss=expected_loss, expected_metrics=expected_metrics)
def test_eval_with_label_vocabulary(self): n_classes = 2 head = head_lib.multi_label_head(n_classes, label_vocabulary=['class0', 'class1']) logits = np.array([[-1., 1.], [-1.5, 1.5]], dtype=np.float32) # Equivalent to multi_hot = [[1, 0], [1, 1]] labels = sparse_tensor.SparseTensor( values=['class0', 'class0', 'class1'], indices=[[0, 0], [1, 0], [1, 1]], dense_shape=[2, 2]) labels_multi_hot = np.array([[1, 0], [1, 1]], dtype=np.int64) # loss = labels * -log(sigmoid(logits)) + # (1 - labels) * -log(1 - sigmoid(logits)) # Sum over examples. expected_loss = (np.sum( _sigmoid_cross_entropy(labels=labels_multi_hot, logits=logits))) keys = metric_keys.MetricKeys expected_metrics = { # Average loss over examples. keys.LOSS_MEAN: expected_loss / 2, # auc and auc_pr cannot be reliably calculated for only 4 samples, but # this assert tests that the algorithm remains consistent. keys.AUC: 0.3333, keys.AUC_PR: 0.7639, } self._test_eval(head=head, logits=logits, labels=labels, expected_loss=expected_loss, expected_metrics=expected_metrics)
def test_train_create_loss_large_logits(self): """Tests head.create_loss for train mode and large logits.""" n_classes = 2 head = head_lib.multi_label_head(n_classes, weight_column='label_weights') logits = np.array([[-10., 10.], [-15., 10.]], dtype=np.float32) labels = np.array([[1, 0], [1, 1]], dtype=np.int64) weights = np.array([[1.], [2.]], dtype=np.float32) # loss = labels * -log(sigmoid(logits)) + # (1 - labels) * -log(1 - sigmoid(logits)) # For large logits, this is approximated as: # loss = labels * (logits < 0) * (-logits) + # (1 - labels) * (logits > 0) * logits expected_weighted_sum_loss = np.sum( np.array([[1. * (10. + 10.) / 2.], [2. * (15. + 0.) / 2.]], dtype=np.float32)) expected_example_weight_sum = 1. + 2. actual_weighted_sum_loss, actual_example_weight_sum, _ = head.create_loss( features={ 'x': np.array(((42, ), ), dtype=np.int32), 'label_weights': weights }, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels) with self.test_session(): _initialize_variables(self, monitored_session.Scaffold()) self.assertAllClose(expected_weighted_sum_loss, actual_weighted_sum_loss.eval(), atol=1e-4) self.assertAllClose(expected_example_weight_sum, actual_example_weight_sum.eval(), atol=1e-4)
def test_weight_should_not_impact_prediction(self): n_classes = 4 head = head_lib.multi_label_head(n_classes, weight_column='label_weights') self.assertEqual(n_classes, head.logits_dimension) logits = np.array([[0., 1., 2., -1.], [-1., -2., -3., 1.]], dtype=np.float32) expected_probabilities = _sigmoid(logits) weights_2x1 = [[1.], [2.]] spec = head.create_estimator_spec(features={ 'x': np.array(((42, ), ), dtype=np.int32), 'label_weights': weights_2x1, }, mode=model_fn.ModeKeys.PREDICT, logits=logits) # Assert predictions and export_outputs. with self.test_session() as sess: _initialize_variables(self, spec.scaffold) self.assertIsNone(spec.scaffold.summary_op) predictions = sess.run(spec.predictions) self.assertAllClose( logits, predictions[prediction_keys.PredictionKeys.LOGITS]) self.assertAllClose( expected_probabilities, predictions[prediction_keys.PredictionKeys.PROBABILITIES])
def test_eval_with_label_vocabulary(self): n_classes = 2 head = head_lib.multi_label_head( n_classes, label_vocabulary=['class0', 'class1']) logits = np.array([[-1., 1.], [-1.5, 1.5]], dtype=np.float32) # Equivalent to multi_hot = [[1, 0], [1, 1]] labels = sparse_tensor.SparseTensor( values=['class0', 'class0', 'class1'], indices=[[0, 0], [1, 0], [1, 1]], dense_shape=[2, 2]) labels_multi_hot = np.array([[1, 0], [1, 1]], dtype=np.int64) # loss = labels * -log(sigmoid(logits)) + # (1 - labels) * -log(1 - sigmoid(logits)) # Sum over examples. expected_loss = ( np.sum(_sigmoid_cross_entropy(labels=labels_multi_hot, logits=logits)) ) keys = metric_keys.MetricKeys expected_metrics = { # Average loss over examples. keys.LOSS_MEAN: expected_loss / 2, # auc and auc_pr cannot be reliably calculated for only 4 samples, but # this assert tests that the algorithm remains consistent. keys.AUC: 0.3333, keys.AUC_PR: 0.7639, } self._test_eval( head=head, logits=logits, labels=labels, expected_loss=expected_loss, expected_metrics=expected_metrics)
def test_eval_create_loss_labels_wrong_shape(self): """Tests head.create_loss for eval mode when labels has the wrong shape.""" n_classes = 2 head = head_lib.multi_label_head(n_classes) logits = np.array([[-1., 1.], [-1.5, 1.]], dtype=np.float32) labels_placeholder = array_ops.placeholder(dtype=dtypes.int64) actual_training_loss = head.create_loss( features={'x': np.array(((42,),), dtype=np.int32)}, mode=model_fn.ModeKeys.EVAL, logits=logits, labels=labels_placeholder)[0] with self.test_session(): _initialize_variables(self, monitored_session.Scaffold()) with self.assertRaisesRegexp( errors.InvalidArgumentError, r'\[expected_labels_shape: \] \[2 2\] \[labels_shape: \] \[2 1\]'): actual_training_loss.eval({ labels_placeholder: np.array([[1], [1]], dtype=np.int64) }) with self.assertRaisesRegexp( errors.InvalidArgumentError, r'labels shape must be \[D0, D1, ... DN, 2\]\..*' r'\[Received shape: \] \[2\]'): actual_training_loss.eval({ labels_placeholder: np.array([1, 1], dtype=np.int64) })
def test_train_create_loss_loss_reduction(self): """Tests head.create_loss with loss_reduction.""" n_classes = 2 head = head_lib.multi_label_head( n_classes, weight_column='example_weights', loss_reduction=losses.Reduction.SUM_BY_NONZERO_WEIGHTS) logits = np.array([[-10., 10.], [-15., 10.]], dtype=np.float32) labels = np.array([[1, 0], [1, 1]], dtype=np.int64) weights = np.array([[1.], [2.]], dtype=np.float32) # loss = labels * -log(sigmoid(logits)) + # (1 - labels) * -log(1 - sigmoid(logits)) # For large logits, this is approximated as: # loss = labels * (logits < 0) * (-logits) + # (1 - labels) * (logits > 0) * logits expected_unreduced_loss = [[(10. + 10.) / 2.], [(15. + 0.) / 2.]] expected_weights = [[1.], [2.]] expected_training_loss = (1. * (10. + 10.) / 2. + 2. * (15. + 0.) / 2.) / 2. training_loss, unreduced_loss, actual_weights, _ = head.create_loss( features={ 'x': np.array(((42,),), dtype=np.int32), 'example_weights': weights }, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels) with self.test_session(): _initialize_variables(self, monitored_session.Scaffold()) self.assertAllClose( expected_training_loss, training_loss.eval(), atol=1e-4) self.assertAllClose( expected_unreduced_loss, unreduced_loss.eval(), atol=1e-4) self.assertAllClose(expected_weights, actual_weights.eval())
def test_multi_dim_weights_wrong_outer_dim(self): """Logits and labels of shape [2, 2, 3], weights [2, 2, 3].""" head = head_lib.multi_label_head(n_classes=3, weight_column='weights') logits = np.array([[[-10., 10., -10.], [10., -10., 10.]], [[-12., 12., -12.], [12., -12., 12.]]], dtype=np.float32) labels = np.array([[[1, 0, 0], [1, 0, 0]], [[0, 1, 1], [0, 1, 1]]], dtype=np.int64) weights = np.array([[[1., 1., 1.], [1.5, 1.5, 1.5]], [[2., 2., 2.], [2.5, 2.5, 2.5]]], dtype=np.float32) weights_placeholder = array_ops.placeholder(dtype=dtypes.float32) def _train_op_fn(loss): del loss return control_flow_ops.no_op() spec = head.create_estimator_spec( features={'weights': weights_placeholder}, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels, train_op_fn=_train_op_fn) with self.test_session(): _initialize_variables(self, monitored_session.Scaffold()) with self.assertRaisesRegexp( errors.InvalidArgumentError, r'\[logits_shape: \] \[2 2 3\] \[weights_shape: \] \[2 2 3\]'): spec.loss.eval({weights_placeholder: weights})
def test_weight_should_not_impact_prediction(self): n_classes = 4 head = head_lib.multi_label_head(n_classes, weight_column='example_weights') self.assertEqual(n_classes, head.logits_dimension) logits = np.array( [[0., 1., 2., -1.], [-1., -2., -3., 1.]], dtype=np.float32) expected_probabilities = _sigmoid(logits) weights_2x1 = [[1.], [2.]] spec = head.create_estimator_spec( features={ 'x': np.array(((42,),), dtype=np.int32), 'example_weights': weights_2x1, }, mode=model_fn.ModeKeys.PREDICT, logits=logits) # Assert predictions and export_outputs. with self.test_session() as sess: _initialize_variables(self, spec.scaffold) self.assertIsNone(spec.scaffold.summary_op) predictions = sess.run(spec.predictions) self.assertAllClose(logits, predictions[prediction_keys.PredictionKeys.LOGITS]) self.assertAllClose( expected_probabilities, predictions[prediction_keys.PredictionKeys.PROBABILITIES])
def test_train_create_loss_logits_tensor(self): """Tests create_loss with logits Tensor.""" weights1 = np.array([[1.], [2.]], dtype=np.float32) weights2 = np.array([[2.], [3.]]) head1 = head_lib.multi_label_head(n_classes=2, name='head1', weight_column='weights1') head2 = head_lib.multi_label_head(n_classes=3, name='head2', weight_column='weights2') multi_head = multi_head_lib.multi_head( [head1, head2], head_weights=[1., 2.]) logits = np.array([[-10., 10., 20., -20., 20.], [-15., 10., -30., 20., -20.]], dtype=np.float32) labels = { 'head1': np.array([[1, 0], [1, 1]], dtype=np.int64), 'head2': np.array([[0, 1, 0], [1, 1, 0]], dtype=np.int64), } training_loss, unreduced_losses, weights, _ = multi_head.create_loss( features={ 'x': np.array(((42,),), dtype=np.int32), 'weights1': weights1, 'weights2': weights2 }, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels) tol = 1e-3 with self.test_session(): # loss of the first head is [[(10 + 10) / 2], [(15 + 0) / 2]] # = [10, 7.5] # training_loss = 1 * 10 + 2 * 7.5 = 25 # head-weighted unreduced_loss = 1 * [10, 7.5] self.assertAllClose( [[10.], [7.5]], unreduced_losses['head1'].eval(), rtol=tol, atol=tol) # loss of the second head is [[(20 + 20 + 20) / 3], [(30 + 0 + 0) / 3]] # = [20, 10] # training_loss = 2 * 20 + 3 * 10 = 70 # head-weighted unreduced_loss = 2 * [20, 10] self.assertAllClose( [[40.], [20.]], unreduced_losses['head2'].eval(), rtol=tol, atol=tol) # head-weighted training_loss = 1 * 25 + 2 * 70 = 165 self.assertAllClose(165, training_loss.eval(), rtol=tol, atol=tol) # head-weighted example weights self.assertAllClose( [[1.], [2.]], weights['head1'].eval(), rtol=tol, atol=tol) self.assertAllClose( [[4.], [6.]], weights['head2'].eval(), rtol=tol, atol=tol)
def test_eval_with_weights(self): n_classes = 2 head = head_lib.multi_label_head(n_classes, weight_column='example_weights') logits = np.array([[-10., 10.], [-15., 10.]], dtype=np.float32) labels = np.array([[1, 0], [1, 1]], dtype=np.int64) # For large logits, sigmoid cross entropy loss is approximated as: # loss = labels * (logits < 0) * (-logits) + # (1 - labels) * (logits > 0) * logits => # expected_unweighted_loss = [[10., 10.], [15., 0.]] # Average over classes, weighted sum over examples, divide by batch_size. # loss = ( 1 * (10 + 10) / 2 + 2 * (15 + 0) / 2) / 2 expected_loss = 12.5 spec = head.create_estimator_spec( features={ 'x': np.array([[41], [42]], dtype=np.int32), 'example_weights': np.array([[1.], [2.]], dtype=np.float32), }, mode=model_fn.ModeKeys.EVAL, logits=logits, labels=labels) keys = metric_keys.MetricKeys expected_metrics = { # Average loss over weighted examples (denominator is sum(weights)). keys.LOSS_MEAN: expected_loss * (2. / 3.), # auc and auc_pr cannot be reliably calculated for only 4 samples, but # this assert tests that the algorithm remains consistent. keys.AUC: 0.2000, keys.AUC_PR: 0.7833, } # Assert spec contains expected tensors. self.assertIsNotNone(spec.loss) self.assertItemsEqual(expected_metrics.keys(), spec.eval_metric_ops.keys()) self.assertIsNone(spec.train_op) self.assertIsNone(spec.export_outputs) _assert_no_hooks(self, spec) # Assert predictions, loss, and metrics. tol = 1e-3 with self.test_session() as sess: _initialize_variables(self, spec.scaffold) self.assertIsNone(spec.scaffold.summary_op) value_ops = {k: spec.eval_metric_ops[k][0] for k in spec.eval_metric_ops} update_ops = {k: spec.eval_metric_ops[k][1] for k in spec.eval_metric_ops} loss, metrics = sess.run((spec.loss, update_ops)) self.assertAllClose(expected_loss, loss, rtol=tol, atol=tol) # Check results of both update (in `metrics`) and value ops. self.assertAllClose(expected_metrics, metrics, rtol=tol, atol=tol) self.assertAllClose( expected_metrics, {k: value_ops[k].eval() for k in value_ops}, rtol=tol, atol=tol)
def test_eval_labels_none(self): """Tests that error is raised when labels is None.""" head = head_lib.multi_label_head(n_classes=2) with self.assertRaisesRegexp( ValueError, r'You must provide a labels Tensor\. Given: None\.'): head.create_estimator_spec( features={'x': np.array(((42,),), dtype=np.int32)}, mode=model_fn.ModeKeys.EVAL, logits=np.array([[-10., 10.], [-15., 10.]], dtype=np.float32), labels=None)
def test_eval_labels_none(self): """Tests that error is raised when labels is None.""" head = head_lib.multi_label_head(n_classes=2) with self.assertRaisesRegexp( ValueError, r'You must provide a labels Tensor\. Given: None\.'): head.create_estimator_spec( features={'x': np.array(((42,),), dtype=np.int32)}, mode=model_fn.ModeKeys.EVAL, logits=np.array([[-10., 10.], [-15., 10.]], dtype=np.float32), labels=None)
def test_eval_with_weights(self): n_classes = 2 head = head_lib.multi_label_head(n_classes, weight_column='label_weights') logits = np.array([[-10., 10.], [-15., 10.]], dtype=np.float32) labels = np.array([[1, 0], [1, 1]], dtype=np.int64) # For large logits, sigmoid cross entropy loss is approximated as: # loss = labels * (logits < 0) * (-logits) + # (1 - labels) * (logits > 0) * logits => # expected_unweighted_loss = [[10., 10.], [15., 0.]] # Average over classes, weighted sum over examples. expected_loss = 25. spec = head.create_estimator_spec( features={ 'x': np.array([[41], [42]], dtype=np.int32), 'label_weights': np.array([[1.], [2.]], dtype=np.float32), }, mode=model_fn.ModeKeys.EVAL, logits=logits, labels=labels) keys = metric_keys.MetricKeys expected_metrics = { # Average loss over weighted examples. keys.LOSS_MEAN: expected_loss / 3, # auc and auc_pr cannot be reliably calculated for only 4 samples, but # this assert tests that the algorithm remains consistent. keys.AUC: 0.2000, keys.AUC_PR: 0.7833, } # Assert spec contains expected tensors. self.assertIsNotNone(spec.loss) self.assertItemsEqual(expected_metrics.keys(), spec.eval_metric_ops.keys()) self.assertIsNone(spec.train_op) self.assertIsNone(spec.export_outputs) _assert_no_hooks(self, spec) # Assert predictions, loss, and metrics. tol = 1e-3 with self.test_session() as sess: _initialize_variables(self, spec.scaffold) self.assertIsNone(spec.scaffold.summary_op) value_ops = {k: spec.eval_metric_ops[k][0] for k in spec.eval_metric_ops} update_ops = {k: spec.eval_metric_ops[k][1] for k in spec.eval_metric_ops} loss, metrics = sess.run((spec.loss, update_ops)) self.assertAllClose(expected_loss, loss, rtol=tol, atol=tol) # Check results of both update (in `metrics`) and value ops. self.assertAllClose(expected_metrics, metrics, rtol=tol, atol=tol) self.assertAllClose( expected_metrics, {k: value_ops[k].eval() for k in value_ops}, rtol=tol, atol=tol)
def test_train_create_loss_two_heads_with_weights(self): # Use different example weighting for each head weighting. weights1 = np.array([[1.], [2.]], dtype=np.float32) weights2 = np.array([[2.], [3.]]) head1 = head_lib.multi_label_head(n_classes=2, name='head1', weight_column='weights1') head2 = head_lib.multi_label_head(n_classes=3, name='head2', weight_column='weights2') multi_head = multi_head_lib.multi_head( [head1, head2], head_weights=[1., 2.]) logits = { 'head1': np.array([[-10., 10.], [-15., 10.]], dtype=np.float32), 'head2': np.array([[20., -20., 20.], [-30., 20., -20.]], dtype=np.float32), } labels = { 'head1': np.array([[1, 0], [1, 1]], dtype=np.int64), 'head2': np.array([[0, 1, 0], [1, 1, 0]], dtype=np.int64), } weighted_sum_loss, example_weight_sum, _ = multi_head.create_loss( features={ 'x': np.array(((42,),), dtype=np.int32), 'weights1': weights1, 'weights2': weights2 }, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels) tol = 1e-3 with self.test_session(): # loss of the first head is [[(10 + 10) / 2], [(15 + 0) / 2]] # = [10, 7.5] # weighted_sum_loss = 1 * 10 + 2 * 7.5 = 25 # loss of the second head is [[(20 + 20 + 20) / 3], [(30 + 0 + 0) / 3]] # = [20, 10] # weighted_sum_loss = 2 * 20 + 3 * 10 = 70 # head-weighted merge = 1 * 25 + 2 * 70 = 165 self.assertAllClose(165, weighted_sum_loss.eval(), rtol=tol, atol=tol) # example_weight_sum = 1 * (1 + 2) + 2 * (2 + 3) = 13 self.assertAllClose(13., example_weight_sum.eval(), rtol=tol, atol=tol)
def test_train(self): head = head_lib.multi_label_head(n_classes=2) logits = np.array([[-10., 10.], [-15., 10.]], dtype=np.float32) labels = np.array([[1, 0], [1, 1]], dtype=np.int64) # For large logits, sigmoid cross entropy loss is approximated as: # loss = labels * (logits < 0) * (-logits) + # (1 - labels) * (logits > 0) * logits => # expected_unweighted_loss = [[10., 10.], [15., 0.]] # Average over classes, sum over weights. expected_loss = 17.5 self._test_train( head=head, logits=logits, labels=labels, expected_loss=expected_loss)
def test_train(self): head = head_lib.multi_label_head(n_classes=2) logits = np.array([[-10., 10.], [-15., 10.]], dtype=np.float32) labels = np.array([[1, 0], [1, 1]], dtype=np.int64) # For large logits, sigmoid cross entropy loss is approximated as: # loss = labels * (logits < 0) * (-logits) + # (1 - labels) * (logits > 0) * logits => # expected_unweighted_loss = [[10., 10.], [15., 0.]] # Average over classes, sum over weights. expected_loss = 17.5 self._test_train( head=head, logits=logits, labels=labels, expected_loss=expected_loss)
def test_eval(self): n_classes = 2 head = head_lib.multi_label_head(n_classes) logits = np.array([[-1., 1.], [-1.5, 1.5]], dtype=np.float32) labels = np.array([[1, 0], [1, 1]], dtype=np.int64) # loss = labels * -log(sigmoid(logits)) + # (1 - labels) * -log(1 - sigmoid(logits)) # Average over classes, and sum over examples. expected_loss = ( np.sum(_sigmoid_cross_entropy(labels=labels, logits=logits)) / n_classes ) spec = head.create_estimator_spec( features={'x': np.array(((42,),), dtype=np.int32)}, mode=model_fn.ModeKeys.EVAL, logits=logits, labels=labels) keys = metric_keys.MetricKeys expected_metrics = { # Average loss over examples. keys.LOSS_MEAN: expected_loss / 2, # auc and auc_pr cannot be reliably calculated for only 4 samples, but # this assert tests that the algorithm remains consistent. keys.AUC: 0.3333, keys.AUC_PR: 0.7639, } # Assert spec contains expected tensors. self.assertIsNotNone(spec.loss) self.assertItemsEqual(expected_metrics.keys(), spec.eval_metric_ops.keys()) self.assertIsNone(spec.train_op) self.assertIsNone(spec.export_outputs) _assert_no_hooks(self, spec) # Assert predictions, loss, and metrics. tol = 1e-3 with self.test_session() as sess: _initialize_variables(self, spec.scaffold) self.assertIsNone(spec.scaffold.summary_op) value_ops = {k: spec.eval_metric_ops[k][0] for k in spec.eval_metric_ops} update_ops = {k: spec.eval_metric_ops[k][1] for k in spec.eval_metric_ops} loss, metrics = sess.run((spec.loss, update_ops)) self.assertAllClose(expected_loss, loss, rtol=tol, atol=tol) # Check results of both update (in `metrics`) and value ops. self.assertAllClose(expected_metrics, metrics, rtol=tol, atol=tol) self.assertAllClose( expected_metrics, {k: value_ops[k].eval() for k in value_ops}, rtol=tol, atol=tol)
def test_train_one_head(self): head1 = head_lib.multi_label_head(n_classes=2, name='head1') multi_head = multi_head_lib.multi_head([head1]) logits = { 'head1': np.array([[-10., 10.], [-15., 10.]], dtype=np.float32) } labels = {'head1': np.array([[1, 0], [1, 1]], dtype=np.int64)} # For large logits, sigmoid cross entropy loss is approximated as: # loss = labels * (logits < 0) * (-logits) + # (1 - labels) * (logits > 0) * logits => # expected_unweighted_loss = [[10., 10.], [15., 0.]] # loss = ( (10 + 10) / 2 + (15 + 0) / 2 ) / 2 = 8.75 expected_loss = 8.75 expected_train_result = 'my_train_op' def _train_op_fn(loss): return string_ops.string_join([ constant_op.constant(expected_train_result), string_ops.as_string(loss, precision=3) ]) spec = multi_head.create_estimator_spec( features={'x': np.array(((42, ), ), dtype=np.int32)}, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels, train_op_fn=_train_op_fn) self.assertIsNotNone(spec.loss) self.assertEqual({}, spec.eval_metric_ops) self.assertIsNotNone(spec.train_op) self.assertIsNone(spec.export_outputs) _assert_no_hooks(self, spec) # Assert predictions, loss, train_op, and summaries. tol = 1e-3 with self.cached_session() as sess: _initialize_variables(self, spec.scaffold) self.assertIsNotNone(spec.scaffold.summary_op) loss, train_result, summary_str = sess.run( (spec.loss, spec.train_op, spec.scaffold.summary_op)) self.assertAllClose(expected_loss, loss, rtol=tol, atol=tol) self.assertEqual( six.b('{0:s}{1:.3f}'.format(expected_train_result, expected_loss)), train_result) _assert_simple_summaries( self, { metric_keys.MetricKeys.LOSS: expected_loss, metric_keys.MetricKeys.LOSS + '/head1': expected_loss, }, summary_str, tol)
def test_train_create_loss_one_head(self): head1 = head_lib.multi_label_head(n_classes=2, name='head1') multi_head = multi_head_lib.multi_head([head1]) logits = {'head1': np.array([[-10., 10.], [-15., 10.]], dtype=np.float32)} labels = {'head1': np.array([[1, 0], [1, 1]], dtype=np.int64)} with self.assertRaisesRegexp( NotImplementedError, r'create_loss not yet implemented for MultiHead\.'): multi_head.create_loss( features={'x': np.array(((42,),), dtype=np.int32)}, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels)
def test_train_create_loss_two_heads_with_weights(self): head1 = head_lib.multi_label_head(n_classes=2, name='head1') head2 = head_lib.multi_label_head(n_classes=3, name='head2') multi_head = multi_head_lib.multi_head( [head1, head2], head_weights=[1., 2.]) logits = { 'head1': np.array([[-10., 10.], [-15., 10.]], dtype=np.float32), 'head2': np.array([[20., -20., 20.], [-30., 20., -20.]], dtype=np.float32), } labels = { 'head1': np.array([[1, 0], [1, 1]], dtype=np.int64), 'head2': np.array([[0, 1, 0], [1, 1, 0]], dtype=np.int64), } with self.assertRaisesRegexp( NotImplementedError, r'create_loss not yet implemented for MultiHead\.'): multi_head.create_loss( features={'x': np.array(((42,),), dtype=np.int32)}, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels)
def test_eval_with_thresholds(self): n_classes = 2 thresholds = [0.25, 0.5, 0.75] head = head_lib.multi_label_head(n_classes, thresholds=thresholds) logits = np.array([[-1., 1.], [-1.5, 1.5]], dtype=np.float32) labels = np.array([[1, 0], [1, 1]], dtype=np.int64) # loss = labels * -log(sigmoid(logits)) + # (1 - labels) * -log(1 - sigmoid(logits)) # Average over classes, and sum over examples. expected_loss = ( np.sum(_sigmoid_cross_entropy(labels=labels, logits=logits)) / n_classes) keys = metric_keys.MetricKeys expected_metrics = { # Average loss over examples. keys.LOSS_MEAN: expected_loss / 2, # auc and auc_pr cannot be reliably calculated for only 4 samples, but # this assert tests that the algorithm remains consistent. keys.AUC: 0.3333, keys.AUC_PR: 0.7639, keys.ACCURACY_AT_THRESHOLD % thresholds[0]: 2. / 4., keys.PRECISION_AT_THRESHOLD % thresholds[0]: 2. / 3., keys.RECALL_AT_THRESHOLD % thresholds[0]: 2. / 3., keys.ACCURACY_AT_THRESHOLD % thresholds[1]: 1. / 4., keys.PRECISION_AT_THRESHOLD % thresholds[1]: 1. / 2., keys.RECALL_AT_THRESHOLD % thresholds[1]: 1. / 3., keys.ACCURACY_AT_THRESHOLD % thresholds[2]: 2. / 4., keys.PRECISION_AT_THRESHOLD % thresholds[2]: 1. / 1., keys.RECALL_AT_THRESHOLD % thresholds[2]: 1. / 3., } self._test_eval(head=head, logits=logits, labels=labels, expected_loss=expected_loss, expected_metrics=expected_metrics)
def test_train_with_weights(self): n_classes = 2 head = head_lib.multi_label_head(n_classes, weight_column='example_weights') logits = np.array([[-10., 10.], [-15., 10.]], dtype=np.float32) labels = np.array([[1, 0], [1, 1]], dtype=np.int64) # For large logits, sigmoid cross entropy loss is approximated as: # loss = labels * (logits < 0) * (-logits) + # (1 - labels) * (logits > 0) * logits => # expected_unweighted_loss = [[10., 10.], [15., 0.]] # Average over classes, weighted sum over examples. expected_loss = 25. expected_train_result = 'my_train_op' def _train_op_fn(loss): return string_ops.string_join( [constant_op.constant(expected_train_result), string_ops.as_string(loss, precision=3)]) spec = head.create_estimator_spec( features={ 'x': np.array([[41], [42]], dtype=np.int32), 'example_weights': np.array([[1.], [2.]], dtype=np.float32), }, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels, train_op_fn=_train_op_fn) self.assertIsNotNone(spec.loss) self.assertEqual({}, spec.eval_metric_ops) self.assertIsNotNone(spec.train_op) self.assertIsNone(spec.export_outputs) _assert_no_hooks(self, spec) # Assert predictions, loss, train_op, and summaries. tol = 1e-3 with self.test_session() as sess: _initialize_variables(self, spec.scaffold) self.assertIsNotNone(spec.scaffold.summary_op) loss, train_result, summary_str = sess.run((spec.loss, spec.train_op, spec.scaffold.summary_op)) self.assertAllClose(expected_loss, loss, rtol=tol, atol=tol) self.assertEqual( six.b('{0:s}{1:.3f}'.format(expected_train_result, expected_loss)), train_result) _assert_simple_summaries(self, { metric_keys.MetricKeys.LOSS: expected_loss, # Average loss over weighted examples. metric_keys.MetricKeys.LOSS_MEAN: expected_loss / 3, }, summary_str, tol)
def test_train_create_loss_two_heads_with_weights(self): head1 = head_lib.multi_label_head(n_classes=2, name='head1') head2 = head_lib.multi_label_head(n_classes=3, name='head2') multi_head = multi_head_lib.multi_head([head1, head2], head_weights=[1., 2.]) logits = { 'head1': np.array([[-10., 10.], [-15., 10.]], dtype=np.float32), 'head2': np.array([[20., -20., 20.], [-30., 20., -20.]], dtype=np.float32), } labels = { 'head1': np.array([[1, 0], [1, 1]], dtype=np.int64), 'head2': np.array([[0, 1, 0], [1, 1, 0]], dtype=np.int64), } with self.assertRaisesRegexp( NotImplementedError, r'create_loss not yet implemented for MultiHead\.'): multi_head.create_loss( features={'x': np.array(((42, ), ), dtype=np.int32)}, mode=model_fn.ModeKeys.TRAIN, logits=logits, labels=labels)
def test_eval_tpu(self): head1 = head_lib.multi_label_head(n_classes=2, name='head1') head2 = head_lib.multi_label_head(n_classes=3, name='head2') multi_head = multi_head_lib.multi_head( [head1, head2], head_weights=[1., 2.]) logits = { 'head1': np.array([[-10., 10.], [-15., 10.]], dtype=np.float32), 'head2': np.array([[20., -20., 20.], [-30., 20., -20.]], dtype=np.float32), } labels = { 'head1': np.array([[1, 0], [1, 1]], dtype=np.int64), 'head2': np.array([[0, 1, 0], [1, 1, 0]], dtype=np.int64), } with self.assertRaisesRegexp( NotImplementedError, r'TPU evaluation is not implemented for multi_head\.'): multi_head._create_tpu_estimator_spec( features={'x': np.array(((42,),), dtype=np.int32)}, mode=model_fn.ModeKeys.EVAL, logits=logits, labels=labels)
def test_train_labels_none(self): """Tests that error is raised when labels is None.""" head = head_lib.multi_label_head(n_classes=2) def _no_op_train_fn(loss): del loss return control_flow_ops.no_op() with self.assertRaisesRegexp( ValueError, r'You must provide a labels Tensor\. Given: None\.'): head.create_estimator_spec( features={'x': np.array(((42,),), dtype=np.int32)}, mode=model_fn.ModeKeys.TRAIN, logits=np.array([[-10., 10.], [-15., 10.]], dtype=np.float32), labels=None, train_op_fn=_no_op_train_fn)
def test_train_labels_none(self): """Tests that error is raised when labels is None.""" head = head_lib.multi_label_head(n_classes=2) def _no_op_train_fn(loss): del loss return control_flow_ops.no_op() with self.assertRaisesRegexp( ValueError, r'You must provide a labels Tensor\. Given: None\.'): head.create_estimator_spec( features={'x': np.array(((42,),), dtype=np.int32)}, mode=model_fn.ModeKeys.TRAIN, logits=np.array([[-10., 10.], [-15., 10.]], dtype=np.float32), labels=None, train_op_fn=_no_op_train_fn)