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_summarymodel.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 byget_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 bysave_weights
). By default, the architecture is expected to be unchanged. To load weights into a different architecture (with some layers in common), useby_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
andmodel.outputs
.model.to_yaml()
andmodel.to_json()
model.get_config()
andmodel.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.