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)
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
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
def result(self): """Give the Rk coefficient""" return core.rk_coeff_tf(self.table)
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)
def test_ones(self): ones = tf.constant(np.ones((4, 4))) result = catcorr.rk_coeff_tf(ones) self.assertEqual(result, 0)
def test_zero(self): zeros = tf.constant(np.zeros((4, 4))) result = catcorr.rk_coeff_tf(zeros) self.assertEqual(result, 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)
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)
def test_large_table(self): large_table = tf.constant(np.ones((500, 500))) result = catcorr.rk_coeff_tf(large_table) self.assertEqual(result, 0)