Add VGG and Xception Keras Applications.
This commit is contained in:
parent
b5b0796752
commit
0b3660a038
95
keras_core/applications/applications_test.py
Normal file
95
keras_core/applications/applications_test.py
Normal file
@ -0,0 +1,95 @@
|
||||
import numpy as np
|
||||
from absl.testing import parameterized
|
||||
|
||||
from keras_core import backend
|
||||
from keras_core import testing
|
||||
from keras_core.applications import vgg16
|
||||
from keras_core.applications import vgg19
|
||||
from keras_core.applications import xception
|
||||
from keras_core.utils import file_utils
|
||||
from keras_core.utils import image_utils
|
||||
|
||||
MODEL_LIST = [
|
||||
# cls, last_dim
|
||||
(vgg16.VGG16, 512, vgg16),
|
||||
(vgg19.VGG19, 512, vgg19),
|
||||
(xception.Xception, 2048, xception),
|
||||
]
|
||||
|
||||
|
||||
def _get_elephant(target_size):
|
||||
# For models that don't include a Flatten step,
|
||||
# the default is to accept variable-size inputs
|
||||
# even when loading ImageNet weights (since it is possible).
|
||||
# In this case, default to 299x299.
|
||||
TEST_IMAGE_PATH = (
|
||||
"https://storage.googleapis.com/tensorflow/"
|
||||
"keras-applications/tests/elephant.jpg"
|
||||
)
|
||||
|
||||
if target_size[0] is None:
|
||||
target_size = (299, 299)
|
||||
test_image = file_utils.get_file("elephant.jpg", TEST_IMAGE_PATH)
|
||||
img = image_utils.load_img(test_image, target_size=tuple(target_size))
|
||||
x = image_utils.img_to_array(img)
|
||||
return np.expand_dims(x, axis=0)
|
||||
|
||||
|
||||
class ApplicationsTest(testing.TestCase, parameterized.TestCase):
|
||||
@parameterized.parameters(MODEL_LIST)
|
||||
def test_application_notop_variable_input_channels(self, app, last_dim, _):
|
||||
# Test compatibility with 1 channel
|
||||
if backend.image_data_format() == "channels_first":
|
||||
input_shape = (1, None, None)
|
||||
else:
|
||||
input_shape = (None, None, 1)
|
||||
model = app(weights=None, include_top=False, input_shape=input_shape)
|
||||
output_shape = list(model.outputs[0].shape)
|
||||
self.assertEqual(output_shape, [None, None, None, last_dim])
|
||||
|
||||
# Test compatibility with 4 channels
|
||||
if backend.image_data_format() == "channels_first":
|
||||
input_shape = (4, None, None)
|
||||
else:
|
||||
input_shape = (None, None, 4)
|
||||
model = app(weights=None, include_top=False, input_shape=input_shape)
|
||||
output_shape = list(model.outputs[0].shape)
|
||||
self.assertEqual(output_shape, [None, None, None, last_dim])
|
||||
|
||||
@parameterized.parameters(MODEL_LIST)
|
||||
def test_application_base(self, app, _, app_module):
|
||||
# Can be instantiated with default arguments
|
||||
model = app(weights="imagenet")
|
||||
|
||||
# Can run a correct inference on a test image
|
||||
x = _get_elephant(model.input_shape[1:3])
|
||||
x = app_module.preprocess_input(x)
|
||||
preds = model.predict(x)
|
||||
names = [p[1] for p in app_module.decode_predictions(preds)[0]]
|
||||
# Test correct label is in top 3 (weak correctness test).
|
||||
self.assertIn("African_elephant", names[:3])
|
||||
|
||||
# Can be serialized and deserialized
|
||||
config = model.get_config()
|
||||
reconstructed_model = model.__class__.from_config(config)
|
||||
self.assertEqual(len(model.weights), len(reconstructed_model.weights))
|
||||
|
||||
@parameterized.parameters(MODEL_LIST)
|
||||
def test_application_notop_custom_input_shape(self, app, last_dim, _):
|
||||
model = app(weights=None, include_top=False, input_shape=(123, 123, 3))
|
||||
output_shape = list(model.outputs[0].shape)
|
||||
self.assertEqual(output_shape[-1], last_dim)
|
||||
|
||||
@parameterized.parameters(MODEL_LIST)
|
||||
def test_application_pooling(self, app, last_dim, _):
|
||||
model = app(weights=None, include_top=False, pooling="max")
|
||||
output_shape = list(model.outputs[0].shape)
|
||||
self.assertEqual(output_shape, [None, last_dim])
|
||||
|
||||
@parameterized.parameters(MODEL_LIST)
|
||||
def test_application_classifier_activation(self, app, *_):
|
||||
model = app(
|
||||
weights=None, include_top=True, classifier_activation="softmax"
|
||||
)
|
||||
last_layer_act = model.layers[-1].activation.__name__
|
||||
self.assertEqual(last_layer_act, "softmax")
|
460
keras_core/applications/imagenet_utils.py
Normal file
460
keras_core/applications/imagenet_utils.py
Normal file
@ -0,0 +1,460 @@
|
||||
import json
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
|
||||
from keras_core import activations
|
||||
from keras_core import backend
|
||||
from keras_core.api_export import keras_core_export
|
||||
from keras_core.utils import file_utils
|
||||
|
||||
CLASS_INDEX = None
|
||||
CLASS_INDEX_PATH = (
|
||||
"https://storage.googleapis.com/download.tensorflow.org/"
|
||||
"data/imagenet_class_index.json"
|
||||
)
|
||||
|
||||
|
||||
PREPROCESS_INPUT_DOC = """
|
||||
Preprocesses a tensor or Numpy array encoding a batch of images.
|
||||
|
||||
Usage example with `applications.MobileNet`:
|
||||
|
||||
```python
|
||||
i = keras_core.layers.Input([None, None, 3], dtype="uint8")
|
||||
x = ops.cast(i, "float32")
|
||||
x = keras_core.applications.mobilenet.preprocess_input(x)
|
||||
core = keras_core.applications.MobileNet()
|
||||
x = core(x)
|
||||
model = keras_core.Model(inputs=[i], outputs=[x])
|
||||
result = model(image)
|
||||
```
|
||||
|
||||
Args:
|
||||
x: A floating point `numpy.array` or a backend-native tensor,
|
||||
3D or 4D with 3 color
|
||||
channels, with values in the range [0, 255].
|
||||
The preprocessed data are written over the input data
|
||||
if the data types are compatible. To avoid this
|
||||
behaviour, `numpy.copy(x)` can be used.
|
||||
data_format: Optional data format of the image tensor/array. None, means
|
||||
the global setting `keras_core.backend.image_data_format()` is used
|
||||
(unless you changed it, it uses "channels_last").{mode}
|
||||
Defaults to `None`.
|
||||
|
||||
Returns:
|
||||
Preprocessed array with type `float32`.
|
||||
{ret}
|
||||
|
||||
Raises:
|
||||
{error}
|
||||
"""
|
||||
|
||||
PREPROCESS_INPUT_MODE_DOC = """
|
||||
mode: One of "caffe", "tf" or "torch".
|
||||
- caffe: will convert the images from RGB to BGR,
|
||||
then will zero-center each color channel with
|
||||
respect to the ImageNet dataset,
|
||||
without scaling.
|
||||
- tf: will scale pixels between -1 and 1,
|
||||
sample-wise.
|
||||
- torch: will scale pixels between 0 and 1 and then
|
||||
will normalize each channel with respect to the
|
||||
ImageNet dataset.
|
||||
Defaults to "caffe".
|
||||
"""
|
||||
|
||||
PREPROCESS_INPUT_DEFAULT_ERROR_DOC = """
|
||||
ValueError: In case of unknown `mode` or `data_format` argument."""
|
||||
|
||||
PREPROCESS_INPUT_ERROR_DOC = """
|
||||
ValueError: In case of unknown `data_format` argument."""
|
||||
|
||||
PREPROCESS_INPUT_RET_DOC_TF = """
|
||||
The inputs pixel values are scaled between -1 and 1, sample-wise."""
|
||||
|
||||
PREPROCESS_INPUT_RET_DOC_TORCH = """
|
||||
The input pixels values are scaled between 0 and 1 and each channel is
|
||||
normalized with respect to the ImageNet dataset."""
|
||||
|
||||
PREPROCESS_INPUT_RET_DOC_CAFFE = """
|
||||
The images are converted from RGB to BGR, then each color channel is
|
||||
zero-centered with respect to the ImageNet dataset, without scaling."""
|
||||
|
||||
|
||||
@keras_core_export("keras_core.applications.imagenet_utils.preprocess_input")
|
||||
def preprocess_input(x, data_format=None, mode="caffe"):
|
||||
"""Preprocesses a tensor or Numpy array encoding a batch of images."""
|
||||
if mode not in {"caffe", "tf", "torch"}:
|
||||
raise ValueError(
|
||||
"Expected mode to be one of `caffe`, `tf` or `torch`. "
|
||||
f"Received: mode={mode}"
|
||||
)
|
||||
|
||||
if data_format is None:
|
||||
data_format = backend.image_data_format()
|
||||
elif data_format not in {"channels_first", "channels_last"}:
|
||||
raise ValueError(
|
||||
"Expected data_format to be one of `channels_first` or "
|
||||
f"`channels_last`. Received: data_format={data_format}"
|
||||
)
|
||||
|
||||
if isinstance(x, np.ndarray):
|
||||
return _preprocess_numpy_input(x, data_format=data_format, mode=mode)
|
||||
else:
|
||||
return _preprocess_symbolic_input(x, data_format=data_format, mode=mode)
|
||||
|
||||
|
||||
preprocess_input.__doc__ = PREPROCESS_INPUT_DOC.format(
|
||||
mode=PREPROCESS_INPUT_MODE_DOC,
|
||||
ret="",
|
||||
error=PREPROCESS_INPUT_DEFAULT_ERROR_DOC,
|
||||
)
|
||||
|
||||
|
||||
@keras_core_export("keras_core.applications.imagenet_utils.decode_predictions")
|
||||
def decode_predictions(preds, top=5):
|
||||
"""Decodes the prediction of an ImageNet model.
|
||||
|
||||
Args:
|
||||
preds: Numpy array encoding a batch of predictions.
|
||||
top: Integer, how many top-guesses to return. Defaults to 5.
|
||||
|
||||
Returns:
|
||||
A list of lists of top class prediction tuples
|
||||
`(class_name, class_description, score)`.
|
||||
One list of tuples per sample in batch input.
|
||||
|
||||
Raises:
|
||||
ValueError: In case of invalid shape of the `pred` array
|
||||
(must be 2D).
|
||||
"""
|
||||
global CLASS_INDEX
|
||||
|
||||
if len(preds.shape) != 2 or preds.shape[1] != 1000:
|
||||
raise ValueError(
|
||||
"`decode_predictions` expects "
|
||||
"a batch of predictions "
|
||||
"(i.e. a 2D array of shape (samples, 1000)). "
|
||||
"Found array with shape: " + str(preds.shape)
|
||||
)
|
||||
if CLASS_INDEX is None:
|
||||
fpath = file_utils.get_file(
|
||||
"imagenet_class_index.json",
|
||||
CLASS_INDEX_PATH,
|
||||
cache_subdir="models",
|
||||
file_hash="c2c37ea517e94d9795004a39431a14cb",
|
||||
)
|
||||
with open(fpath) as f:
|
||||
CLASS_INDEX = json.load(f)
|
||||
results = []
|
||||
for pred in preds:
|
||||
top_indices = pred.argsort()[-top:][::-1]
|
||||
result = [tuple(CLASS_INDEX[str(i)]) + (pred[i],) for i in top_indices]
|
||||
result.sort(key=lambda x: x[2], reverse=True)
|
||||
results.append(result)
|
||||
return results
|
||||
|
||||
|
||||
def _preprocess_numpy_input(x, data_format, mode):
|
||||
"""Preprocesses a NumPy array encoding a batch of images.
|
||||
|
||||
Args:
|
||||
x: Input array, 3D or 4D.
|
||||
data_format: Data format of the image array.
|
||||
mode: One of "caffe", "tf" or "torch".
|
||||
- caffe: will convert the images from RGB to BGR,
|
||||
then will zero-center each color channel with
|
||||
respect to the ImageNet dataset,
|
||||
without scaling.
|
||||
- tf: will scale pixels between -1 and 1,
|
||||
sample-wise.
|
||||
- torch: will scale pixels between 0 and 1 and then
|
||||
will normalize each channel with respect to the
|
||||
ImageNet dataset.
|
||||
|
||||
Returns:
|
||||
Preprocessed Numpy array.
|
||||
"""
|
||||
if not issubclass(x.dtype.type, np.floating):
|
||||
x = x.astype(backend.floatx(), copy=False)
|
||||
|
||||
if mode == "tf":
|
||||
x /= 127.5
|
||||
x -= 1.0
|
||||
return x
|
||||
elif mode == "torch":
|
||||
x /= 255.0
|
||||
mean = [0.485, 0.456, 0.406]
|
||||
std = [0.229, 0.224, 0.225]
|
||||
else:
|
||||
if data_format == "channels_first":
|
||||
# 'RGB'->'BGR'
|
||||
if x.ndim == 3:
|
||||
x = x[::-1, ...]
|
||||
else:
|
||||
x = x[:, ::-1, ...]
|
||||
else:
|
||||
# 'RGB'->'BGR'
|
||||
x = x[..., ::-1]
|
||||
mean = [103.939, 116.779, 123.68]
|
||||
std = None
|
||||
|
||||
# Zero-center by mean pixel
|
||||
if data_format == "channels_first":
|
||||
if x.ndim == 3:
|
||||
x[0, :, :] -= mean[0]
|
||||
x[1, :, :] -= mean[1]
|
||||
x[2, :, :] -= mean[2]
|
||||
if std is not None:
|
||||
x[0, :, :] /= std[0]
|
||||
x[1, :, :] /= std[1]
|
||||
x[2, :, :] /= std[2]
|
||||
else:
|
||||
x[:, 0, :, :] -= mean[0]
|
||||
x[:, 1, :, :] -= mean[1]
|
||||
x[:, 2, :, :] -= mean[2]
|
||||
if std is not None:
|
||||
x[:, 0, :, :] /= std[0]
|
||||
x[:, 1, :, :] /= std[1]
|
||||
x[:, 2, :, :] /= std[2]
|
||||
else:
|
||||
x[..., 0] -= mean[0]
|
||||
x[..., 1] -= mean[1]
|
||||
x[..., 2] -= mean[2]
|
||||
if std is not None:
|
||||
x[..., 0] /= std[0]
|
||||
x[..., 1] /= std[1]
|
||||
x[..., 2] /= std[2]
|
||||
return x
|
||||
|
||||
|
||||
def _preprocess_symbolic_input(x, data_format, mode):
|
||||
"""Preprocesses a tensor encoding a batch of images.
|
||||
|
||||
Args:
|
||||
x: Input tensor, 3D or 4D.
|
||||
data_format: Data format of the image tensor.
|
||||
mode: One of "caffe", "tf" or "torch".
|
||||
- caffe: will convert the images from RGB to BGR,
|
||||
then will zero-center each color channel with
|
||||
respect to the ImageNet dataset,
|
||||
without scaling.
|
||||
- tf: will scale pixels between -1 and 1,
|
||||
sample-wise.
|
||||
- torch: will scale pixels between 0 and 1 and then
|
||||
will normalize each channel with respect to the
|
||||
ImageNet dataset.
|
||||
|
||||
Returns:
|
||||
Preprocessed tensor.
|
||||
"""
|
||||
if mode == "tf":
|
||||
x /= 127.5
|
||||
x -= 1.0
|
||||
return x
|
||||
elif mode == "torch":
|
||||
x /= 255.0
|
||||
mean = [0.485, 0.456, 0.406]
|
||||
std = [0.229, 0.224, 0.225]
|
||||
else:
|
||||
if data_format == "channels_first":
|
||||
# 'RGB'->'BGR'
|
||||
if backend.ndim(x) == 3:
|
||||
x = x[::-1, ...]
|
||||
else:
|
||||
x = x[:, ::-1, ...]
|
||||
else:
|
||||
# 'RGB'->'BGR'
|
||||
x = x[..., ::-1]
|
||||
mean = [103.939, 116.779, 123.68]
|
||||
std = None
|
||||
|
||||
mean_tensor = backend.constant(-np.array(mean))
|
||||
|
||||
# Zero-center by mean pixel
|
||||
if backend.dtype(x) != backend.dtype(mean_tensor):
|
||||
x = backend.bias_add(
|
||||
x,
|
||||
backend.cast(mean_tensor, backend.dtype(x)),
|
||||
data_format=data_format,
|
||||
)
|
||||
else:
|
||||
x = backend.bias_add(x, mean_tensor, data_format)
|
||||
if std is not None:
|
||||
std_tensor = backend.constant(np.array(std), dtype=backend.dtype(x))
|
||||
if data_format == "channels_first":
|
||||
std_tensor = backend.reshape(std_tensor, (-1, 1, 1))
|
||||
x /= std_tensor
|
||||
return x
|
||||
|
||||
|
||||
def obtain_input_shape(
|
||||
input_shape,
|
||||
default_size,
|
||||
min_size,
|
||||
data_format,
|
||||
require_flatten,
|
||||
weights=None,
|
||||
):
|
||||
"""Internal utility to compute/validate a model's input shape.
|
||||
|
||||
Args:
|
||||
input_shape: Either None (will return the default network input shape),
|
||||
or a user-provided shape to be validated.
|
||||
default_size: Default input width/height for the model.
|
||||
min_size: Minimum input width/height accepted by the model.
|
||||
data_format: Image data format to use.
|
||||
require_flatten: Whether the model is expected to
|
||||
be linked to a classifier via a Flatten layer.
|
||||
weights: One of `None` (random initialization)
|
||||
or 'imagenet' (pre-training on ImageNet).
|
||||
If weights='imagenet' input channels must be equal to 3.
|
||||
|
||||
Returns:
|
||||
An integer shape tuple (may include None entries).
|
||||
|
||||
Raises:
|
||||
ValueError: In case of invalid argument values.
|
||||
"""
|
||||
if weights != "imagenet" and input_shape and len(input_shape) == 3:
|
||||
if data_format == "channels_first":
|
||||
if input_shape[0] not in {1, 3}:
|
||||
warnings.warn(
|
||||
"This model usually expects 1 or 3 input channels. "
|
||||
"However, it was passed an input_shape "
|
||||
f"with {input_shape[0]} input channels.",
|
||||
stacklevel=2,
|
||||
)
|
||||
default_shape = (input_shape[0], default_size, default_size)
|
||||
else:
|
||||
if input_shape[-1] not in {1, 3}:
|
||||
warnings.warn(
|
||||
"This model usually expects 1 or 3 input channels. "
|
||||
"However, it was passed an input_shape "
|
||||
f"with {input_shape[-1]} input channels.",
|
||||
stacklevel=2,
|
||||
)
|
||||
default_shape = (default_size, default_size, input_shape[-1])
|
||||
else:
|
||||
if data_format == "channels_first":
|
||||
default_shape = (3, default_size, default_size)
|
||||
else:
|
||||
default_shape = (default_size, default_size, 3)
|
||||
if weights == "imagenet" and require_flatten:
|
||||
if input_shape is not None:
|
||||
if input_shape != default_shape:
|
||||
raise ValueError(
|
||||
"When setting `include_top=True` "
|
||||
"and loading `imagenet` weights, "
|
||||
f"`input_shape` should be {default_shape}. "
|
||||
f"Received: input_shape={input_shape}"
|
||||
)
|
||||
return default_shape
|
||||
if input_shape:
|
||||
if data_format == "channels_first":
|
||||
if input_shape is not None:
|
||||
if len(input_shape) != 3:
|
||||
raise ValueError(
|
||||
"`input_shape` must be a tuple of three integers."
|
||||
)
|
||||
if input_shape[0] != 3 and weights == "imagenet":
|
||||
raise ValueError(
|
||||
"The input must have 3 channels; Received "
|
||||
f"`input_shape={input_shape}`"
|
||||
)
|
||||
if (
|
||||
input_shape[1] is not None and input_shape[1] < min_size
|
||||
) or (input_shape[2] is not None and input_shape[2] < min_size):
|
||||
raise ValueError(
|
||||
f"Input size must be at least {min_size}"
|
||||
f"x{min_size}; Received: "
|
||||
f"input_shape={input_shape}"
|
||||
)
|
||||
else:
|
||||
if input_shape is not None:
|
||||
if len(input_shape) != 3:
|
||||
raise ValueError(
|
||||
"`input_shape` must be a tuple of three integers."
|
||||
)
|
||||
if input_shape[-1] != 3 and weights == "imagenet":
|
||||
raise ValueError(
|
||||
"The input must have 3 channels; Received "
|
||||
f"`input_shape={input_shape}`"
|
||||
)
|
||||
if (
|
||||
input_shape[0] is not None and input_shape[0] < min_size
|
||||
) or (input_shape[1] is not None and input_shape[1] < min_size):
|
||||
raise ValueError(
|
||||
"Input size must be at least "
|
||||
f"{min_size}x{min_size}; Received: "
|
||||
f"input_shape={input_shape}"
|
||||
)
|
||||
else:
|
||||
if require_flatten:
|
||||
input_shape = default_shape
|
||||
else:
|
||||
if data_format == "channels_first":
|
||||
input_shape = (3, None, None)
|
||||
else:
|
||||
input_shape = (None, None, 3)
|
||||
if require_flatten:
|
||||
if None in input_shape:
|
||||
raise ValueError(
|
||||
"If `include_top` is True, "
|
||||
"you should specify a static `input_shape`. "
|
||||
f"Received: input_shape={input_shape}"
|
||||
)
|
||||
return input_shape
|
||||
|
||||
|
||||
def correct_pad(inputs, kernel_size):
|
||||
"""Returns a tuple for zero-padding for 2D convolution with downsampling.
|
||||
|
||||
Args:
|
||||
inputs: Input tensor.
|
||||
kernel_size: An integer or tuple/list of 2 integers.
|
||||
|
||||
Returns:
|
||||
A tuple.
|
||||
"""
|
||||
img_dim = 2 if backend.image_data_format() == "channels_first" else 1
|
||||
input_size = backend.int_shape(inputs)[img_dim : (img_dim + 2)]
|
||||
if isinstance(kernel_size, int):
|
||||
kernel_size = (kernel_size, kernel_size)
|
||||
if input_size[0] is None:
|
||||
adjust = (1, 1)
|
||||
else:
|
||||
adjust = (1 - input_size[0] % 2, 1 - input_size[1] % 2)
|
||||
correct = (kernel_size[0] // 2, kernel_size[1] // 2)
|
||||
return (
|
||||
(correct[0] - adjust[0], correct[0]),
|
||||
(correct[1] - adjust[1], correct[1]),
|
||||
)
|
||||
|
||||
|
||||
def validate_activation(classifier_activation, weights):
|
||||
"""validates that the classifer_activation is compatible with the weights.
|
||||
|
||||
Args:
|
||||
classifier_activation: str or callable activation function
|
||||
weights: The pretrained weights to load.
|
||||
|
||||
Raises:
|
||||
ValueError: if an activation other than `None` or `softmax` are used with
|
||||
pretrained weights.
|
||||
"""
|
||||
if weights is None:
|
||||
return
|
||||
|
||||
classifier_activation = activations.get(classifier_activation)
|
||||
if classifier_activation not in {
|
||||
activations.get("softmax"),
|
||||
activations.get(None),
|
||||
}:
|
||||
raise ValueError(
|
||||
"Only `None` and `softmax` activations are allowed "
|
||||
"for the `classifier_activation` argument when using "
|
||||
"pretrained weights, with `include_top=True`; Received: "
|
||||
f"classifier_activation={classifier_activation}"
|
||||
)
|
249
keras_core/applications/vgg16.py
Normal file
249
keras_core/applications/vgg16.py
Normal file
@ -0,0 +1,249 @@
|
||||
from tensorflow.io import gfile
|
||||
|
||||
from keras_core import backend
|
||||
from keras_core import layers
|
||||
from keras_core.api_export import keras_core_export
|
||||
from keras_core.applications import imagenet_utils
|
||||
from keras_core.models import Functional
|
||||
from keras_core.operations import operation_utils
|
||||
from keras_core.utils import file_utils
|
||||
|
||||
WEIGHTS_PATH = (
|
||||
"https://storage.googleapis.com/tensorflow/keras-applications/"
|
||||
"vgg16/vgg16_weights_tf_dim_ordering_tf_kernels.h5"
|
||||
)
|
||||
WEIGHTS_PATH_NO_TOP = (
|
||||
"https://storage.googleapis.com/tensorflow/"
|
||||
"keras-applications/vgg16/"
|
||||
"vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5"
|
||||
)
|
||||
|
||||
|
||||
@keras_core_export(
|
||||
["keras_core.applications.vgg16.VGG16", "keras_core.applications.VGG16"]
|
||||
)
|
||||
def VGG16(
|
||||
include_top=True,
|
||||
weights="imagenet",
|
||||
input_tensor=None,
|
||||
input_shape=None,
|
||||
pooling=None,
|
||||
classes=1000,
|
||||
classifier_activation="softmax",
|
||||
):
|
||||
"""Instantiates the VGG16 model.
|
||||
|
||||
Reference:
|
||||
- [Very Deep Convolutional Networks for Large-Scale Image Recognition](
|
||||
https://arxiv.org/abs/1409.1556) (ICLR 2015)
|
||||
|
||||
For image classification use cases, see
|
||||
[this page for detailed examples](
|
||||
https://keras.io/api/applications/#usage-examples-for-image-classification-models).
|
||||
|
||||
For transfer learning use cases, make sure to read the
|
||||
[guide to transfer learning & fine-tuning](
|
||||
https://keras.io/guides/transfer_learning/).
|
||||
|
||||
The default input size for this model is 224x224.
|
||||
|
||||
Note: each Keras Application expects a specific kind of input preprocessing.
|
||||
For VGG16, call `keras_core.applications.vgg16.preprocess_input` on your
|
||||
inputs before passing them to the model.
|
||||
`vgg16.preprocess_input` will convert the input images from RGB to BGR,
|
||||
then will zero-center each color channel with respect to the ImageNet
|
||||
dataset, without scaling.
|
||||
|
||||
Args:
|
||||
include_top: whether to include the 3 fully-connected
|
||||
layers at the top of the network.
|
||||
weights: one of `None` (random initialization),
|
||||
`"imagenet"` (pre-training on ImageNet),
|
||||
or the path to the weights file to be loaded.
|
||||
input_tensor: optional Keras tensor
|
||||
(i.e. output of `layers.Input()`)
|
||||
to use as image input for the model.
|
||||
input_shape: optional shape tuple, only to be specified
|
||||
if `include_top` is `False` (otherwise the input shape
|
||||
has to be `(224, 224, 3)`
|
||||
(with `channels_last` data format) or
|
||||
`(3, 224, 224)` (with `"channels_first"` data format).
|
||||
It should have exactly 3 input channels,
|
||||
and width and height should be no smaller than 32.
|
||||
E.g. `(200, 200, 3)` would be one valid value.
|
||||
pooling: Optional pooling mode for feature extraction
|
||||
when `include_top` is `False`.
|
||||
- `None` means that the output of the model will be
|
||||
the 4D tensor output of the
|
||||
last convolutional block.
|
||||
- `avg` means that global average pooling
|
||||
will be applied to the output of the
|
||||
last convolutional block, and thus
|
||||
the output of the model will be a 2D tensor.
|
||||
- `max` means that global max pooling will
|
||||
be applied.
|
||||
classes: optional number of classes to classify images
|
||||
into, only to be specified if `include_top` is `True`, and
|
||||
if no `weights` argument is specified.
|
||||
classifier_activation: A `str` or callable. The activation function to
|
||||
use on the "top" layer. Ignored unless `include_top=True`. Set
|
||||
`classifier_activation=None` to return the logits of the "top"
|
||||
layer. When loading pretrained weights, `classifier_activation` can
|
||||
only be `None` or `"softmax"`.
|
||||
|
||||
Returns:
|
||||
A model instance.
|
||||
"""
|
||||
if not (weights in {"imagenet", None} or gfile.exists(weights)):
|
||||
raise ValueError(
|
||||
"The `weights` argument should be either "
|
||||
"`None` (random initialization), `imagenet` "
|
||||
"(pre-training on ImageNet), "
|
||||
"or the path to the weights file to be loaded. Received: "
|
||||
f"weights={weights}"
|
||||
)
|
||||
|
||||
if weights == "imagenet" and include_top and classes != 1000:
|
||||
raise ValueError(
|
||||
'If using `weights` as `"imagenet"` with `include_top` '
|
||||
"as true, `classes` should be 1000. "
|
||||
f"Received `classes={classes}`"
|
||||
)
|
||||
# Determine proper input shape
|
||||
input_shape = imagenet_utils.obtain_input_shape(
|
||||
input_shape,
|
||||
default_size=224,
|
||||
min_size=32,
|
||||
data_format=backend.image_data_format(),
|
||||
require_flatten=include_top,
|
||||
weights=weights,
|
||||
)
|
||||
|
||||
if input_tensor is None:
|
||||
img_input = layers.Input(shape=input_shape)
|
||||
else:
|
||||
if not backend.is_keras_tensor(input_tensor):
|
||||
img_input = layers.Input(tensor=input_tensor, shape=input_shape)
|
||||
else:
|
||||
img_input = input_tensor
|
||||
# Block 1
|
||||
x = layers.Conv2D(
|
||||
64, (3, 3), activation="relu", padding="same", name="block1_conv1"
|
||||
)(img_input)
|
||||
x = layers.Conv2D(
|
||||
64, (3, 3), activation="relu", padding="same", name="block1_conv2"
|
||||
)(x)
|
||||
x = layers.MaxPooling2D((2, 2), strides=(2, 2), name="block1_pool")(x)
|
||||
|
||||
# Block 2
|
||||
x = layers.Conv2D(
|
||||
128, (3, 3), activation="relu", padding="same", name="block2_conv1"
|
||||
)(x)
|
||||
x = layers.Conv2D(
|
||||
128, (3, 3), activation="relu", padding="same", name="block2_conv2"
|
||||
)(x)
|
||||
x = layers.MaxPooling2D((2, 2), strides=(2, 2), name="block2_pool")(x)
|
||||
|
||||
# Block 3
|
||||
x = layers.Conv2D(
|
||||
256, (3, 3), activation="relu", padding="same", name="block3_conv1"
|
||||
)(x)
|
||||
x = layers.Conv2D(
|
||||
256, (3, 3), activation="relu", padding="same", name="block3_conv2"
|
||||
)(x)
|
||||
x = layers.Conv2D(
|
||||
256, (3, 3), activation="relu", padding="same", name="block3_conv3"
|
||||
)(x)
|
||||
x = layers.MaxPooling2D((2, 2), strides=(2, 2), name="block3_pool")(x)
|
||||
|
||||
# Block 4
|
||||
x = layers.Conv2D(
|
||||
512, (3, 3), activation="relu", padding="same", name="block4_conv1"
|
||||
)(x)
|
||||
x = layers.Conv2D(
|
||||
512, (3, 3), activation="relu", padding="same", name="block4_conv2"
|
||||
)(x)
|
||||
x = layers.Conv2D(
|
||||
512, (3, 3), activation="relu", padding="same", name="block4_conv3"
|
||||
)(x)
|
||||
x = layers.MaxPooling2D((2, 2), strides=(2, 2), name="block4_pool")(x)
|
||||
|
||||
# Block 5
|
||||
x = layers.Conv2D(
|
||||
512, (3, 3), activation="relu", padding="same", name="block5_conv1"
|
||||
)(x)
|
||||
x = layers.Conv2D(
|
||||
512, (3, 3), activation="relu", padding="same", name="block5_conv2"
|
||||
)(x)
|
||||
x = layers.Conv2D(
|
||||
512, (3, 3), activation="relu", padding="same", name="block5_conv3"
|
||||
)(x)
|
||||
x = layers.MaxPooling2D((2, 2), strides=(2, 2), name="block5_pool")(x)
|
||||
|
||||
if include_top:
|
||||
# Classification block
|
||||
x = layers.Flatten(name="flatten")(x)
|
||||
x = layers.Dense(4096, activation="relu", name="fc1")(x)
|
||||
x = layers.Dense(4096, activation="relu", name="fc2")(x)
|
||||
|
||||
imagenet_utils.validate_activation(classifier_activation, weights)
|
||||
x = layers.Dense(
|
||||
classes, activation=classifier_activation, name="predictions"
|
||||
)(x)
|
||||
else:
|
||||
if pooling == "avg":
|
||||
x = layers.GlobalAveragePooling2D()(x)
|
||||
elif pooling == "max":
|
||||
x = layers.GlobalMaxPooling2D()(x)
|
||||
|
||||
# Ensure that the model takes into account
|
||||
# any potential predecessors of `input_tensor`.
|
||||
if input_tensor is not None:
|
||||
inputs = operation_utils.get_source_inputs(input_tensor)
|
||||
else:
|
||||
inputs = img_input
|
||||
|
||||
# Create model.
|
||||
model = Functional(inputs, x, name="vgg16")
|
||||
|
||||
# Load weights.
|
||||
if weights == "imagenet":
|
||||
if include_top:
|
||||
weights_path = file_utils.get_file(
|
||||
"vgg16_weights_tf_dim_ordering_tf_kernels.h5",
|
||||
WEIGHTS_PATH,
|
||||
cache_subdir="models",
|
||||
file_hash="64373286793e3c8b2b4e3219cbf3544b",
|
||||
)
|
||||
else:
|
||||
weights_path = file_utils.get_file(
|
||||
"vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5",
|
||||
WEIGHTS_PATH_NO_TOP,
|
||||
cache_subdir="models",
|
||||
file_hash="6d6bbae143d832006294945121d1f1fc",
|
||||
)
|
||||
model.load_weights(weights_path)
|
||||
elif weights is not None:
|
||||
model.load_weights(weights)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
@keras_core_export("keras_core.applications.vgg16.preprocess_input")
|
||||
def preprocess_input(x, data_format=None):
|
||||
return imagenet_utils.preprocess_input(
|
||||
x, data_format=data_format, mode="caffe"
|
||||
)
|
||||
|
||||
|
||||
@keras_core_export("keras_core.applications.vgg16.decode_predictions")
|
||||
def decode_predictions(preds, top=5):
|
||||
return imagenet_utils.decode_predictions(preds, top=top)
|
||||
|
||||
|
||||
preprocess_input.__doc__ = imagenet_utils.PREPROCESS_INPUT_DOC.format(
|
||||
mode="",
|
||||
ret=imagenet_utils.PREPROCESS_INPUT_RET_DOC_CAFFE,
|
||||
error=imagenet_utils.PREPROCESS_INPUT_ERROR_DOC,
|
||||
)
|
||||
decode_predictions.__doc__ = imagenet_utils.decode_predictions.__doc__
|
257
keras_core/applications/vgg19.py
Normal file
257
keras_core/applications/vgg19.py
Normal file
@ -0,0 +1,257 @@
|
||||
from tensorflow.io import gfile
|
||||
|
||||
from keras_core import backend
|
||||
from keras_core import layers
|
||||
from keras_core.api_export import keras_core_export
|
||||
from keras_core.applications import imagenet_utils
|
||||
from keras_core.models import Functional
|
||||
from keras_core.operations import operation_utils
|
||||
from keras_core.utils import file_utils
|
||||
|
||||
WEIGHTS_PATH = (
|
||||
"https://storage.googleapis.com/tensorflow/keras-applications/"
|
||||
"vgg19/vgg19_weights_tf_dim_ordering_tf_kernels.h5"
|
||||
)
|
||||
WEIGHTS_PATH_NO_TOP = (
|
||||
"https://storage.googleapis.com/tensorflow/"
|
||||
"keras-applications/vgg19/"
|
||||
"vgg19_weights_tf_dim_ordering_tf_kernels_notop.h5"
|
||||
)
|
||||
|
||||
|
||||
@keras_core_export(
|
||||
["keras_core.applications.vgg19.VGG19", "keras_core.applications.VGG19"]
|
||||
)
|
||||
def VGG19(
|
||||
include_top=True,
|
||||
weights="imagenet",
|
||||
input_tensor=None,
|
||||
input_shape=None,
|
||||
pooling=None,
|
||||
classes=1000,
|
||||
classifier_activation="softmax",
|
||||
):
|
||||
"""Instantiates the VGG19 model.
|
||||
|
||||
Reference:
|
||||
- [Very Deep Convolutional Networks for Large-Scale Image Recognition](
|
||||
https://arxiv.org/abs/1409.1556) (ICLR 2015)
|
||||
|
||||
For image classification use cases, see
|
||||
[this page for detailed examples](
|
||||
https://keras.io/api/applications/#usage-examples-for-image-classification-models).
|
||||
|
||||
For transfer learning use cases, make sure to read the
|
||||
[guide to transfer learning & fine-tuning](
|
||||
https://keras.io/guides/transfer_learning/).
|
||||
|
||||
The default input size for this model is 224x224.
|
||||
|
||||
Note: each Keras Application expects a specific kind of input preprocessing.
|
||||
For VGG19, call `keras_core.applications.vgg19.preprocess_input` on your
|
||||
inputs before passing them to the model.
|
||||
`vgg19.preprocess_input` will convert the input images from RGB to BGR,
|
||||
then will zero-center each color channel with respect to the ImageNet
|
||||
dataset, without scaling.
|
||||
|
||||
Args:
|
||||
include_top: whether to include the 3 fully-connected
|
||||
layers at the top of the network.
|
||||
weights: one of `None` (random initialization),
|
||||
`"imagenet"` (pre-training on ImageNet),
|
||||
or the path to the weights file to be loaded.
|
||||
input_tensor: optional Keras tensor
|
||||
(i.e. output of `layers.Input()`)
|
||||
to use as image input for the model.
|
||||
input_shape: optional shape tuple, only to be specified
|
||||
if `include_top` is `False` (otherwise the input shape
|
||||
has to be `(224, 224, 3)`
|
||||
(with `channels_last` data format) or
|
||||
`(3, 224, 224)` (with `"channels_first"` data format).
|
||||
It should have exactly 3 input channels,
|
||||
and width and height should be no smaller than 32.
|
||||
E.g. `(200, 200, 3)` would be one valid value.
|
||||
pooling: Optional pooling mode for feature extraction
|
||||
when `include_top` is `False`.
|
||||
- `None` means that the output of the model will be
|
||||
the 4D tensor output of the
|
||||
last convolutional block.
|
||||
- `avg` means that global average pooling
|
||||
will be applied to the output of the
|
||||
last convolutional block, and thus
|
||||
the output of the model will be a 2D tensor.
|
||||
- `max` means that global max pooling will
|
||||
be applied.
|
||||
classes: optional number of classes to classify images
|
||||
into, only to be specified if `include_top` is `True`, and
|
||||
if no `weights` argument is specified.
|
||||
classifier_activation: A `str` or callable. The activation function to
|
||||
use on the "top" layer. Ignored unless `include_top=True`. Set
|
||||
`classifier_activation=None` to return the logits of the "top"
|
||||
layer. When loading pretrained weights, `classifier_activation` can
|
||||
only be `None` or `"softmax"`.
|
||||
|
||||
Returns:
|
||||
A model instance.
|
||||
"""
|
||||
if not (weights in {"imagenet", None} or gfile.exists(weights)):
|
||||
raise ValueError(
|
||||
"The `weights` argument should be either "
|
||||
"`None` (random initialization), `imagenet` "
|
||||
"(pre-training on ImageNet), "
|
||||
"or the path to the weights file to be loaded. Received: "
|
||||
f"weights={weights}"
|
||||
)
|
||||
|
||||
if weights == "imagenet" and include_top and classes != 1000:
|
||||
raise ValueError(
|
||||
'If using `weights` as `"imagenet"` with `include_top` '
|
||||
"as true, `classes` should be 1000. "
|
||||
f"Received `classes={classes}`"
|
||||
)
|
||||
# Determine proper input shape
|
||||
input_shape = imagenet_utils.obtain_input_shape(
|
||||
input_shape,
|
||||
default_size=224,
|
||||
min_size=32,
|
||||
data_format=backend.image_data_format(),
|
||||
require_flatten=include_top,
|
||||
weights=weights,
|
||||
)
|
||||
|
||||
if input_tensor is None:
|
||||
img_input = layers.Input(shape=input_shape)
|
||||
else:
|
||||
if not backend.is_keras_tensor(input_tensor):
|
||||
img_input = layers.Input(tensor=input_tensor, shape=input_shape)
|
||||
else:
|
||||
img_input = input_tensor
|
||||
# Block 1
|
||||
x = layers.Conv2D(
|
||||
64, (3, 3), activation="relu", padding="same", name="block1_conv1"
|
||||
)(img_input)
|
||||
x = layers.Conv2D(
|
||||
64, (3, 3), activation="relu", padding="same", name="block1_conv2"
|
||||
)(x)
|
||||
x = layers.MaxPooling2D((2, 2), strides=(2, 2), name="block1_pool")(x)
|
||||
|
||||
# Block 2
|
||||
x = layers.Conv2D(
|
||||
128, (3, 3), activation="relu", padding="same", name="block2_conv1"
|
||||
)(x)
|
||||
x = layers.Conv2D(
|
||||
128, (3, 3), activation="relu", padding="same", name="block2_conv2"
|
||||
)(x)
|
||||
x = layers.MaxPooling2D((2, 2), strides=(2, 2), name="block2_pool")(x)
|
||||
|
||||
# Block 3
|
||||
x = layers.Conv2D(
|
||||
256, (3, 3), activation="relu", padding="same", name="block3_conv1"
|
||||
)(x)
|
||||
x = layers.Conv2D(
|
||||
256, (3, 3), activation="relu", padding="same", name="block3_conv2"
|
||||
)(x)
|
||||
x = layers.Conv2D(
|
||||
256, (3, 3), activation="relu", padding="same", name="block3_conv3"
|
||||
)(x)
|
||||
x = layers.Conv2D(
|
||||
256, (3, 3), activation="relu", padding="same", name="block3_conv4"
|
||||
)(x)
|
||||
x = layers.MaxPooling2D((2, 2), strides=(2, 2), name="block3_pool")(x)
|
||||
|
||||
# Block 4
|
||||
x = layers.Conv2D(
|
||||
512, (3, 3), activation="relu", padding="same", name="block4_conv1"
|
||||
)(x)
|
||||
x = layers.Conv2D(
|
||||
512, (3, 3), activation="relu", padding="same", name="block4_conv2"
|
||||
)(x)
|
||||
x = layers.Conv2D(
|
||||
512, (3, 3), activation="relu", padding="same", name="block4_conv3"
|
||||
)(x)
|
||||
x = layers.Conv2D(
|
||||
512, (3, 3), activation="relu", padding="same", name="block4_conv4"
|
||||
)(x)
|
||||
x = layers.MaxPooling2D((2, 2), strides=(2, 2), name="block4_pool")(x)
|
||||
|
||||
# Block 5
|
||||
x = layers.Conv2D(
|
||||
512, (3, 3), activation="relu", padding="same", name="block5_conv1"
|
||||
)(x)
|
||||
x = layers.Conv2D(
|
||||
512, (3, 3), activation="relu", padding="same", name="block5_conv2"
|
||||
)(x)
|
||||
x = layers.Conv2D(
|
||||
512, (3, 3), activation="relu", padding="same", name="block5_conv3"
|
||||
)(x)
|
||||
x = layers.Conv2D(
|
||||
512, (3, 3), activation="relu", padding="same", name="block5_conv4"
|
||||
)(x)
|
||||
x = layers.MaxPooling2D((2, 2), strides=(2, 2), name="block5_pool")(x)
|
||||
|
||||
if include_top:
|
||||
# Classification block
|
||||
x = layers.Flatten(name="flatten")(x)
|
||||
x = layers.Dense(4096, activation="relu", name="fc1")(x)
|
||||
x = layers.Dense(4096, activation="relu", name="fc2")(x)
|
||||
imagenet_utils.validate_activation(classifier_activation, weights)
|
||||
x = layers.Dense(
|
||||
classes, activation=classifier_activation, name="predictions"
|
||||
)(x)
|
||||
else:
|
||||
if pooling == "avg":
|
||||
x = layers.GlobalAveragePooling2D()(x)
|
||||
elif pooling == "max":
|
||||
x = layers.GlobalMaxPooling2D()(x)
|
||||
|
||||
# Ensure that the model takes into account
|
||||
# any potential predecessors of `input_tensor`.
|
||||
if input_tensor is not None:
|
||||
inputs = operation_utils.get_source_inputs(input_tensor)
|
||||
else:
|
||||
inputs = img_input
|
||||
|
||||
# Create model.
|
||||
model = Functional(inputs, x, name="vgg19")
|
||||
|
||||
# Load weights.
|
||||
if weights == "imagenet":
|
||||
if include_top:
|
||||
weights_path = file_utils.get_file(
|
||||
"vgg19_weights_tf_dim_ordering_tf_kernels.h5",
|
||||
WEIGHTS_PATH,
|
||||
cache_subdir="models",
|
||||
file_hash="cbe5617147190e668d6c5d5026f83318",
|
||||
)
|
||||
else:
|
||||
weights_path = file_utils.get_file(
|
||||
"vgg19_weights_tf_dim_ordering_tf_kernels_notop.h5",
|
||||
WEIGHTS_PATH_NO_TOP,
|
||||
cache_subdir="models",
|
||||
file_hash="253f8cb515780f3b799900260a226db6",
|
||||
)
|
||||
model.load_weights(weights_path)
|
||||
elif weights is not None:
|
||||
model.load_weights(weights)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
@keras_core_export("keras_core.applications.vgg19.preprocess_input")
|
||||
def preprocess_input(x, data_format=None):
|
||||
return imagenet_utils.preprocess_input(
|
||||
x, data_format=data_format, mode="caffe"
|
||||
)
|
||||
|
||||
|
||||
@keras_core_export("keras_core.applications.vgg19.decode_predictions")
|
||||
def decode_predictions(preds, top=5):
|
||||
return imagenet_utils.decode_predictions(preds, top=top)
|
||||
|
||||
|
||||
preprocess_input.__doc__ = imagenet_utils.PREPROCESS_INPUT_DOC.format(
|
||||
mode="",
|
||||
ret=imagenet_utils.PREPROCESS_INPUT_RET_DOC_CAFFE,
|
||||
error=imagenet_utils.PREPROCESS_INPUT_ERROR_DOC,
|
||||
)
|
||||
decode_predictions.__doc__ = imagenet_utils.decode_predictions.__doc__
|
354
keras_core/applications/xception.py
Normal file
354
keras_core/applications/xception.py
Normal file
@ -0,0 +1,354 @@
|
||||
from tensorflow.io import gfile
|
||||
|
||||
from keras_core import backend
|
||||
from keras_core import layers
|
||||
from keras_core.api_export import keras_core_export
|
||||
from keras_core.applications import imagenet_utils
|
||||
from keras_core.models import Functional
|
||||
from keras_core.operations import operation_utils
|
||||
from keras_core.utils import file_utils
|
||||
|
||||
WEIGHTS_PATH = (
|
||||
"https://storage.googleapis.com/tensorflow/keras-applications/"
|
||||
"xception/xception_weights_tf_dim_ordering_tf_kernels.h5"
|
||||
)
|
||||
WEIGHTS_PATH_NO_TOP = (
|
||||
"https://storage.googleapis.com/tensorflow/keras-applications/"
|
||||
"xception/xception_weights_tf_dim_ordering_tf_kernels_notop.h5"
|
||||
)
|
||||
|
||||
|
||||
@keras_core_export(
|
||||
[
|
||||
"keras_core.applications.xception.Xception",
|
||||
"keras_core.applications.Xception",
|
||||
]
|
||||
)
|
||||
def Xception(
|
||||
include_top=True,
|
||||
weights="imagenet",
|
||||
input_tensor=None,
|
||||
input_shape=None,
|
||||
pooling=None,
|
||||
classes=1000,
|
||||
classifier_activation="softmax",
|
||||
):
|
||||
"""Instantiates the Xception architecture.
|
||||
|
||||
Reference:
|
||||
- [Xception: Deep Learning with Depthwise Separable Convolutions](
|
||||
https://arxiv.org/abs/1610.02357) (CVPR 2017)
|
||||
|
||||
For image classification use cases, see
|
||||
[this page for detailed examples](
|
||||
https://keras.io/api/applications/#usage-examples-for-image-classification-models).
|
||||
|
||||
For transfer learning use cases, make sure to read the
|
||||
[guide to transfer learning & fine-tuning](
|
||||
https://keras.io/guides/transfer_learning/).
|
||||
|
||||
The default input image size for this model is 299x299.
|
||||
|
||||
Note: each Keras Application expects a specific kind of input preprocessing.
|
||||
For Xception, call `tf.keras.applications.xception.preprocess_input` on your
|
||||
inputs before passing them to the model.
|
||||
`xception.preprocess_input` will scale input pixels between -1 and 1.
|
||||
|
||||
Args:
|
||||
include_top: whether to include the 3 fully-connected
|
||||
layers at the top of the network.
|
||||
weights: one of `None` (random initialization),
|
||||
`"imagenet"` (pre-training on ImageNet),
|
||||
or the path to the weights file to be loaded.
|
||||
input_tensor: optional Keras tensor
|
||||
(i.e. output of `layers.Input()`)
|
||||
to use as image input for the model.
|
||||
input_shape: optional shape tuple, only to be specified
|
||||
if `include_top` is `False` (otherwise the input shape
|
||||
has to be `(299, 299, 3)`.
|
||||
It should have exactly 3 inputs channels,
|
||||
and width and height should be no smaller than 71.
|
||||
E.g. `(150, 150, 3)` would be one valid value.
|
||||
pooling: Optional pooling mode for feature extraction
|
||||
when `include_top` is `False`.
|
||||
- `None` means that the output of the model will be
|
||||
the 4D tensor output of the
|
||||
last convolutional block.
|
||||
- `avg` means that global average pooling
|
||||
will be applied to the output of the
|
||||
last convolutional block, and thus
|
||||
the output of the model will be a 2D tensor.
|
||||
- `max` means that global max pooling will
|
||||
be applied.
|
||||
classes: optional number of classes to classify images
|
||||
into, only to be specified if `include_top` is `True`, and
|
||||
if no `weights` argument is specified.
|
||||
classifier_activation: A `str` or callable. The activation function to
|
||||
use on the "top" layer. Ignored unless `include_top=True`. Set
|
||||
`classifier_activation=None` to return the logits of the "top"
|
||||
layer. When loading pretrained weights, `classifier_activation` can
|
||||
only be `None` or `"softmax"`.
|
||||
|
||||
Returns:
|
||||
A model instance.
|
||||
"""
|
||||
if not (weights in {"imagenet", None} or gfile.exists(weights)):
|
||||
raise ValueError(
|
||||
"The `weights` argument should be either "
|
||||
"`None` (random initialization), `imagenet` "
|
||||
"(pre-training on ImageNet), "
|
||||
"or the path to the weights file to be loaded."
|
||||
)
|
||||
|
||||
if weights == "imagenet" and include_top and classes != 1000:
|
||||
raise ValueError(
|
||||
'If using `weights` as `"imagenet"` with `include_top`'
|
||||
" as true, `classes` should be 1000"
|
||||
)
|
||||
|
||||
# Determine proper input shape
|
||||
input_shape = imagenet_utils.obtain_input_shape(
|
||||
input_shape,
|
||||
default_size=299,
|
||||
min_size=71,
|
||||
data_format=backend.image_data_format(),
|
||||
require_flatten=include_top,
|
||||
weights=weights,
|
||||
)
|
||||
|
||||
if input_tensor is None:
|
||||
img_input = layers.Input(shape=input_shape)
|
||||
else:
|
||||
if not backend.is_keras_tensor(input_tensor):
|
||||
img_input = layers.Input(tensor=input_tensor, shape=input_shape)
|
||||
else:
|
||||
img_input = input_tensor
|
||||
|
||||
channel_axis = 1 if backend.image_data_format() == "channels_first" else -1
|
||||
|
||||
x = layers.Conv2D(
|
||||
32, (3, 3), strides=(2, 2), use_bias=False, name="block1_conv1"
|
||||
)(img_input)
|
||||
x = layers.BatchNormalization(axis=channel_axis, name="block1_conv1_bn")(x)
|
||||
x = layers.Activation("relu", name="block1_conv1_act")(x)
|
||||
x = layers.Conv2D(64, (3, 3), use_bias=False, name="block1_conv2")(x)
|
||||
x = layers.BatchNormalization(axis=channel_axis, name="block1_conv2_bn")(x)
|
||||
x = layers.Activation("relu", name="block1_conv2_act")(x)
|
||||
|
||||
residual = layers.Conv2D(
|
||||
128, (1, 1), strides=(2, 2), padding="same", use_bias=False
|
||||
)(x)
|
||||
residual = layers.BatchNormalization(axis=channel_axis)(residual)
|
||||
|
||||
x = layers.SeparableConv2D(
|
||||
128, (3, 3), padding="same", use_bias=False, name="block2_sepconv1"
|
||||
)(x)
|
||||
x = layers.BatchNormalization(axis=channel_axis, name="block2_sepconv1_bn")(
|
||||
x
|
||||
)
|
||||
x = layers.Activation("relu", name="block2_sepconv2_act")(x)
|
||||
x = layers.SeparableConv2D(
|
||||
128, (3, 3), padding="same", use_bias=False, name="block2_sepconv2"
|
||||
)(x)
|
||||
x = layers.BatchNormalization(axis=channel_axis, name="block2_sepconv2_bn")(
|
||||
x
|
||||
)
|
||||
|
||||
x = layers.MaxPooling2D(
|
||||
(3, 3), strides=(2, 2), padding="same", name="block2_pool"
|
||||
)(x)
|
||||
x = layers.add([x, residual])
|
||||
|
||||
residual = layers.Conv2D(
|
||||
256, (1, 1), strides=(2, 2), padding="same", use_bias=False
|
||||
)(x)
|
||||
residual = layers.BatchNormalization(axis=channel_axis)(residual)
|
||||
|
||||
x = layers.Activation("relu", name="block3_sepconv1_act")(x)
|
||||
x = layers.SeparableConv2D(
|
||||
256, (3, 3), padding="same", use_bias=False, name="block3_sepconv1"
|
||||
)(x)
|
||||
x = layers.BatchNormalization(axis=channel_axis, name="block3_sepconv1_bn")(
|
||||
x
|
||||
)
|
||||
x = layers.Activation("relu", name="block3_sepconv2_act")(x)
|
||||
x = layers.SeparableConv2D(
|
||||
256, (3, 3), padding="same", use_bias=False, name="block3_sepconv2"
|
||||
)(x)
|
||||
x = layers.BatchNormalization(axis=channel_axis, name="block3_sepconv2_bn")(
|
||||
x
|
||||
)
|
||||
|
||||
x = layers.MaxPooling2D(
|
||||
(3, 3), strides=(2, 2), padding="same", name="block3_pool"
|
||||
)(x)
|
||||
x = layers.add([x, residual])
|
||||
|
||||
residual = layers.Conv2D(
|
||||
728, (1, 1), strides=(2, 2), padding="same", use_bias=False
|
||||
)(x)
|
||||
residual = layers.BatchNormalization(axis=channel_axis)(residual)
|
||||
|
||||
x = layers.Activation("relu", name="block4_sepconv1_act")(x)
|
||||
x = layers.SeparableConv2D(
|
||||
728, (3, 3), padding="same", use_bias=False, name="block4_sepconv1"
|
||||
)(x)
|
||||
x = layers.BatchNormalization(axis=channel_axis, name="block4_sepconv1_bn")(
|
||||
x
|
||||
)
|
||||
x = layers.Activation("relu", name="block4_sepconv2_act")(x)
|
||||
x = layers.SeparableConv2D(
|
||||
728, (3, 3), padding="same", use_bias=False, name="block4_sepconv2"
|
||||
)(x)
|
||||
x = layers.BatchNormalization(axis=channel_axis, name="block4_sepconv2_bn")(
|
||||
x
|
||||
)
|
||||
|
||||
x = layers.MaxPooling2D(
|
||||
(3, 3), strides=(2, 2), padding="same", name="block4_pool"
|
||||
)(x)
|
||||
x = layers.add([x, residual])
|
||||
|
||||
for i in range(8):
|
||||
residual = x
|
||||
prefix = "block" + str(i + 5)
|
||||
|
||||
x = layers.Activation("relu", name=prefix + "_sepconv1_act")(x)
|
||||
x = layers.SeparableConv2D(
|
||||
728,
|
||||
(3, 3),
|
||||
padding="same",
|
||||
use_bias=False,
|
||||
name=prefix + "_sepconv1",
|
||||
)(x)
|
||||
x = layers.BatchNormalization(
|
||||
axis=channel_axis, name=prefix + "_sepconv1_bn"
|
||||
)(x)
|
||||
x = layers.Activation("relu", name=prefix + "_sepconv2_act")(x)
|
||||
x = layers.SeparableConv2D(
|
||||
728,
|
||||
(3, 3),
|
||||
padding="same",
|
||||
use_bias=False,
|
||||
name=prefix + "_sepconv2",
|
||||
)(x)
|
||||
x = layers.BatchNormalization(
|
||||
axis=channel_axis, name=prefix + "_sepconv2_bn"
|
||||
)(x)
|
||||
x = layers.Activation("relu", name=prefix + "_sepconv3_act")(x)
|
||||
x = layers.SeparableConv2D(
|
||||
728,
|
||||
(3, 3),
|
||||
padding="same",
|
||||
use_bias=False,
|
||||
name=prefix + "_sepconv3",
|
||||
)(x)
|
||||
x = layers.BatchNormalization(
|
||||
axis=channel_axis, name=prefix + "_sepconv3_bn"
|
||||
)(x)
|
||||
|
||||
x = layers.add([x, residual])
|
||||
|
||||
residual = layers.Conv2D(
|
||||
1024, (1, 1), strides=(2, 2), padding="same", use_bias=False
|
||||
)(x)
|
||||
residual = layers.BatchNormalization(axis=channel_axis)(residual)
|
||||
|
||||
x = layers.Activation("relu", name="block13_sepconv1_act")(x)
|
||||
x = layers.SeparableConv2D(
|
||||
728, (3, 3), padding="same", use_bias=False, name="block13_sepconv1"
|
||||
)(x)
|
||||
x = layers.BatchNormalization(
|
||||
axis=channel_axis, name="block13_sepconv1_bn"
|
||||
)(x)
|
||||
x = layers.Activation("relu", name="block13_sepconv2_act")(x)
|
||||
x = layers.SeparableConv2D(
|
||||
1024, (3, 3), padding="same", use_bias=False, name="block13_sepconv2"
|
||||
)(x)
|
||||
x = layers.BatchNormalization(
|
||||
axis=channel_axis, name="block13_sepconv2_bn"
|
||||
)(x)
|
||||
|
||||
x = layers.MaxPooling2D(
|
||||
(3, 3), strides=(2, 2), padding="same", name="block13_pool"
|
||||
)(x)
|
||||
x = layers.add([x, residual])
|
||||
|
||||
x = layers.SeparableConv2D(
|
||||
1536, (3, 3), padding="same", use_bias=False, name="block14_sepconv1"
|
||||
)(x)
|
||||
x = layers.BatchNormalization(
|
||||
axis=channel_axis, name="block14_sepconv1_bn"
|
||||
)(x)
|
||||
x = layers.Activation("relu", name="block14_sepconv1_act")(x)
|
||||
|
||||
x = layers.SeparableConv2D(
|
||||
2048, (3, 3), padding="same", use_bias=False, name="block14_sepconv2"
|
||||
)(x)
|
||||
x = layers.BatchNormalization(
|
||||
axis=channel_axis, name="block14_sepconv2_bn"
|
||||
)(x)
|
||||
x = layers.Activation("relu", name="block14_sepconv2_act")(x)
|
||||
|
||||
if include_top:
|
||||
x = layers.GlobalAveragePooling2D(name="avg_pool")(x)
|
||||
imagenet_utils.validate_activation(classifier_activation, weights)
|
||||
x = layers.Dense(
|
||||
classes, activation=classifier_activation, name="predictions"
|
||||
)(x)
|
||||
else:
|
||||
if pooling == "avg":
|
||||
x = layers.GlobalAveragePooling2D()(x)
|
||||
elif pooling == "max":
|
||||
x = layers.GlobalMaxPooling2D()(x)
|
||||
|
||||
# Ensure that the model takes into account
|
||||
# any potential predecessors of `input_tensor`.
|
||||
if input_tensor is not None:
|
||||
inputs = operation_utils.get_source_inputs(input_tensor)
|
||||
else:
|
||||
inputs = img_input
|
||||
# Create model.
|
||||
model = Functional(inputs, x, name="xception")
|
||||
|
||||
# Load weights.
|
||||
if weights == "imagenet":
|
||||
if include_top:
|
||||
weights_path = file_utils.get_file(
|
||||
"xception_weights_tf_dim_ordering_tf_kernels.h5",
|
||||
WEIGHTS_PATH,
|
||||
cache_subdir="models",
|
||||
file_hash="0a58e3b7378bc2990ea3b43d5981f1f6",
|
||||
)
|
||||
else:
|
||||
weights_path = file_utils.get_file(
|
||||
"xception_weights_tf_dim_ordering_tf_kernels_notop.h5",
|
||||
WEIGHTS_PATH_NO_TOP,
|
||||
cache_subdir="models",
|
||||
file_hash="b0042744bf5b25fce3cb969f33bebb97",
|
||||
)
|
||||
model.load_weights(weights_path)
|
||||
elif weights is not None:
|
||||
model.load_weights(weights)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
@keras_core_export("keras_core.applications.xception.preprocess_input")
|
||||
def preprocess_input(x, data_format=None):
|
||||
return imagenet_utils.preprocess_input(
|
||||
x, data_format=data_format, mode="tf"
|
||||
)
|
||||
|
||||
|
||||
@keras_core_export("keras_core.applications.xception.decode_predictions")
|
||||
def decode_predictions(preds, top=5):
|
||||
return imagenet_utils.decode_predictions(preds, top=top)
|
||||
|
||||
|
||||
preprocess_input.__doc__ = imagenet_utils.PREPROCESS_INPUT_DOC.format(
|
||||
mode="",
|
||||
ret=imagenet_utils.PREPROCESS_INPUT_RET_DOC_TF,
|
||||
error=imagenet_utils.PREPROCESS_INPUT_ERROR_DOC,
|
||||
)
|
||||
decode_predictions.__doc__ = imagenet_utils.decode_predictions.__doc__
|
@ -118,6 +118,20 @@ class Functional(Function, Model):
|
||||
def build(self, input_shape):
|
||||
self.built = True
|
||||
|
||||
@property
|
||||
def input_shape(self):
|
||||
input_shapes = nest.map_structure(lambda x: x.shape, self.inputs)
|
||||
if isinstance(input_shapes, list) and len(input_shapes) == 1:
|
||||
return input_shapes[0]
|
||||
return input_shapes
|
||||
|
||||
@property
|
||||
def output_shape(self):
|
||||
output_shapes = nest.map_structure(lambda x: x.shape, self.outputs)
|
||||
if isinstance(output_shapes, list) and len(output_shapes) == 1:
|
||||
return output_shapes[0]
|
||||
return output_shapes
|
||||
|
||||
def _assert_input_compatibility(self, *args):
|
||||
return super(Model, self)._assert_input_compatibility(*args)
|
||||
|
||||
|
@ -165,6 +165,22 @@ class Sequential(Model):
|
||||
inputs = outputs
|
||||
return outputs
|
||||
|
||||
@property
|
||||
def input_shape(self):
|
||||
if self._functional:
|
||||
return self._functional.input_shape
|
||||
raise ValueError(
|
||||
f"Sequential model '{self.name}' has no defined input shape yet."
|
||||
)
|
||||
|
||||
@property
|
||||
def output_shape(self):
|
||||
if self._functional:
|
||||
return self._functional.output_shape
|
||||
raise ValueError(
|
||||
f"Sequential model '{self.name}' has no defined output shape yet."
|
||||
)
|
||||
|
||||
def _is_layer_name_unique(self, layer):
|
||||
for ref_layer in self._layers:
|
||||
if layer.name == ref_layer.name and ref_layer is not layer:
|
||||
|
Loading…
Reference in New Issue
Block a user