(Keras) Custom subclass subclass model/layer, serialization and model.save(path) preservation

Directory

23.08.09

Custom LayerNormalization layer

For custom layers and model calls passed to __int__ in subclassed models

23.08.10

Use the constructor def construct(cla, class_attribute1, class_attribute2) to construct the serialized model


23.08.09

For the problem that the custom subclass subclass model cannot use model.save() to save the entire model, the entire model can be saved by serializing the model.

In the process of writing a keras custom layer or model, only the logic of the code needs to be defined, but when exporting the entire model, the code needs to be converted into a flat file (Flat File). The code of the custom layer or model will be lost when the model is exported, so all constructors need to be passed in order to save and load correctly.

For custom layers and classes, the get_config() method must be used. If the constructor (__int__() method) passed to the custom object is not a common python object (basic types such as integers and strings), it must also be in form_config( ) to explicitly deserialize these parameter class methods.
1. get_config() should return a JSON serializable dictionary for compatibility with Keras architecture and model saving API.
2 .from_config(config) (classmethod) should return a new layer or model object created from config. The default implementation returns cls(**config).

@tf.keras.utils.register_keras_serializable(package="My_LayerNormalization")
class LayerNormalization(tf.keras.layers.Layer):
    def __init__(self, epsilon=1e-6, **kwargs):
        # super(LayerNormalization, self).__init__(**kwargs)
        self.eps = epsilon
        super(LayerNormalization, self).__init__(**kwargs)

    def build(self, input_shape):
        self.gamma = self.add_weight(name='gamma', shape=input_shape[-1:],
                                     initializer=tf.ones_initializer(), trainable=True)
        self.beta = self.add_weight(name='beta', shape=input_shape[-1:],
                                    initializer=tf.zeros_initializer(), trainable=True)
        super(LayerNormalization, self).build(input_shape)

    def call(self, x):
        mean = tf.keras.backend.mean(x, axis=-1, keepdims=True)
        std = tf.keras.backend.std(x, axis=-1, keepdims=True)
        return self.gamma * (x - mean) / (std + self.eps) + self.beta

    def get_config(self):
        config = super().get_config()
        config.update({
            'epsilon': self.eps,
        })
        return config

Custom LayerNormalization layer

1. Add the @tf.keras.utils.register_keras_serializable decorator to make custom layers or models serializable or deserializable.

Serialization: After keras serializes the custom layer or model into JSON format, the entire model structure can be saved with model.save().

2. class LayerNormalization(tf.keras.layers.Layer):

Inheritance class, from tf.keras.layers.Layer.
3. def __init__(self, epsilon=1e-6, **kwargs):

self.eps = epsilon => save custom parameters => get_config(), save construction parameters as instance fields.

4. def build(self, input_shape):

5. def get_config(self):
config = super().get_config()
config.update({
‘epsilon’: self.eps,
})
return config

Use get_config to save construction parameters.

For custom layer and model calls passed to __int__ in subclass models

Must be deserialized explicitly.

@keras.saving.register_keras_serializable(package="ComplexModels")
# keras.saving can not be used can write tf.compat.v1.keras.saving......
class CustomModel(keras. layers. Layer):
    def __init__(self, first_layer, second_layer=None, **kwargs):
        super().__init__(**kwargs)
        self. first_layer = first_layer
        if second_layer is not None:
            self. second_layer = second_layer
        else:
            self.second_layer = keras.layers.Dense(8)

    def get_config(self):
        config = super().get_config()
        config. update(
            {
                "first_layer": self. first_layer,
                "second_layer": self. second_layer,
            }
        )
        return config

    @classmethod
    def from_config(cls, config):
        # Note that you can also use `keras.saving.deserialize_keras_object` here
        config["first_layer"] = keras. layers. deserialize(config["first_layer"])
        config["second_layer"] = keras.layers.deserialize(config["second_layer"])
        return cls(**config)

    def call(self, inputs):
        return self. first_layer(self. second_layer(inputs))


# Let's make our first layer the custom layer from the previous example (MyDense)
inputs = keras. Input((32,))
outputs = CustomModel(first_layer=layer)(inputs)
model = keras.Model(inputs, outputs)

config = model. get_config()
new_model = keras.Model.from_config(config)

First, the custom layer needs to be uploaded in get_config; second, it needs to be in the class method @classmethod

@classmethod # class method
def form_config(cls, config)
    config["name_of_customlayer1"] =
             keras.layer.deserialize(config["name_of_customlayer1"])
    config["name_of_customlayer2"] =
             keras.layer.deserialize(config["name_of_customlayer2"])
    # Realize the deserialization of the custom layer
    return cls(**config)

Among them, cls(**config) builds an instance of a class, cls means to define the class itself, and refers to the class being operated or constructed, which is CustomModel in the above code; **config is a keyword parameter, representing Attributes and values (key-value pairs) required to construct an object.

23.08.10

Use the constructor def construct(cla, class_attribute1, class_attribute2) to construct the serialized model

import keras.layers
import tensorflow as tf
import keras
import time
import os
from matplotlib import pyplot as plt
from IPython import display
from keras.utils.vis_utils import plot_model
from scipy.io import loadmat
from keras.models import Model
import keras.layers as layers

os.environ["KERAS_BACKEND"] = "tensorflow" # keras backend uses tensorflow framework for operations
os.environ["TF_FORCE_GPU_ALLOW_GROWTH"] = "true" # Gradually increase the use of GPU memory
os.environ["TF_CP_MIN_LOG_LEVEL"] = "2" # only display warning and error
from PIL import Image
from tensorflow.python.keras.utils.np_utils import to_categorical
import matplotlib


# Download Data
from keras.datasets import mnist

(train_imgs, train_labels), (test_imgs, test_labels) = mnist. load_data()

# grayscale normalization
train_imgs = train_imgs / 255
test_imgs = test_imgs / 255

# Adjust the dimension and split the image data according to the size of 28*28
train_imgs = train_imgs.reshape(-1, 28, 28, 1)
test_imgs = test_imgs.reshape(-1, 28, 28, 1)

# Adjust the label to onehot
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)


@tf.keras.utils.register_keras_serializable(package="Conv", name="Conv11")
class Conv11(keras.layers.Layer):
    def __init__(self, units, num_label, conv1, conv2, fla, output_layer, **kwargs):
        super(Conv11, self).__init__(**kwargs)
        self.units = units
        self.num_label = num_label
        self.conv1 = conv1
        self.conv2 = conv2
        self.fla = fla
        self. output_layer = output_layer

    @classmethod
    def construct(cls, units, num_label):
        return cls(
            units=units,
            num_label=num_label,
            conv1=layers.Conv2D(filters=units, kernel_size=(3, 3), padding='same'),
            conv2=layers.Conv2D(filters=units * 2, kernel_size=(3, 3), padding='same'),
            fla = layers. Flatten(),
            output_layer=layers.Dense(units=num_label, activation='softmax')
        )

    def get_config(self):
        config = super(Conv11, self).get_config()
        config.update({"units": self.units,
                       "num_label": self. num_label,
                       "conv1": self.conv1,
                       "conv2": self.conv2,
                       "fla": self.fla,
                       "output_layer": self. output_layer})
        return config

    @classmethod
    def form_config(cls, config):
        return cls(**config)

    def call(self, inputs, **kwargs):
        x_conv = self.conv1(inputs)
        x_conv = self.conv2(x_conv)
        x_conv = self. fla(x_conv)
        output = self. output_layer(x_conv)

        return output


layer_conv_2 = Conv11. construct(3, 10)
model = tf.keras.Sequential([layer_conv_2])
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.1), loss=tf.keras.losses.CategoricalCrossentropy(from_logits=False), metrics=['categorical_accuracy'])

# train the model
history = model.fit(train_imgs, train_labels, epochs=1, batch_size=64)
print('**************\\
')
# print(model. trainable_weights)
print('**************\\
')
model.evaluate(x=test_imgs, y=test_labels)
# save the model
model.save('test_model.h5')
# load the model
mdoel_1 = keras.models.load_model('test_model.h5')
mdoel_1.evaluate(test_imgs, test_labels)

Output: After using model.save to save as a .h5 file, use keras.models.load_model(path) to load the entire model. It can be seen that the results of reloading the model and the training model on the test set are the same, and no error is reported.

938/938 [================================] - 4s 2ms/step - loss: 1.5829 - categorical_accuracy: 0.8487

313/313 [===============================] - 1s 1ms/step - loss: 0.4335 - categorical_accuracy: 0.8664
313/313 [==============================] - 0s 1ms/step - loss: 0.4335 - categorical_accuracy: 0.8664

refer to:
How to write a Custom Keras model so that it can be deployed for Serving | by Lak Lakshmanan | Towards Data Sciencehttps://towardsdatascience.com/how-to-write- a-custom-keras-model-so-that-it-can-be-deployed-for-serving-7d81ace4a1f8

Making new layers and models via subclassing (keras.io)https://keras.io/guides/making_new_layers_and_models_via_subclassing/
Saving nested layers in TensorFlow | by Oliver K. Ernst, Ph.D. | Practical coding | Mediumhttps://medium.com/practical-coding/saving-nested- layers-in-tensorflow-6d85cd11159b

Save, serialize, and export models (keras.io)https://keras.io/guides/serialization_and_saving/