/
loss_utils.py
94 lines (80 loc) · 3.66 KB
/
loss_utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# coding=utf-8
# Copyright 2020 The TF-Agents Authors.
#
# 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
#
# https://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.
"""Loss utility code."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from typing import Optional, Text
import tensorflow as tf # pylint: disable=g-explicit-tensorflow-version-import
from tf_agents.typing import types
def pinball_loss(
y_true: types.Tensor,
y_pred: types.Tensor,
weights: types.Float = 1.0,
scope: Optional[Text] = None,
loss_collection: tf.compat.v1.GraphKeys = tf.compat.v1.GraphKeys.LOSSES,
reduction: tf.compat.v1.losses.Reduction = tf.compat.v1.losses.Reduction.SUM_BY_NONZERO_WEIGHTS,
quantile: float = 0.5,
) -> types.Float:
"""Adds a Pinball loss for quantile regression.
```
loss = quantile * (y_true - y_pred) if y_true > y_pred
loss = (quantile - 1) * (y_true - y_pred) otherwise
```
See: https://en.wikipedia.org/wiki/Quantile_regression#Quantiles
`weights` acts as a coefficient for the loss. If a scalar is provided, then
the loss is simply scaled by the given value. If `weights` is a tensor of size
`[batch_size]`, then the total loss for each sample of the batch is rescaled
by the corresponding element in the `weights` vector. If the shape of
`weights` matches the shape of `predictions`, then the loss of each
measurable element of `predictions` is scaled by the corresponding value of
`weights`.
Args:
y_true: tensor of true targets.
y_pred: tensor of predicted targets.
weights: Optional `Tensor` whose rank is either 0, or the same rank as
`labels`, and must be broadcastable to `labels` (i.e., all dimensions must
be either `1`, or the same as the corresponding `losses` dimension).
scope: The scope for the operations performed in computing the loss.
loss_collection: collection to which the loss will be added.
reduction: Type of reduction to apply to loss.
quantile: A float between 0. and 1., the quantile we want to regress.
Returns:
Weighted Pinball loss float `Tensor`. If `reduction` is `NONE`, this has the
same shape as `labels`; otherwise, it is scalar.
Raises:
ValueError: If the shape of `predictions` doesn't match that of `labels` or
if the shape of `weights` is invalid. Also if `labels` or `predictions`
is None.
@compatibility(eager)
The `loss_collection` argument is ignored when executing eagerly. Consider
holding on to the return value or collecting losses via a `tf.keras.Model`.
@end_compatibility
"""
if y_true is None:
raise ValueError('y_true must not be None.')
if y_pred is None:
raise ValueError('y_pred must not be None.')
with tf.compat.v1.name_scope(
scope, 'pinball_loss', (y_pred, y_true, weights)
) as scope:
y_pred = tf.cast(y_pred, dtype=tf.float32)
y_true = tf.cast(y_true, dtype=tf.float32)
y_pred.get_shape().assert_is_compatible_with(y_true.get_shape())
error = tf.subtract(y_true, y_pred)
loss_tensor = tf.maximum(quantile * error, (quantile - 1) * error)
return tf.compat.v1.losses.compute_weighted_loss(
loss_tensor, weights, scope, loss_collection, reduction=reduction
)