Caffe2 - Python API
A deep learning, cross platform ML framework
layer_model_helper.py
1 
3 from __future__ import absolute_import
4 from __future__ import division
5 from __future__ import print_function
6 from __future__ import unicode_literals
7 
8 from caffe2.python import core, model_helper, schema
9 from caffe2.python.layers import layers
10 
11 import logging
12 import numpy as np
13 logger = logging.getLogger(__name__)
14 
15 
17  """
18  Model helper for building models on top of layers abstractions.
19 
20  Each layer is the abstraction that is higher level than Operator. Layer
21  is responsible for ownership of it's own parameters and can easily be
22  instantiated in multiple nets possible with different sets of ops.
23  As an example: one can easily instantiate predict and train nets from
24  the same set of layers, where predict net will have subset of the
25  operators from train net.
26  """
27 
28  def __init__(self, name, input_feature_schema, trainer_extra_schema):
29  super(LayerModelHelper, self).__init__(name=name)
30  self._layer_names = set()
31  self._layers = []
32 
33  # optimizer bookkeeping
34  self.param_to_optim = {}
35 
36  self._default_optimizer = None
37  self._loss = None
38  self._output_schema = None
39 
40  # Connect Schema to self.net. That particular instance of schmea will be
41  # use for generation of the Layers accross the network and would be used
42  # for connection with Readers.
44  self.net,
45  input_feature_schema
46  )
48  self.net,
49  trainer_extra_schema
50  )
52 
54  self.param_init_net = self.create_init_net('param_init_net')
55 
56  def add_metric_field(self, name, value):
57  assert name not in self._metrics_schema.fields, (
58  "Try to add metric field twice: {}".format(name))
60  (name, value)
61  )
62 
63  def add_global_constant(self, name, array=None, dtype=None,
64  initializer=None):
65  # This is global namescope for constants. They will be created in all
66  # init_nets and there should be very few of them.
67  assert name not in self.global_constants
68  self.global_constants[name] = self.net.NextBlob(name)
69 
70  if array is not None:
71  assert initializer is None,\
72  "Only one from array and initializer should be specified"
73  if dtype is None:
74  array = np.array(array)
75  else:
76  array = np.array(array, dtype=dtype)
77 
78  # TODO: make GivenTensor generic
79  op_name = None
80  if array.dtype == np.int32:
81  op_name = 'GivenTensorIntFill'
82  elif array.dtype == np.int64:
83  op_name = 'GivenTensorInt64Fill'
84  elif array.dtype == np.str:
85  op_name = 'GivenTensorStringFill'
86  else:
87  op_name = 'GivenTensorFill'
88 
89  def initializer(blob_name):
90  return core.CreateOperator(op_name,
91  [],
92  blob_name,
93  shape=array.shape,
94  values=array.flatten().tolist()
95  )
96  else:
97  assert initializer is not None
98 
99  self.global_constant_initializers.append(
100  initializer(self.global_constants[name]))
101  return self.global_constants[name]
102 
103  def _init_global_constants(self):
104  self.global_constants = {}
106  self.add_global_constant('ONE', 1.0)
107  self.add_global_constant('ZERO', 0.0)
108  self.add_global_constant('ZERO_RANGE', [0, 0], dtype='int32')
109 
110  def _add_global_constants(self, init_net):
111  for initializer_op in self.global_constant_initializers:
112  init_net._net.op.extend([initializer_op])
113 
114  def create_init_net(self, name):
115  init_net = core.Net(name)
116  self._add_global_constants(init_net)
117  return init_net
118 
119  def next_layer_name(self, prefix):
120  base_name = core.ScopedName(prefix)
121  name = base_name
122  index = 0
123  while name in self._layer_names:
124  name = base_name + '_auto_' + str(index)
125  index += 1
126 
127  self._layer_names.add(name)
128  return name
129 
130  def add_layer(self, layer):
131  self._layers.append(layer)
132  for param in layer.get_parameters():
133  assert isinstance(param.parameter, core.BlobReference)
134  self.param_to_optim[str(param.parameter)] = param.optimizer
135 
136  # The primary value of adding everything to self.net - generation of the
137  # operators right away, i.e. if error happens it'll be detected
138  # immediately. Other then this - create_x_net should be called.
139  layer.add_operators(self.net, self.param_init_net)
140  return layer.output_schema
141 
142  def get_parameter_blobs(self):
143  param_blobs = []
144  for layer in self._layers:
145  for param in layer.get_parameters():
146  param_blobs.append(param.parameter)
147 
148  return param_blobs
149 
150  @property
151  def default_optimizer(self):
152  return self._default_optimizer
153 
154  @default_optimizer.setter
155  def default_optimizer(self, optimizer):
156  self._default_optimizer = optimizer
157 
158  @property
159  def input_feature_schema(self):
160  return self._input_feature_schema
161 
162  @property
163  def trainer_extra_schema(self):
164  return self._trainer_extra_schema
165 
166  @property
167  def metrics_schema(self):
168  """
169  Returns the schema that represents model output that should be used for
170  metric reporting.
171 
172  During the training/evaluation this schema will be appended to the
173  schema that represents model output.
174  """
175  return self._metrics_schema
176 
177  @property
178  def output_schema(self):
179  assert self._output_schema is not None
180  return self._output_schema
181 
182  @output_schema.setter
183  def output_schema(self, schema):
184  assert self._output_schema is None
185  self._output_schema = schema
186 
187  @property
188  def loss(self):
189  assert self._loss is not None
190  return self._loss
191 
192  @loss.setter
193  def loss(self, loss):
194  assert self._loss is None
195  self._loss = loss
196 
197  def __getattr__(self, layer):
198  # TODO(amalevich): Add add support for ifbpy inline documentation
199  if layers.layer_exists(layer):
200  def wrapper(*args, **kwargs):
201  return self.add_layer(
202  layers.create_layer(layer, self, *args, **kwargs))
203  return wrapper
204  elif core.IsOperator(layer):
205  def wrapper(*args, **kwargs):
206  def apply_operator(net, in_record, out_record):
207  # TODO(amalevich): Switch to net.operator as soon as it gets
208  # landed
209  net.__getattr__(layer)(in_record.field_blobs(),
210  out_record.field_blobs(),
211  **kwargs)
212  if 'name' not in kwargs:
213  kwargs['name'] = layer
214  return self.add_layer(
215  layers.create_layer('Functional',
216  self, *args, function=apply_operator,
217  **kwargs))
218  return wrapper
219  else:
220  raise ValueError(
221  "Tring to create non-registered layer: {0}".format(layer))
222 
223  @property
224  def layers(self):
225  return self._layers
226 
227  def apply_optimizers(self, train_net, train_init_net, grad_map):
228  for param, optimizer in self.param_to_optim.items():
229  if not optimizer:
230  optimizer = self.default_optimizer
231  # note that not all params has gradient and thus we sent None if
232  # gradient does not exists
233  optimizer(
234  train_net, train_init_net, param, grad_map.get(str(param)))
235 
236  def _GetOne(self):
237  return self.global_constants['ONE']
238 
239  # An optimizer which allows us to do NO optimization
240  def NoOptim(self, *args, **kwargs):
241  pass
def IsOperator(op_type)
Definition: core.py:95
Module caffe2.python.optimizer.
Definition: optimizer.py:1
def NewRecord(net, schema)
Definition: schema.py:908
Module caffe2.python.layers.layers.
Definition: __init__.py:1
def ScopedName(name)
Definition: core.py:207
def CreateOperator(operator_type, inputs, outputs, name='', control_input=None, device_option=None, arg=None, engine=None, kwargs)
Definition: core.py:259
def add_global_constant(self, name, array=None, dtype=None, initializer=None)