10 from caffe2.proto
import caffe2_pb2, caffe2_legacy_pb2
11 from caffe.proto
import caffe_pb2
12 from caffe2.python
import core, utils, workspace
13 from google.protobuf
import text_format
16 log = logging.getLogger(
"caffe_translator")
17 log.setLevel(logging.INFO)
20 def _StateMeetsRule(state, rule):
21 """A function that reproduces Caffe's StateMeetsRule functionality.""" 22 if rule.HasField(
'phase')
and rule.phase != state.phase:
24 if rule.HasField(
'min_level')
and state.level < rule.min_level:
26 if rule.HasField(
'max_level')
and state.level > rule.max_lavel:
28 curr_stages = set(list(state.stage))
30 if len(rule.stage)
and any([s
not in curr_stages
for s
in rule.stage]):
33 if len(rule.not_stage)
and any([s
in curr_stages
for s
in rule.not_stage]):
39 def _ShouldInclude(net_state, layer):
40 """A function that reproduces Caffe's inclusion and exclusion rule.""" 41 ret = (len(layer.include) == 0)
43 ret &=
not any([_StateMeetsRule(net_state, rule)
for rule
in layer.exclude])
44 if len(layer.include):
46 ret |= any([_StateMeetsRule(net_state, rule)
for rule
in layer.include])
55 """A decorator for registering gradient mappings.""" 64 def TranslateLayer(cls, layer, pretrained_blobs, is_test):
66 caffe_ops, params = cls.
registry_[layer.type](
67 layer, pretrained_blobs, is_test)
69 raise KeyError(
'No translator registered for layer: %s yet.' %
73 if type(caffe_ops)
is not list:
74 caffe_ops = [caffe_ops]
75 return caffe_ops, params
85 net_state = caffe_pb2.NetState()
if net_state
is None else net_state
86 net = caffe2_pb2.NetDef()
87 net.name = caffe_net.name
88 net_params = caffe2_pb2.TensorProtos()
89 if len(caffe_net.layers) > 0:
91 'I think something is wrong. This translation script ' 92 'only accepts new style layers that are stored in the ' 95 for layer
in caffe_net.layer:
96 if not _ShouldInclude(net_state, layer):
97 log.info(
'Current net state does not need layer {}' 100 log.info(
'Translate layer {}'.format(layer.name))
102 pretrained_layers = (
103 [l
for l
in pretrained_net.layer
104 if l.name == layer.name] + [l
105 for l
in pretrained_net.layers
106 if l.name == layer.name]
108 if len(pretrained_layers) > 1:
110 'huh? more than one pretrained layer of one name?')
111 elif len(pretrained_layers) == 1:
114 for blob
in pretrained_layers[0].blobs
120 pretrained_blobs = []
122 layer, pretrained_blobs, is_test)
123 net.op.extend(operators)
124 net_params.protos.extend(params)
125 return net, net_params
128 def TranslateModel(*args, **kwargs):
129 return TranslatorRegistry.TranslateModel(*args, **kwargs)
133 """Takes the net_params returned from TranslateModel, and wrap it as an 134 init net that contain GivenTensorFill. 136 This is a very simple feature that only works with float tensors, and is 137 only intended to be used in an environment where you want a single 138 initialization file - for more complex cases, use a db to store the 141 init_net = caffe2_pb2.NetDef()
142 for tensor
in net_params.protos:
143 if len(tensor.float_data) == 0:
145 "Only float tensors are supported in this util.")
147 "GivenTensorFill", [], [tensor.name],
151 init_net.op.extend([op])
157 """A simple translate interface that maps the layer input and output.""" 158 caffe2_op = caffe2_pb2.OperatorDef()
159 caffe2_op.type = caffe2_type
160 caffe2_op.input.extend(layer.bottom)
161 caffe2_op.output.extend(layer.top)
166 """Makes an argument based on the value type.""" 174 @TranslatorRegistry.Register(
"Input")
179 @TranslatorRegistry.Register(
"Data")
180 def TranslateData(layer, pretrained_blobs, is_test):
186 def _TranslateStridePadKernelHelper(param, caffe_op):
188 if (len(param.stride) > 1
or len(param.kernel_size) > 1
or 190 raise NotImplementedError(
191 "Translator currently does not support non-conventional " 192 "pad/kernel/stride settings." 194 stride = param.stride[0]
if len(param.stride)
else 1
195 pad = param.pad[0]
if len(param.pad)
else 0
196 kernel = param.kernel_size[0]
if len(param.kernel_size)
else 0
200 stride = param.stride
202 kernel = param.kernel_size
204 if param.HasField(
"stride_h")
or param.HasField(
"stride_w"):
210 if param.HasField(
"pad_h")
or param.HasField(
"pad_w"):
211 if param.pad_h == param.pad_w:
221 if param.HasField(
"kernel_h")
or param.HasField(
"kernel_w"):
228 @TranslatorRegistry.Register(
"Convolution")
229 def TranslateConv(layer, pretrained_blobs, is_test):
230 param = layer.convolution_param
232 output = caffe_op.output[0]
233 caffe_op.input.append(output +
'_w')
234 _TranslateStridePadKernelHelper(param, caffe_op)
239 if len(pretrained_blobs) == 2:
240 caffe_op.input.append(output +
'_b')
243 pretrained_blobs[1].flatten(), output +
'_b'))
249 if len(param.dilation) > 0:
250 if len(param.dilation) == 1:
251 AddArgument(caffe_op,
"dilation", param.dilation[0])
252 elif len(param.dilation) == 2:
253 AddArgument(caffe_op,
"dilation_h", param.dilation[0])
254 AddArgument(caffe_op,
"dilation_w", param.dilation[1])
255 return caffe_op, params
258 @TranslatorRegistry.Register(
"Deconvolution")
259 def TranslateDeconv(layer, pretrained_blobs, is_test):
260 param = layer.convolution_param
262 raise NotImplementedError(
263 "Translator currently does not support group deconvolution." 266 output = caffe_op.output[0]
267 _TranslateStridePadKernelHelper(param, caffe_op)
268 caffe_op.input.extend([output +
'_w', output +
'_b'])
272 pretrained_blobs[1].flatten(), output +
'_b' 274 return caffe_op, [weight, bias]
277 @TranslatorRegistry.Register(
"ReLU")
278 def TranslateRelu(layer, pretrained_blobs, is_test):
282 @TranslatorRegistry.Register(
"Pooling")
283 def TranslatePool(layer, pretrained_blobs, is_test):
284 param = layer.pooling_param
285 if param.pool == caffe_pb2.PoolingParameter.MAX:
287 elif param.pool == caffe_pb2.PoolingParameter.AVE:
289 _TranslateStridePadKernelHelper(param, caffe_op)
300 is_torch_pooling = param.torch_pooling
301 except AttributeError:
302 is_torch_pooling =
False 303 if not is_torch_pooling:
305 caffe2_legacy_pb2.CAFFE_LEGACY_POOLING)
306 if param.global_pooling:
311 @TranslatorRegistry.Register(
"LRN")
312 def TranslateLRN(layer, pretrained_blobs, is_test):
314 caffe_op.output.extend([
'_' + caffe_op.output[0] +
'_scale'])
315 param = layer.lrn_param
316 if param.norm_region != caffe_pb2.LRNParameter.ACROSS_CHANNELS:
318 "Does not support norm region other than across channels.")
319 AddArgument(caffe_op,
"size", int(param.local_size))
327 @TranslatorRegistry.Register(
"InnerProduct")
328 def TranslateInnerProduct(layer, pretrained_blobs, is_test):
329 param = layer.inner_product_param
331 if param.axis != 1
or param.transpose:
333 "We don't have testing case for non-default axis and transpose " 334 "cases yet so we are disabling it for now. If you have a model " 335 "with this, please do send us your model for us to update this " 336 "support, and you are more than welcome to send a PR for this.")
337 except AttributeError:
342 output = caffe_op.output[0]
343 caffe_op.input.extend([output +
'_w', output +
'_b'])
346 if pretrained_blobs[0].ndim
not in [2, 4]:
347 raise ValueError(
"Unexpected weight ndim.")
348 if (pretrained_blobs[0].ndim == 4
and 349 list(pretrained_blobs[0].shape[:2]) != [1, 1]):
351 "If pretrained blob has 4 dims (old-style Caffe), the first two " 352 "should be of value 1, but I got " + str(pretrained_blobs[0].shape))
354 pretrained_blobs[0].reshape(-1, pretrained_blobs[0].shape[-1]),
358 pretrained_blobs[1].flatten(), output +
'_b' 360 return caffe_op, [weight, bias]
363 @TranslatorRegistry.Register(
"Dropout")
364 def TranslateDropout(layer, pretrained_blobs, is_test):
366 caffe_op.output.extend([
'_' + caffe_op.output[0] +
'_mask'])
367 param = layer.dropout_param
368 AddArgument(caffe_op,
"ratio", param.dropout_ratio)
374 @TranslatorRegistry.Register(
"Softmax")
375 def TranslateSoftmax(layer, pretrained_blobs, is_test):
380 @TranslatorRegistry.Register(
"SoftmaxWithLoss")
381 def TranslateSoftmaxWithLoss(layer, pretrained_blobs, is_test):
383 "Softmax", [layer.bottom[0]],
384 layer.bottom[0] +
"_translator_autogen_softmax")
387 [softmax_op.output[0], layer.bottom[1]],
388 layer.bottom[0] +
"_translator_autogen_xent")
393 return [softmax_op, xent_op, loss_op], []
396 @TranslatorRegistry.Register(
"Accuracy")
397 def TranslateAccuracy(layer, pretrained_blobs, is_test):
399 if layer.accuracy_param.top_k != 1:
400 AddArgument(caffe_op,
"top_k", layer.accuracy_param.top_k)
404 @TranslatorRegistry.Register(
"Concat")
405 def TranslateConcat(layer, pretrained_blobs, is_test):
407 caffe_op.output.extend([
'_' + caffe_op.output[0] +
'_dims'])
412 @TranslatorRegistry.Register(
"TanH")
413 def TranslateTanH(layer, pretrained_blobs, is_test):
418 @TranslatorRegistry.Register(
"InstanceNorm")
419 def TranslateInstanceNorm(layer, pretrained_blobs, is_test):
421 output = caffe_op.output[0]
423 pretrained_blobs[0].flatten(), output +
'_w')
425 pretrained_blobs[1].flatten(), output +
'_b')
426 caffe_op.input.extend([output +
'_w', output +
'_b'])
428 return caffe_op, [weight, bias]
431 @TranslatorRegistry.Register(
"Eltwise")
432 def TranslateElementWise(layer, pretrained_blobs, is_test):
433 param = layer.eltwise_param
436 if len(param.coeff)
or param.operation != 1:
437 raise RuntimeError(
"This eltwise layer is not yet supported.")
442 @TranslatorRegistry.Register(
"Scale")
443 def TranslateScale(layer, pretrained_blobs, is_test):
445 scale_param = layer.scale_param
448 if len(mul_op.input) == 1:
450 if scale_param.num_axes != 1:
451 raise RuntimeError(
"This path has not been verified yet.")
453 output = mul_op.output[0]
454 mul_op_param = output +
'_w' 455 mul_op.input.append(mul_op_param)
458 pretrained_blobs[0].flatten(), mul_op_param))
461 if len(pretrained_blobs) == 1:
464 elif len(pretrained_blobs) == 2:
468 add_op = copy.deepcopy(mul_op)
470 add_op_param = output +
'_b' 471 internal_blob = output +
"_internal" 473 mul_op.output.append(internal_blob)
475 add_op.input.append(internal_blob)
476 add_op.input.append(add_op_param)
478 pretrained_blobs[1].flatten(), add_op_param))
480 raise RuntimeError(
"Unexpected number of pretrained blobs in Scale")
484 caffe_ops.append(add_op)
485 assert len(caffe_ops) == len(weights)
486 return caffe_ops, weights
487 elif len(mul_op.input) == 2:
489 raise RuntimeError(
"This path has not been verified yet.")
491 raise RuntimeError(
"Unexpected number of inputs.")
494 @TranslatorRegistry.Register(
"Reshape")
495 def TranslateReshape(layer, pretrained_blobs, is_test):
497 caffe_op.output.append(
"_" + caffe_op.input[0] +
"_dims")
498 reshape_param = layer.reshape_param
499 AddArgument(caffe_op,
'shape', reshape_param.shape.dim)
503 @TranslatorRegistry.Register(
"Flatten")
504 def TranslateFlatten(layer, pretrained_blobs, is_test):
505 param = layer.flatten_param
506 if param.end_axis != -1:
507 raise NotImplementedError(
"flatten_param.end_axis not supported yet.")
511 elif param.axis == 1:
515 raise NotImplementedError(
516 "Not supported yet for flatten_param.axis {}.".format(param.axis))
521 @TranslatorRegistry.Register(
"Sigmoid")
522 def TranslateSigmoid(layer, pretrained_blobs, is_test):
527 @TranslatorRegistry.Register(
"ROIPooling")
528 def TranslateROIPooling(layer, pretrained_blobs, is_test):
536 caffe_op.output.append(caffe_op.output[0] +
'_argmaxes')
538 param = layer.roi_pooling_param
539 if param.HasField(
'pooled_h'):
541 if param.HasField(
'pooled_w'):
543 if param.HasField(
'spatial_scale'):
544 AddArgument(caffe_op,
'spatial_scale', param.spatial_scale)
549 @TranslatorRegistry.Register(
"PReLU")
550 def TranslatePRelu(layer, pretrained_blobs, is_test):
552 output = caffe_op.output[0]
553 caffe_op.input.extend([output +
'_Slope'])
556 return caffe_op, [slope]
559 @TranslatorRegistry.Register(
"Reduction")
560 def TranslateReduction(layer, pretrained_blobs, is_test):
561 param = layer.reduction_param
562 if param.operation == caffe_pb2.ReductionParameter.SUM:
564 elif param.operation == caffe_pb2.ReductionParameter.MEAN:
567 raise NotImplementedError(
"Not yet supported")
572 raise NotImplementedError(
"Not yet supported")
573 num_reduce_dim = -param.axis
574 AddArgument(caffe_op,
"num_reduce_dim", num_reduce_dim)
579 if __name__ ==
'__main__':
580 parser = argparse.ArgumentParser(
581 description=
"Utilitity to convert pretrained caffe models to Caffe2 models.")
582 parser.add_argument(
"prototext", help=
"Caffe prototext.")
583 parser.add_argument(
"caffemodel", help=
"Caffe trained model.")
584 parser.add_argument(
"--init_net", help=
"Caffe2 initialization net.", default=
"init_net.pb")
585 parser.add_argument(
"--predict_net", help=
"Caffe2 prediction net.", default=
"predict_net.pb")
586 args = parser.parse_args()
588 caffenet = caffe_pb2.NetParameter()
589 caffenet_pretrained = caffe_pb2.NetParameter()
590 input_proto = args.prototext
591 input_caffemodel = args.caffemodel
592 output_init_net = args.init_net
593 output_predict_net = args.predict_net
596 open(input_proto).read(), caffenet
598 caffenet_pretrained.ParseFromString(
599 open(input_caffemodel).read()
601 net, pretrained_params = TranslateModel(
602 caffenet, caffenet_pretrained, is_test=
True 606 external_input = net.op[0].input[0]
607 external_output = net.op[-1].output[0]
609 net.external_input.extend([external_input])
610 net.external_input.extend([param.name
for param
in pretrained_params.protos])
611 net.external_output.extend([external_output])
614 for param
in pretrained_params.protos:
616 with open(output_predict_net,
'wb')
as f:
617 f.write(net.SerializeToString())
618 with open(output_init_net,
'wb')
as f:
619 f.write(init_net.SerializeToString())
def Register(cls, op_name)
def Caffe2TensorToNumpyArray(tensor)
def NumpyArrayToCaffe2Tensor(arr, name=None)
def MakeArgument(key, value)
def CaffeBlobToNumpyArray(blob)
def AddArgument(op, key, value)
def FeedBlob(name, arr, device_option=None)
def CreateOperator(operator_type, inputs, outputs, name='', control_input=None, device_option=None, arg=None, engine=None, kwargs)
def BaseTranslate(layer, caffe2_type)
def TranslateLayer(cls, layer, pretrained_blobs, is_test)
def ConvertTensorProtosToInitNet(net_params, input_name)
def TranslateInput(layer, pretrained_blobs, is_test)
Common translators for layers.