Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tensorflow_addons/metrics/r_square.py: 24%
83 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 07:57 +0000
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 07:57 +0000
1# Copyright 2019 The TensorFlow Authors. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ==============================================================================
15"""Implements R^2 scores."""
16import warnings
18import numpy as np
19import tensorflow as tf
20from tensorflow.keras import backend as K
21from tensorflow.keras.metrics import Metric
22from tensorflow.python.ops import weights_broadcast_ops
24from typeguard import typechecked
25from tensorflow_addons.utils.types import AcceptableDTypes
28_VALID_MULTIOUTPUT = {"raw_values", "uniform_average", "variance_weighted"}
31def _reduce_average(
32 input_tensor: tf.Tensor, axis=None, keepdims=False, weights=None
33) -> tf.Tensor:
34 """Computes the (weighted) mean of elements across dimensions of a tensor."""
35 if weights is None:
36 return tf.reduce_mean(input_tensor, axis=axis, keepdims=keepdims)
38 weighted_sum = tf.reduce_sum(weights * input_tensor, axis=axis, keepdims=keepdims)
39 sum_of_weights = tf.reduce_sum(weights, axis=axis, keepdims=keepdims)
40 average = weighted_sum / sum_of_weights
41 return average
44@tf.keras.utils.register_keras_serializable(package="Addons")
45class RSquare(Metric):
46 """Compute R^2 score.
48 This is also called the [coefficient of determination
49 ](https://en.wikipedia.org/wiki/Coefficient_of_determination).
50 It tells how close are data to the fitted regression line.
52 - Highest score can be 1.0 and it indicates that the predictors
53 perfectly accounts for variation in the target.
54 - Score 0.0 indicates that the predictors do not
55 account for variation in the target.
56 - It can also be negative if the model is worse.
58 The sample weighting for this metric implementation mimics the
59 behaviour of the [scikit-learn implementation
60 ](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.r2_score.html)
61 of the same metric.
63 Can also calculate the Adjusted R2 Score.
65 Args:
66 multioutput: `string`, the reduce method for scores.
67 Should be one of `["raw_values", "uniform_average", "variance_weighted"]`.
68 name: (Optional) string name of the metric instance.
69 dtype: (Optional) data type of the metric result.
70 num_regressors: (Optional) Number of indepedent regressors used (Adjusted R2).
71 Defaults to zero(standard R2 score).
73 Usage:
75 >>> y_true = np.array([1, 4, 3], dtype=np.float32)
76 >>> y_pred = np.array([2, 4, 4], dtype=np.float32)
77 >>> metric = tfa.metrics.r_square.RSquare()
78 >>> metric.update_state(y_true, y_pred)
79 >>> result = metric.result()
80 >>> result.numpy()
81 0.57142854
82 """
84 @typechecked
85 def __init__(
86 self,
87 name: str = "r_square",
88 dtype: AcceptableDTypes = None,
89 multioutput: str = "uniform_average",
90 num_regressors: tf.int32 = 0,
91 **kwargs,
92 ):
93 super().__init__(name=name, dtype=dtype, **kwargs)
95 if "y_shape" in kwargs:
96 warnings.warn(
97 "y_shape has been removed, because it's automatically derived,"
98 "and will be deprecated in Addons 0.18.",
99 DeprecationWarning,
100 )
102 if multioutput not in _VALID_MULTIOUTPUT:
103 raise ValueError(
104 "The multioutput argument must be one of {}, but was: {}".format(
105 _VALID_MULTIOUTPUT, multioutput
106 )
107 )
108 self.multioutput = multioutput
109 self.num_regressors = num_regressors
110 self.num_samples = self.add_weight(name="num_samples", dtype=tf.int32)
112 def update_state(self, y_true, y_pred, sample_weight=None) -> None:
113 if not hasattr(self, "squared_sum"):
114 self.squared_sum = self.add_weight(
115 name="squared_sum",
116 shape=y_true.shape[1:],
117 initializer="zeros",
118 dtype=self._dtype,
119 )
120 if not hasattr(self, "sum"):
121 self.sum = self.add_weight(
122 name="sum",
123 shape=y_true.shape[1:],
124 initializer="zeros",
125 dtype=self._dtype,
126 )
127 if not hasattr(self, "res"):
128 self.res = self.add_weight(
129 name="residual",
130 shape=y_true.shape[1:],
131 initializer="zeros",
132 dtype=self._dtype,
133 )
134 if not hasattr(self, "count"):
135 self.count = self.add_weight(
136 name="count",
137 shape=y_true.shape[1:],
138 initializer="zeros",
139 dtype=self._dtype,
140 )
142 y_true = tf.cast(y_true, dtype=self._dtype)
143 y_pred = tf.cast(y_pred, dtype=self._dtype)
144 if sample_weight is None:
145 sample_weight = 1
146 sample_weight = tf.cast(sample_weight, dtype=self._dtype)
147 sample_weight = weights_broadcast_ops.broadcast_weights(
148 weights=sample_weight, values=y_true
149 )
151 weighted_y_true = y_true * sample_weight
152 self.sum.assign_add(tf.reduce_sum(weighted_y_true, axis=0))
153 self.squared_sum.assign_add(tf.reduce_sum(y_true * weighted_y_true, axis=0))
154 self.res.assign_add(
155 tf.reduce_sum((y_true - y_pred) ** 2 * sample_weight, axis=0)
156 )
157 self.count.assign_add(tf.reduce_sum(sample_weight, axis=0))
158 self.num_samples.assign_add(tf.size(y_true))
160 def result(self) -> tf.Tensor:
161 mean = self.sum / self.count
162 total = self.squared_sum - self.sum * mean
163 raw_scores = 1 - (self.res / total)
164 raw_scores = tf.where(tf.math.is_inf(raw_scores), 0.0, raw_scores)
166 if self.multioutput == "raw_values":
167 r2_score = raw_scores
168 elif self.multioutput == "uniform_average":
169 r2_score = tf.reduce_mean(raw_scores)
170 elif self.multioutput == "variance_weighted":
171 r2_score = _reduce_average(raw_scores, weights=total)
172 else:
173 raise RuntimeError(
174 "The multioutput attribute must be one of {}, but was: {}".format(
175 _VALID_MULTIOUTPUT, self.multioutput
176 )
177 )
179 if self.num_regressors < 0:
180 raise ValueError(
181 "num_regressors parameter should be greater than or equal to zero"
182 )
184 if self.num_regressors != 0:
185 if self.num_regressors > self.num_samples - 1:
186 UserWarning(
187 "More independent predictors than datapoints in adjusted r2 score. Falls back to standard r2 "
188 "score."
189 )
190 elif self.num_regressors == self.num_samples - 1:
191 UserWarning(
192 "Division by zero in adjusted r2 score. Falls back to standard r2 score."
193 )
194 else:
195 n = tf.cast(self.num_samples, dtype=tf.float32)
196 p = tf.cast(self.num_regressors, dtype=tf.float32)
198 num = tf.multiply(tf.subtract(1.0, r2_score), tf.subtract(n, 1.0))
199 den = tf.subtract(tf.subtract(n, p), 1.0)
200 r2_score = tf.subtract(1.0, tf.divide(num, den))
202 return r2_score
204 def reset_state(self) -> None:
205 # The state of the metric will be reset at the start of each epoch.
206 K.batch_set_value([(v, np.zeros(v.shape)) for v in self.variables])
208 def reset_states(self):
209 # Backwards compatibility alias of `reset_state`. New classes should
210 # only implement `reset_state`.
211 # Required in Tensorflow < 2.5.0
212 return self.reset_state()
214 def get_config(self):
215 config = {
216 "multioutput": self.multioutput,
217 }
218 base_config = super().get_config()
219 return {**base_config, **config}