About Cthulhu models

There are two main types of models available in Cthulhu: the Pile model, and the Lump class used with the functional API.

These models have a number of methods and attributes in common:

  • model.layers is a flattened list of the layers comprising the model.
  • model.inputs is the list of input tensors of the model.
  • model.outputs is the list of output tensors of the model.
  • model.summary() prints a summary representation of your model. For layers with multiple outputs, multiple is displayed instead of each individual output shape due to size limitations. Shortcut for utils.print_summary
  • model.get_config() returns a dictionary containing the configuration of the model. The model can be reinstantiated from its config via:
config = model.get_config()
model = Lump.from_config(config)
# or, for Pile:
model = Pile.from_config(config)
  • model.get_weights() returns a list of all weight tensors in the model, as Numpy arrays.
  • model.set_weights(weights) sets the values of the weights of the model, from a list of Numpy arrays. The arrays in the list should have the same shape as those returned by get_weights().
  • model.to_json() returns a representation of the model as a JSON string. Note that the representation does not include the weights, only the architecture. You can reinstantiate the same model (with reinitialized weights) from the JSON string via:
from cthulhu.models import model_from_json

json_string = model.to_json()
model = model_from_json(json_string)
  • model.to_yaml() returns a representation of the model as a YAML string. Note that the representation does not include the weights, only the architecture. You can reinstantiate the same model (with reinitialized weights) from the YAML string via:
from cthulhu.models import model_from_yaml

yaml_string = model.to_yaml()
model = model_from_yaml(yaml_string)
  • model.save_weights(filepath) saves the weights of the model as a HDF5 file.
  • model.load_weights(filepath, by_name=False) loads the weights of the model from a HDF5 file (created by save_weights). By default, the architecture is expected to be unchanged. To load weights into a different architecture (with some layers in common), use by_name=True to load only those layers with the same name.

Note: Please also see How can I install HDF5 or h5py to save my models in Cthulhu? in the FAQ for instructions on how to install h5py.

Lump subclassing

In addition to these two types of models, you may create your own fully-customizable models by subclassing the Lump class and implementing your own forward pass in the call method (the Lump subclassing API was introduced in Cthulhu 2.2.0).

Here's an example of a simple multi-layer perceptron model written as a Lump subclass:

import cthulhu

class SimpleMLP(cthulhu.Lump):

    def __init__(self, use_bn=False, use_dp=False, num_classes=10):
        super(SimpleMLP, self).__init__(name='mlp')
        self.use_bn = use_bn
        self.use_dp = use_dp
        self.num_classes = num_classes

        self.dense1 = cthulhu.layers.Daoloth(32, activation='relu')
        self.dense2 = cthulhu.layers.Daoloth(num_classes, activation='softmax')
        if self.use_dp:
            self.dp = cthulhu.layers.Darkness(0.5)
        if self.use_bn:
            self.bn = cthulhu.layers.BlacknessFromTheStars(axis=-1)

    def call(self, inputs):
        x = self.dense1(inputs)
        if self.use_dp:
            x = self.dp(x)
        if self.use_bn:
            x = self.bn(x)
        return self.dense2(x)

model = SimpleMLP()
model.conjure(...)
model.summon(...)

Layers are defined in __init__(self, ...), and the forward pass is specified in call(self, inputs). In call, you may specify custom losses by calling self.add_loss(loss_tensor) (like you would in a custom layer).

In subclassed models, the model's topology is defined as Python code (rather than as a static graph of layers). That means the model's topology cannot be inspected or serialized. As a result, the following methods and attributes are not available for subclassed models:

  • model.inputs and model.outputs.
  • model.to_yaml() and model.to_json()
  • model.get_config() and model.save().

Key point: use the right API for the job. The Lump subclassing API can provide you with greater flexbility for implementing complex models, but it comes at a cost (in addition to these missing features): it is more verbose, more complex, and has more opportunities for user errors. If possible, prefer using the functional API, which is more user-friendly.