From d1ef2423e706f04197d83e7ac091ffadf046e590 Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Fri, 13 Dec 2019 08:54:52 +0800 Subject: [PATCH 01/27] add weighted kappa loss --- .../losses/weighted_kappa_loss.py | 129 ++++++++++++++++++ .../losses/weighted_kappa_loss_test.py | 0 2 files changed, 129 insertions(+) create mode 100644 tensorflow_addons/losses/weighted_kappa_loss.py create mode 100644 tensorflow_addons/losses/weighted_kappa_loss_test.py diff --git a/tensorflow_addons/losses/weighted_kappa_loss.py b/tensorflow_addons/losses/weighted_kappa_loss.py new file mode 100644 index 0000000000..8ec4c6238d --- /dev/null +++ b/tensorflow_addons/losses/weighted_kappa_loss.py @@ -0,0 +1,129 @@ +# Copyright 2019 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Implements Weighted kappa loss.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import tensorflow as tf + +@tf.function +def _weighted_kappa_loss(y_true, y_pred, row_label_vec, col_label_vec, + weight_mat, eps=1e-6, weightage='quadratic', + dtype=tf.float32): + labels = tf.matmul(y_true, col_label_vec) + if weightage == 'linear': + weight = tf.abs(tf.tile(labels, [1, tf.shape(y_true)[1]]) - tf.tile(row_label_vec, [tf.shape(y_true)[0], 1])) + weight /= tf.cast(tf.shape(y_true)[1] - 1, dtype=dtype) + else: + weight = tf.pow(tf.tile(labels, [1, tf.shape(y_true)[1]]) - tf.tile(row_label_vec, [tf.shape(y_true)[0], 1]), 2) + weight /= tf.cast(tf.pow(tf.shape(y_true)[1] - 1, 2), dtype=dtype) + numerator = tf.reduce_sum(weight * y_pred) + + denominator = tf.reduce_sum( + tf.matmul( + tf.reduce_sum(y_true, axis=0, keepdims=True), + tf.matmul(weight_mat, tf.transpose(tf.reduce_sum(y_pred, axis=0, keepdims=True))) + ) + ) + denominator /= tf.cast(tf.shape(y_true)[0], dtype=dtype) + return tf.math.log(numerator / denominator + eps) + + +class WeightedKappaLoss(tf.keras.losses.Loss): + """Implements the Weighted Kappa loss function. + This Weighted Kappa loss was introduced in the + [Weighted kappa loss function for multi-class classification + of ordinal data in deep learning] + (https://www.sciencedirect.com/science/article/abs/pii/S0167865517301666). + Weighted Kappa is widely used in Ordinal Classification Problems + The score lies in [-∞, log2], where log2 means the random prediction + Usage: + ```python + + qwk = tfa.losses.WeightedKappa(num_classes=4) + y_true = tf.constant([[4.0, 3.0, 7.0, 5.0], [5.0, 6.0, 10.0, 7.0]]) + y_pred = tf.constant([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0]]) + loss = qwk(y_true, y_pred) + print('Loss: ', loss.numpy()) # Loss: [1.07500000298023224] + ``` + + Usage with tf.keras API: + ```python + # outputs should be softmax results + # if you want to weight the samples, just multiply the outputs by the sample weight. + model = tf.keras.Model(inputs, outputs) + model.compile('sgd', loss=tfa.losses.WeightedKappa(num_classes=4)) + ``` + """ + + def __init__(self, + num_classes, + weightage='quadratic', + name='cohen_kappa_loss', + eps=1e-6, + dtype=tf.float32): + """Creates a `WeightedKappa` instance. + Args: + num_classes: Number of unique classes in your dataset. + name: (Optional) String name of the metric instance. + weightage: (Optional) Weighting to be considered for calculating + kappa statistics. A valid value is one of + ['linear', 'quadratic']. Defaults to `quadratic` since it's mostly used. + dtype: (Optional) Data type of the metric result. + Defaults to `tf.float32`. + Raises: + ValueError: If the value passed for `weightage` is invalid + i.e. not any one of ['linear', 'quadratic'] + """ + + super(WeightedKappaLoss, self).__init__(name=name, reduction=tf.keras.losses.Reduction.NONE) + + if weightage not in ('linear', 'quadratic'): + raise ValueError("Unknown kappa weighting type.") + + self.weightage = weightage + self.num_classes = num_classes + self.eps = eps + self.dtype = dtype + label_vec = tf.range(num_classes, dtype=dtype) + self.row_label_vec = tf.reshape(label_vec, [1, num_classes]) + self.col_label_vec = tf.reshape(label_vec, [num_classes, 1]) + if weightage == 'linear': + self.weight_mat = tf.abs( + tf.tile(self.col_label_vec, [1, num_classes]) - tf.tile(self.row_label_vec, [num_classes, 1]), + ) / tf.cast(num_classes - 1, dtype=dtype) + else: + self.weight_mat = tf.pow( + tf.tile(self.col_label_vec, [1, num_classes]) - tf.tile(self.row_label_vec, [num_classes, 1]), + 2) / tf.cast(tf.pow(num_classes - 1, 2), dtype=dtype) + + + def call(self, y_true, y_pred): + return _weighted_kappa_loss( + y_true, y_pred, self.row_label_vec, self.col_label_vec, self.weight_mat, self.eps, self.weightage, self.dtype + ) + + + def get_config(self): + config = { + "num_classes": self.num_classes, + "weightage": self.weightage, + "eps": self.eps, + "dtype": self.dtype + } + base_config = super(WeightedKappaLoss, self).get_config() + return dict(list(base_config.items()) + list(config.items())) \ No newline at end of file diff --git a/tensorflow_addons/losses/weighted_kappa_loss_test.py b/tensorflow_addons/losses/weighted_kappa_loss_test.py new file mode 100644 index 0000000000..e69de29bb2 From cbcec18014853e267ffe90a2f3775c3949b43b00 Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Fri, 13 Dec 2019 12:46:28 +0800 Subject: [PATCH 02/27] add unit tests --- .../losses/weighted_kappa_loss.py | 23 +++-- .../losses/weighted_kappa_loss_test.py | 90 +++++++++++++++++++ 2 files changed, 101 insertions(+), 12 deletions(-) diff --git a/tensorflow_addons/losses/weighted_kappa_loss.py b/tensorflow_addons/losses/weighted_kappa_loss.py index 8ec4c6238d..11c98d46ed 100644 --- a/tensorflow_addons/losses/weighted_kappa_loss.py +++ b/tensorflow_addons/losses/weighted_kappa_loss.py @@ -24,6 +24,7 @@ def _weighted_kappa_loss(y_true, y_pred, row_label_vec, col_label_vec, weight_mat, eps=1e-6, weightage='quadratic', dtype=tf.float32): + y_true = tf.cast(y_true, dtype=dtype) labels = tf.matmul(y_true, col_label_vec) if weightage == 'linear': weight = tf.abs(tf.tile(labels, [1, tf.shape(y_true)[1]]) - tf.tile(row_label_vec, [tf.shape(y_true)[0], 1])) @@ -43,6 +44,7 @@ def _weighted_kappa_loss(y_true, y_pred, row_label_vec, col_label_vec, return tf.math.log(numerator / denominator + eps) +@tf.keras.utils.register_keras_serializable(package='Addons') class WeightedKappaLoss(tf.keras.losses.Loss): """Implements the Weighted Kappa loss function. This Weighted Kappa loss was introduced in the @@ -52,13 +54,14 @@ class WeightedKappaLoss(tf.keras.losses.Loss): Weighted Kappa is widely used in Ordinal Classification Problems The score lies in [-∞, log2], where log2 means the random prediction Usage: - ```python - qwk = tfa.losses.WeightedKappa(num_classes=4) - y_true = tf.constant([[4.0, 3.0, 7.0, 5.0], [5.0, 6.0, 10.0, 7.0]]) - y_pred = tf.constant([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0]]) - loss = qwk(y_true, y_pred) - print('Loss: ', loss.numpy()) # Loss: [1.07500000298023224] + ```python + kappa_loss = WeightedKappaLoss(num_classes=4) + y_true = tf.constant([[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]]) + y_pred = tf.constant([[0.1, 0.2, 0.6, 0.1], [0.1, 0.5, 0.3, 0.1], + [0.8, 0.05, 0.05, 0.1], [0.01, 0.09, 0.1, 0.8]]) + loss = kappa_loss(y_true, y_pred) + print('Loss: ', loss.numpy()) # Loss: -1.1611923 ``` Usage with tf.keras API: @@ -69,12 +72,9 @@ class WeightedKappaLoss(tf.keras.losses.Loss): model.compile('sgd', loss=tfa.losses.WeightedKappa(num_classes=4)) ``` """ - def __init__(self, - num_classes, - weightage='quadratic', - name='cohen_kappa_loss', - eps=1e-6, + num_classes, weightage='quadratic', + name='cohen_kappa_loss', eps=1e-6, dtype=tf.float32): """Creates a `WeightedKappa` instance. Args: @@ -117,7 +117,6 @@ def call(self, y_true, y_pred): y_true, y_pred, self.row_label_vec, self.col_label_vec, self.weight_mat, self.eps, self.weightage, self.dtype ) - def get_config(self): config = { "num_classes": self.num_classes, diff --git a/tensorflow_addons/losses/weighted_kappa_loss_test.py b/tensorflow_addons/losses/weighted_kappa_loss_test.py index e69de29bb2..18aabb7348 100644 --- a/tensorflow_addons/losses/weighted_kappa_loss_test.py +++ b/tensorflow_addons/losses/weighted_kappa_loss_test.py @@ -0,0 +1,90 @@ +# Copyright 2019 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Tests for Weighted Kappa loss.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +import tensorflow as tf + +from tensorflow_addons.losses.weighted_kappa_loss import WeightedKappaLoss +from tensorflow_addons.utils import test_utils + +def weighted_kappa_loss_np(y_true, y_pred, weightage='quadratic', eps=1e-6): + """ + Implemented in non-optimized python code to avoid mistakes + """ + num_samples, num_classes = y_true.shape + numerator = 0 + true_classes = y_true.argmax(axis=1) + for i in range(num_samples): + true_class = true_classes[i] + for j in range(num_classes): + if weightage == 'quadratic': + w_ij = np.power(j - true_class, 2) + else: + w_ij = np.abs(j - true_class) + numerator += w_ij * y_pred[i, j] + + classes_count = y_true.sum(axis=0) + prob_sum = y_pred.sum(axis=0) + + denominator = 0 + for i in range(num_classes): + inner_sum = 0 + for j in range(num_classes): + if weightage == 'quadratic': + w_ij = np.power(i - j, 2) + else: + w_ij = np.abs(i - j) + inner_sum += w_ij * prob_sum[j] + denominator += inner_sum * classes_count[i] / num_samples + return np.log(numerator / denominator + eps) + +@test_utils.run_all_in_graph_and_eager_modes +class WeightedKappaLossTest(tf.test.TestCase): + def test_linear_weighted_kappa_loss(self): + y_true = np.array([[0, 0, 1, 0], + [0, 1, 0, 0], + [1, 0, 0, 0], + [0, 0, 0, 1]]) + + y_pred = np.array([[0.1, 0.2, 0.6, 0.1], + [0.1, 0.5, 0.3, 0.1], + [0.8, 0.05, 0.05, 0.1], + [0.01, 0.09, 0.1, 0.8]], dtype=np.float32) + kappa_loss = WeightedKappaLoss(num_classes=4, weightage='linear') + loss = kappa_loss(y_true, y_pred) + loss_np = weighted_kappa_loss_np(y_true, y_pred, weightage='linear') + self.assertAlmostEqual(self.evaluate(loss), loss_np, 5) + + def test_quadratic_weighted_kappa_loss(self): + y_true = np.array([[0, 0, 1, 0], + [0, 1, 0, 0], + [1, 0, 0, 0], + [0, 0, 0, 1]]) + + y_pred = np.array([[0.1, 0.2, 0.6, 0.1], + [0.1, 0.5, 0.3, 0.1], + [0.8, 0.05, 0.05, 0.1], + [0.01, 0.09, 0.1, 0.8]], dtype=np.float32) + kappa_loss = WeightedKappaLoss(num_classes=4) + loss = kappa_loss(y_true, y_pred) + loss_np = weighted_kappa_loss_np(y_true, y_pred) + self.assertAlmostEqual(self.evaluate(loss), loss_np, 5) + +if __name__ == "__main__": + tf.test.main() \ No newline at end of file From 4237f91d9d2bfbfb8b980935fb36451d27d448c3 Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Fri, 13 Dec 2019 12:50:07 +0800 Subject: [PATCH 03/27] change some docs --- tensorflow_addons/losses/weighted_kappa_loss.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tensorflow_addons/losses/weighted_kappa_loss.py b/tensorflow_addons/losses/weighted_kappa_loss.py index 11c98d46ed..94a350d517 100644 --- a/tensorflow_addons/losses/weighted_kappa_loss.py +++ b/tensorflow_addons/losses/weighted_kappa_loss.py @@ -52,7 +52,7 @@ class WeightedKappaLoss(tf.keras.losses.Loss): of ordinal data in deep learning] (https://www.sciencedirect.com/science/article/abs/pii/S0167865517301666). Weighted Kappa is widely used in Ordinal Classification Problems - The score lies in [-∞, log2], where log2 means the random prediction + The loss value lies in [-∞, log2], where log2 means the random prediction Usage: ```python @@ -79,10 +79,10 @@ def __init__(self, """Creates a `WeightedKappa` instance. Args: num_classes: Number of unique classes in your dataset. - name: (Optional) String name of the metric instance. weightage: (Optional) Weighting to be considered for calculating kappa statistics. A valid value is one of ['linear', 'quadratic']. Defaults to `quadratic` since it's mostly used. + name: (Optional) String name of the metric instance. dtype: (Optional) Data type of the metric result. Defaults to `tf.float32`. Raises: From 61cba306e5acc6d952b0c02fa50b9f875e104be2 Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Thu, 19 Dec 2019 15:03:28 +0800 Subject: [PATCH 04/27] change python files format --- .../losses/weighted_kappa_loss.py | 51 ++++++++++++------- .../losses/weighted_kappa_loss_test.py | 27 +++++----- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/tensorflow_addons/losses/weighted_kappa_loss.py b/tensorflow_addons/losses/weighted_kappa_loss.py index 94a350d517..68bfaa9fcc 100644 --- a/tensorflow_addons/losses/weighted_kappa_loss.py +++ b/tensorflow_addons/losses/weighted_kappa_loss.py @@ -20,26 +20,36 @@ import tensorflow as tf + @tf.function -def _weighted_kappa_loss(y_true, y_pred, row_label_vec, col_label_vec, - weight_mat, eps=1e-6, weightage='quadratic', +def _weighted_kappa_loss(y_true, + y_pred, + row_label_vec, + col_label_vec, + weight_mat, + eps=1e-6, + weightage='quadratic', dtype=tf.float32): y_true = tf.cast(y_true, dtype=dtype) labels = tf.matmul(y_true, col_label_vec) if weightage == 'linear': - weight = tf.abs(tf.tile(labels, [1, tf.shape(y_true)[1]]) - tf.tile(row_label_vec, [tf.shape(y_true)[0], 1])) + weight = tf.abs( + tf.tile(labels, [1, tf.shape(y_true)[1]]) - + tf.tile(row_label_vec, [tf.shape(y_true)[0], 1])) weight /= tf.cast(tf.shape(y_true)[1] - 1, dtype=dtype) else: - weight = tf.pow(tf.tile(labels, [1, tf.shape(y_true)[1]]) - tf.tile(row_label_vec, [tf.shape(y_true)[0], 1]), 2) + weight = tf.pow( + tf.tile(labels, [1, tf.shape(y_true)[1]]) - tf.tile( + row_label_vec, [tf.shape(y_true)[0], 1]), 2) weight /= tf.cast(tf.pow(tf.shape(y_true)[1] - 1, 2), dtype=dtype) numerator = tf.reduce_sum(weight * y_pred) denominator = tf.reduce_sum( tf.matmul( tf.reduce_sum(y_true, axis=0, keepdims=True), - tf.matmul(weight_mat, tf.transpose(tf.reduce_sum(y_pred, axis=0, keepdims=True))) - ) - ) + tf.matmul( + weight_mat, + tf.transpose(tf.reduce_sum(y_pred, axis=0, keepdims=True))))) denominator /= tf.cast(tf.shape(y_true)[0], dtype=dtype) return tf.math.log(numerator / denominator + eps) @@ -72,9 +82,12 @@ class WeightedKappaLoss(tf.keras.losses.Loss): model.compile('sgd', loss=tfa.losses.WeightedKappa(num_classes=4)) ``` """ + def __init__(self, - num_classes, weightage='quadratic', - name='cohen_kappa_loss', eps=1e-6, + num_classes, + weightage='quadratic', + name='cohen_kappa_loss', + eps=1e-6, dtype=tf.float32): """Creates a `WeightedKappa` instance. Args: @@ -90,7 +103,8 @@ def __init__(self, i.e. not any one of ['linear', 'quadratic'] """ - super(WeightedKappaLoss, self).__init__(name=name, reduction=tf.keras.losses.Reduction.NONE) + super(WeightedKappaLoss, self).__init__( + name=name, reduction=tf.keras.losses.Reduction.NONE) if weightage not in ('linear', 'quadratic'): raise ValueError("Unknown kappa weighting type.") @@ -104,18 +118,19 @@ def __init__(self, self.col_label_vec = tf.reshape(label_vec, [num_classes, 1]) if weightage == 'linear': self.weight_mat = tf.abs( - tf.tile(self.col_label_vec, [1, num_classes]) - tf.tile(self.row_label_vec, [num_classes, 1]), - ) / tf.cast(num_classes - 1, dtype=dtype) + tf.tile(self.col_label_vec, [1, num_classes]) - tf.tile( + self.row_label_vec, [num_classes, 1]),) / tf.cast( + num_classes - 1, dtype=dtype) else: self.weight_mat = tf.pow( - tf.tile(self.col_label_vec, [1, num_classes]) - tf.tile(self.row_label_vec, [num_classes, 1]), - 2) / tf.cast(tf.pow(num_classes - 1, 2), dtype=dtype) - + tf.tile(self.col_label_vec, [1, num_classes]) - tf.tile( + self.row_label_vec, [num_classes, 1]), 2) / tf.cast( + tf.pow(num_classes - 1, 2), dtype=dtype) def call(self, y_true, y_pred): - return _weighted_kappa_loss( - y_true, y_pred, self.row_label_vec, self.col_label_vec, self.weight_mat, self.eps, self.weightage, self.dtype - ) + return _weighted_kappa_loss(y_true, y_pred, self.row_label_vec, + self.col_label_vec, self.weight_mat, + self.eps, self.weightage, self.dtype) def get_config(self): config = { diff --git a/tensorflow_addons/losses/weighted_kappa_loss_test.py b/tensorflow_addons/losses/weighted_kappa_loss_test.py index 18aabb7348..ae6e7560e5 100644 --- a/tensorflow_addons/losses/weighted_kappa_loss_test.py +++ b/tensorflow_addons/losses/weighted_kappa_loss_test.py @@ -23,6 +23,7 @@ from tensorflow_addons.losses.weighted_kappa_loss import WeightedKappaLoss from tensorflow_addons.utils import test_utils + def weighted_kappa_loss_np(y_true, y_pred, weightage='quadratic', eps=1e-6): """ Implemented in non-optimized python code to avoid mistakes @@ -38,7 +39,7 @@ def weighted_kappa_loss_np(y_true, y_pred, weightage='quadratic', eps=1e-6): else: w_ij = np.abs(j - true_class) numerator += w_ij * y_pred[i, j] - + classes_count = y_true.sum(axis=0) prob_sum = y_pred.sum(axis=0) @@ -54,37 +55,33 @@ def weighted_kappa_loss_np(y_true, y_pred, weightage='quadratic', eps=1e-6): denominator += inner_sum * classes_count[i] / num_samples return np.log(numerator / denominator + eps) + @test_utils.run_all_in_graph_and_eager_modes class WeightedKappaLossTest(tf.test.TestCase): def test_linear_weighted_kappa_loss(self): - y_true = np.array([[0, 0, 1, 0], - [0, 1, 0, 0], - [1, 0, 0, 0], + y_true = np.array([[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]]) - y_pred = np.array([[0.1, 0.2, 0.6, 0.1], - [0.1, 0.5, 0.3, 0.1], - [0.8, 0.05, 0.05, 0.1], - [0.01, 0.09, 0.1, 0.8]], dtype=np.float32) + y_pred = np.array([[0.1, 0.2, 0.6, 0.1], [0.1, 0.5, 0.3, 0.1], + [0.8, 0.05, 0.05, 0.1], [0.01, 0.09, 0.1, 0.8]], + dtype=np.float32) kappa_loss = WeightedKappaLoss(num_classes=4, weightage='linear') loss = kappa_loss(y_true, y_pred) loss_np = weighted_kappa_loss_np(y_true, y_pred, weightage='linear') self.assertAlmostEqual(self.evaluate(loss), loss_np, 5) def test_quadratic_weighted_kappa_loss(self): - y_true = np.array([[0, 0, 1, 0], - [0, 1, 0, 0], - [1, 0, 0, 0], + y_true = np.array([[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]]) - y_pred = np.array([[0.1, 0.2, 0.6, 0.1], - [0.1, 0.5, 0.3, 0.1], - [0.8, 0.05, 0.05, 0.1], - [0.01, 0.09, 0.1, 0.8]], dtype=np.float32) + y_pred = np.array([[0.1, 0.2, 0.6, 0.1], [0.1, 0.5, 0.3, 0.1], + [0.8, 0.05, 0.05, 0.1], [0.01, 0.09, 0.1, 0.8]], + dtype=np.float32) kappa_loss = WeightedKappaLoss(num_classes=4) loss = kappa_loss(y_true, y_pred) loss_np = weighted_kappa_loss_np(y_true, y_pred) self.assertAlmostEqual(self.evaluate(loss), loss_np, 5) + if __name__ == "__main__": tf.test.main() \ No newline at end of file From e58fa6a67db16149310133e46f46a34f01cfac11 Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Thu, 19 Dec 2019 23:01:56 +0800 Subject: [PATCH 05/27] shorten some lines --- tensorflow_addons/losses/weighted_kappa_loss.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tensorflow_addons/losses/weighted_kappa_loss.py b/tensorflow_addons/losses/weighted_kappa_loss.py index 68bfaa9fcc..3ef1026a76 100644 --- a/tensorflow_addons/losses/weighted_kappa_loss.py +++ b/tensorflow_addons/losses/weighted_kappa_loss.py @@ -67,7 +67,8 @@ class WeightedKappaLoss(tf.keras.losses.Loss): ```python kappa_loss = WeightedKappaLoss(num_classes=4) - y_true = tf.constant([[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]]) + y_true = tf.constant([[0, 0, 1, 0], [0, 1, 0, 0], + [1, 0, 0, 0], [0, 0, 0, 1]]) y_pred = tf.constant([[0.1, 0.2, 0.6, 0.1], [0.1, 0.5, 0.3, 0.1], [0.8, 0.05, 0.05, 0.1], [0.01, 0.09, 0.1, 0.8]]) loss = kappa_loss(y_true, y_pred) @@ -77,7 +78,8 @@ class WeightedKappaLoss(tf.keras.losses.Loss): Usage with tf.keras API: ```python # outputs should be softmax results - # if you want to weight the samples, just multiply the outputs by the sample weight. + # if you want to weight the samples, just multiply the outputs + # by the sample weight. model = tf.keras.Model(inputs, outputs) model.compile('sgd', loss=tfa.losses.WeightedKappa(num_classes=4)) ``` @@ -94,7 +96,8 @@ def __init__(self, num_classes: Number of unique classes in your dataset. weightage: (Optional) Weighting to be considered for calculating kappa statistics. A valid value is one of - ['linear', 'quadratic']. Defaults to `quadratic` since it's mostly used. + ['linear', 'quadratic']. Defaults to `quadratic` since it's + mostly used. name: (Optional) String name of the metric instance. dtype: (Optional) Data type of the metric result. Defaults to `tf.float32`. @@ -140,4 +143,4 @@ def get_config(self): "dtype": self.dtype } base_config = super(WeightedKappaLoss, self).get_config() - return dict(list(base_config.items()) + list(config.items())) \ No newline at end of file + return dict(list(base_config.items()) + list(config.items())) From dcfc5047f2aae1a9ad5e35a02b434eb8d2ef84aa Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Sat, 21 Dec 2019 08:21:22 +0800 Subject: [PATCH 06/27] rename and update README and BUILD --- tensorflow_addons/losses/BUILD | 12 ++++++++++++ tensorflow_addons/losses/README.md | 2 ++ tensorflow_addons/losses/__init__.py | 1 + .../losses/{weighted_kappa_loss.py => kappa_loss.py} | 0 ...eighted_kappa_loss_test.py => kappa_loss_test.py} | 2 +- 5 files changed, 16 insertions(+), 1 deletion(-) rename tensorflow_addons/losses/{weighted_kappa_loss.py => kappa_loss.py} (100%) rename tensorflow_addons/losses/{weighted_kappa_loss_test.py => kappa_loss_test.py} (97%) diff --git a/tensorflow_addons/losses/BUILD b/tensorflow_addons/losses/BUILD index 4fba5cab60..b487eba2e3 100644 --- a/tensorflow_addons/losses/BUILD +++ b/tensorflow_addons/losses/BUILD @@ -116,3 +116,15 @@ py_test( ":losses", ], ) + +py_test( + name = "kappa_loss_test", + size = "small", + srcs = [ + "kappa_loss_test.py" + ], + main = "kappa_loss_test.py", + deps = [ + ":losses", + ], +) diff --git a/tensorflow_addons/losses/README.md b/tensorflow_addons/losses/README.md index cab8283d42..519ae4acd5 100644 --- a/tensorflow_addons/losses/README.md +++ b/tensorflow_addons/losses/README.md @@ -10,6 +10,7 @@ | npairs | @WindQAQ | windqaq@gmail.com | | sparsemax_loss | @AndreasMadsen | amwwebdk+github@gmail.com | | triplet | @rahulunair | rahulunair@gmail.com | +| kappa_loss | @wenmin-wu | wuwenmin1991@gmail.com | ## Components | Submodule | Loss | Reference | @@ -22,6 +23,7 @@ | npairs | NpairsMultilabelLoss | http://www.nec-labs.com/uploads/images/Department-Images/MediaAnalytics/papers/nips16_npairmetriclearning.pdf | | sparsemax_loss | SparsemaxLoss | https://arxiv.org/abs/1602.02068 | | triplet | TripletSemiHardLoss | https://arxiv.org/abs/1503.03832 | +| kappa_loss | WeightedKappaLoss | https://www.sciencedirect.com/science/article/abs/pii/S0167865517301666 | ## Contribution Guidelines diff --git a/tensorflow_addons/losses/__init__.py b/tensorflow_addons/losses/__init__.py index 7aa292527f..58af316359 100644 --- a/tensorflow_addons/losses/__init__.py +++ b/tensorflow_addons/losses/__init__.py @@ -25,3 +25,4 @@ from tensorflow_addons.losses.npairs import npairs_loss, NpairsLoss, npairs_multilabel_loss, NpairsMultilabelLoss from tensorflow_addons.losses.sparsemax_loss import sparsemax_loss, SparsemaxLoss from tensorflow_addons.losses.triplet import triplet_semihard_loss, TripletSemiHardLoss +from tensorflow_addons.losses.kappa_loss import WeightedKappaLoss diff --git a/tensorflow_addons/losses/weighted_kappa_loss.py b/tensorflow_addons/losses/kappa_loss.py similarity index 100% rename from tensorflow_addons/losses/weighted_kappa_loss.py rename to tensorflow_addons/losses/kappa_loss.py diff --git a/tensorflow_addons/losses/weighted_kappa_loss_test.py b/tensorflow_addons/losses/kappa_loss_test.py similarity index 97% rename from tensorflow_addons/losses/weighted_kappa_loss_test.py rename to tensorflow_addons/losses/kappa_loss_test.py index ae6e7560e5..fa8a1417bb 100644 --- a/tensorflow_addons/losses/weighted_kappa_loss_test.py +++ b/tensorflow_addons/losses/kappa_loss_test.py @@ -20,7 +20,7 @@ import numpy as np import tensorflow as tf -from tensorflow_addons.losses.weighted_kappa_loss import WeightedKappaLoss +from tensorflow_addons.losses.kappa_loss import WeightedKappaLoss from tensorflow_addons.utils import test_utils From fd03150622d5c7b9208081c233e63a8de763fa93 Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Mon, 23 Mar 2020 11:39:21 +0800 Subject: [PATCH 07/27] resolve conversations --- tensorflow_addons/losses/BUILD | 2 + tensorflow_addons/losses/kappa_loss.py | 57 ++++++++++----------- tensorflow_addons/losses/kappa_loss_test.py | 17 ++++-- 3 files changed, 44 insertions(+), 32 deletions(-) diff --git a/tensorflow_addons/losses/BUILD b/tensorflow_addons/losses/BUILD index 8f24287775..6f80a9406d 100644 --- a/tensorflow_addons/losses/BUILD +++ b/tensorflow_addons/losses/BUILD @@ -17,6 +17,7 @@ py_library( "quantiles.py", "sparsemax_loss.py", "triplet.py", + "kappa_loss.py", ], "//conditions:default": [ "__init__.py", @@ -29,6 +30,7 @@ py_library( "quantiles.py", "sparsemax_loss.py", "triplet.py", + "kappa_loss.py" ], }), deps = [ diff --git a/tensorflow_addons/losses/kappa_loss.py b/tensorflow_addons/losses/kappa_loss.py index a86fbdba00..5b0a53e359 100644 --- a/tensorflow_addons/losses/kappa_loss.py +++ b/tensorflow_addons/losses/kappa_loss.py @@ -21,14 +21,13 @@ import tensorflow as tf -@tf.function def _weighted_kappa_loss( y_true, y_pred, row_label_vec, col_label_vec, weight_mat, - eps=1e-6, + epsilon=1e-6, weightage="quadratic", dtype=tf.float32, ): @@ -36,14 +35,13 @@ def _weighted_kappa_loss( labels = tf.matmul(y_true, col_label_vec) if weightage == "linear": weight = tf.abs( - tf.tile(labels, [1, tf.shape(y_true)[1]]) - - tf.tile(row_label_vec, [tf.shape(y_true)[0], 1]) - ) + tf.tile(labels, [1, tf.shape(y_true)[1]]) - + tf.tile(row_label_vec, [tf.shape(y_true)[0], 1])) weight /= tf.cast(tf.shape(y_true)[1] - 1, dtype=dtype) else: weight = tf.pow( - tf.tile(labels, [1, tf.shape(y_true)[1]]) - - tf.tile(row_label_vec, [tf.shape(y_true)[0], 1]), + tf.tile(labels, [1, tf.shape(y_true)[1]]) - + tf.tile(row_label_vec, [tf.shape(y_true)[0], 1]), 2, ) weight /= tf.cast(tf.pow(tf.shape(y_true)[1] - 1, 2), dtype=dtype) @@ -52,13 +50,12 @@ def _weighted_kappa_loss( denominator = tf.reduce_sum( tf.matmul( tf.reduce_sum(y_true, axis=0, keepdims=True), - tf.matmul( - weight_mat, tf.transpose(tf.reduce_sum(y_pred, axis=0, keepdims=True)) - ), - ) - ) + tf.matmul(weight_mat, + tf.reduce_sum(y_pred, axis=0, keepdims=True), + transpose_b=True), + )) denominator /= tf.cast(tf.shape(y_true)[0], dtype=dtype) - return tf.math.log(numerator / denominator + eps) + return tf.math.log(tf.math.divide_no_nan(numerator, denominator) + epsilon) @tf.keras.utils.register_keras_serializable(package="Addons") @@ -91,16 +88,16 @@ class WeightedKappaLoss(tf.keras.losses.Loss): model.compile('sgd', loss=tfa.losses.WeightedKappa(num_classes=4)) ``` """ - def __init__( self, num_classes, weightage="quadratic", name="cohen_kappa_loss", - eps=1e-6, + epsilon=1e-6, dtype=tf.float32, ): """Creates a `WeightedKappa` instance. + Args: num_classes: Number of unique classes in your dataset. weightage: (Optional) Weighting to be considered for calculating @@ -108,6 +105,9 @@ def __init__( ['linear', 'quadratic']. Defaults to `quadratic` since it's mostly used. name: (Optional) String name of the metric instance. + epsilon: (Optional) increment to avoid log zero, + so the loss will be log(1 - k + epsilon), where k belongs to + [-1, 1], usually you can use the default value which is 1e-6. dtype: (Optional) Data type of the metric result. Defaults to `tf.float32`. Raises: @@ -115,31 +115,30 @@ def __init__( i.e. not any one of ['linear', 'quadratic'] """ - super(WeightedKappaLoss, self).__init__( - name=name, reduction=tf.keras.losses.Reduction.NONE - ) + super(WeightedKappaLoss, + self).__init__(name=name, + reduction=tf.keras.losses.Reduction.NONE) if weightage not in ("linear", "quadratic"): raise ValueError("Unknown kappa weighting type.") self.weightage = weightage self.num_classes = num_classes - self.eps = eps + self.epsilon = epsilon self.dtype = dtype label_vec = tf.range(num_classes, dtype=dtype) self.row_label_vec = tf.reshape(label_vec, [1, num_classes]) self.col_label_vec = tf.reshape(label_vec, [num_classes, 1]) if weightage == "linear": self.weight_mat = tf.abs( - tf.tile(self.col_label_vec, [1, num_classes]) - - tf.tile(self.row_label_vec, [num_classes, 1]), - ) / tf.cast(num_classes - 1, dtype=dtype) + tf.tile(self.col_label_vec, [1, num_classes]) - + tf.tile(self.row_label_vec, [num_classes, 1]),) / tf.cast( + num_classes - 1, dtype=dtype) else: - self.weight_mat = tf.pow( - tf.tile(self.col_label_vec, [1, num_classes]) - - tf.tile(self.row_label_vec, [num_classes, 1]), - 2, - ) / tf.cast(tf.pow(num_classes - 1, 2), dtype=dtype) + self.weight_mat = ( + tf.tile(self.col_label_vec, [1, num_classes]) - + tf.tile(self.row_label_vec, [num_classes, 1]) + ) ** 2 / tf.cast((num_classes - 1) ** 2, dtype=dtype) def call(self, y_true, y_pred): return _weighted_kappa_loss( @@ -148,7 +147,7 @@ def call(self, y_true, y_pred): self.row_label_vec, self.col_label_vec, self.weight_mat, - self.eps, + self.epsilon, self.weightage, self.dtype, ) @@ -157,7 +156,7 @@ def get_config(self): config = { "num_classes": self.num_classes, "weightage": self.weightage, - "eps": self.eps, + "epsilon": self.epsilon, "dtype": self.dtype, } base_config = super(WeightedKappaLoss, self).get_config() diff --git a/tensorflow_addons/losses/kappa_loss_test.py b/tensorflow_addons/losses/kappa_loss_test.py index f438b5ccb2..6bbe6b67c7 100644 --- a/tensorflow_addons/losses/kappa_loss_test.py +++ b/tensorflow_addons/losses/kappa_loss_test.py @@ -25,9 +25,8 @@ def weighted_kappa_loss_np(y_true, y_pred, weightage="quadratic", eps=1e-6): - """ - Implemented in non-optimized python code to avoid mistakes - """ + """Implemented in non-optimized python code to avoid mistakes""" + num_samples, num_classes = y_true.shape numerator = 0 true_classes = y_true.argmax(axis=1) @@ -92,6 +91,18 @@ def test_quadratic_weighted_kappa_loss(self): loss_np = weighted_kappa_loss_np(y_true, y_pred) self.assertAlmostEqual(self.evaluate(loss), loss_np, 5) + def test_config(self): + kappa_loss = WeightedKappaLoss( + num_classes=4, + weightage="linear", + name="kappa_loss", + epsilon=0.001, + ) + self.assertEqual(kappa_loss.num_classes, 4) + self.assertEqual(kappa_loss.weightage, "linear") + self.assertEqual(kappa_loss.name, "kappa_loss") + self.assertAlmostEqual(kappa_loss.epsilon, 0.001, 1e-6) + if __name__ == "__main__": tf.test.main() From 2dca416c56c0ed84d1911882ecb0278f64d377a1 Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Mon, 23 Mar 2020 12:07:09 +0800 Subject: [PATCH 08/27] resolve converstions --- .github/CODEOWNERS | 1 + tensorflow_addons/losses/kappa_loss.py | 15 +++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6ce517bc57..937cd768b0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -68,6 +68,7 @@ /tensorflow_addons/losses/quantiles*.py @romainbrault /tensorflow_addons/losses/sparsemax*.py @andreasmadsen /tensorflow_addons/losses/triplet*.py @lc0 +/tensorflow_addons/losses/kappa_loss*.py @wenmin-wu /tensorflow_addons/metrics/cohens_kappa*.py @aakashkumarnain /tensorflow_addons/metrics/f_scores*.py @ssaishruthi diff --git a/tensorflow_addons/losses/kappa_loss.py b/tensorflow_addons/losses/kappa_loss.py index 5b0a53e359..c57ce0d2bb 100644 --- a/tensorflow_addons/losses/kappa_loss.py +++ b/tensorflow_addons/losses/kappa_loss.py @@ -61,12 +61,15 @@ def _weighted_kappa_loss( @tf.keras.utils.register_keras_serializable(package="Addons") class WeightedKappaLoss(tf.keras.losses.Loss): """Implements the Weighted Kappa loss function. - This Weighted Kappa loss was introduced in the + + Weighted Kappa loss was introduced in the [Weighted kappa loss function for multi-class classification of ordinal data in deep learning] (https://www.sciencedirect.com/science/article/abs/pii/S0167865517301666). - Weighted Kappa is widely used in Ordinal Classification Problems - The loss value lies in [-∞, log2], where log2 means the random prediction + Weighted Kappa is widely used in Ordinal Classification Problems. + The loss value lies in [-\infty, \log 2], + where \log 2 means the random prediction. + Usage: ```python @@ -79,7 +82,7 @@ class WeightedKappaLoss(tf.keras.losses.Loss): print('Loss: ', loss.numpy()) # Loss: -1.1611923 ``` - Usage with tf.keras API: + Usage with `tf.keras` API: ```python # outputs should be softmax results # if you want to weight the samples, just multiply the outputs @@ -105,8 +108,8 @@ def __init__( ['linear', 'quadratic']. Defaults to `quadratic` since it's mostly used. name: (Optional) String name of the metric instance. - epsilon: (Optional) increment to avoid log zero, - so the loss will be log(1 - k + epsilon), where k belongs to + epsilon: (Optional) increment to avoid \log zero, + so the loss will be \log (1 - k + epsilon), where k belongs to [-1, 1], usually you can use the default value which is 1e-6. dtype: (Optional) Data type of the metric result. Defaults to `tf.float32`. From ac062199d07c7fa0ffa777e00ec634e2d743bdd3 Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Mon, 23 Mar 2020 12:12:34 +0800 Subject: [PATCH 09/27] remove escape --- tensorflow_addons/losses/kappa_loss.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tensorflow_addons/losses/kappa_loss.py b/tensorflow_addons/losses/kappa_loss.py index c57ce0d2bb..52f7bcbc73 100644 --- a/tensorflow_addons/losses/kappa_loss.py +++ b/tensorflow_addons/losses/kappa_loss.py @@ -67,8 +67,8 @@ class WeightedKappaLoss(tf.keras.losses.Loss): of ordinal data in deep learning] (https://www.sciencedirect.com/science/article/abs/pii/S0167865517301666). Weighted Kappa is widely used in Ordinal Classification Problems. - The loss value lies in [-\infty, \log 2], - where \log 2 means the random prediction. + The loss value lies in [-inf, log 2], where log 2 + means the random prediction. Usage: @@ -108,8 +108,8 @@ def __init__( ['linear', 'quadratic']. Defaults to `quadratic` since it's mostly used. name: (Optional) String name of the metric instance. - epsilon: (Optional) increment to avoid \log zero, - so the loss will be \log (1 - k + epsilon), where k belongs to + epsilon: (Optional) increment to avoid log zero, + so the loss will be log(1 - k + epsilon), where k belongs to [-1, 1], usually you can use the default value which is 1e-6. dtype: (Optional) Data type of the metric result. Defaults to `tf.float32`. From a950ce7c3cc846cdb94c5ead8661bd9aeeda86fe Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Mon, 23 Mar 2020 12:36:15 +0800 Subject: [PATCH 10/27] reformat tensorflow_addons/losses/kappa_loss* with black --- tensorflow_addons/losses/kappa_loss.py | 37 ++++++++++++--------- tensorflow_addons/losses/kappa_loss_test.py | 5 +-- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/tensorflow_addons/losses/kappa_loss.py b/tensorflow_addons/losses/kappa_loss.py index 52f7bcbc73..447c273bd8 100644 --- a/tensorflow_addons/losses/kappa_loss.py +++ b/tensorflow_addons/losses/kappa_loss.py @@ -35,13 +35,14 @@ def _weighted_kappa_loss( labels = tf.matmul(y_true, col_label_vec) if weightage == "linear": weight = tf.abs( - tf.tile(labels, [1, tf.shape(y_true)[1]]) - - tf.tile(row_label_vec, [tf.shape(y_true)[0], 1])) + tf.tile(labels, [1, tf.shape(y_true)[1]]) + - tf.tile(row_label_vec, [tf.shape(y_true)[0], 1]) + ) weight /= tf.cast(tf.shape(y_true)[1] - 1, dtype=dtype) else: weight = tf.pow( - tf.tile(labels, [1, tf.shape(y_true)[1]]) - - tf.tile(row_label_vec, [tf.shape(y_true)[0], 1]), + tf.tile(labels, [1, tf.shape(y_true)[1]]) + - tf.tile(row_label_vec, [tf.shape(y_true)[0], 1]), 2, ) weight /= tf.cast(tf.pow(tf.shape(y_true)[1] - 1, 2), dtype=dtype) @@ -50,10 +51,13 @@ def _weighted_kappa_loss( denominator = tf.reduce_sum( tf.matmul( tf.reduce_sum(y_true, axis=0, keepdims=True), - tf.matmul(weight_mat, - tf.reduce_sum(y_pred, axis=0, keepdims=True), - transpose_b=True), - )) + tf.matmul( + weight_mat, + tf.reduce_sum(y_pred, axis=0, keepdims=True), + transpose_b=True, + ), + ) + ) denominator /= tf.cast(tf.shape(y_true)[0], dtype=dtype) return tf.math.log(tf.math.divide_no_nan(numerator, denominator) + epsilon) @@ -91,6 +95,7 @@ class WeightedKappaLoss(tf.keras.losses.Loss): model.compile('sgd', loss=tfa.losses.WeightedKappa(num_classes=4)) ``` """ + def __init__( self, num_classes, @@ -118,9 +123,9 @@ def __init__( i.e. not any one of ['linear', 'quadratic'] """ - super(WeightedKappaLoss, - self).__init__(name=name, - reduction=tf.keras.losses.Reduction.NONE) + super(WeightedKappaLoss, self).__init__( + name=name, reduction=tf.keras.losses.Reduction.NONE + ) if weightage not in ("linear", "quadratic"): raise ValueError("Unknown kappa weighting type.") @@ -134,13 +139,13 @@ def __init__( self.col_label_vec = tf.reshape(label_vec, [num_classes, 1]) if weightage == "linear": self.weight_mat = tf.abs( - tf.tile(self.col_label_vec, [1, num_classes]) - - tf.tile(self.row_label_vec, [num_classes, 1]),) / tf.cast( - num_classes - 1, dtype=dtype) + tf.tile(self.col_label_vec, [1, num_classes]) + - tf.tile(self.row_label_vec, [num_classes, 1]), + ) / tf.cast(num_classes - 1, dtype=dtype) else: self.weight_mat = ( - tf.tile(self.col_label_vec, [1, num_classes]) - - tf.tile(self.row_label_vec, [num_classes, 1]) + tf.tile(self.col_label_vec, [1, num_classes]) + - tf.tile(self.row_label_vec, [num_classes, 1]) ) ** 2 / tf.cast((num_classes - 1) ** 2, dtype=dtype) def call(self, y_true, y_pred): diff --git a/tensorflow_addons/losses/kappa_loss_test.py b/tensorflow_addons/losses/kappa_loss_test.py index 6bbe6b67c7..87c52964cb 100644 --- a/tensorflow_addons/losses/kappa_loss_test.py +++ b/tensorflow_addons/losses/kappa_loss_test.py @@ -93,10 +93,7 @@ def test_quadratic_weighted_kappa_loss(self): def test_config(self): kappa_loss = WeightedKappaLoss( - num_classes=4, - weightage="linear", - name="kappa_loss", - epsilon=0.001, + num_classes=4, weightage="linear", name="kappa_loss", epsilon=0.001, ) self.assertEqual(kappa_loss.num_classes, 4) self.assertEqual(kappa_loss.weightage, "linear") From 174e7f4a19c4657662ef073da7042e1a58ddcf90 Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Mon, 23 Mar 2020 12:52:14 +0800 Subject: [PATCH 11/27] reformat code --- tensorflow_addons/losses/kappa_loss.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tensorflow_addons/losses/kappa_loss.py b/tensorflow_addons/losses/kappa_loss.py index 447c273bd8..d889fe7b46 100644 --- a/tensorflow_addons/losses/kappa_loss.py +++ b/tensorflow_addons/losses/kappa_loss.py @@ -19,6 +19,12 @@ from __future__ import print_function import tensorflow as tf +from tensorflow_addons.utils.types import FloatTensorLike, Number, TensorLike +from typeguard import typechecked +from typing import Optional, Union + + +FloatTensorType = Union[tf.float32, tf.float64] def _weighted_kappa_loss( @@ -96,13 +102,14 @@ class WeightedKappaLoss(tf.keras.losses.Loss): ``` """ + @typechecked def __init__( self, - num_classes, - weightage="quadratic", - name="cohen_kappa_loss", - epsilon=1e-6, - dtype=tf.float32, + num_classes: int, + weightage: Optional[str] = "quadratic", + name: Optional[str] = "cohen_kappa_loss", + epsilon: Number = 1e-6, + dtype: FloatTensorType = tf.float32, ): """Creates a `WeightedKappa` instance. From 1836ae325d6ce5001c5af4819dd2dc8f54889edb Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Mon, 23 Mar 2020 13:09:08 +0800 Subject: [PATCH 12/27] reformat code --- tensorflow_addons/losses/BUILD | 36 +++++++++++++------------- tensorflow_addons/losses/kappa_loss.py | 9 +++---- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/tensorflow_addons/losses/BUILD b/tensorflow_addons/losses/BUILD index fdac11ceb5..f19406ba22 100644 --- a/tensorflow_addons/losses/BUILD +++ b/tensorflow_addons/losses/BUILD @@ -3,24 +3,24 @@ licenses(["notice"]) # Apache 2.0 package(default_visibility = ["//visibility:public"]) py_library( - name = "losses", - srcs = [ - "__init__.py", - "contrastive.py", - "focal_loss.py", - "giou_loss.py", - "lifted.py", - "metric_learning.py", - "npairs.py", - "quantiles.py", - "sparsemax_loss.py", - "triplet.py", - "kappa_loss.py" - ], - deps = [ - "//tensorflow_addons/activations", - "//tensorflow_addons/utils", - ], + name = "losses", + srcs = [ + "__init__.py", + "contrastive.py", + "focal_loss.py", + "giou_loss.py", + "kappa_loss.py", + "lifted.py", + "metric_learning.py", + "npairs.py", + "quantiles.py", + "sparsemax_loss.py", + "triplet.py", + ], + deps = [ + "//tensorflow_addons/activations", + "//tensorflow_addons/utils", + ], ) py_test( diff --git a/tensorflow_addons/losses/kappa_loss.py b/tensorflow_addons/losses/kappa_loss.py index d889fe7b46..7e00fb54cd 100644 --- a/tensorflow_addons/losses/kappa_loss.py +++ b/tensorflow_addons/losses/kappa_loss.py @@ -19,12 +19,11 @@ from __future__ import print_function import tensorflow as tf -from tensorflow_addons.utils.types import FloatTensorLike, Number, TensorLike +from tensorflow_addons.utils.types import Number from typeguard import typechecked -from typing import Optional, Union +from typing import Optional -FloatTensorType = Union[tf.float32, tf.float64] def _weighted_kappa_loss( @@ -108,8 +107,8 @@ def __init__( num_classes: int, weightage: Optional[str] = "quadratic", name: Optional[str] = "cohen_kappa_loss", - epsilon: Number = 1e-6, - dtype: FloatTensorType = tf.float32, + epsilon: Optional[Number] = 1e-6, + dtype: Optional[tf.DType] = tf.float32, ): """Creates a `WeightedKappa` instance. From 5055c9a99d5d5bca643b5903dbaeb7283e8120f4 Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Mon, 23 Mar 2020 13:09:55 +0800 Subject: [PATCH 13/27] reformat code with black --- tensorflow_addons/losses/kappa_loss.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tensorflow_addons/losses/kappa_loss.py b/tensorflow_addons/losses/kappa_loss.py index 7e00fb54cd..590e908d3c 100644 --- a/tensorflow_addons/losses/kappa_loss.py +++ b/tensorflow_addons/losses/kappa_loss.py @@ -24,8 +24,6 @@ from typing import Optional - - def _weighted_kappa_loss( y_true, y_pred, From 3715631bc2c3afae50831e4cf88f895ba6c53581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E6=96=87=E6=95=8F?= Date: Fri, 10 Apr 2020 17:00:34 +0800 Subject: [PATCH 14/27] Update tensorflow_addons/losses/kappa_loss.py Co-Authored-By: Gabriel de Marmiesse --- tensorflow_addons/losses/kappa_loss.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow_addons/losses/kappa_loss.py b/tensorflow_addons/losses/kappa_loss.py index 590e908d3c..7e27ff5af9 100644 --- a/tensorflow_addons/losses/kappa_loss.py +++ b/tensorflow_addons/losses/kappa_loss.py @@ -127,7 +127,7 @@ def __init__( i.e. not any one of ['linear', 'quadratic'] """ - super(WeightedKappaLoss, self).__init__( + super().__init__( name=name, reduction=tf.keras.losses.Reduction.NONE ) From 0b69dd6d083c545e03a6671daa3868e7d84612e5 Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Fri, 10 Apr 2020 17:02:42 +0800 Subject: [PATCH 15/27] [KappaLoss] change according to review --- tensorflow_addons/losses/BUILD | 32 ++++---------------------- tensorflow_addons/losses/README.md | 1 - tensorflow_addons/losses/__init__.py | 5 ++-- tensorflow_addons/losses/kappa_loss.py | 4 ---- 4 files changed, 7 insertions(+), 35 deletions(-) diff --git a/tensorflow_addons/losses/BUILD b/tensorflow_addons/losses/BUILD index e44c14e59c..fa215dc049 100644 --- a/tensorflow_addons/losses/BUILD +++ b/tensorflow_addons/losses/BUILD @@ -4,19 +4,7 @@ package(default_visibility = ["//visibility:public"]) py_library( name = "losses", - srcs = [ - "__init__.py", - "contrastive.py", - "focal_loss.py", - "giou_loss.py", - "kappa_loss.py", - "lifted.py", - "metric_learning.py", - "npairs.py", - "quantiles.py", - "sparsemax_loss.py", - "triplet.py", - ], + srcs = glob(["*.py"]), deps = [ "//tensorflow_addons/activations", "//tensorflow_addons/utils", @@ -26,21 +14,9 @@ py_library( py_test( name = "losses_test", size = "small", - srcs = glob(["*_test.py"]), - main = "run_all_test.py", - deps = [ - ":losses", - ], -) - -py_test( - name = "kappa_loss_test", - size = "small", - srcs = [ - "kappa_loss_test.py", - ], - main = "kappa_loss_test.py", + srcs = glob(["tests/*"]), + main = "tests/run_all_test.py", deps = [ ":losses", ], -) +) \ No newline at end of file diff --git a/tensorflow_addons/losses/README.md b/tensorflow_addons/losses/README.md index 1564ade51c..b0f6b0d799 100644 --- a/tensorflow_addons/losses/README.md +++ b/tensorflow_addons/losses/README.md @@ -3,7 +3,6 @@ ## Components https://www.tensorflow.org/addons/api_docs/python/tfa/losses - ## Contribution Guidelines #### Standard API In order to conform with the current API standard, all losses diff --git a/tensorflow_addons/losses/__init__.py b/tensorflow_addons/losses/__init__.py index 789b20835f..bf3952dc75 100644 --- a/tensorflow_addons/losses/__init__.py +++ b/tensorflow_addons/losses/__init__.py @@ -22,10 +22,10 @@ from tensorflow_addons.losses.giou_loss import giou_loss, GIoULoss from tensorflow_addons.losses.lifted import lifted_struct_loss, LiftedStructLoss from tensorflow_addons.losses.sparsemax_loss import sparsemax_loss, SparsemaxLoss -from tensorflow_addons.losses.triplet import triplet_semihard_loss, TripletSemiHardLoss -from tensorflow_addons.losses.kappa_loss import WeightedKappaLoss from tensorflow_addons.losses.triplet import ( + triplet_semihard_loss, triplet_hard_loss, + TripletSemiHardLoss, TripletHardLoss, ) from tensorflow_addons.losses.quantiles import pinball_loss, PinballLoss @@ -37,3 +37,4 @@ npairs_multilabel_loss, NpairsMultilabelLoss, ) +from tensorflow_addons.losses.kappa_loss import WeightedKappaLoss \ No newline at end of file diff --git a/tensorflow_addons/losses/kappa_loss.py b/tensorflow_addons/losses/kappa_loss.py index 590e908d3c..b912fc41d9 100644 --- a/tensorflow_addons/losses/kappa_loss.py +++ b/tensorflow_addons/losses/kappa_loss.py @@ -14,10 +14,6 @@ # ============================================================================== """Implements Weighted kappa loss.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import tensorflow as tf from tensorflow_addons.utils.types import Number from typeguard import typechecked From c8cb0c30b5c0e9133a9753cca9cadf249d67b582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E6=96=87=E6=95=8F?= Date: Fri, 10 Apr 2020 17:04:40 +0800 Subject: [PATCH 16/27] Update tensorflow_addons/losses/kappa_loss.py Co-Authored-By: Gabriel de Marmiesse --- tensorflow_addons/losses/kappa_loss.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow_addons/losses/kappa_loss.py b/tensorflow_addons/losses/kappa_loss.py index d390e75483..f51a5e3e2c 100644 --- a/tensorflow_addons/losses/kappa_loss.py +++ b/tensorflow_addons/losses/kappa_loss.py @@ -167,5 +167,5 @@ def get_config(self): "epsilon": self.epsilon, "dtype": self.dtype, } - base_config = super(WeightedKappaLoss, self).get_config() + base_config = super().get_config() return dict(list(base_config.items()) + list(config.items())) From 32a3a86fc329e0ac8a8ff430c27099e9a4844d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E6=96=87=E6=95=8F?= Date: Fri, 10 Apr 2020 17:04:50 +0800 Subject: [PATCH 17/27] Update tensorflow_addons/losses/kappa_loss.py Co-Authored-By: Gabriel de Marmiesse --- tensorflow_addons/losses/kappa_loss.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow_addons/losses/kappa_loss.py b/tensorflow_addons/losses/kappa_loss.py index f51a5e3e2c..78797d1f5b 100644 --- a/tensorflow_addons/losses/kappa_loss.py +++ b/tensorflow_addons/losses/kappa_loss.py @@ -168,4 +168,4 @@ def get_config(self): "dtype": self.dtype, } base_config = super().get_config() - return dict(list(base_config.items()) + list(config.items())) + return {**base_config, **config} From 5020c1b43f7138000bf483deb7ad840ca4ebcd38 Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Fri, 10 Apr 2020 18:15:15 +0800 Subject: [PATCH 18/27] [KappaLoss] change accroding to code review --- tensorflow_addons/losses/kappa_loss.py | 14 ++-- tensorflow_addons/losses/kappa_loss_test.py | 87 +++++++++------------ 2 files changed, 44 insertions(+), 57 deletions(-) diff --git a/tensorflow_addons/losses/kappa_loss.py b/tensorflow_addons/losses/kappa_loss.py index 78797d1f5b..c786032061 100644 --- a/tensorflow_addons/losses/kappa_loss.py +++ b/tensorflow_addons/losses/kappa_loss.py @@ -137,16 +137,14 @@ def __init__( label_vec = tf.range(num_classes, dtype=dtype) self.row_label_vec = tf.reshape(label_vec, [1, num_classes]) self.col_label_vec = tf.reshape(label_vec, [num_classes, 1]) + col_mat = tf.tile(self.col_label_vec, [1, num_classes]) + row_mat = tf.tile(self.row_label_vec, [num_classes, 1]) if weightage == "linear": - self.weight_mat = tf.abs( - tf.tile(self.col_label_vec, [1, num_classes]) - - tf.tile(self.row_label_vec, [num_classes, 1]), - ) / tf.cast(num_classes - 1, dtype=dtype) + divisor = tf.cast(num_classes - 1, dtype=dtype) + self.weight_mat = tf.abs(col_mat - row_mat) / divisor else: - self.weight_mat = ( - tf.tile(self.col_label_vec, [1, num_classes]) - - tf.tile(self.row_label_vec, [num_classes, 1]) - ) ** 2 / tf.cast((num_classes - 1) ** 2, dtype=dtype) + divisor = tf.cast((num_classes - 1) ** 2, dtype=dtype) + self.weight_mat = (col_mat - row_mat) ** 2 / divisor def call(self, y_true, y_pred): return _weighted_kappa_loss( diff --git a/tensorflow_addons/losses/kappa_loss_test.py b/tensorflow_addons/losses/kappa_loss_test.py index 87c52964cb..eaf1c68077 100644 --- a/tensorflow_addons/losses/kappa_loss_test.py +++ b/tensorflow_addons/losses/kappa_loss_test.py @@ -13,15 +13,9 @@ # limitations under the License. # ============================================================================== """Tests for Weighted Kappa loss.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function import numpy as np -import tensorflow as tf - from tensorflow_addons.losses.kappa_loss import WeightedKappaLoss -from tensorflow_addons.utils import test_utils def weighted_kappa_loss_np(y_true, y_pred, weightage="quadratic", eps=1e-6): @@ -55,51 +49,46 @@ def weighted_kappa_loss_np(y_true, y_pred, weightage="quadratic", eps=1e-6): return np.log(numerator / denominator + eps) -@test_utils.run_all_in_graph_and_eager_modes -class WeightedKappaLossTest(tf.test.TestCase): - def test_linear_weighted_kappa_loss(self): - y_true = np.array([[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]]) - - y_pred = np.array( - [ - [0.1, 0.2, 0.6, 0.1], - [0.1, 0.5, 0.3, 0.1], - [0.8, 0.05, 0.05, 0.1], - [0.01, 0.09, 0.1, 0.8], - ], - dtype=np.float32, - ) - kappa_loss = WeightedKappaLoss(num_classes=4, weightage="linear") - loss = kappa_loss(y_true, y_pred) - loss_np = weighted_kappa_loss_np(y_true, y_pred, weightage="linear") - self.assertAlmostEqual(self.evaluate(loss), loss_np, 5) +def test_linear_weighted_kappa_loss(): + y_true = np.array([[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]]) - def test_quadratic_weighted_kappa_loss(self): - y_true = np.array([[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]]) + y_pred = np.array( + [ + [0.1, 0.2, 0.6, 0.1], + [0.1, 0.5, 0.3, 0.1], + [0.8, 0.05, 0.05, 0.1], + [0.01, 0.09, 0.1, 0.8], + ], + dtype=np.float32, + ) + kappa_loss = WeightedKappaLoss(num_classes=4, weightage="linear") + loss = kappa_loss(y_true, y_pred) + loss_np = weighted_kappa_loss_np(y_true, y_pred, weightage="linear") + np.testing.assert_allclose(loss, loss_np, rtol=5, atol=5) - y_pred = np.array( - [ - [0.1, 0.2, 0.6, 0.1], - [0.1, 0.5, 0.3, 0.1], - [0.8, 0.05, 0.05, 0.1], - [0.01, 0.09, 0.1, 0.8], - ], - dtype=np.float32, - ) - kappa_loss = WeightedKappaLoss(num_classes=4) - loss = kappa_loss(y_true, y_pred) - loss_np = weighted_kappa_loss_np(y_true, y_pred) - self.assertAlmostEqual(self.evaluate(loss), loss_np, 5) - def test_config(self): - kappa_loss = WeightedKappaLoss( - num_classes=4, weightage="linear", name="kappa_loss", epsilon=0.001, - ) - self.assertEqual(kappa_loss.num_classes, 4) - self.assertEqual(kappa_loss.weightage, "linear") - self.assertEqual(kappa_loss.name, "kappa_loss") - self.assertAlmostEqual(kappa_loss.epsilon, 0.001, 1e-6) +def test_quadratic_weighted_kappa_loss(): + y_true = np.array([[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]]) + y_pred = np.array( + [ + [0.1, 0.2, 0.6, 0.1], + [0.1, 0.5, 0.3, 0.1], + [0.8, 0.05, 0.05, 0.1], + [0.01, 0.09, 0.1, 0.8], + ], + dtype=np.float32, + ) + kappa_loss = WeightedKappaLoss(num_classes=4) + loss = kappa_loss(y_true, y_pred) + loss_np = weighted_kappa_loss_np(y_true, y_pred) + np.testing.assert_allclose(loss, loss_np, rtol=5, atol=5) -if __name__ == "__main__": - tf.test.main() +def test_config(): + kappa_loss = WeightedKappaLoss( + num_classes=4, weightage="linear", name="kappa_loss", epsilon=0.001, + ) + assert kappa_loss.num_classes == 4 + assert kappa_loss.weightage == "linear" + assert kappa_loss.name == "kappa_loss" + np.testing.assert_allclose(kappa_loss.epsilon, 0.001, 1e-6) From 8cc4bb20602d32ce520767311cae2a22f5ef310c Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Fri, 10 Apr 2020 18:41:54 +0800 Subject: [PATCH 19/27] [KappaLoss] change code format --- tensorflow_addons/losses/BUILD | 2 +- tensorflow_addons/losses/__init__.py | 2 +- tensorflow_addons/losses/kappa_loss.py | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tensorflow_addons/losses/BUILD b/tensorflow_addons/losses/BUILD index fa215dc049..8b7d39bf64 100644 --- a/tensorflow_addons/losses/BUILD +++ b/tensorflow_addons/losses/BUILD @@ -19,4 +19,4 @@ py_test( deps = [ ":losses", ], -) \ No newline at end of file +) diff --git a/tensorflow_addons/losses/__init__.py b/tensorflow_addons/losses/__init__.py index bf3952dc75..1109c72e85 100644 --- a/tensorflow_addons/losses/__init__.py +++ b/tensorflow_addons/losses/__init__.py @@ -37,4 +37,4 @@ npairs_multilabel_loss, NpairsMultilabelLoss, ) -from tensorflow_addons.losses.kappa_loss import WeightedKappaLoss \ No newline at end of file +from tensorflow_addons.losses.kappa_loss import WeightedKappaLoss diff --git a/tensorflow_addons/losses/kappa_loss.py b/tensorflow_addons/losses/kappa_loss.py index c786032061..d9588d76a2 100644 --- a/tensorflow_addons/losses/kappa_loss.py +++ b/tensorflow_addons/losses/kappa_loss.py @@ -123,9 +123,7 @@ def __init__( i.e. not any one of ['linear', 'quadratic'] """ - super().__init__( - name=name, reduction=tf.keras.losses.Reduction.NONE - ) + super().__init__(name=name, reduction=tf.keras.losses.Reduction.NONE) if weightage not in ("linear", "quadratic"): raise ValueError("Unknown kappa weighting type.") From 491396bc69bd7273aee48250a52e81625ab0eaf1 Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Sun, 12 Apr 2020 09:30:48 +0800 Subject: [PATCH 20/27] [SoftKappaLoss] mv kappa_loss_test.py to losses/tests --- tensorflow_addons/losses/{ => tests}/kappa_loss_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename tensorflow_addons/losses/{ => tests}/kappa_loss_test.py (95%) diff --git a/tensorflow_addons/losses/kappa_loss_test.py b/tensorflow_addons/losses/tests/kappa_loss_test.py similarity index 95% rename from tensorflow_addons/losses/kappa_loss_test.py rename to tensorflow_addons/losses/tests/kappa_loss_test.py index eaf1c68077..1592a8faba 100644 --- a/tensorflow_addons/losses/kappa_loss_test.py +++ b/tensorflow_addons/losses/tests/kappa_loss_test.py @@ -64,7 +64,7 @@ def test_linear_weighted_kappa_loss(): kappa_loss = WeightedKappaLoss(num_classes=4, weightage="linear") loss = kappa_loss(y_true, y_pred) loss_np = weighted_kappa_loss_np(y_true, y_pred, weightage="linear") - np.testing.assert_allclose(loss, loss_np, rtol=5, atol=5) + np.testing.assert_allclose(loss, loss_np, rtol=1e-5, atol=1e-5) def test_quadratic_weighted_kappa_loss(): @@ -81,7 +81,7 @@ def test_quadratic_weighted_kappa_loss(): kappa_loss = WeightedKappaLoss(num_classes=4) loss = kappa_loss(y_true, y_pred) loss_np = weighted_kappa_loss_np(y_true, y_pred) - np.testing.assert_allclose(loss, loss_np, rtol=5, atol=5) + np.testing.assert_allclose(loss, loss_np, rtol=1e-5, atol=1e-5) def test_config(): From a31f279dea234089b46dcc631fcde793a8263716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E6=96=87=E6=95=8F?= Date: Sun, 12 Apr 2020 19:09:47 +0800 Subject: [PATCH 21/27] Update .github/CODEOWNERS Co-Authored-By: Gabriel de Marmiesse --- .github/CODEOWNERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 71cdc7265e..b5d997ebe3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -111,7 +111,8 @@ /tensorflow_addons/losses/tests/sparsemax_loss_test.py @andreasmadsen /tensorflow_addons/losses/triplet.py @lc0 /tensorflow_addons/losses/tests/triplet_test.py @lc0 -/tensorflow_addons/losses/kappa_loss*.py @wenmin-wu +/tensorflow_addons/losses/kappa_loss.py @wenmin-wu +/tensorflow_addons/losses/tests/kappa_loss_test.py @wenmin-wu /tensorflow_addons/metrics/cohens_kappa.py @aakashkumarnain /tensorflow_addons/metrics/tests/cohens_kappa_test.py @aakashkumarnain From 44c0deb8f33a291b16044aeaf3da6a42ccad7e03 Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Mon, 13 Apr 2020 13:58:22 +0800 Subject: [PATCH 22/27] [SoftKappaLoss] refine codes according to code review --- tensorflow_addons/losses/kappa_loss.py | 75 ++++----------- .../losses/tests/kappa_loss_test.py | 92 +++++++++---------- 2 files changed, 66 insertions(+), 101 deletions(-) diff --git a/tensorflow_addons/losses/kappa_loss.py b/tensorflow_addons/losses/kappa_loss.py index d9588d76a2..9456d199a2 100644 --- a/tensorflow_addons/losses/kappa_loss.py +++ b/tensorflow_addons/losses/kappa_loss.py @@ -20,47 +20,6 @@ from typing import Optional -def _weighted_kappa_loss( - y_true, - y_pred, - row_label_vec, - col_label_vec, - weight_mat, - epsilon=1e-6, - weightage="quadratic", - dtype=tf.float32, -): - y_true = tf.cast(y_true, dtype=dtype) - labels = tf.matmul(y_true, col_label_vec) - if weightage == "linear": - weight = tf.abs( - tf.tile(labels, [1, tf.shape(y_true)[1]]) - - tf.tile(row_label_vec, [tf.shape(y_true)[0], 1]) - ) - weight /= tf.cast(tf.shape(y_true)[1] - 1, dtype=dtype) - else: - weight = tf.pow( - tf.tile(labels, [1, tf.shape(y_true)[1]]) - - tf.tile(row_label_vec, [tf.shape(y_true)[0], 1]), - 2, - ) - weight /= tf.cast(tf.pow(tf.shape(y_true)[1] - 1, 2), dtype=dtype) - numerator = tf.reduce_sum(weight * y_pred) - - denominator = tf.reduce_sum( - tf.matmul( - tf.reduce_sum(y_true, axis=0, keepdims=True), - tf.matmul( - weight_mat, - tf.reduce_sum(y_pred, axis=0, keepdims=True), - transpose_b=True, - ), - ) - ) - denominator /= tf.cast(tf.shape(y_true)[0], dtype=dtype) - return tf.math.log(tf.math.divide_no_nan(numerator, denominator) + epsilon) - - @tf.keras.utils.register_keras_serializable(package="Addons") class WeightedKappaLoss(tf.keras.losses.Loss): """Implements the Weighted Kappa loss function. @@ -138,23 +97,29 @@ def __init__( col_mat = tf.tile(self.col_label_vec, [1, num_classes]) row_mat = tf.tile(self.row_label_vec, [num_classes, 1]) if weightage == "linear": - divisor = tf.cast(num_classes - 1, dtype=dtype) - self.weight_mat = tf.abs(col_mat - row_mat) / divisor + self.weight_mat = tf.abs(col_mat - row_mat) else: - divisor = tf.cast((num_classes - 1) ** 2, dtype=dtype) - self.weight_mat = (col_mat - row_mat) ** 2 / divisor + self.weight_mat = (col_mat - row_mat) ** 2 def call(self, y_true, y_pred): - return _weighted_kappa_loss( - y_true, - y_pred, - self.row_label_vec, - self.col_label_vec, - self.weight_mat, - self.epsilon, - self.weightage, - self.dtype, - ) + y_true = tf.cast(y_true, dtype=self.dtype) + batch_size = tf.shape(y_true)[0] + cat_labels = tf.matmul(y_true, self.col_label_vec) + cat_label_mat = tf.tile(cat_labels, [1, self.num_classes]) + row_label_mat = tf.tile(self.row_label_vec, [batch_size, 1]) + if self.weightage == "linear": + weight = tf.abs(cat_label_mat - row_label_mat) + else: + weight = (cat_label_mat - row_label_mat) ** 2 + numerator = tf.reduce_sum(weight * y_pred) + label_dist = tf.reduce_sum(y_true, axis=0, keepdims=True) + pred_dist = tf.reduce_sum(y_pred, axis=0, keepdims=True) + weighted_pred_dist = tf.matmul(self.weight_mat, pred_dist, + transpose_b=True) + denominator = tf.reduce_sum(tf.matmul(label_dist, weighted_pred_dist)) + denominator /= tf.cast(batch_size, dtype=self.dtype) + loss = tf.math.divide_no_nan(numerator, denominator) + return tf.math.log(loss + self.epsilon) def get_config(self): config = { diff --git a/tensorflow_addons/losses/tests/kappa_loss_test.py b/tensorflow_addons/losses/tests/kappa_loss_test.py index 1592a8faba..bf6821186c 100644 --- a/tensorflow_addons/losses/tests/kappa_loss_test.py +++ b/tensorflow_addons/losses/tests/kappa_loss_test.py @@ -14,71 +14,66 @@ # ============================================================================== """Tests for Weighted Kappa loss.""" +import pytest import numpy as np +import tensorflow as tf from tensorflow_addons.losses.kappa_loss import WeightedKappaLoss def weighted_kappa_loss_np(y_true, y_pred, weightage="quadratic", eps=1e-6): - """Implemented in non-optimized python code to avoid mistakes""" - num_samples, num_classes = y_true.shape numerator = 0 - true_classes = y_true.argmax(axis=1) - for i in range(num_samples): - true_class = true_classes[i] - for j in range(num_classes): - if weightage == "quadratic": - w_ij = np.power(j - true_class, 2) - else: - w_ij = np.abs(j - true_class) - numerator += w_ij * y_pred[i, j] + cat_labels = y_true.argmax(axis=1).reshape((-1, 1)) + label_mat = np.tile(cat_labels, (1, num_classes)) + label_mat_ = np.tile( + np.arange(num_classes).reshape(1, -1), + (num_samples, 1) + ) + if weightage == "linear": + weight = np.abs(label_mat - label_mat_) + else: + weight = (label_mat - label_mat_) ** 2 + numerator = (y_pred * weight).sum() + label_dist = y_true.sum(axis=0, keepdims=True) + pred_dist = y_pred.sum(axis=0, keepdims=True) - classes_count = y_true.sum(axis=0) - prob_sum = y_pred.sum(axis=0) + row_label_vec = np.arange(num_classes).reshape((1, num_classes)) + col_label_vec = row_label_vec.T + row_mat = np.tile(row_label_vec, (num_classes, 1)) + col_mat = np.tile(col_label_vec, (1, num_classes)) + if weightage == "quadratic": + weight_ = (col_mat - row_mat) ** 2 + else: + weight_ = np.abs(col_mat - row_mat) + weighted_pred_dist = np.matmul(weight_, pred_dist.T) + denominator = np.matmul(label_dist, weighted_pred_dist).sum() + denominator /= num_samples + return np.log(np.nan_to_num(numerator / denominator) + eps) - denominator = 0 - for i in range(num_classes): - inner_sum = 0 - for j in range(num_classes): - if weightage == "quadratic": - w_ij = np.power(i - j, 2) - else: - w_ij = np.abs(i - j) - inner_sum += w_ij * prob_sum[j] - denominator += inner_sum * classes_count[i] / num_samples - return np.log(numerator / denominator + eps) +def gen_labels_and_preds(num_samples, num_classes, seed=3): + np.random.seed(seed) + cat_labels = np.random.uniform(size=(num_samples, num_classes)) \ + .argmax(axis=1) + y_true = np.eye(num_classes, dtype='int')[cat_labels] + y_pred = np.random.uniform(size=(num_samples, num_classes)) + y_pred /= y_pred.sum(axis=1, keepdims=True) + return y_true, y_pred -def test_linear_weighted_kappa_loss(): - y_true = np.array([[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]]) - y_pred = np.array( - [ - [0.1, 0.2, 0.6, 0.1], - [0.1, 0.5, 0.3, 0.1], - [0.8, 0.05, 0.05, 0.1], - [0.01, 0.09, 0.1, 0.8], - ], - dtype=np.float32, - ) +@pytest.mark.parametrize("np_seed", [0, 1, 2, 3]) +def test_linear_weighted_kappa_loss(): + y_true, y_pred = gen_labels_and_preds(50, 4, np_seed) kappa_loss = WeightedKappaLoss(num_classes=4, weightage="linear") loss = kappa_loss(y_true, y_pred) loss_np = weighted_kappa_loss_np(y_true, y_pred, weightage="linear") np.testing.assert_allclose(loss, loss_np, rtol=1e-5, atol=1e-5) +@pytest.mark.parametrize("np_seed", [0, 1, 2, 3]) def test_quadratic_weighted_kappa_loss(): - y_true = np.array([[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]]) - y_pred = np.array( - [ - [0.1, 0.2, 0.6, 0.1], - [0.1, 0.5, 0.3, 0.1], - [0.8, 0.05, 0.05, 0.1], - [0.01, 0.09, 0.1, 0.8], - ], - dtype=np.float32, - ) - kappa_loss = WeightedKappaLoss(num_classes=4) + y_true, y_pred = gen_labels_and_preds(100, 3, np_seed) + kappa_loss = WeightedKappaLoss(num_classes=3) loss = kappa_loss(y_true, y_pred) loss_np = weighted_kappa_loss_np(y_true, y_pred) np.testing.assert_allclose(loss, loss_np, rtol=1e-5, atol=1e-5) @@ -92,3 +87,8 @@ def test_config(): assert kappa_loss.weightage == "linear" assert kappa_loss.name == "kappa_loss" np.testing.assert_allclose(kappa_loss.epsilon, 0.001, 1e-6) + + +def test_serialization(): + loss = WeightedKappaLoss(num_classes=3) + tf.keras.losses.deserialize(tf.keras.losses.serialize(loss)) From f557ef24e97d931b0dc3d4905b95ab3a51589cf3 Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Mon, 13 Apr 2020 14:05:13 +0800 Subject: [PATCH 23/27] [SoftKappaLoss] reformat codes --- tensorflow_addons/losses/kappa_loss.py | 5 ++--- tensorflow_addons/losses/tests/kappa_loss_test.py | 14 +++++--------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/tensorflow_addons/losses/kappa_loss.py b/tensorflow_addons/losses/kappa_loss.py index 9456d199a2..832cba3baa 100644 --- a/tensorflow_addons/losses/kappa_loss.py +++ b/tensorflow_addons/losses/kappa_loss.py @@ -114,9 +114,8 @@ def call(self, y_true, y_pred): numerator = tf.reduce_sum(weight * y_pred) label_dist = tf.reduce_sum(y_true, axis=0, keepdims=True) pred_dist = tf.reduce_sum(y_pred, axis=0, keepdims=True) - weighted_pred_dist = tf.matmul(self.weight_mat, pred_dist, - transpose_b=True) - denominator = tf.reduce_sum(tf.matmul(label_dist, weighted_pred_dist)) + w_pred_dist = tf.matmul(self.weight_mat, pred_dist, transpose_b=True) + denominator = tf.reduce_sum(tf.matmul(label_dist, w_pred_dist)) denominator /= tf.cast(batch_size, dtype=self.dtype) loss = tf.math.divide_no_nan(numerator, denominator) return tf.math.log(loss + self.epsilon) diff --git a/tensorflow_addons/losses/tests/kappa_loss_test.py b/tensorflow_addons/losses/tests/kappa_loss_test.py index bf6821186c..328bb3b517 100644 --- a/tensorflow_addons/losses/tests/kappa_loss_test.py +++ b/tensorflow_addons/losses/tests/kappa_loss_test.py @@ -22,13 +22,10 @@ def weighted_kappa_loss_np(y_true, y_pred, weightage="quadratic", eps=1e-6): num_samples, num_classes = y_true.shape - numerator = 0 cat_labels = y_true.argmax(axis=1).reshape((-1, 1)) label_mat = np.tile(cat_labels, (1, num_classes)) - label_mat_ = np.tile( - np.arange(num_classes).reshape(1, -1), - (num_samples, 1) - ) + row_label_vec = np.arange(num_classes).reshape((1, num_classes)) + label_mat_ = np.tile(row_label_vec, (num_samples, 1)) if weightage == "linear": weight = np.abs(label_mat - label_mat_) else: @@ -37,7 +34,6 @@ def weighted_kappa_loss_np(y_true, y_pred, weightage="quadratic", eps=1e-6): label_dist = y_true.sum(axis=0, keepdims=True) pred_dist = y_pred.sum(axis=0, keepdims=True) - row_label_vec = np.arange(num_classes).reshape((1, num_classes)) col_label_vec = row_label_vec.T row_mat = np.tile(row_label_vec, (num_classes, 1)) col_mat = np.tile(col_label_vec, (1, num_classes)) @@ -53,9 +49,9 @@ def weighted_kappa_loss_np(y_true, y_pred, weightage="quadratic", eps=1e-6): def gen_labels_and_preds(num_samples, num_classes, seed=3): np.random.seed(seed) - cat_labels = np.random.uniform(size=(num_samples, num_classes)) \ - .argmax(axis=1) - y_true = np.eye(num_classes, dtype='int')[cat_labels] + rands = np.random.uniform(size=(num_samples, num_classes)) + cat_labels = rands.argmax(axis=1) + y_true = np.eye(num_classes, dtype="int")[cat_labels] y_pred = np.random.uniform(size=(num_samples, num_classes)) y_pred /= y_pred.sum(axis=1, keepdims=True) return y_true, y_pred From ae5d52e502143cef860b25432e9339c5dc7c32a3 Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Mon, 13 Apr 2020 14:27:35 +0800 Subject: [PATCH 24/27] [SoftKappaLoss] fix np_deep not defined --- tensorflow_addons/losses/tests/kappa_loss_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tensorflow_addons/losses/tests/kappa_loss_test.py b/tensorflow_addons/losses/tests/kappa_loss_test.py index 328bb3b517..2ad88da529 100644 --- a/tensorflow_addons/losses/tests/kappa_loss_test.py +++ b/tensorflow_addons/losses/tests/kappa_loss_test.py @@ -58,7 +58,7 @@ def gen_labels_and_preds(num_samples, num_classes, seed=3): @pytest.mark.parametrize("np_seed", [0, 1, 2, 3]) -def test_linear_weighted_kappa_loss(): +def test_linear_weighted_kappa_loss(np_seed): y_true, y_pred = gen_labels_and_preds(50, 4, np_seed) kappa_loss = WeightedKappaLoss(num_classes=4, weightage="linear") loss = kappa_loss(y_true, y_pred) @@ -67,7 +67,7 @@ def test_linear_weighted_kappa_loss(): @pytest.mark.parametrize("np_seed", [0, 1, 2, 3]) -def test_quadratic_weighted_kappa_loss(): +def test_quadratic_weighted_kappa_loss(np_seed): y_true, y_pred = gen_labels_and_preds(100, 3, np_seed) kappa_loss = WeightedKappaLoss(num_classes=3) loss = kappa_loss(y_true, y_pred) From 42821bba17f3ebde50c81a5af318c21fb7f0a84c Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Mon, 13 Apr 2020 15:18:21 +0800 Subject: [PATCH 25/27] [SoftKappaLoss] fix tests problem --- tensorflow_addons/losses/kappa_loss.py | 3 ++- tensorflow_addons/losses/tests/kappa_loss_test.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tensorflow_addons/losses/kappa_loss.py b/tensorflow_addons/losses/kappa_loss.py index 832cba3baa..06a70572cb 100644 --- a/tensorflow_addons/losses/kappa_loss.py +++ b/tensorflow_addons/losses/kappa_loss.py @@ -62,6 +62,7 @@ def __init__( name: Optional[str] = "cohen_kappa_loss", epsilon: Optional[Number] = 1e-6, dtype: Optional[tf.DType] = tf.float32, + reduction: str = tf.keras.losses.Reduction.NONE, ): """Creates a `WeightedKappa` instance. @@ -82,7 +83,7 @@ def __init__( i.e. not any one of ['linear', 'quadratic'] """ - super().__init__(name=name, reduction=tf.keras.losses.Reduction.NONE) + super().__init__(name=name, reduction=reduction) if weightage not in ("linear", "quadratic"): raise ValueError("Unknown kappa weighting type.") diff --git a/tensorflow_addons/losses/tests/kappa_loss_test.py b/tensorflow_addons/losses/tests/kappa_loss_test.py index 2ad88da529..7e2077c351 100644 --- a/tensorflow_addons/losses/tests/kappa_loss_test.py +++ b/tensorflow_addons/losses/tests/kappa_loss_test.py @@ -61,6 +61,7 @@ def gen_labels_and_preds(num_samples, num_classes, seed=3): def test_linear_weighted_kappa_loss(np_seed): y_true, y_pred = gen_labels_and_preds(50, 4, np_seed) kappa_loss = WeightedKappaLoss(num_classes=4, weightage="linear") + y_pred = y_pred.astype(kappa_loss.dtype.as_numpy_dtype) loss = kappa_loss(y_true, y_pred) loss_np = weighted_kappa_loss_np(y_true, y_pred, weightage="linear") np.testing.assert_allclose(loss, loss_np, rtol=1e-5, atol=1e-5) @@ -70,6 +71,7 @@ def test_linear_weighted_kappa_loss(np_seed): def test_quadratic_weighted_kappa_loss(np_seed): y_true, y_pred = gen_labels_and_preds(100, 3, np_seed) kappa_loss = WeightedKappaLoss(num_classes=3) + y_pred = y_pred.astype(kappa_loss.dtype.as_numpy_dtype) loss = kappa_loss(y_true, y_pred) loss_np = weighted_kappa_loss_np(y_true, y_pred) np.testing.assert_allclose(loss, loss_np, rtol=1e-5, atol=1e-5) From ebfea4c46ec1715aa9663f3cc095ffe7af54a437 Mon Sep 17 00:00:00 2001 From: Wenmin Wu Date: Mon, 13 Apr 2020 17:17:29 +0800 Subject: [PATCH 26/27] [SoftKappaLoss] unnecessary change to tigger CI --- tensorflow_addons/losses/tests/kappa_loss_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow_addons/losses/tests/kappa_loss_test.py b/tensorflow_addons/losses/tests/kappa_loss_test.py index 7e2077c351..c77ebf8160 100644 --- a/tensorflow_addons/losses/tests/kappa_loss_test.py +++ b/tensorflow_addons/losses/tests/kappa_loss_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Tests for Weighted Kappa loss.""" +"""Tests for Weighted Kappa Loss.""" import pytest import numpy as np From 8633927be4c73992678ebbf7c373eab34a3f3917 Mon Sep 17 00:00:00 2001 From: gabrieldemarmiesse Date: Mon, 13 Apr 2020 09:37:52 +0000 Subject: [PATCH 27/27] Default value for the seed is not needed. --- tensorflow_addons/losses/tests/kappa_loss_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow_addons/losses/tests/kappa_loss_test.py b/tensorflow_addons/losses/tests/kappa_loss_test.py index c77ebf8160..3f450d319e 100644 --- a/tensorflow_addons/losses/tests/kappa_loss_test.py +++ b/tensorflow_addons/losses/tests/kappa_loss_test.py @@ -47,7 +47,7 @@ def weighted_kappa_loss_np(y_true, y_pred, weightage="quadratic", eps=1e-6): return np.log(np.nan_to_num(numerator / denominator) + eps) -def gen_labels_and_preds(num_samples, num_classes, seed=3): +def gen_labels_and_preds(num_samples, num_classes, seed): np.random.seed(seed) rands = np.random.uniform(size=(num_samples, num_classes)) cat_labels = rands.argmax(axis=1)