3 from __future__
import absolute_import
4 from __future__
import division
5 from __future__
import print_function
6 from __future__
import unicode_literals
8 from caffe2.python import core, model_helper, schema, scope, utils, muji
13 parameter_sharing_context,
20 from caffe2.proto
import caffe2_pb2
21 from future.utils
import viewitems, viewvalues
27 logger = logging.getLogger(__name__)
32 Model helper for building models on top of layers abstractions. 34 Each layer is the abstraction that is higher level than Operator. Layer 35 is responsible for ownership of it's own parameters and can easily be 36 instantiated in multiple nets possible with different sets of ops. 37 As an example: one can easily instantiate predict and train nets from 38 the same set of layers, where predict net will have subset of the 39 operators from train net. 42 def __init__(self, name, input_feature_schema, trainer_extra_schema,
44 ''' TODO(amalevich): more documnetation on input args 47 super(LayerModelHelper, self).
__init__(name=name)
78 )
if not keep_blobs
else input_feature_schema.clone()
82 )
if not keep_blobs
else trainer_extra_schema.clone()
96 def clear_output_schema(self):
99 def set_initialize_params(self, initialize_params):
102 def add_metric_field(self, name, value):
103 assert name
not in self._metrics_schema.fields, (
104 "Try to add metric field twice: {}".format(name))
109 def add_ad_hoc_plot_blob(self, blob, dtype=None):
112 ),
"expect type str or BlobReference, but got {}".format(type(blob))
113 dtype = dtype
or (np.float, (1, ))
115 self.ad_hoc_plot_blobs.append(blob)
118 def _get_global_constant_initializer_op(
119 blob_name, array=
None, dtype=
None, initializer=
None 123 if array
is not None:
124 assert initializer
is None,\
125 "Only one from array and initializer should be specified" 127 array = np.array(array)
129 array = np.array(array, dtype=dtype)
133 if array.dtype == np.int32:
134 op_name =
'GivenTensorIntFill' 135 elif array.dtype == np.int64:
136 op_name =
'GivenTensorInt64Fill' 137 elif array.dtype == np.str:
138 op_name =
'GivenTensorStringFill' 139 elif array.dtype == np.bool:
140 op_name =
'GivenTensorBoolFill' 142 op_name =
'GivenTensorFill' 144 def initializer(blob_name):
145 return core.CreateOperator(
149 values=array.flatten().tolist()
152 assert initializer
is not None 153 initializer_op = initializer(blob_name)
154 return initializer_op
156 def add_global_constant(
157 self, name, array=
None, dtype=
None, initializer=
None 159 assert isinstance(name, six.string_types), (
160 'name should be a string as we are using it as map key')
164 "%s already added in global_constants" % name
165 blob_name = self.net.NextBlob(name)
167 initializer_op = LayerModelHelper._get_global_constant_initializer_op(
168 blob_name, array, dtype, initializer
171 "there is already a initializer op associated with blob %s" % \
176 def maybe_add_global_constant(self, name, *args, **kwargs):
184 LayerModelHelper._get_global_constant_initializer_op(
185 blob_name, *args, **kwargs
189 assert utils.OpAlmostEqual(
194 "conflict initializers for global constant %s, " \
195 "previous %s, now %s" % (
196 blob_name, str(initializer_op),
201 def _init_global_constants(self):
208 def _add_global_constants(self, init_net):
210 init_net._net.op.extend([initializer_op])
212 def create_init_net(self, name):
217 def _validate_param_shape(self, param_name, shape):
223 if shape != ref_shape:
225 "Got inconsistent shapes between shared parameters " 226 "when trying to map a blob in scope {0} to {1}. ref_shape : " 227 " {2}, shape : {3}".format(
228 scope.CurrentNameScope(), param_name, ref_shape, shape)
231 def create_param(self, param_name, shape, initializer, optimizer=None,
232 ps_param=
None, regularizer=
None):
234 param_name = str(param_name)
235 elif isinstance(param_name, six.string_types):
238 param_name = parameter_sharing_context.get_parameter_name(
241 raise ValueError(
"Unsupported type for param_name")
245 if len(initializer) == 1:
248 assert len(initializer) == 2
249 init_op_args = copy.deepcopy(initializer[1])
250 if shape
is not None:
251 assert 'shape' not in init_op_args
252 init_op_args.update({
'shape': shape})
254 initializer_op =
None 256 initializer_op = core.CreateOperator(
263 param = layers.LayerParameter(
264 parameter=param_blob,
265 initializer=initializer_op,
268 regularizer=regularizer
277 def next_layer_name(self, prefix):
278 base_name = core.ScopedName(prefix)
282 name = base_name +
'_auto_' + str(index)
285 self._layer_names.add(name)
288 def add_layer(self, layer):
289 self._layers.append(layer)
290 for param
in layer.get_parameters():
296 self.params.append(param.parameter)
297 if isinstance(param, layers.LayerParameter):
299 elif isinstance(param, ParameterInfo):
306 logger.info(
'regularization is unsupported for ParameterInfo object')
309 'unknown object type besides ParameterInfo and LayerParameter: {}' 317 return layer.output_schema
319 def get_parameter_blobs(self):
322 for param
in layer.get_parameters():
323 param_blobs.append(param.parameter)
327 def add_post_grad_net_modifiers(self, modifier):
330 assert isinstance(modifier, NetModifier),\
331 "{} has to be a NetModifier instance".format(modifier)
332 self._post_grad_net_modifiers.append(modifier)
334 def add_final_net_modifiers(self, modifier):
337 assert isinstance(modifier, NetModifier),\
338 "{} has to be a NetModifier instance".format(modifier)
339 self._final_net_modifiers.append(modifier)
346 def sequence_seed(self):
349 def store_seed(self, seed, sequence_seed=True):
355 def apply_seed(self, net):
360 def default_optimizer(self):
363 @default_optimizer.setter
364 def default_optimizer(self, optimizer):
368 def input_feature_schema(self):
372 def trainer_extra_schema(self):
378 Returns the schema that represents model output that should be used for 381 During the training/evaluation this schema will be appended to the 382 schema that represents model output. 387 def output_schema(self):
391 @output_schema.setter
392 def output_schema(self, schema):
397 def preproc_output_schema(self):
401 @preproc_output_schema.setter
402 def preproc_output_schema(self, schema):
407 def prediction(self):
408 assert self.
_prediction,
"model prediction is empty" 411 def add_prediction(self, prediction, weight=1.0):
412 assert prediction
is not None,
"Added prediction should not be None" 413 self._prediction.append((prediction, weight))
417 assert self.
_loss is not None 421 def loss(self, loss):
422 assert self.
_loss is None 426 return self.
_loss is not None 428 def add_loss(self, loss, name='unnamed'):
429 assert loss
is not None,
"Added loss should not be None" 432 ),
"Added loss should be a scalar or a struct" 433 if self.
_loss is None:
441 prefix_base = name +
'_auto_' 444 while prefix
in self.
_loss:
445 prefix = prefix_base + str(index)
450 def add_output_schema(self, name, value):
451 assert value
is not None, \
452 'Added output schema {} should not be None'.format(name)
455 'Added output schema {} should be a scalar or a struct.\n\ 456 Now it is {}.'.format(name, type(value))
460 assert name
not in self._output_schema.fields, \
461 'Output Schema Field {} already exists'.format(name)
465 def add_trainer_extra_schema(self, trainer_extra_schema):
466 trainer_extra_record = schema.NewRecord(self.
net, trainer_extra_schema)
469 def __getattr__(self, layer):
470 def is_functional_layer(layer):
471 if core.IsOperator(layer):
473 elif layer.startswith(
'FunctionalLayer'):
478 def resolve_functional_layer(layer):
479 if core.IsOperator(layer):
481 elif layer.startswith(
'FunctionalLayer'):
482 return layer[len(
'FunctionalLayer'):]
485 '%s cannot be resolved as functional layer' % layer
488 if layer.startswith(
'__'):
489 raise AttributeError(layer)
492 if layers.layer_exists(layer):
493 def wrapper(*args, **kwargs):
494 new_layer = layers.create_layer(layer, self, *args, **kwargs)
495 if kwargs.get(
"output_to_metrics",
False):
496 new_layer.export_output_for_metrics()
497 if kwargs.get(
"params_to_metrics",
False):
498 new_layer.export_params_for_metrics()
501 elif is_functional_layer(layer):
505 layer = resolve_functional_layer(layer)
507 def wrapper(*args, **kwargs):
508 def apply_operator(net, in_record, out_record, **kwargs):
511 net.__getattr__(layer)(in_record.field_blobs(),
512 out_record.field_blobs(),
515 if 'name' not in kwargs:
516 kwargs[
'name'] = layer
518 new_layer = layers.create_layer(
520 self, *args, function=apply_operator,
524 if kwargs.get(
"output_to_metrics",
False):
525 new_layer.export_output_for_metrics()
526 if kwargs.get(
"params_to_metrics",
False):
527 new_layer.export_params_for_metrics()
533 raise AttributeError(
534 "Trying to create non-registered layer: {}".format(layer))
540 def apply_regularizers_on_loss(
547 if regularizer
is None:
549 assert isinstance(regularizer, Regularizer)
550 added_loss_blob = regularizer(train_net, train_init_net, param, grad=
None,
551 by=RegularizationBy.ON_LOSS)
552 if added_loss_blob
is not None:
558 def apply_regularizers_after_optimizer(
567 blob_to_device = blob_to_device
or {}
569 if regularizer
is None:
571 assert isinstance(regularizer, Regularizer)
572 device = get_param_device(
574 grad_map.get(str(param)),
575 param_to_device=blob_to_device,
578 with core.DeviceScope(device):
580 train_net, train_init_net, param, grad=grad_map.get(str(param)),
581 by=RegularizationBy.AFTER_OPTIMIZER
584 def apply_post_grad_net_modifiers(
590 modify_output_record=
False,
592 param_grad_map = {param: grad_map[param]
593 for param
in self.param_to_optim.keys()
if param
in grad_map}
596 modifier(trainer_net, trainer_init_net, param_grad_map,
597 blob_to_device=blob_to_device,
598 modify_output_record=modify_output_record)
600 def apply_final_net_modifiers(
606 modify_output_record=
False,
609 modifier(trainer_net, trainer_init_net, grad_map,
610 blob_to_device=blob_to_device,
611 modify_output_record=modify_output_record)
613 def apply_optimizers(
622 blob_to_device = blob_to_device
or {}
624 assert optimizer
is not None, \
625 "default optimizer must have been set in add_layer" 628 device = get_param_device(
630 grad_map.get(str(param)),
631 param_to_device=blob_to_device,
634 if device
is not None:
636 del device.extra_info[:]
638 with core.DeviceScope(device):
640 train_net, train_init_net, param, grad_map.get(str(param)))
646 def NoOptim(self, *args, **kwargs):
650 def breakdown_map(self):
653 @breakdown_map.setter
654 def breakdown_map(self, breakdown_map):
657 assert isinstance(breakdown_map, dict)
658 assert all(isinstance(k, six.string_types)
for k
in breakdown_map)
659 assert sorted(breakdown_map.values()) == list(range(len(breakdown_map)))
def default_optimizer(self)
def add_global_constant(self, name, array=None, dtype=None, initializer=None)
def add_metric_field(self, name, value)
def _validate_param_shape(self, param_name, shape)
global_constant_initializers
Module caffe2.python.layers.layers.
def _init_global_constants(self)
def add_loss(self, loss, name='unnamed')
ad_hoc_diagnose_blobs_and_operations
def add_layer(self, layer)
def _add_global_constants(self, init_net)
def __init__(self, name, input_feature_schema, trainer_extra_schema, keep_blobs=False)
def create_init_net(self, name)