Source code for akida.layer

import numpy as np
from .core import evaluate_bitwidth


def layer_str(self):
    layer_type = str(self.parameters.layer_type).split('.')[-1]
    layer_type = layer_type.replace("FullyConnected", "Fully.")
    layer_type = layer_type.replace("Convolutional", "Conv.")
    layer_type = layer_type.replace("Separable", "Sep.")
    return self.name + " (" + layer_type + ")"


def layer_repr(self):
    data = "<akida.Layer, type=" + str(self.parameters.layer_type)
    data += ", name=" + self.name
    data += ", input_dims=" + str(self.input_dims)
    data += ", output_dims=" + str(self.output_dims)
    if self.mapping is not None:
        data += ",nps=" + repr(self.mapping.nps)
    data += ">"
    return data


def layer_to_dict(self):
    """Provide a dict representation of the Layer

    Returns:
        dict: a Layer dictionary.
    """
    params = {name: getattr(self.parameters, name) for name in dir(self.parameters)}
    params["layer_type"] = self.parameters.layer_type.name
    variables = {}
    for name in self.variables.names:
        var = self.variables[name]
        variables[name] = {
            "shape": var.shape,
            "dtype": str(var.dtype),
            "bitwidth": evaluate_bitwidth(var),
            "data": var.tolist()
        }
    has_shapes = "input_channels" in dir(self.parameters) or len(self.inbounds) > 0
    return {
        "name": self.name,
        "parameters": params,
        "variables": variables,
        "inbounds": [layer.name for layer in self.inbounds],
        "input_shape": self.input_dims if has_shapes else None,
        "output_shape": self.output_dims if has_shapes else None
    }


def set_variable(self, name, values):
    """Set the value of a layer variable.

    Layer variables are named entities representing the weights or
    thresholds used during inference:

    * Weights variables are typically integer arrays of shape:

      (num_neurons, features/channels, y, x) col-major ordered ('F')

    or equivalently:

      (x, y, features/channels, num_neurons) row-major ('C').

    * Threshold variables are typically integer or float arrays of shape:
      (num_neurons).

    Args:
        name (str): the variable name.
        values (:obj:`numpy.ndarray`): a numpy.ndarray containing the variable values.

    """
    self.variables[name] = np.ascontiguousarray(values)


def get_variable(self, name):
    """Get the value of a layer variable.

    Layer variables are named entities representing the weights or
    thresholds used during inference:

    * Weights variables are typically integer arrays of shape:
      (x, y, features/channels, num_neurons) row-major ('C').
    * Threshold variables are typically integer or float arrays of shape:
      (num_neurons).

    Args:
        name (str): the variable name.

    Returns:
        :obj:`numpy.ndarray`: an array containing the variable.

    """
    return self.variables[name]


def get_variable_names(self):
    """Get the list of variable names for this layer.

    Returns:
        a list of variable names.

    """
    return self.variables.names


def get_learning_histogram(self):
    """Returns an histogram of learning percentages.

    Returns a list of learning percentages and the associated number of
    neurons.

    Returns:
        :obj:`numpy.ndarray`: a (n,2) numpy.ndarray containing the learning
        percentages and the number of neurons.

    """
    histogram = np.zeros((100, 2), dtype=np.uint32)
    num_neurons = self.get_variable("weights").shape[3]
    num_weights = np.count_nonzero(self.get_variable("weights")) / num_neurons
    for i in range(num_neurons):
        threshold_learn = self.get_variable("threshold_learning")[i]
        learn_percentage = int(100 * threshold_learn / num_weights)
        histogram[learn_percentage, 0] = learn_percentage
        histogram[learn_percentage, 1] += 1
    return histogram[histogram[:, 0] != 0, :]