Example #1
0
 def test_inv_diagonal(self):
     precalculated_result = -0.333
     confusion_matrix = np.full((4,4), 5)
     np.fill_diagonal(confusion_matrix, 0)
     result = catcorr.rk_coeff_tf(tf.constant(confusion_matrix))
     difference = abs(precalculated_result - result)
     self.assertLess(difference, 0.001)
Example #2
0
def rk_loss_tf(labels, predictions, name=None):
    """Computes 1-RK as a loss function between prediction probabilities
    and labels.

    Prediction probabilities are accumulated in the confusion matrix rows.

    Let N be batch size and L be the number of class labels.

    Parameters:
      labels      : 1-D (rank 1) tensor (length N) of ground-truth labels
                      (indices) in {0,...,L-1} or 2-D (rank 2) NxL tensor
                      of one-hot encoded labels 
      predictions : 2-D (rank 2) tensor (size NxL) probabilities for each
                      class in [0,1]

    Returns:
      loss : Scalar tensor in [0,2]
    """

    C = core.soft_confusion_matrix_tf(labels, predictions)
    RK = core.rk_coeff_tf(C)
    loss = 1 - RK
    loss = tf.cast(loss, tf.float32)  # Must cast for autodiff on GPU

    return loss
Example #3
0
def rk_coeff(labels, predictions, num_classes,
             streaming=True,
             name=None):
    """Tensorflow metric calculating Gorodkin's Rk correlation coefficient.

    The `rk_coeff` function creates a local variable, `table` to store
    the confusion matrix used to compute the Rk coefficient. That value
    is ultimately returned as `coeff`: an idempotent operation.

    For estimation of the metric over a stream of data (indicated by
    `streaming`), the function creates an `update_op` operation that
    updates these variables and returns the `coeff`. The `update_op`
    accumulates values in the confusion matrix.

    Let N be batch size and L be the number of class labels.

    Parameters:
      labels      : 1-D (rank 1) tensor (length N) of ground-truth labels
                      (indices) in {0,...,L-1} or 2-D (rank 2) NxL tensor
                      of one-hot encoded labels 
      predictions : 2-D (rank 2) tensor (size NxL) probabilities for each
                      class in [0,1]
      num_classes : A scalar integer value (or tensor) indicating the number
                      of class labels (length of one side of the confusion
                       matrix table)
    Returns:
      coefficient : A `Tensor` representing the Rk coefficient of the `table`.
      update_op :   An operation that increments the `table` variable
                      appropriately and whose value matches `coefficient`.

    """

    #var_collections = [ tf.compat.v1.GraphKeys.LOCAL_VARIABLES,
    #                    tf.compat.v1.GraphKeys.METRIC_VARIABLES ]
    
    batch_table = core.soft_confusion_matrix_tf(
        labels, predictions, name='rk_coeff/batch_table' )

    # After futzing for hours, I could not get a more elegant solution
    # inferring the dimensions of batch_table to work with v1-oriented
    # TF code, (it is a variable initialization issue) which
    # necessitates the num_classes argument to hard code the
    # dimensions

    # This line in and of itself works, but fails when passed to tf.Variable
    #zero_table = tf.zeros_like(batch_table)
    # So we fall back to the following:
    zero_table = tf.zeros([num_classes,num_classes],dtype=batch_table.dtype)

    #table = tf.compat.v1.Variable( zero_table, dtype=batch_table.dtype),
    #                               trainable=False,
    #                               name='rk_coeff/table',
    #                               collections=var_collections,
    #                               aggregation=tf.VariableAggregation.SUM )
    table = tf.Variable( zero_table,
                         trainable=False,
                         validate_shape=True,
                         name='rk_coeff/table',
                         dtype=batch_table.dtype,
                         aggregation=tf.VariableAggregation.SUM )

    if streaming: # Accumulate confusion matrix
        update_table_op = tf.compat.v1.assign_add(table, batch_table) # +=
    else: # Reset confusion matrix
        update_table_op = tf.compat.v1.assign(table, batch_table)     # := 

    coeff = tf.identity( core.rk_coeff_tf(table), name='rk_coeff/value')
    update_op = tf.identity( core.rk_coeff_tf(update_table_op),
                             name='rk_coeff/update_op')
    return coeff, update_op
Example #4
0
 def result(self):
     """Give the Rk coefficient"""
     return core.rk_coeff_tf(self.table)
Example #5
0
 def test_arange(self):
     precalculated_result = -0.07007127538
     arange = tf.constant(np.arange(9, dtype=np.float).reshape((3,3)))
     result = catcorr.rk_coeff_tf(arange)
     difference = abs(precalculated_result - result)
     self.assertLess(difference, 1e-08)
Example #6
0
 def test_ones(self):
     ones = tf.constant(np.ones((4, 4)))
     result = catcorr.rk_coeff_tf(ones)
     self.assertEqual(result, 0)
Example #7
0
 def test_zero(self):
     zeros = tf.constant(np.zeros((4, 4)))
     result = catcorr.rk_coeff_tf(zeros)
     self.assertEqual(result, 0)
Example #8
0
 def test_col(self):
     confusion_matrix = np.zeros((4,4))
     confusion_matrix[:,1] = [15,15,15,15]
     result = catcorr.rk_coeff_tf(tf.constant(confusion_matrix))
     self.assertEqual(result, 0)
Example #9
0
 def test_arange_2(self):
     precalculated_result = -0.031676277618
     arange = tf.constant(np.arange(16, dtype=np.float).reshape((4,4)))
     result = catcorr.rk_coeff_tf(arange)
     difference = abs(precalculated_result - result)
     self.assertLess(difference, 1e-08)
Example #10
0
 def test_large_table(self):
     large_table = tf.constant(np.ones((500, 500)))
     result = catcorr.rk_coeff_tf(large_table)
     self.assertEqual(result, 0)