Source code for n3fit.backends.keras_backend.MetaLayer

"""
    The class MetaLayer is an extension of the backend Layer class
    with a number of methods and helpers to facilitate writing new custom layers
    in such a way that the new custom layer don't need to rely in anything backend-dependent

    In other words, if you want to implement a new layer and need functions not included here
    it is better to add a new method which is just a call to the relevant backend-dependent function
    For instance: np_to_tensor is just a call to K.constant
"""

from keras.initializers import Constant, RandomUniform, glorot_normal, glorot_uniform
from keras.layers import Layer

# Define in this dictionary new initializers as well as the arguments they accept (with default values if needed be)
initializers = {
    "random_uniform": (RandomUniform, {"minval": -0.5, "maxval": 0.5}),
    "glorot_uniform": (glorot_uniform, {}),
    "glorot_normal": (glorot_normal, {}),
}


[docs] class MetaLayer(Layer): """ This metalayer function must contain all backend-dependent functions In order to write a custom Keras layer you usually need to override: - __init__ - call """ initializers = initializers weight_inits = [] # Building function
[docs] def builder_helper(self, name, kernel_shape, initializer, trainable=True, constraint=None): """ Creates a kernel that should be saved as an attribute of the caller class name: name of the kernel shape: tuple with its shape initializer: one of the initializers from this class (actually, any keras initializer) trainable: if it is constraint: one of the constraints from this class (actually, any keras constraints) """ kernel = self.add_weight( name=name, shape=kernel_shape, initializer=initializer, trainable=trainable, constraint=constraint, ) if trainable: self.weight_inits.append((kernel, initializer)) return kernel
[docs] def get_weight_by_name(self, weight_name, internal_count=0): """ Returns a weight of the layer by name, returns None if the layer does not include the named weight. Note that internally weights of a layer are prefaced by the name of the layer, this should not be added to the input of this function. i.e., if the internal name is "layer/weight:0", the argument to this method should be just "weight". Parameters ---------- weight_name: str Name of the weight """ main_name = f"{self.name}/{weight_name}" for weight in self.weights: if weight.name in (f"{main_name}:{internal_count}", main_name, weight_name): return weight return None
# Implemented initializers
[docs] @staticmethod def init_constant(value): return Constant(value=value)
[docs] @staticmethod def select_initializer(ini_name, seed=None, **kwargs): """ Selects one of the initializers (which does initialize, i.e., not constant) All of them should accept seed """ try: ini_tuple = initializers[ini_name] except KeyError as e: raise NotImplementedError( f"[MetaLayer.select_initializer] initializer not implemented: {ini_name}" ) from e ini_class = ini_tuple[0] ini_args = ini_tuple[1] ini_args["seed"] = seed for key, value in kwargs.items(): if key in ini_args.keys(): ini_args[key] = value return ini_class(**ini_args)