File size: 7,205 Bytes
1f5470c |
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 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
import numpy as np
from keras.src import backend
from keras.src.api_export import keras_export
from keras.src.utils import tf_utils
@keras_export("keras.utils.normalize")
def normalize(x, axis=-1, order=2):
"""Normalizes an array.
If the input is a NumPy array, a NumPy array will be returned.
If it's a backend tensor, a backend tensor will be returned.
Args:
x: Array to normalize.
axis: axis along which to normalize.
order: Normalization order (e.g. `order=2` for L2 norm).
Returns:
A normalized copy of the array.
"""
from keras.src import ops
if isinstance(x, np.ndarray):
# NumPy input
norm = np.atleast_1d(np.linalg.norm(x, order, axis))
norm[norm == 0] = 1
# axis cannot be `None`
axis = axis or -1
return x / np.expand_dims(norm, axis)
# Backend tensor input
return ops.nn.normalize(x, axis=axis, order=order)
@keras_export("keras.utils.to_categorical")
def to_categorical(x, num_classes=None):
"""Converts a class vector (integers) to binary class matrix.
E.g. for use with `categorical_crossentropy`.
Args:
x: Array-like with class values to be converted into a matrix
(integers from 0 to `num_classes - 1`).
num_classes: Total number of classes. If `None`, this would be inferred
as `max(x) + 1`. Defaults to `None`.
Returns:
A binary matrix representation of the input as a NumPy array. The class
axis is placed last.
Example:
>>> a = keras.utils.to_categorical([0, 1, 2, 3], num_classes=4)
>>> print(a)
[[1. 0. 0. 0.]
[0. 1. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]]
>>> b = np.array([.9, .04, .03, .03,
... .3, .45, .15, .13,
... .04, .01, .94, .05,
... .12, .21, .5, .17]).reshape(4,4)
>>> loss = keras.ops.categorical_crossentropy(a, b)
>>> print(np.around(loss, 5))
[0.10536 0.82807 0.1011 1.77196]
>>> loss = keras.ops.categorical_crossentropy(a, a)
>>> print(np.around(loss, 5))
[0. 0. 0. 0.]
"""
if backend.is_tensor(x):
input_shape = backend.core.shape(x)
# Shrink the last dimension if the shape is (..., 1).
if (
input_shape is not None
and len(input_shape) > 1
and input_shape[-1] == 1
):
newshape = tuple(input_shape[:-1])
x = backend.numpy.reshape(x, newshape)
return backend.nn.one_hot(x, num_classes)
x = np.array(x, dtype="int64")
input_shape = x.shape
# Shrink the last dimension if the shape is (..., 1).
if input_shape and input_shape[-1] == 1 and len(input_shape) > 1:
input_shape = tuple(input_shape[:-1])
x = x.reshape(-1)
if not num_classes:
num_classes = np.max(x) + 1
batch_size = x.shape[0]
categorical = np.zeros((batch_size, num_classes))
categorical[np.arange(batch_size), x] = 1
output_shape = input_shape + (num_classes,)
categorical = np.reshape(categorical, output_shape)
return categorical
def encode_categorical_inputs(
inputs,
output_mode,
depth,
dtype,
sparse=False,
count_weights=None,
backend_module=None,
):
"""Encodes categorical inputs according to output_mode.
Args:
inputs: the inputs to encode.
output_mode: one of `"int"`, `"one_hot"`, `"multi_hot"`, or `"count"`.
depth: number of classes, this will be the last dimension of the output.
dtype: the dtype of the output, unless `count_weights` is not `None`.
sparse: whether the output should be sparse for backends supporting it.
count_weights: weights to apply if `output_mode` is `"count"`.
backend_module: the backend to use instead of the current one.
Returns: the encoded inputs.
"""
backend_module = backend_module or backend
if output_mode == "int":
return backend_module.cast(inputs, dtype=dtype)
rank_of_inputs = len(backend_module.shape(inputs))
# In all cases, we should uprank scalar input to a single sample.
if rank_of_inputs == 0:
inputs = backend_module.numpy.expand_dims(inputs, -1)
rank_of_inputs = 1
if (
backend_module.__name__.endswith("tensorflow")
and rank_of_inputs <= 2
and output_mode in ("multi_hot", "count")
):
# TF only fastpath. Uses bincount; faster. Doesn't work for rank 3+.
try:
return tf_utils.tf_encode_categorical_inputs(
inputs,
output_mode,
depth,
dtype=dtype,
sparse=sparse,
count_weights=count_weights,
)
except ValueError:
pass
if output_mode == "multi_hot":
return backend_module.nn.multi_hot(
inputs, depth, dtype=dtype, sparse=sparse
)
elif output_mode == "one_hot":
input_shape = backend_module.core.shape(inputs)
# Shrink the last dimension if the shape is (..., 1).
if (
input_shape is not None
and len(input_shape) > 1
and input_shape[-1] == 1
):
newshape = tuple(input_shape[:-1])
inputs = backend_module.numpy.reshape(inputs, newshape)
return backend_module.nn.one_hot(
inputs, depth, dtype=dtype, sparse=sparse
)
elif output_mode == "count":
# We don't use `ops.bincount` because its output has a dynamic shape
# (last dimension is the highest value of `inputs`). We implement a
# narrower use case where `minlength` and `maxlength` (not supported by
# `ops.bincount`) are the same and static value: `depth`. We also don't
# need to support indices that are negative or greater than `depth`.
reduction_axis = 1 if len(inputs.shape) > 1 else 0
if count_weights is not None:
dtype = count_weights.dtype
one_hot_encoding = backend_module.nn.one_hot(
inputs, depth, dtype=dtype, sparse=sparse
)
if count_weights is not None:
count_weights = backend_module.numpy.expand_dims(count_weights, -1)
one_hot_encoding = one_hot_encoding * count_weights
outputs = backend_module.numpy.sum(
one_hot_encoding,
axis=reduction_axis,
)
return outputs
def build_pos_neg_masks(
query_labels,
key_labels,
remove_diagonal=True,
):
from keras.src import ops
if ops.ndim(query_labels) == 1:
query_labels = ops.reshape(query_labels, (-1, 1))
if ops.ndim(key_labels) == 1:
key_labels = ops.reshape(key_labels, (-1, 1))
positive_mask = ops.equal(query_labels, ops.transpose(key_labels))
negative_mask = ops.logical_not(positive_mask)
if remove_diagonal:
positive_mask = ops.logical_and(
positive_mask,
~ops.eye(
ops.size(query_labels),
ops.size(key_labels),
k=0,
dtype="bool",
),
)
return positive_mask, negative_mask
|