Module degann.networks.layers.tf_dense
Expand source code
from typing import Optional, List, Tuple, Dict
import numpy as np
import tensorflow as tf
from tensorflow import keras
from degann.networks import activations
from degann.networks.config_format import LAYER_DICT_NAMES
def _dec_params_to_list(
params: Optional[Dict[str, float]]
) -> Optional[List[Tuple[str, float]]]:
if params is None:
return None
res = []
for key in params:
res.append((key, params[key]))
return res
def _dec_params_from_list(
params: Optional[List[Tuple[str, float]]]
) -> Optional[Dict[str, float]]:
if params is None:
return None
res = {}
for pair in params:
res[pair[0]] = pair[1]
return res
class TensorflowDense(keras.layers.Layer):
def __init__(
self,
input_dim=32,
units=32,
activation_func: str = "linear",
weight_initializer=tf.random_normal_initializer(),
bias_initializer=tf.random_normal_initializer(),
is_debug=False,
**kwargs,
):
decorator_params = None
if "decorator_params" in kwargs.keys():
decorator_params = kwargs.get("decorator_params")
kwargs.pop("decorator_params")
if not isinstance(decorator_params, dict) and decorator_params is not None:
raise "Additional parameters for activation function must be dictionary"
if input_dim == 0 or units == 0:
raise "Layer cannot have zero inputs or zero size"
super(TensorflowDense, self).__init__(**kwargs)
w_init = weight_initializer
self.w = self.add_weight(
shape=(input_dim, units),
initializer=w_init,
dtype="float32",
# initial_value=w_init(shape=(input_dim, units), dtype="float32"),
name=f"Var_w_{self.name}",
trainable=True,
)
b_init = bias_initializer
self.b = self.add_weight(
shape=(units,),
initializer=b_init,
dtype="float32",
# initial_value=b_init(shape=(units,), dtype="float32"),
name=f"Var_b_{self.name}",
trainable=True,
)
self.units = units
self.input_dim = input_dim
self._is_debug = is_debug
self.activation_func = activations.get(activation_func)
self.activation_name = activation_func
self.weight_initializer = weight_initializer
self.bias_initializer = bias_initializer
self.decorator_params: Optional[dict] = decorator_params
def call(self, inputs, **kwargs):
"""
Obtaining a layer response on the input data vector
Parameters
----------
inputs
kwargs
Returns
-------
"""
if self.decorator_params is None:
return self.activation_func(tf.matmul(inputs, self.w) + self.b)
else:
return self.activation_func(
tf.matmul(inputs, self.w) + self.b, **self.decorator_params
)
def __str__(self):
res = f"Layer {self.name}\n"
res += f"weights shape = {self.w.shape}\n"
if self._is_debug:
# res += f"weights = {self.w.numpy()}\n"
# res += f"biases = {self.b.numpy()}\n"
res += f"activation = {self.activation_name}\n"
return res
def to_dict(self) -> dict:
"""
Export layer to dictionary of parameters
Returns
-------
config: dict
dictionary of parameters
"""
w = self.w.value.numpy()
b = self.b.value.numpy()
res = {
LAYER_DICT_NAMES["shape"]: self.units,
LAYER_DICT_NAMES["inp_size"]: self.input_dim,
LAYER_DICT_NAMES["weights"]: w.tolist(),
LAYER_DICT_NAMES["biases"]: b.tolist(),
LAYER_DICT_NAMES["layer_type"]: type(self).__name__,
LAYER_DICT_NAMES["dtype"]: w.dtype.name,
LAYER_DICT_NAMES["activation"]: self.activation_name
if self.activation_name is None
else self.activation_name,
LAYER_DICT_NAMES["decorator_params"]: _dec_params_to_list(
self.decorator_params
),
}
return res
def from_dict(self, config):
"""
Restore layer from dictionary of parameters
Parameters
----------
config
Returns
-------
"""
w = np.array(config[LAYER_DICT_NAMES["weights"]])
b = np.array(config[LAYER_DICT_NAMES["biases"]])
act = config[LAYER_DICT_NAMES["activation"]]
dec_params = _dec_params_from_list(config[LAYER_DICT_NAMES["decorator_params"]])
self.set_weights([w, b])
# self.b = tf.Variable(
# initial_value=b, dtype=config[LAYER_DICT_NAMES["dtype"]], trainable=True
# )
self.activation_func = activations.get(act)
self.activation_name = act
self.decorator_params = dec_params
@classmethod
def from_config(cls, config):
return cls(**config)
@property
def get_activation(self) -> str:
return self.activation_name
Classes
class TensorflowDense (input_dim=32, units=32, activation_func: str = 'linear', weight_initializer=<tensorflow.python.ops.init_ops_v2.RandomNormal object>, bias_initializer=<tensorflow.python.ops.init_ops_v2.RandomNormal object>, is_debug=False, **kwargs)
-
This is the class from which all layers inherit.
A layer is a callable object that takes as input one or more tensors and that outputs one or more tensors. It involves computation, defined in the
call()
method, and a state (weight variables). State can be created:- in
__init__()
, for instance viaself.add_weight()
; - in the optional
build()
method, which is invoked by the first__call__()
to the layer, and supplies the shape(s) of the input(s), which may not have been known at initialization time.
Layers are recursively composable: If you assign a Layer instance as an attribute of another Layer, the outer layer will start tracking the weights created by the inner layer. Nested layers should be instantiated in the
__init__()
method orbuild()
method.Users will just instantiate a layer and then treat it as a callable.
Args
trainable
- Boolean, whether the layer's variables should be trainable.
name
- String name of the layer.
dtype
- The dtype of the layer's computations and weights. Can also be a
keras.DTypePolicy
, which allows the computation and weight dtype to differ. Defaults toNone
.None
means to usekeras.config.dtype_policy()
, which is afloat32
policy unless set to different value (viakeras.config.set_dtype_policy()
).
Attributes
name
- The name of the layer (string).
dtype
- Dtype of the layer's weights. Alias of
layer.variable_dtype
. variable_dtype
- Dtype of the layer's weights.
compute_dtype
- The dtype of the layer's computations.
Layers automatically cast inputs to this dtype, which causes
the computations and output to also be in this dtype.
When mixed precision is used with a
keras.DTypePolicy
, this will be different thanvariable_dtype
. trainable_weights
- List of variables to be included in backprop.
non_trainable_weights
- List of variables that should not be included in backprop.
weights
- The concatenation of the lists trainable_weights and non_trainable_weights (in this order).
trainable
- Whether the layer should be trained (boolean), i.e.
whether its potentially-trainable weights should be returned
as part of
layer.trainable_weights
. input_spec
- Optional (list of)
InputSpec
object(s) specifying the constraints on inputs that can be accepted by the layer.
We recommend that descendants of
Layer
implement the following methods:__init__()
: Defines custom layer attributes, and creates layer weights that do not depend on input shapes, usingadd_weight()
, or other state.build(self, input_shape)
: This method can be used to create weights that depend on the shape(s) of the input(s), usingadd_weight()
, or other state.__call__()
will automatically build the layer (if it has not been built yet) by callingbuild()
.call(self, *args, **kwargs)
: Called in__call__
after making surebuild()
has been called.call()
performs the logic of applying the layer to the input arguments. Two reserved keyword arguments you can optionally use incall()
are: 1.training
(boolean, whether the call is in inference mode or training mode). 2.mask
(boolean tensor encoding masked timesteps in the input, used e.g. in RNN layers). A typical signature for this method iscall(self, inputs)
, and user could optionally addtraining
andmask
if the layer need them.get_config(self)
: Returns a dictionary containing the configuration used to initialize this layer. If the keys differ from the arguments in__init__()
, then overridefrom_config(self)
as well. This method is used when saving the layer or a model that contains this layer.
Examples:
Here's a basic example: a layer with two variables,
w
andb
, that returnsy = w . x + b
. It shows how to implementbuild()
andcall()
. Variables set as attributes of a layer are tracked as weights of the layers (inlayer.weights
).class SimpleDense(Layer): def __init__(self, units=32): super().__init__() self.units = units # Create the state of the layer (weights) def build(self, input_shape): self.kernel = self.add_weight( shape=(input_shape[-1], self.units), initializer="glorot_uniform", trainable=True, name="kernel", ) self.bias = self.add_weight( shape=(self.units,), initializer="zeros", trainable=True, name="bias", ) # Defines the computation def call(self, inputs): return ops.matmul(inputs, self.kernel) + self.bias # Instantiates the layer. linear_layer = SimpleDense(4) # This will also call `build(input_shape)` and create the weights. y = linear_layer(ops.ones((2, 2))) assert len(linear_layer.weights) == 2 # These weights are trainable, so they're listed in `trainable_weights`: assert len(linear_layer.trainable_weights) == 2
Besides trainable weights, updated via backpropagation during training, layers can also have non-trainable weights. These weights are meant to be updated manually during
call()
. Here's a example layer that computes the running sum of its inputs:class ComputeSum(Layer): def __init__(self, input_dim): super(ComputeSum, self).__init__() # Create a non-trainable weight. self.total = self.add_weight( shape=(), initializer="zeros", trainable=False, name="total", ) def call(self, inputs): self.total.assign(self.total + ops.sum(inputs)) return self.total my_sum = ComputeSum(2) x = ops.ones((2, 2)) y = my_sum(x) assert my_sum.weights == [my_sum.total] assert my_sum.non_trainable_weights == [my_sum.total] assert my_sum.trainable_weights == []
Expand source code
class TensorflowDense(keras.layers.Layer): def __init__( self, input_dim=32, units=32, activation_func: str = "linear", weight_initializer=tf.random_normal_initializer(), bias_initializer=tf.random_normal_initializer(), is_debug=False, **kwargs, ): decorator_params = None if "decorator_params" in kwargs.keys(): decorator_params = kwargs.get("decorator_params") kwargs.pop("decorator_params") if not isinstance(decorator_params, dict) and decorator_params is not None: raise "Additional parameters for activation function must be dictionary" if input_dim == 0 or units == 0: raise "Layer cannot have zero inputs or zero size" super(TensorflowDense, self).__init__(**kwargs) w_init = weight_initializer self.w = self.add_weight( shape=(input_dim, units), initializer=w_init, dtype="float32", # initial_value=w_init(shape=(input_dim, units), dtype="float32"), name=f"Var_w_{self.name}", trainable=True, ) b_init = bias_initializer self.b = self.add_weight( shape=(units,), initializer=b_init, dtype="float32", # initial_value=b_init(shape=(units,), dtype="float32"), name=f"Var_b_{self.name}", trainable=True, ) self.units = units self.input_dim = input_dim self._is_debug = is_debug self.activation_func = activations.get(activation_func) self.activation_name = activation_func self.weight_initializer = weight_initializer self.bias_initializer = bias_initializer self.decorator_params: Optional[dict] = decorator_params def call(self, inputs, **kwargs): """ Obtaining a layer response on the input data vector Parameters ---------- inputs kwargs Returns ------- """ if self.decorator_params is None: return self.activation_func(tf.matmul(inputs, self.w) + self.b) else: return self.activation_func( tf.matmul(inputs, self.w) + self.b, **self.decorator_params ) def __str__(self): res = f"Layer {self.name}\n" res += f"weights shape = {self.w.shape}\n" if self._is_debug: # res += f"weights = {self.w.numpy()}\n" # res += f"biases = {self.b.numpy()}\n" res += f"activation = {self.activation_name}\n" return res def to_dict(self) -> dict: """ Export layer to dictionary of parameters Returns ------- config: dict dictionary of parameters """ w = self.w.value.numpy() b = self.b.value.numpy() res = { LAYER_DICT_NAMES["shape"]: self.units, LAYER_DICT_NAMES["inp_size"]: self.input_dim, LAYER_DICT_NAMES["weights"]: w.tolist(), LAYER_DICT_NAMES["biases"]: b.tolist(), LAYER_DICT_NAMES["layer_type"]: type(self).__name__, LAYER_DICT_NAMES["dtype"]: w.dtype.name, LAYER_DICT_NAMES["activation"]: self.activation_name if self.activation_name is None else self.activation_name, LAYER_DICT_NAMES["decorator_params"]: _dec_params_to_list( self.decorator_params ), } return res def from_dict(self, config): """ Restore layer from dictionary of parameters Parameters ---------- config Returns ------- """ w = np.array(config[LAYER_DICT_NAMES["weights"]]) b = np.array(config[LAYER_DICT_NAMES["biases"]]) act = config[LAYER_DICT_NAMES["activation"]] dec_params = _dec_params_from_list(config[LAYER_DICT_NAMES["decorator_params"]]) self.set_weights([w, b]) # self.b = tf.Variable( # initial_value=b, dtype=config[LAYER_DICT_NAMES["dtype"]], trainable=True # ) self.activation_func = activations.get(act) self.activation_name = act self.decorator_params = dec_params @classmethod def from_config(cls, config): return cls(**config) @property def get_activation(self) -> str: return self.activation_name
Ancestors
- keras.src.layers.layer.Layer
- keras.src.backend.tensorflow.layer.TFLayer
- keras.src.backend.tensorflow.trackable.KerasAutoTrackable
- tensorflow.python.trackable.autotrackable.AutoTrackable
- tensorflow.python.trackable.base.Trackable
- keras.src.ops.operation.Operation
- keras.src.saving.keras_saveable.KerasSaveable
Static methods
def from_config(config)
-
Creates a layer from its config.
This method is the reverse of
get_config
, capable of instantiating the same layer from the config dictionary. It does not handle layer connectivity (handled by Network), nor weights (handled byset_weights
).Args
config
- A Python dictionary, typically the output of get_config.
Returns
A layer instance.
Expand source code
@classmethod def from_config(cls, config): return cls(**config)
Instance variables
var get_activation : str
-
Expand source code
@property def get_activation(self) -> str: return self.activation_name
Methods
def call(self, inputs, **kwargs)
-
Obtaining a layer response on the input data vector Parameters
inputs
kwargs
Returns
Expand source code
def call(self, inputs, **kwargs): """ Obtaining a layer response on the input data vector Parameters ---------- inputs kwargs Returns ------- """ if self.decorator_params is None: return self.activation_func(tf.matmul(inputs, self.w) + self.b) else: return self.activation_func( tf.matmul(inputs, self.w) + self.b, **self.decorator_params )
def from_dict(self, config)
-
Restore layer from dictionary of parameters
Parameters
config
Returns
Expand source code
def from_dict(self, config): """ Restore layer from dictionary of parameters Parameters ---------- config Returns ------- """ w = np.array(config[LAYER_DICT_NAMES["weights"]]) b = np.array(config[LAYER_DICT_NAMES["biases"]]) act = config[LAYER_DICT_NAMES["activation"]] dec_params = _dec_params_from_list(config[LAYER_DICT_NAMES["decorator_params"]]) self.set_weights([w, b]) # self.b = tf.Variable( # initial_value=b, dtype=config[LAYER_DICT_NAMES["dtype"]], trainable=True # ) self.activation_func = activations.get(act) self.activation_name = act self.decorator_params = dec_params
def to_dict(self) ‑> dict
-
Export layer to dictionary of parameters Returns
config
:dict
- dictionary of parameters
Expand source code
def to_dict(self) -> dict: """ Export layer to dictionary of parameters Returns ------- config: dict dictionary of parameters """ w = self.w.value.numpy() b = self.b.value.numpy() res = { LAYER_DICT_NAMES["shape"]: self.units, LAYER_DICT_NAMES["inp_size"]: self.input_dim, LAYER_DICT_NAMES["weights"]: w.tolist(), LAYER_DICT_NAMES["biases"]: b.tolist(), LAYER_DICT_NAMES["layer_type"]: type(self).__name__, LAYER_DICT_NAMES["dtype"]: w.dtype.name, LAYER_DICT_NAMES["activation"]: self.activation_name if self.activation_name is None else self.activation_name, LAYER_DICT_NAMES["decorator_params"]: _dec_params_to_list( self.decorator_params ), } return res
- in