blob: 30f942c6af155b6544c39cedc3f0875d71cf9c14 [file] [log] [blame]
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)