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

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 

17 

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 

23 

24from typeguard import typechecked 

25from tensorflow_addons.utils.types import AcceptableDTypes 

26 

27 

28_VALID_MULTIOUTPUT = {"raw_values", "uniform_average", "variance_weighted"} 

29 

30 

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) 

37 

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 

42 

43 

44@tf.keras.utils.register_keras_serializable(package="Addons") 

45class RSquare(Metric): 

46 """Compute R^2 score. 

47 

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. 

51 

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. 

57 

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. 

62 

63 Can also calculate the Adjusted R2 Score. 

64 

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). 

72 

73 Usage: 

74 

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 """ 

83 

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) 

94 

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 ) 

101 

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) 

111 

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 ) 

141 

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 ) 

150 

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)) 

159 

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) 

165 

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 ) 

178 

179 if self.num_regressors < 0: 

180 raise ValueError( 

181 "num_regressors parameter should be greater than or equal to zero" 

182 ) 

183 

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) 

197 

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)) 

201 

202 return r2_score 

203 

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]) 

207 

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() 

213 

214 def get_config(self): 

215 config = { 

216 "multioutput": self.multioutput, 

217 } 

218 base_config = super().get_config() 

219 return {**base_config, **config}