| from __future__ import absolute_import |
| from __future__ import division |
| from __future__ import print_function |
| from __future__ import unicode_literals |
| |
| from caffe2.python import schema, scope |
| from caffe2.python.layers.tags import TagContext |
| |
| from collections import namedtuple |
| import numpy as np |
| |
| # Some types to simplify descriptions of things traveling between ops |
| IdList = schema.List(np.int64) |
| IdScoreList = schema.Map(np.int64, np.float32) |
| |
| |
| class InstantiationContext(object): |
| """ |
| List of contexts where layer could be instantitated |
| """ |
| TRAINING = 'training' |
| PREDICTION = 'prediction' |
| |
| |
| _LAYER_REGISTRY = {} |
| |
| |
| def register_layer(name, layer): |
| assert name not in _LAYER_REGISTRY, "{0} already exists".format(name) |
| _LAYER_REGISTRY[name] = layer |
| |
| |
| def layer_exists(name): |
| return name in _LAYER_REGISTRY |
| |
| |
| def create_layer(layer_name, *args, **kwargs): |
| return _LAYER_REGISTRY[layer_name](*args, **kwargs) |
| |
| |
| # TODO(amalevich): Modify this to some better struct, something closer to |
| # ParameterInfo. |
| LayerParameter = namedtuple( |
| 'LayerParameter', ['parameter', 'optimizer', 'initializer']) |
| |
| |
| def _is_request_only_scalar(scalar): |
| if len(scalar.field_metadata()) == 0: |
| return False |
| for metadata in scalar.field_metadata(): |
| if not (metadata and metadata.feature_specs and getattr( |
| metadata.feature_specs, 'feature_is_request_only', False)): |
| return False |
| return True |
| |
| |
| class ModelLayer(object): |
| |
| def __init__(self, model, prefix, input_record, tags=set(), **kwargs): |
| self.name = model.next_layer_name(prefix) |
| self.model = model |
| self.kwargs = kwargs |
| self.input_record = input_record |
| self.request_only = True |
| if len(input_record.all_scalars()) == 0: |
| self.request_only = False |
| for scalar in input_record.all_scalars(): |
| if not _is_request_only_scalar(scalar): |
| self.request_only = False |
| break |
| self.output_schema = None |
| self.tags = set(tags) |
| self.tags.update(TagContext.current().tags) |
| self.params = [] |
| |
| def get_type(self): |
| return self.__class__.__name__ |
| |
| def get_output_schema(self): |
| assert self.output_schema is not None, "Schema is not initialized" |
| return self.output_schema |
| |
| def get_parameters(self): |
| return self.params |
| |
| def get_fp16_compatible_parameters(self): |
| """Return a subset of parameters which can be converted to fp16""" |
| return [] |
| |
| def get_memory_usage(self): |
| return 0 |
| |
| def add_operators(self, net, init_net=None, |
| context=InstantiationContext.TRAINING): |
| # Namescope below should warranty that all intermediate blobs will be |
| # assiciated with the layer that produces them |
| with scope.NameScope(self.name): |
| if context != InstantiationContext.PREDICTION: |
| assert init_net,\ |
| "Only prediction context can be used without init_net" |
| if init_net: |
| for param in self.params: |
| # TODO(amalevich): Either return back to lambdas, that add |
| # all params (looks a bit safer and breaking less |
| # abstractions) or extend Net interface to this type of |
| # operations better |
| init_net._net.op.extend([param.initializer]) |
| if context == InstantiationContext.TRAINING: |
| self.add_train_ops(net) |
| else: |
| self.add_ops(net) |
| |
| def add_ops(self, net): |
| raise NotImplementedError |
| |
| def add_train_ops(self, net): |
| # Default train layer implementation is completely matching predict |
| # layer implementation. |
| self.add_ops(net) |