braintools 0.0.2__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- braintools/__init__.py +27 -0
- braintools/functional/__init__.py +25 -0
- braintools/functional/_activations.py +733 -0
- braintools/functional/_normalization.py +62 -0
- braintools/functional/_spikes.py +90 -0
- braintools/init/__init__.py +26 -0
- braintools/init/_base.py +36 -0
- braintools/init/_generic.py +205 -0
- braintools/init/_random_inits.py +489 -0
- braintools/init/_regular_inits.py +109 -0
- braintools/input/__init__.py +27 -0
- braintools/input/currents.py +380 -0
- braintools/input/currents_test.py +111 -0
- braintools/metric/__init__.py +46 -0
- braintools/metric/_classification.py +569 -0
- braintools/metric/_classification_test.py +625 -0
- braintools/metric/_correlation.py +302 -0
- braintools/metric/_correlation_test.py +124 -0
- braintools/metric/_fenchel_young.py +67 -0
- braintools/metric/_fenchel_young_test.py +55 -0
- braintools/metric/_firings.py +85 -0
- braintools/metric/_firings_test.py +41 -0
- braintools/metric/_lfp.py +139 -0
- braintools/metric/_ranking.py +163 -0
- braintools/metric/_ranking_test.py +242 -0
- braintools/metric/_regression.py +425 -0
- braintools/metric/_regression_test.py +133 -0
- braintools/metric/_smoothing.py +46 -0
- braintools/metric/_smoothing_test.py +43 -0
- braintools/metric/_util.py +25 -0
- braintools/optim/__init__.py +23 -0
- braintools/optim/_lr_scheduler.py +482 -0
- braintools/optim/_sgd_optimizer.py +1130 -0
- braintools-0.0.2.dist-info/LICENSE +202 -0
- braintools-0.0.2.dist-info/METADATA +93 -0
- braintools-0.0.2.dist-info/RECORD +38 -0
- braintools-0.0.2.dist-info/WHEEL +6 -0
- braintools-0.0.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
# This file is modified from [optax/losses](https://github.com/google-deepmind/optax).
|
|
2
|
+
# The copyright notice is as follows:
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2024 BrainPy Ecosystem Limited. All Rights Reserved.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
# ==============================================================================
|
|
18
|
+
|
|
19
|
+
"""
|
|
20
|
+
Classification losses.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from typing import Optional
|
|
24
|
+
|
|
25
|
+
import braincore as bc
|
|
26
|
+
import jax
|
|
27
|
+
import jax.numpy as jnp
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
'sigmoid_binary_cross_entropy',
|
|
31
|
+
'hinge_loss',
|
|
32
|
+
'perceptron_loss',
|
|
33
|
+
'softmax_cross_entropy',
|
|
34
|
+
'softmax_cross_entropy_with_integer_labels',
|
|
35
|
+
'multiclass_hinge_loss',
|
|
36
|
+
'multiclass_perceptron_loss',
|
|
37
|
+
'poly_loss_cross_entropy',
|
|
38
|
+
'kl_divergence',
|
|
39
|
+
'kl_divergence_with_log_targets',
|
|
40
|
+
'convex_kl_divergence',
|
|
41
|
+
'ctc_loss',
|
|
42
|
+
'ctc_loss_with_forward_probs',
|
|
43
|
+
'sigmoid_focal_loss',
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def assert_is_float(array):
|
|
48
|
+
assert bc.math.is_float(array), 'Array must be float.'
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def assert_is_int(array):
|
|
52
|
+
assert bc.math.is_int(array), 'Array must be int.'
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def sigmoid_binary_cross_entropy(
|
|
56
|
+
logits: bc.typing.ArrayLike,
|
|
57
|
+
labels: bc.typing.ArrayLike,
|
|
58
|
+
):
|
|
59
|
+
"""Computes element-wise sigmoid cross entropy given logits and labels.
|
|
60
|
+
|
|
61
|
+
This function can be used for binary or multiclass classification (where each
|
|
62
|
+
class is an independent binary prediction and different classes are not
|
|
63
|
+
mutually exclusive e.g. predicting that an image contains both a cat
|
|
64
|
+
and a dog.)
|
|
65
|
+
|
|
66
|
+
Because this function is overloaded, please ensure your `logits` and `labels`
|
|
67
|
+
are compatible with each other. If you're passing in binary `labels` (values
|
|
68
|
+
in {0, 1}), ensure your `logits` correspond to class 1 only. If you're
|
|
69
|
+
passing in per-class target probabilities or one-hot `labels`, please ensure
|
|
70
|
+
your `logits` are also multiclass. Be particularly careful if you're relying
|
|
71
|
+
on implicit broadcasting to reshape `logits` or `labels`.
|
|
72
|
+
|
|
73
|
+
References:
|
|
74
|
+
[Goodfellow et al, 2016](http://www.deeplearningbook.org/contents/prob.html)
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
logits: Each element is the unnormalized log probability of a binary
|
|
78
|
+
prediction. See note about compatibility with `labels` above.
|
|
79
|
+
labels: Binary labels whose values are {0,1} or multi-class target
|
|
80
|
+
probabilities. See note about compatibility with `logits` above.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
cross entropy for each binary prediction, same shape as `logits`.
|
|
84
|
+
"""
|
|
85
|
+
labels = labels.astype(logits.dtype)
|
|
86
|
+
log_p = jax.nn.log_sigmoid(logits)
|
|
87
|
+
log_not_p = jax.nn.log_sigmoid(-logits)
|
|
88
|
+
return -labels * log_p - (1. - labels) * log_not_p
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def hinge_loss(
|
|
92
|
+
predictor_outputs: bc.typing.ArrayLike,
|
|
93
|
+
targets: bc.typing.ArrayLike
|
|
94
|
+
) -> bc.typing.ArrayLike:
|
|
95
|
+
"""Computes the hinge loss for binary classification.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
predictor_outputs: Outputs of the decision function.
|
|
99
|
+
targets: Target values. Target values should be strictly in the set {-1, 1}.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
loss value.
|
|
103
|
+
"""
|
|
104
|
+
return jnp.maximum(0, 1 - predictor_outputs * targets)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def perceptron_loss(
|
|
108
|
+
predictor_outputs: bc.typing.ArrayLike,
|
|
109
|
+
targets: bc.typing.ArrayLike
|
|
110
|
+
) -> bc.typing.ArrayLike:
|
|
111
|
+
"""Binary perceptron loss.
|
|
112
|
+
|
|
113
|
+
References:
|
|
114
|
+
https://en.wikipedia.org/wiki/Perceptron
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
predictor_outputs: score produced by the model (float).
|
|
118
|
+
targets: Target values. Target values should be strictly in the set {-1, 1}.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
loss value.
|
|
122
|
+
"""
|
|
123
|
+
assert jnp.shape(predictor_outputs) == jnp.shape(targets), 'shape mismatch'
|
|
124
|
+
return jnp.maximum(0, - predictor_outputs * targets)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def softmax_cross_entropy(
|
|
128
|
+
logits: bc.typing.ArrayLike,
|
|
129
|
+
labels: bc.typing.ArrayLike,
|
|
130
|
+
) -> bc.typing.ArrayLike:
|
|
131
|
+
"""Computes the softmax cross entropy between sets of logits and labels.
|
|
132
|
+
|
|
133
|
+
Measures the probability error in discrete classification tasks in which
|
|
134
|
+
the classes are mutually exclusive (each entry is in exactly one class).
|
|
135
|
+
For example, each CIFAR-10 image is labeled with one and only one label:
|
|
136
|
+
an image can be a dog or a truck, but not both.
|
|
137
|
+
|
|
138
|
+
References:
|
|
139
|
+
[Goodfellow et al, 2016](http://www.deeplearningbook.org/contents/prob.html)
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
logits: Unnormalized log probabilities, with shape `[..., num_classes]`.
|
|
143
|
+
labels: Valid probability distributions (non-negative, sum to 1), e.g a
|
|
144
|
+
one hot encoding specifying the correct class for each input;
|
|
145
|
+
must have a shape broadcastable to `[..., num_classes]`.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
cross entropy between each prediction and the corresponding target
|
|
149
|
+
distributions, with shape `[...]`.
|
|
150
|
+
"""
|
|
151
|
+
ret = -jnp.sum(labels * jax.nn.log_softmax(logits, axis=-1), axis=-1)
|
|
152
|
+
return ret
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def softmax_cross_entropy_with_integer_labels(
|
|
156
|
+
logits: bc.typing.ArrayLike,
|
|
157
|
+
labels: bc.typing.ArrayLike,
|
|
158
|
+
) -> bc.typing.ArrayLike:
|
|
159
|
+
"""Computes softmax cross entropy between sets of logits and integer labels.
|
|
160
|
+
|
|
161
|
+
Measures the probability error in discrete classification tasks in which
|
|
162
|
+
the classes are mutually exclusive (each entry is in exactly one class).
|
|
163
|
+
For example, each CIFAR-10 image is labeled with one and only one label:
|
|
164
|
+
an image can be a dog or a truck, but not both.
|
|
165
|
+
|
|
166
|
+
References:
|
|
167
|
+
[Goodfellow et al, 2016](http://www.deeplearningbook.org/contents/prob.html)
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
logits: Unnormalized log probabilities, with shape `[..., num_classes]`.
|
|
171
|
+
labels: Integers specifying the correct class for each input, with shape
|
|
172
|
+
`[...]`.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Cross entropy between each prediction and the corresponding target
|
|
176
|
+
distributions, with shape `[...]`.
|
|
177
|
+
"""
|
|
178
|
+
assert_is_float(logits)
|
|
179
|
+
assert_is_int(labels)
|
|
180
|
+
# This is like jnp.take_along_axis(jax.nn.log_softmax(...), ...) except that
|
|
181
|
+
# we avoid subtracting the normalizer from all values, just from the values
|
|
182
|
+
# for the correct labels.
|
|
183
|
+
logits_max = jnp.max(logits, axis=-1, keepdims=True)
|
|
184
|
+
logits -= jax.lax.stop_gradient(logits_max)
|
|
185
|
+
label_logits = jnp.take_along_axis(logits, labels[..., None], axis=-1)[..., 0]
|
|
186
|
+
log_normalizers = jnp.log(jnp.sum(jnp.exp(logits), axis=-1))
|
|
187
|
+
return log_normalizers - label_logits
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
_dot_last_dim = jnp.vectorize(jnp.dot, signature='(n),(n)->()')
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def multiclass_hinge_loss(
|
|
194
|
+
scores: bc.typing.ArrayLike,
|
|
195
|
+
labels: bc.typing.ArrayLike,
|
|
196
|
+
) -> bc.typing.ArrayLike:
|
|
197
|
+
"""Multiclass hinge loss.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
scores: scores produced by the model (floats).
|
|
201
|
+
labels: ground-truth integer label.
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
loss value
|
|
205
|
+
|
|
206
|
+
References:
|
|
207
|
+
https://en.wikipedia.org/wiki/Hinge_loss
|
|
208
|
+
|
|
209
|
+
"""
|
|
210
|
+
one_hot_labels = jax.nn.one_hot(labels, scores.shape[-1])
|
|
211
|
+
return (jnp.max(scores + 1.0 - one_hot_labels, axis=-1) -
|
|
212
|
+
_dot_last_dim(scores, one_hot_labels))
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def multiclass_perceptron_loss(
|
|
216
|
+
scores: bc.typing.ArrayLike,
|
|
217
|
+
labels: bc.typing.ArrayLike,
|
|
218
|
+
) -> bc.typing.ArrayLike:
|
|
219
|
+
"""Binary perceptron loss.
|
|
220
|
+
|
|
221
|
+
References:
|
|
222
|
+
Michael Collins. Discriminative training methods for Hidden Markov Models:
|
|
223
|
+
Theory and experiments with perceptron algorithms. EMNLP 2002
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
scores: score produced by the model.
|
|
227
|
+
labels: ground-truth integer label.
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
loss value.
|
|
231
|
+
"""
|
|
232
|
+
one_hot_labels = jax.nn.one_hot(labels, scores.shape[-1])
|
|
233
|
+
return jnp.max(scores, axis=-1) - _dot_last_dim(scores, one_hot_labels)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def poly_loss_cross_entropy(
|
|
237
|
+
logits: bc.typing.ArrayLike,
|
|
238
|
+
labels: bc.typing.ArrayLike,
|
|
239
|
+
epsilon: float = 2.0
|
|
240
|
+
) -> bc.typing.ArrayLike:
|
|
241
|
+
r"""
|
|
242
|
+
Computes PolyLoss between logits and labels.
|
|
243
|
+
|
|
244
|
+
The PolyLoss is a loss function that decomposes commonly
|
|
245
|
+
used classification loss functions into a series of weighted
|
|
246
|
+
polynomial bases. It is inspired by the Taylor expansion of
|
|
247
|
+
cross-entropy loss and focal loss in the bases of :math:`(1 − P_t)^j`.
|
|
248
|
+
|
|
249
|
+
.. math::
|
|
250
|
+
L_{Poly} = \sum_1^\infty \alpha_j \cdot (1 - P_t)^j \\
|
|
251
|
+
L_{Poly-N} = (\epsilon_1 + 1) \cdot (1 - P_t) + \ldots + \\
|
|
252
|
+
(\epsilon_N + \frac{1}{N}) \cdot (1 - P_t)^N +
|
|
253
|
+
\frac{1}{N + 1} \cdot (1 - P_t)^{N + 1} + \ldots = \\
|
|
254
|
+
- \log(P_t) + \sum_{j = 1}^N \epsilon_j \cdot (1 - P_t)^j
|
|
255
|
+
|
|
256
|
+
This function provides a simplified version of :math:`L_{Poly-N}`
|
|
257
|
+
with only the coefficient of the first polynomial term being changed.
|
|
258
|
+
|
|
259
|
+
References:
|
|
260
|
+
[Zhaoqi Leng et al, 2022](https://arxiv.org/pdf/2204.12511.pdf)
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
logits: Unnormalized log probabilities, with shape `[..., num_classes]`.
|
|
264
|
+
labels: Valid probability distributions (non-negative, sum to 1), e.g. a
|
|
265
|
+
one hot encoding specifying the correct class for each input;
|
|
266
|
+
must have a shape broadcastable to `[..., num_classes]`.
|
|
267
|
+
epsilon: The coefficient of the first polynomial term.
|
|
268
|
+
According to the paper, the following values are recommended:
|
|
269
|
+
- For the ImageNet 2d image classification, epsilon = 2.0.
|
|
270
|
+
- For the 2d Instance Segmentation and object detection, epsilon = -1.0.
|
|
271
|
+
- It is also recommended to adjust this value based on the task, e.g. by
|
|
272
|
+
using grid search.
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
Poly loss between each prediction and the corresponding target
|
|
276
|
+
distributions, with shape `[...]`.
|
|
277
|
+
"""
|
|
278
|
+
one_minus_pt = jnp.sum(labels * (1 - jax.nn.softmax(logits)), axis=-1)
|
|
279
|
+
cross_entropy = softmax_cross_entropy(logits=logits, labels=labels)
|
|
280
|
+
return cross_entropy + epsilon * one_minus_pt
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def kl_divergence(
|
|
284
|
+
log_predictions: bc.typing.ArrayLike,
|
|
285
|
+
targets: bc.typing.ArrayLike
|
|
286
|
+
) -> bc.typing.ArrayLike:
|
|
287
|
+
"""Computes the Kullback-Leibler divergence (relative entropy) loss.
|
|
288
|
+
|
|
289
|
+
Measures the information gain achieved if target probability distribution
|
|
290
|
+
would be used instead of predicted probability distribution.
|
|
291
|
+
|
|
292
|
+
References:
|
|
293
|
+
[Kullback, Leibler, 1951](https://www.jstor.org/stable/2236703)
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
log_predictions: Probabilities of predicted distribution with shape [...,
|
|
297
|
+
dim]. Expected to be in the log-space to avoid underflow.
|
|
298
|
+
targets: Probabilities of target distribution with shape [..., dim].
|
|
299
|
+
Expected to be strictly positive.
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
Kullback-Leibler divergence of predicted distribution from target
|
|
303
|
+
distribution with shape [...].
|
|
304
|
+
"""
|
|
305
|
+
assert_is_float(log_predictions)
|
|
306
|
+
assert_is_float(targets)
|
|
307
|
+
loss = targets * (jnp.where(targets == 0, 0, jnp.log(targets)) - log_predictions)
|
|
308
|
+
return jnp.sum(loss, axis=-1)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def kl_divergence_with_log_targets(
|
|
312
|
+
log_predictions: bc.typing.ArrayLike,
|
|
313
|
+
log_targets: bc.typing.ArrayLike
|
|
314
|
+
) -> bc.typing.ArrayLike:
|
|
315
|
+
"""Computes the Kullback-Leibler divergence (relative entropy) loss.
|
|
316
|
+
|
|
317
|
+
Version of kl_div_loss where targets are given in log-space.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
log_predictions: Probabilities of predicted distribution with shape
|
|
321
|
+
[..., dim]. Expected to be in the log-space to avoid underflow.
|
|
322
|
+
log_targets: Probabilities of target distribution with shape [..., dim].
|
|
323
|
+
Expected to be in the log-space.
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
Kullback-Leibler divergence of predicted distribution from target
|
|
327
|
+
distribution with shape [...].
|
|
328
|
+
"""
|
|
329
|
+
assert_is_float(log_predictions)
|
|
330
|
+
assert_is_float(log_targets)
|
|
331
|
+
loss = jnp.exp(log_targets) * (log_targets - log_predictions)
|
|
332
|
+
return jnp.sum(loss, axis=-1)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def convex_kl_divergence(
|
|
336
|
+
log_predictions: bc.typing.ArrayLike,
|
|
337
|
+
targets: bc.typing.ArrayLike
|
|
338
|
+
) -> bc.typing.ArrayLike:
|
|
339
|
+
"""Computes a convex version of the Kullback-Leibler divergence loss.
|
|
340
|
+
|
|
341
|
+
Measures the information gain achieved if target probability distribution
|
|
342
|
+
would be used instead of predicted probability distribution.
|
|
343
|
+
This version is jointly convex in p (targets) and q (log_predictions).
|
|
344
|
+
|
|
345
|
+
References:
|
|
346
|
+
[Kullback, Leibler, 1951](https://www.jstor.org/stable/2236703)
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
log_predictions: Probabilities of predicted distribution with shape [...,
|
|
350
|
+
dim]. Expected to be in the log-space to avoid underflow.
|
|
351
|
+
targets: Probabilities of target distribution with shape [..., dim].
|
|
352
|
+
Expected to be strictly positive.
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
Kullback-Leibler divergence of predicted distribution from target
|
|
356
|
+
distribution with shape [...].
|
|
357
|
+
"""
|
|
358
|
+
return kl_divergence(log_predictions, targets) + jnp.sum(jnp.exp(log_predictions) - targets, axis=-1)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def ctc_loss_with_forward_probs(
|
|
362
|
+
logits: bc.typing.ArrayLike,
|
|
363
|
+
logit_paddings: bc.typing.ArrayLike,
|
|
364
|
+
labels: bc.typing.ArrayLike,
|
|
365
|
+
label_paddings: bc.typing.ArrayLike,
|
|
366
|
+
blank_id: int = 0,
|
|
367
|
+
log_epsilon: float = -1e5
|
|
368
|
+
) -> tuple[bc.typing.ArrayLike, bc.typing.ArrayLike, bc.typing.ArrayLike]:
|
|
369
|
+
r"""Computes CTC loss and CTC forward-probabilities.
|
|
370
|
+
|
|
371
|
+
The CTC loss is a loss function based on log-likelihoods of the model that
|
|
372
|
+
introduces a special blank symbol :math:`\phi` to represent variable-length
|
|
373
|
+
output sequences.
|
|
374
|
+
|
|
375
|
+
Forward probabilities returned by this function, as auxiliary results, are
|
|
376
|
+
grouped into two part: blank alpha-probability and non-blank alpha
|
|
377
|
+
probability. Those are defined as follows:
|
|
378
|
+
|
|
379
|
+
.. math::
|
|
380
|
+
\alpha_{\mathrm{BLANK}}(t, n) =
|
|
381
|
+
\sum_{\pi_{1:t-1}} p(\pi_t = \phi | \pi_{1:t-1}, y_{1:n-1}, \cdots), \\
|
|
382
|
+
\alpha_{\mathrm{LABEL}}(t, n) =
|
|
383
|
+
\sum_{\pi_{1:t-1}} p(\pi_t = y_n | \pi_{1:t-1}, y_{1:n-1}, \cdots).
|
|
384
|
+
|
|
385
|
+
Here, :math:`\pi` denotes the alignment sequence in the reference
|
|
386
|
+
[Graves et al, 2006] that is blank-inserted representations of ``labels``.
|
|
387
|
+
The return values are the logarithms of the above probabilities.
|
|
388
|
+
|
|
389
|
+
References:
|
|
390
|
+
[Graves et al, 2006](https://dl.acm.org/doi/abs/10.1145/1143844.1143891)
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
logits: (B, T, K)-array containing logits of each class where B denotes
|
|
394
|
+
the batch size, T denotes the max time frames in ``logits``, and K
|
|
395
|
+
denotes the number of classes including a class for blanks.
|
|
396
|
+
logit_paddings: (B, T)-array. Padding indicators for ``logits``. Each
|
|
397
|
+
element must be either 1.0 or 0.0, and ``logitpaddings[b, t] == 1.0``
|
|
398
|
+
denotes that ``logits[b, t, :]`` are padded values.
|
|
399
|
+
labels: (B, N)-array containing reference integer labels where N denotes
|
|
400
|
+
the max time frames in the label sequence.
|
|
401
|
+
label_paddings: (B, N)-array. Padding indicators for ``labels``. Each
|
|
402
|
+
element must be either 1.0 or 0.0, and ``labelpaddings[b, n] == 1.0``
|
|
403
|
+
denotes that ``labels[b, n]`` is a padded label. In the current
|
|
404
|
+
implementation, ``labels`` must be right-padded, i.e. each row
|
|
405
|
+
``labelpaddings[b, :]`` must be repetition of zeroes, followed by
|
|
406
|
+
repetition of ones.
|
|
407
|
+
blank_id: Id for blank token. ``logits[b, :, blank_id]`` are used as
|
|
408
|
+
probabilities of blank symbols.
|
|
409
|
+
log_epsilon: Numerically-stable approximation of log(+0).
|
|
410
|
+
|
|
411
|
+
Returns:
|
|
412
|
+
A tuple ``(loss_value, logalpha_blank, logalpha_nonblank)``. Here,
|
|
413
|
+
``loss_value`` is a (B,)-array containing the loss values for each sequence
|
|
414
|
+
in the batch, ``logalpha_blank`` and ``logalpha_nonblank`` are
|
|
415
|
+
(T, B, N+1)-arrays where the (t, b, n)-th element denotes
|
|
416
|
+
\log \alpha_B(t, n) and \log \alpha_L(t, n), respectively, for ``b``-th
|
|
417
|
+
sequence in the batch.
|
|
418
|
+
"""
|
|
419
|
+
assert logits.ndim == 3, 'logits must have shape (B, T, K)'
|
|
420
|
+
assert labels.ndim == 2, 'labels must have shape (B, N)'
|
|
421
|
+
batchsize, unused_maxinputlen, num_classes = logits.shape
|
|
422
|
+
batchsize_of_labels, maxlabellen = labels.shape
|
|
423
|
+
assert batchsize == batchsize_of_labels, 'batchsize mismatch'
|
|
424
|
+
assert label_paddings.shape == labels.shape, 'padding shape mismatch'
|
|
425
|
+
assert logits.shape[:2] == logit_paddings.shape, 'padding shape mismatch'
|
|
426
|
+
|
|
427
|
+
logprobs = jax.nn.log_softmax(logits)
|
|
428
|
+
labellens = maxlabellen - jnp.sum(label_paddings, axis=1).astype(jnp.int32)
|
|
429
|
+
|
|
430
|
+
# repeat[b, n] == 1.0 when label[b, n] == label[b, n+1].
|
|
431
|
+
repeat = (labels[:, :-1] == labels[:, 1:]).astype(jnp.float32)
|
|
432
|
+
repeat = jnp.pad(repeat, ((0, 0), (0, 1)))
|
|
433
|
+
|
|
434
|
+
logprobs_phi = logprobs[:, :, blank_id:blank_id + 1] # [B, T, 1]
|
|
435
|
+
logprobs_phi = jnp.transpose(logprobs_phi, (1, 0, 2)) # [T, B, 1]
|
|
436
|
+
|
|
437
|
+
one_hot = jax.nn.one_hot(labels, num_classes=num_classes) # [B, N, K]
|
|
438
|
+
logprobs_emit = jnp.einsum('btk,bnk->btn', logprobs, one_hot)
|
|
439
|
+
logprobs_emit = jnp.transpose(logprobs_emit, (1, 0, 2)) # [T, B, N]
|
|
440
|
+
|
|
441
|
+
logalpha_phi_init = jnp.ones((batchsize, maxlabellen + 1)) * log_epsilon # [B, N]
|
|
442
|
+
logalpha_phi_init = logalpha_phi_init.at[:, 0].set(0.0)
|
|
443
|
+
logalpha_emit_init = jnp.ones((batchsize, maxlabellen)) * log_epsilon
|
|
444
|
+
|
|
445
|
+
def update_phi_score(phi, added_score):
|
|
446
|
+
# Update `phi[:, 1:]`` with adding `added_score` in log space.
|
|
447
|
+
return jnp.concatenate([phi[:, :1], jnp.logaddexp(phi[:, 1:], added_score)], axis=-1)
|
|
448
|
+
|
|
449
|
+
def loop_body(prev, x):
|
|
450
|
+
prev_phi, prev_emit = prev
|
|
451
|
+
# emit-to-phi epsilon transition, except if the next label is repetition
|
|
452
|
+
prev_phi_orig = prev_phi
|
|
453
|
+
prev_phi = update_phi_score(prev_phi, prev_emit + log_epsilon * repeat)
|
|
454
|
+
|
|
455
|
+
logprob_emit, logprob_phi, pad = x
|
|
456
|
+
|
|
457
|
+
# phi-to-emit transition
|
|
458
|
+
next_emit = jnp.logaddexp(prev_phi[:, :-1] + logprob_emit,
|
|
459
|
+
prev_emit + logprob_emit)
|
|
460
|
+
# self-loop transition
|
|
461
|
+
next_phi = prev_phi + logprob_phi
|
|
462
|
+
# emit-to-phi blank transition only when the next label is repetition
|
|
463
|
+
next_phi = update_phi_score(
|
|
464
|
+
next_phi, prev_emit + logprob_phi + log_epsilon * (1.0 - repeat))
|
|
465
|
+
|
|
466
|
+
pad = pad.reshape((batchsize, 1))
|
|
467
|
+
next_emit = pad * prev_emit + (1.0 - pad) * next_emit
|
|
468
|
+
next_phi = pad * prev_phi_orig + (1.0 - pad) * next_phi
|
|
469
|
+
|
|
470
|
+
return (next_phi, next_emit), (next_phi, next_emit)
|
|
471
|
+
|
|
472
|
+
xs = (logprobs_emit, logprobs_phi, logit_paddings.transpose((1, 0)))
|
|
473
|
+
_, (logalpha_phi, logalpha_emit) = jax.lax.scan(loop_body, (logalpha_phi_init, logalpha_emit_init), xs)
|
|
474
|
+
|
|
475
|
+
# last row needs to be updated with the last epsilon transition
|
|
476
|
+
logalpha_phi_last = update_phi_score(logalpha_phi[-1], logalpha_emit[-1])
|
|
477
|
+
logalpha_phi = logalpha_phi.at[-1].set(logalpha_phi_last)
|
|
478
|
+
|
|
479
|
+
# extract per_seq_loss
|
|
480
|
+
one_hot = jax.nn.one_hot(labellens, num_classes=maxlabellen + 1) # [B, N+1]
|
|
481
|
+
per_seq_loss = -jnp.einsum('bn,bn->b', logalpha_phi_last, one_hot) # pylint:disable=invalid-unary-operand-type
|
|
482
|
+
|
|
483
|
+
return per_seq_loss, logalpha_phi, logalpha_emit
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def ctc_loss(
|
|
487
|
+
logits: bc.typing.ArrayLike,
|
|
488
|
+
logit_paddings: bc.typing.ArrayLike,
|
|
489
|
+
labels: bc.typing.ArrayLike,
|
|
490
|
+
label_paddings: bc.typing.ArrayLike,
|
|
491
|
+
blank_id: int = 0,
|
|
492
|
+
log_epsilon: float = -1e5
|
|
493
|
+
) -> bc.typing.ArrayLike:
|
|
494
|
+
"""Computes CTC loss.
|
|
495
|
+
|
|
496
|
+
See docstring for ``ctc_loss_with_forward_probs`` for details.
|
|
497
|
+
|
|
498
|
+
Args:
|
|
499
|
+
logits: (B, T, K)-array containing logits of each class where B denotes
|
|
500
|
+
the batch size, T denotes the max time frames in ``logits``, and K
|
|
501
|
+
denotes the number of classes including a class for blanks.
|
|
502
|
+
logit_paddings: (B, T)-array. Padding indicators for ``logits``. Each
|
|
503
|
+
element must be either 1.0 or 0.0, and ``logitpaddings[b, t] == 1.0``
|
|
504
|
+
denotes that ``logits[b, t, :]`` are padded values.
|
|
505
|
+
labels: (B, N)-array containing reference integer labels where N denotes
|
|
506
|
+
the max time frames in the label sequence.
|
|
507
|
+
label_paddings: (B, N)-array. Padding indicators for ``labels``. Each
|
|
508
|
+
element must be either 1.0 or 0.0, and ``labelpaddings[b, n] == 1.0``
|
|
509
|
+
denotes that ``labels[b, n]`` is a padded label. In the current
|
|
510
|
+
implementation, ``labels`` must be right-padded, i.e. each row
|
|
511
|
+
``labelpaddings[b, :]`` must be repetition of zeroes, followed by
|
|
512
|
+
repetition of ones.
|
|
513
|
+
blank_id: Id for blank token. ``logits[b, :, blank_id]`` are used as
|
|
514
|
+
probabilities of blank symbols.
|
|
515
|
+
log_epsilon: Numerically-stable approximation of log(+0).
|
|
516
|
+
|
|
517
|
+
Returns:
|
|
518
|
+
(B,)-array containing loss values for each sequence in the batch.
|
|
519
|
+
"""
|
|
520
|
+
per_seq_loss, _, _ = ctc_loss_with_forward_probs(
|
|
521
|
+
logits, logit_paddings, labels, label_paddings,
|
|
522
|
+
blank_id=blank_id, log_epsilon=log_epsilon
|
|
523
|
+
)
|
|
524
|
+
return per_seq_loss
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
def sigmoid_focal_loss(
|
|
528
|
+
logits: bc.typing.ArrayLike,
|
|
529
|
+
labels: bc.typing.ArrayLike,
|
|
530
|
+
alpha: Optional[float] = None,
|
|
531
|
+
gamma: float = 2.,
|
|
532
|
+
) -> bc.typing.ArrayLike:
|
|
533
|
+
"""Sigmoid focal loss.
|
|
534
|
+
|
|
535
|
+
The focal loss is a re-weighted cross entropy for unbalanced problems.
|
|
536
|
+
Use this loss function if classes are not mutually exclusive.
|
|
537
|
+
See `sigmoid_binary_cross_entropy` for more information.
|
|
538
|
+
|
|
539
|
+
References:
|
|
540
|
+
Lin et al. 2018. https://arxiv.org/pdf/1708.02002.pdf
|
|
541
|
+
|
|
542
|
+
Args:
|
|
543
|
+
logits: Array of floats. The predictions for each example.
|
|
544
|
+
The predictions for each example.
|
|
545
|
+
labels: Array of floats. Labels and logits must have
|
|
546
|
+
the same shape. The label array must contain the binary
|
|
547
|
+
classification labels for each element in the data set
|
|
548
|
+
(0 for the out-of-class and 1 for in-class).
|
|
549
|
+
alpha: (optional) Weighting factor in range (0,1) to balance
|
|
550
|
+
positive vs negative examples. Default None (no weighting).
|
|
551
|
+
gamma: Exponent of the modulating factor (1 - p_t).
|
|
552
|
+
Balances easy vs hard examples.
|
|
553
|
+
|
|
554
|
+
Returns:
|
|
555
|
+
A loss value array with a shape identical to the logits and target
|
|
556
|
+
arrays.
|
|
557
|
+
"""
|
|
558
|
+
alpha = -1 if alpha is None else alpha
|
|
559
|
+
assert_is_float(logits)
|
|
560
|
+
# see also the original paper's implementation at:
|
|
561
|
+
# https://github.com/facebookresearch/fvcore/blob/main/fvcore/nn/focal_loss.py
|
|
562
|
+
p = jax.nn.sigmoid(logits)
|
|
563
|
+
ce_loss = sigmoid_binary_cross_entropy(logits, labels)
|
|
564
|
+
p_t = p * labels + (1 - p) * (1 - labels)
|
|
565
|
+
loss = ce_loss * ((1 - p_t) ** gamma)
|
|
566
|
+
weighted = lambda loss_arg: (alpha * labels + (1 - alpha) * (1 - labels)) * loss_arg
|
|
567
|
+
not_weighted = lambda loss_arg: loss_arg
|
|
568
|
+
loss = jax.lax.cond(alpha >= 0, weighted, not_weighted, loss)
|
|
569
|
+
return loss
|