3 from __future__
import absolute_import
4 from __future__
import division
5 from __future__
import print_function
6 from __future__
import unicode_literals
10 from caffe2.python import core, schema, scope, utils, workspace
12 from caffe2.proto
import caffe2_pb2
14 from collections
import namedtuple
16 logger = logging.getLogger(__name__)
17 logger.setLevel(logging.INFO)
20 IdList = schema.List(np.int64)
21 IdScoreList = schema.Map(np.int64, np.float32)
25 if schema.equal_schemas(record, IdList):
27 elif schema.equal_schemas(record, IdScoreList, check_field_types=
False):
30 raise NotImplementedError(
'Not implemented for {}'.format(record))
31 assert record[key].metadata
is not None, (
32 "Blob {} doesn't have metadata".format(str(record[key]())))
36 def get_categorical_limit(record):
38 return key.metadata.categorical_limit
41 def get_avg_length(record):
42 return record[
'lengths'].metadata.expected_value
45 def set_request_only(field):
46 for f
in field.all_scalars():
47 categorical_limit, expected_value =
None,
None 49 feature_specs = schema.FeatureSpec(
50 feature_is_request_only=
True,
52 elif not f.metadata.feature_specs:
53 categorical_limit = f.metadata.categorical_limit
54 expected_value = f.metadata.expected_value
55 feature_specs = schema.FeatureSpec(
56 feature_is_request_only=
True,
59 categorical_limit = f.metadata.categorical_limit
60 expected_value = f.metadata.expected_value
61 feature_specs = schema.FeatureSpec(
62 feature_type=f.metadata.feature_specs.feature_type,
63 feature_names=f.metadata.feature_specs.feature_names,
64 feature_ids=f.metadata.feature_specs.feature_ids,
65 feature_is_request_only=
True,
66 desired_hash_size=f.metadata.feature_specs.desired_hash_size,
70 if not np.issubdtype(f.field_type(), np.integer):
71 assert categorical_limit
is None, \
72 "categorical_limit shouldn't be set for no-integer field" 76 categorical_limit=categorical_limit,
77 expected_value=expected_value,
78 feature_specs=feature_specs,
85 List of contexts where layer could be instantitated 91 ACCUMULATE_PRED =
'accumulate_pred' 93 PREDICTION =
'prediction' 100 def register_layer(name, layer):
101 assert name
not in _LAYER_REGISTRY,
"{0} already exists".format(name)
102 _LAYER_REGISTRY[name] = layer
105 def layer_exists(name):
106 return name
in _LAYER_REGISTRY
109 def get_layer_class(name):
110 return _LAYER_REGISTRY[name]
113 def create_layer(layer_name, *args, **kwargs):
114 return _LAYER_REGISTRY[layer_name](*args, **kwargs)
117 LayerPsParam = namedtuple(
'LayerPsParam', [
'sparse_key',
'average_length'])
122 def __init__(self, parameter=None, optimizer=None, initializer=None,
123 ps_param=
None, regularizer=
None):
125 "expect {0} to be a blob reference".format(str(parameter))
136 def initializer(self):
140 def initializer(self, op):
141 assert op
is None or core.IsOperator(getattr(op,
'type',
None)), \
142 "initializer expects an operator, got type: {}".format(type(op))
152 def shape(self, shape):
153 assert self.
shape is None or self.
shape == shape, \
154 "inconsistent shape for layer parameter:"\
155 " {}, expect: {}, but got {}".format(self, self.
shape, shape)
158 def _infer_shape_from_initializer(self):
159 for arg
in self.initializer.arg:
160 if arg.name ==
'shape':
161 return list(arg.ints)
162 with workspace.WorkspaceGuard(
"model_init_by_loading_params"):
166 shape_blob = net.NextScopedBlob(self.
parameter +
"_shape")
168 workspace.RunNetOnce(net)
169 shape = workspace.FetchBlob(shape_blob).tolist()
171 workspace.ResetWorkspace()
173 except RuntimeError
as exp:
175 "Cannot infer the shape of blob {} from operator {}: {}".format(
176 self.
parameter, self.initializer.type, exp)
178 workspace.ResetWorkspace()
185 def is_request_only_scalar(scalar):
186 if len(scalar.field_metadata()) == 0:
188 for metadata
in scalar.field_metadata():
189 if not (metadata
and metadata.feature_specs
and getattr(
190 metadata.feature_specs,
'feature_is_request_only',
False)):
197 def __init__(self, model, prefix, input_record,
198 predict_input_record_fields=
None, tags=
None, **kwargs):
200 Base class for model layers. Layer is an abstraction that allows to 201 provide model description in terms of meta-operators, where each of the 202 meta-operators can have different implementations for training, 203 evaluation and prediction, that are instantiated later. As an example 204 SampledSoftmax can do something related to sampling depending on 205 supervision during the training and just apply softmax if it's used for 206 prediction/evaluation. 208 All inputs/outputs from layers are represented as a record (instance of 209 schema bounded to blobs) and are accessible through input_record and 210 output_schema. If Layer needs to have only a subset of inputs/provides 211 subset of outputs during the inference - it should provide 212 predict_input_record and predict_output_schema correspondingly (those 213 records are expected to be a subset of input_record/output_schema). 215 Each layer has a list of Tags associated with it, that depends on 216 current context and arguments. It's possible to use those tags during 217 the instantiation time. 220 self.
name = model.next_layer_name(prefix)
224 if predict_input_record_fields:
225 if not isinstance(predict_input_record_fields, list):
226 predict_input_record_fields = [predict_input_record_fields]
228 predict_input_record_fields]
233 if len(input_record.all_scalars()) == 0:
235 for scalar
in input_record.all_scalars():
236 if not is_request_only_scalar(scalar):
243 self.
tags = set(tags
or [])
244 self.tags.update(TagContext.current().tags)
250 return self.__class__.__name__
252 def _check_output_schema(self):
253 assert self.
_output_schema is not None,
"Schema is not initialized" 257 "predict_output_schema is not a subset of the output_schema")
260 def predict_input_record(self):
264 def input_record(self):
268 def predict_output_schema(self):
272 @predict_output_schema.setter
273 def predict_output_schema(self, output_schema):
278 def output_schema(self):
284 @output_schema.setter
285 def output_schema(self, output_schema):
289 def get_parameters(self):
293 """Return a subset of parameters which can be converted to fp16""" 296 def get_memory_usage(self):
301 Adds layer initialization operators to passed net. 312 init_op = param.initializer
313 current_device_scope = scope.CurrentDeviceScope()
317 if not init_op.HasField(
'device_option')
and\
318 current_device_scope:
319 init_op = caffe2_pb2.OperatorDef()
320 init_op.CopyFrom(param.initializer)
321 init_op.device_option.CopyFrom(current_device_scope)
324 if any(utils.OpAlmostEqual(op, init_op,
'debug_info')
325 for op
in init_net._net.op):
328 init_net._net.op.extend([init_op])
330 def create_param(self, param_name, shape, initializer, optimizer,
331 ps_param=
None, regularizer=
None):
332 with scope.NameScope(self.
name, reset=
True):
333 param = self.model.create_param(param_name=param_name,
335 initializer=initializer,
338 regularizer=regularizer)
341 assert all(param.parameter != p.parameter
for p
in self.
params)
343 self.params.append(param)
344 return param.parameter
346 def get_next_blob_reference(self, name):
347 with scope.NameScope(self.
name, reset=
True):
348 return self.model.net.NextScopedBlob(name)
351 context=InstantiationContext.TRAINING):
353 Adds layer trainig or initialization operators to the passed in net. 354 init_net can be None and can be called independently from add_init_params 358 with scope.NameScope(self.
name):
359 if context
not in {InstantiationContext.PREDICTION,
360 InstantiationContext.EVAL,
361 InstantiationContext.ACCUMULATE_PRED}:
363 "Only prediction and eval context don't need init_net")
366 if context == InstantiationContext.TRAINING:
368 elif context == InstantiationContext.EVAL:
370 elif context == InstantiationContext.ACCUMULATE_PRED:
375 if context
in {InstantiationContext.TRAINING,
376 InstantiationContext.EVAL} \
380 def add_ops(self, net):
382 raise NotImplementedError
384 def add_eval_ops(self, net):
389 def add_train_ops(self, net):
394 def add_ops_to_accumulate_pred(self, net):
401 def add_param_copy_operators(self, net):
403 param_copy_ref = self.model.metrics_schema[str(param.parameter)]
404 net.Copy([param.parameter], param_copy_ref.field_blobs())
406 def export_output_for_metrics(self):
410 export_name = self.
name +
"/output" 413 def export_params_for_metrics(self):
419 str(param).
split(
"/")[-1] +
"_copy")
420 self.model.add_metric_field(str(param.parameter), param_copy_ref)
Module caffe2.python.layers.split.
def add_operators(self, net, init_net=None, context=InstantiationContext.TRAINING)
_export_output_for_metrics
def __init__(self, model, prefix, input_record, predict_input_record_fields=None, tags=None, kwargs)
def add_train_ops(self, net)
def get_next_blob_reference(self, name)
def add_param_copy_operators(self, net)
def get_fp16_compatible_parameters(self)
def _check_output_schema(self)
_export_params_for_metrics
def add_ops_to_accumulate_pred(self, net)
def add_init_params(self, init_net)
def add_eval_ops(self, net)
def _infer_shape_from_initializer(self)