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_level:
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])
50
def _GetLegacyDims(net, net_params, dummy_input, legacy_pad_ops):
52
ws = workspace.C.Workspace()
53
for param in net_params.protos:
54
ws.create_blob(param.name) \
55
.feed(utils.Caffe2TensorToNumpyArray(param))
56
external_input = net.op[0].input[0]
57
ws.create_blob(external_input).feed(dummy_input)
59
for i in range(len(net.op)):
61
ws._run_operator(op_def.SerializeToString())
62
if i in legacy_pad_ops:
63
output = op_def.output[0]
64
blob_legacy = ws.fetch_blob(output)
65
dim_map[i] = blob_legacy.shape
69
def _GetLegacyPadArgs(op_def, arg_map):
71
keys = ['pad_l', 'pad_t', 'pad_r', 'pad_b']
72
is_pad = 'pad' in arg_map
75
pads[k] = arg_map['pad'].i
77
pads = {x: arg_map[x].i for x in keys}
81
def _AdjustDims(op_def, arg_map, pads, dim1, dim2):
86
is_pad = 'pad' in arg_map
87
if h1 != h2 or w1 != w2:
91
raise Exception("Unexpected dimensions for height:", h1, h2)
95
raise Exception("Unexpected dimensions for width:", w1, w2)
97
op_def.arg.remove(arg_map['pad'])
99
for name in pads.keys():
100
arg = caffe2_pb2.Argument()
104
op_def.arg.extend(args)
106
for name in pads.keys():
107
arg_map[name].i = pads[name]
110
def _RemoveLegacyPad(net, net_params, input_dims):
112
for i in range(len(net.op)):
114
if re.match(r'^(Conv|ConvTranspose|MaxPool|AveragePool)(\dD)?$',
116
for arg in op_def.arg:
117
if arg.name == 'legacy_pad':
118
legacy_pad_ops.append(i)
121
n, c, h, w = input_dims
122
dummy_input = np.random.randn(n, c, h, w).astype(np.float32)
123
dim_map = _GetLegacyDims(net, net_params, dummy_input, legacy_pad_ops)
127
ws = workspace.C.Workspace()
129
external_input = net.op[0].input[0]
130
ws.create_blob(external_input).feed_blob(dummy_input)
131
for param in net_params.protos:
132
ws.create_blob(param.name) \
133
.feed_blob(utils.Caffe2TensorToNumpyArray(param))
135
for i in range(len(net.op)):
137
if i in legacy_pad_ops:
139
for arg in op_def.arg:
140
arg_map[arg.name] = arg
141
pads = _GetLegacyPadArgs(op_def, arg_map)
143
for j in range(len(op_def.arg)):
145
if arg.name == 'legacy_pad':
148
output = op_def.output[0]
150
nonlegacy_output = output + '_nonlegacy'
151
op_def.output[0] = nonlegacy_output
152
ws._run_operator(op_def.SerializeToString())
153
blob_nonlegacy = ws.fetch_blob(nonlegacy_output)
155
op_def.output[0] = output
158
dim2 = blob_nonlegacy.shape
159
_AdjustDims(op_def, arg_map, pads, dim1, dim2)
161
ws._run_operator(op_def.SerializeToString())
165
def _GetBlobDimMap(net, net_params, dummy_input):
167
ws = workspace.C.Workspace()
168
for param in net_params.protos:
169
ws.create_blob(param.name) \
170
.feed(utils.Caffe2TensorToNumpyArray(param))
171
external_input = net.op[0].input[0]
172
ws.create_blob(external_input).feed(dummy_input)
174
for i in range(len(net.op)):
176
ws._run_operator(op_def.SerializeToString())
177
for output in op_def.output:
178
blob = ws.fetch_blob(output)
179
dim_map[output] = blob.shape
183
def _GetInputDims(caffe_net):
185
if caffe_net.input_dim:
186
input_dims = caffe_net.input_dim
187
elif caffe_net.input_shape:
188
input_dims = caffe_net.input_shape[0].dim
189
elif caffe_net.layer[0].input_param.shape:
191
input_dims = caffe_net.layer[0].input_param.shape[0].dim
195
class TranslatorRegistry:
199
def Register(cls, op_name):
200
"""A decorator for registering gradient mappings."""
203
cls.registry_[op_name] = func
209
def TranslateLayer(cls, layer, pretrained_blobs, is_test, **kwargs):
211
caffe_ops, params = cls.registry_[layer.type](
212
layer, pretrained_blobs, is_test, **kwargs)
213
except KeyError as e:
214
raise KeyError('No translator registered for layer: %s yet.' %
216
if caffe_ops is None:
218
if type(caffe_ops) is not list:
219
caffe_ops = [caffe_ops]
220
return caffe_ops, params
229
remove_legacy_pad=False,
232
net_state = caffe_pb2.NetState() if net_state is None else net_state
233
net = caffe2_pb2.NetDef()
234
net.name = caffe_net.name
235
net_params = caffe2_pb2.TensorProtos()
236
if len(caffe_net.layers) > 0:
238
'I think something is wrong. This translation script '
239
'only accepts new style layers that are stored in the '
243
input_dims = _GetInputDims(caffe_net)
244
for layer in caffe_net.layer:
245
if not _ShouldInclude(net_state, layer):
246
log.info('Current net state does not need layer {}'
249
log.info('Translate layer {}'.format(layer.name))
251
pretrained_layers = (
252
[l for l in pretrained_net.layer
253
if l.name == layer.name] + [l
254
for l in pretrained_net.layers
255
if l.name == layer.name]
257
if len(pretrained_layers) > 1:
259
'huh? more than one pretrained layer of one name?')
260
elif len(pretrained_layers) == 1:
262
utils.CaffeBlobToNumpyArray(blob)
263
for blob in pretrained_layers[0].blobs
269
pretrained_blobs = []
270
operators, params = cls.TranslateLayer(
271
layer, pretrained_blobs, is_test, net=net,
272
net_params=net_params, input_dims=input_dims)
273
net.op.extend(operators)
274
net_params.protos.extend(params)
275
if remove_legacy_pad:
277
'Please specify input_dims to remove legacy_pad'
278
net = _RemoveLegacyPad(net, net_params, input_dims)
279
return net, net_params
282
def TranslateModel(*args, **kwargs):
283
return TranslatorRegistry.TranslateModel(*args, **kwargs)
286
def ConvertTensorProtosToInitNet(net_params, input_name):
287
"""Takes the net_params returned from TranslateModel, and wrap it as an
288
init net that contain GivenTensorFill.
290
This is a very simple feature that only works with float tensors, and is
291
only intended to be used in an environment where you want a single
292
initialization file - for more complex cases, use a db to store the
295
init_net = caffe2_pb2.NetDef()
296
for tensor in net_params.protos:
297
if len(tensor.float_data) == 0:
299
"Only float tensors are supported in this util.")
300
op = core.CreateOperator(
301
"GivenTensorFill", [], [tensor.name],
303
utils.MakeArgument("shape", list(tensor.dims)),
304
utils.MakeArgument("values", tensor.float_data)])
305
init_net.op.extend([op])
306
init_net.op.extend([core.CreateOperator("ConstantFill", [], [input_name], shape=[1])])
310
def BaseTranslate(layer, caffe2_type):
311
"""A simple translate interface that maps the layer input and output."""
312
caffe2_op = caffe2_pb2.OperatorDef()
313
caffe2_op.type = caffe2_type
314
caffe2_op.input.extend(layer.bottom)
315
caffe2_op.output.extend(layer.top)
319
def AddArgument(op, key, value):
320
"""Makes an argument based on the value type."""
321
op.arg.extend([utils.MakeArgument(key, value)])
328
@TranslatorRegistry.Register("Input")
329
def TranslateInput(layer, pretrained_blobs, is_test, **kwargs):
333
@TranslatorRegistry.Register("VideoData")
334
def TranslateVideoData(layer, pretrained_blobs, is_test, **kwargs):
338
@TranslatorRegistry.Register("Data")
339
def TranslateData(layer, pretrained_blobs, is_test, **kwargs):
345
def _TranslateStridePadKernelHelper(param, caffe_op):
347
if (len(param.stride) > 1 or len(param.kernel_size) > 1 or
349
raise NotImplementedError(
350
"Translator currently does not support non-conventional "
351
"pad/kernel/stride settings."
353
stride = param.stride[0] if len(param.stride) else 1
354
pad = param.pad[0] if len(param.pad) else 0
355
kernel = param.kernel_size[0] if len(param.kernel_size) else 0
359
stride = param.stride
361
kernel = param.kernel_size
363
if param.HasField("stride_h") or param.HasField("stride_w"):
364
AddArgument(caffe_op, "stride_h", param.stride_h)
365
AddArgument(caffe_op, "stride_w", param.stride_w)
367
AddArgument(caffe_op, "stride", stride)
369
if param.HasField("pad_h") or param.HasField("pad_w"):
370
if param.pad_h == param.pad_w:
371
AddArgument(caffe_op, "pad", param.pad_h)
373
AddArgument(caffe_op, "pad_t", param.pad_h)
374
AddArgument(caffe_op, "pad_b", param.pad_h)
375
AddArgument(caffe_op, "pad_l", param.pad_w)
376
AddArgument(caffe_op, "pad_r", param.pad_w)
378
AddArgument(caffe_op, "pad", pad)
380
if param.HasField("kernel_h") or param.HasField("kernel_w"):
381
AddArgument(caffe_op, "kernel_h", param.kernel_h)
382
AddArgument(caffe_op, "kernel_w", param.kernel_w)
384
AddArgument(caffe_op, "kernel", kernel)
387
@TranslatorRegistry.Register("Convolution3D")
388
def TranslateConvNd(layer, pretrained_blobs, is_test, **kwargs):
389
param = layer.convolution3d_param
390
caffe_op = BaseTranslate(layer, "Conv")
391
output = caffe_op.output[0]
392
caffe_op.input.append(output + '_w')
397
[param.kernel_depth, param.kernel_size, param.kernel_size])
401
[param.temporal_stride, param.stride, param.stride])
404
if hasattr(param, 'temporal_pad'):
405
temporal_pad = param.temporal_pad
406
if hasattr(param, 'pad'):
407
spatial_pad = param.pad
408
AddArgument(caffe_op, "pads", [temporal_pad, spatial_pad, spatial_pad] * 2)
412
utils.NumpyArrayToCaffe2Tensor(pretrained_blobs[0], output + '_w')]
414
if len(pretrained_blobs) == 2:
415
caffe_op.input.append(output + '_b')
417
utils.NumpyArrayToCaffe2Tensor(
418
pretrained_blobs[1].flatten(), output + '_b'))
419
return caffe_op, params
422
@TranslatorRegistry.Register("Convolution")
423
def TranslateConv(layer, pretrained_blobs, is_test, **kwargs):
424
param = layer.convolution_param
425
caffe_op = BaseTranslate(layer, "Conv")
426
output = caffe_op.output[0]
427
caffe_op.input.append(output + '_w')
428
_TranslateStridePadKernelHelper(param, caffe_op)
431
utils.NumpyArrayToCaffe2Tensor(pretrained_blobs[0], output + '_w')]
433
if len(pretrained_blobs) == 2:
434
caffe_op.input.append(output + '_b')
436
utils.NumpyArrayToCaffe2Tensor(
437
pretrained_blobs[1].flatten(), output + '_b'))
440
AddArgument(caffe_op, "group", param.group)
443
if len(param.dilation) > 0:
444
if len(param.dilation) == 1:
445
AddArgument(caffe_op, "dilation", param.dilation[0])
446
elif len(param.dilation) == 2:
447
AddArgument(caffe_op, "dilation_h", param.dilation[0])
448
AddArgument(caffe_op, "dilation_w", param.dilation[1])
449
return caffe_op, params
452
@TranslatorRegistry.Register("Deconvolution")
453
def TranslateDeconv(layer, pretrained_blobs, is_test, **kwargs):
454
param = layer.convolution_param
456
raise NotImplementedError(
457
"Translator currently does not support group deconvolution."
459
caffe_op = BaseTranslate(layer, "ConvTranspose")
460
output = caffe_op.output[0]
461
_TranslateStridePadKernelHelper(param, caffe_op)
462
caffe_op.input.extend([output + '_w'])
463
AddArgument(caffe_op, "order", "NCHW")
464
weight = utils.NumpyArrayToCaffe2Tensor(pretrained_blobs[0], output + '_w')
466
bias = utils.NumpyArrayToCaffe2Tensor(
467
pretrained_blobs[1].flatten(), output + '_b'
469
caffe_op.input.extend([output + '_b'])
470
return caffe_op, [weight, bias]
472
return caffe_op, [weight]
475
@TranslatorRegistry.Register("Crop")
476
def TranslateCrop(layer, pretrained_blobs, is_test, **kwargs):
477
net, net_params, input_dims = kwargs['net'], kwargs['net_params'], kwargs['input_dims']
478
n, c, h, w = input_dims
479
dummy_input = np.random.randn(n, c, h, w).astype(np.float32)
480
dim_map = _GetBlobDimMap(net, net_params, dummy_input)
481
param = layer.crop_param
482
axis, offsets = param.axis, param.offset
483
caffe_op = BaseTranslate(layer, "Slice")
484
input_1 = caffe_op.input[1]
485
input_1_dim = dim_map[input_1]
486
starts, ends = [], []
487
dims = len(dim_map[input_1])
488
assert len(offsets) == 1, 'Caffe Translator for Crop only works for offset \
490
for _ in range(axis):
493
end_offset = [int(offsets[0] + input_1_dim[i]) for i in range(axis, dims)]
494
ends.extend(end_offset)
495
starts.extend([offsets[0]] * len(end_offset))
496
op = caffe2_pb2.OperatorDef()
497
op.input.extend([caffe_op.input[0]])
498
op.output.extend(caffe_op.output)
499
op.arg.extend(caffe_op.arg)
500
op.type = caffe_op.type
501
AddArgument(op, "starts", starts)
502
AddArgument(op, "ends", ends)
505
@TranslatorRegistry.Register("ReLU")
506
def TranslateRelu(layer, pretrained_blobs, is_test, **kwargs):
507
return BaseTranslate(layer, "Relu"), []
510
@TranslatorRegistry.Register("Pooling")
511
def TranslatePool(layer, pretrained_blobs, is_test, **kwargs):
512
param = layer.pooling_param
513
if param.pool == caffe_pb2.PoolingParameter.MAX:
514
caffe_op = BaseTranslate(layer, "MaxPool")
515
elif param.pool == caffe_pb2.PoolingParameter.AVE:
516
caffe_op = BaseTranslate(layer, "AveragePool")
517
_TranslateStridePadKernelHelper(param, caffe_op)
518
AddArgument(caffe_op, "order", "NCHW")
528
is_torch_pooling = param.torch_pooling
529
except AttributeError:
530
is_torch_pooling = False
531
if not is_torch_pooling:
532
AddArgument(caffe_op, "legacy_pad",
533
caffe2_legacy_pb2.CAFFE_LEGACY_POOLING)
534
if param.global_pooling:
535
AddArgument(caffe_op, "global_pooling", 1)
539
@TranslatorRegistry.Register("Pooling3D")
540
def TranslatePool3D(layer, pretrained_blobs, is_test, **kwargs):
541
param = layer.pooling3d_param
542
if param.pool == caffe_pb2.Pooling3DParameter.MAX:
543
caffe_op = BaseTranslate(layer, "MaxPool")
545
elif param.pool == caffe_pb2.Pooling3DParameter.AVE:
546
caffe_op = BaseTranslate(layer, "AveragePool")
547
AddArgument(caffe_op, "order", "NCHW")
551
[param.kernel_depth, param.kernel_size, param.kernel_size])
556
[param.temporal_stride, param.stride, param.stride])
559
if hasattr(param, 'temporal_pad'):
560
temporal_pad = param.temporal_pad
561
if hasattr(param, 'pad'):
562
spatial_pad = param.pad
563
AddArgument(caffe_op, "pads", [temporal_pad, spatial_pad, spatial_pad] * 2)
567
@TranslatorRegistry.Register("LRN")
568
def TranslateLRN(layer, pretrained_blobs, is_test, **kwargs):
569
caffe_op = BaseTranslate(layer, "LRN")
570
caffe_op.output.extend(['_' + caffe_op.output[0] + '_scale'])
571
param = layer.lrn_param
572
if param.norm_region != caffe_pb2.LRNParameter.ACROSS_CHANNELS:
574
"Does not support norm region other than across channels.")
575
AddArgument(caffe_op, "size", int(param.local_size))
576
AddArgument(caffe_op, "alpha", float(param.alpha))
577
AddArgument(caffe_op, "beta", float(param.beta))
578
AddArgument(caffe_op, "bias", float(param.k))
579
AddArgument(caffe_op, "order", "NCHW")
583
@TranslatorRegistry.Register("InnerProduct")
584
def TranslateInnerProduct(layer, pretrained_blobs, is_test, **kwargs):
585
param = layer.inner_product_param
587
if param.axis != 1 or param.transpose:
589
"We don't have testing case for non-default axis and transpose "
590
"cases yet so we are disabling it for now. If you have a model "
591
"with this, please do send us your model for us to update this "
592
"support, and you are more than welcome to send a PR for this.")
593
except AttributeError:
597
caffe_op = BaseTranslate(layer, "FC")
598
output = caffe_op.output[0]
599
caffe_op.input.extend([output + '_w', output + '_b'])
602
if pretrained_blobs[0].ndim not in [2, 4]:
603
raise ValueError("Unexpected weight ndim.")
604
if (pretrained_blobs[0].ndim == 4 and
605
list(pretrained_blobs[0].shape[:2]) != [1, 1]):
607
"If pretrained blob has 4 dims (old-style Caffe), the first two "
608
"should be of value 1, but I got " + str(pretrained_blobs[0].shape))
609
weight = utils.NumpyArrayToCaffe2Tensor(
610
pretrained_blobs[0].reshape(-1, pretrained_blobs[0].shape[-1]),
613
bias = utils.NumpyArrayToCaffe2Tensor(
614
pretrained_blobs[1].flatten(), output + '_b'
616
return caffe_op, [weight, bias]
619
@TranslatorRegistry.Register("Dropout")
620
def TranslateDropout(layer, pretrained_blobs, is_test, **kwargs):
621
caffe_op = BaseTranslate(layer, "Dropout")
622
caffe_op.output.extend(['_' + caffe_op.output[0] + '_mask'])
623
param = layer.dropout_param
624
AddArgument(caffe_op, "ratio", param.dropout_ratio)
626
AddArgument(caffe_op, "is_test", 1)
630
@TranslatorRegistry.Register("Softmax")
631
def TranslateSoftmax(layer, pretrained_blobs, is_test, **kwargs):
632
caffe_op = BaseTranslate(layer, "Softmax")
636
@TranslatorRegistry.Register("SoftmaxWithLoss")
637
def TranslateSoftmaxWithLoss(layer, pretrained_blobs, is_test, **kwargs):
638
softmax_op = core.CreateOperator(
639
"Softmax", [layer.bottom[0]],
640
layer.bottom[0] + "_translator_autogen_softmax")
641
xent_op = core.CreateOperator(
643
[softmax_op.output[0], layer.bottom[1]],
644
layer.bottom[0] + "_translator_autogen_xent")
645
loss_op = core.CreateOperator(
649
return [softmax_op, xent_op, loss_op], []
652
@TranslatorRegistry.Register("Accuracy")
653
def TranslateAccuracy(layer, pretrained_blobs, is_test, **kwargs):
654
caffe_op = BaseTranslate(layer, "Accuracy")
655
if layer.accuracy_param.top_k != 1:
656
AddArgument(caffe_op, "top_k", layer.accuracy_param.top_k)
660
@TranslatorRegistry.Register("Concat")
661
def TranslateConcat(layer, pretrained_blobs, is_test, **kwargs):
662
caffe_op = BaseTranslate(layer, "Concat")
663
caffe_op.output.extend(['_' + caffe_op.output[0] + '_dims'])
664
AddArgument(caffe_op, "order", "NCHW")
668
@TranslatorRegistry.Register("TanH")
669
def TranslateTanH(layer, pretrained_blobs, is_test, **kwargs):
670
caffe_op = BaseTranslate(layer, "Tanh")
674
@TranslatorRegistry.Register("InstanceNorm")
675
def TranslateInstanceNorm(layer, pretrained_blobs, is_test, **kwargs):
676
caffe_op = BaseTranslate(layer, "InstanceNorm")
677
output = caffe_op.output[0]
678
weight = utils.NumpyArrayToCaffe2Tensor(
679
pretrained_blobs[0].flatten(), output + '_w')
680
bias = utils.NumpyArrayToCaffe2Tensor(
681
pretrained_blobs[1].flatten(), output + '_b')
682
caffe_op.input.extend([output + '_w', output + '_b'])
683
AddArgument(caffe_op, "order", "NCHW")
684
return caffe_op, [weight, bias]
687
@TranslatorRegistry.Register("BatchNorm")
688
def TranslateBatchNorm(layer, pretrained_blobs, is_test, **kwargs):
689
caffe_op = BaseTranslate(layer, "SpatialBN")
690
output = caffe_op.output[0]
691
param = layer.batch_norm_param
692
AddArgument(caffe_op, "is_test", is_test)
693
AddArgument(caffe_op, "epsilon", param.eps)
694
AddArgument(caffe_op, "order", "NCHW")
696
caffe_op.input.extend(
702
caffe_op.output.extend(
705
output + "_saved_mean",
706
output + "_saved_var"])
708
n_channels = pretrained_blobs[0].shape[0]
709
if pretrained_blobs[2][0] != 0:
710
mean = utils.NumpyArrayToCaffe2Tensor(
711
(1. / pretrained_blobs[2][0]) * pretrained_blobs[0],
713
var = utils.NumpyArrayToCaffe2Tensor(
714
(1. / pretrained_blobs[2][0]) * pretrained_blobs[1],
717
raise RuntimeError("scalar is zero.")
718
if len(pretrained_blobs) > 3:
722
scale = utils.NumpyArrayToCaffe2Tensor(
723
pretrained_blobs[3].flatten(),
725
bias = utils.NumpyArrayToCaffe2Tensor(
726
pretrained_blobs[4].flatten(),
729
pretrained_blobs[2][0] = 1
730
pretrained_blobs[2] = np.tile(pretrained_blobs[2], (n_channels, ))
731
scale = utils.NumpyArrayToCaffe2Tensor(
734
bias = utils.NumpyArrayToCaffe2Tensor(
735
np.zeros_like(pretrained_blobs[2]),
738
return caffe_op, [scale, bias, mean, var]
741
@TranslatorRegistry.Register("Eltwise")
742
def TranslateElementWise(layer, pretrained_blobs, is_test, **kwargs):
743
param = layer.eltwise_param
746
if len(param.coeff) or param.operation != 1:
747
raise RuntimeError("This eltwise layer is not yet supported.")
748
caffe_op = BaseTranslate(layer, "Sum")
752
@TranslatorRegistry.Register("Scale")
753
def TranslateScale(layer, pretrained_blobs, is_test, **kwargs):
754
mul_op = BaseTranslate(layer, "Mul")
755
scale_param = layer.scale_param
756
AddArgument(mul_op, "axis", scale_param.axis)
757
AddArgument(mul_op, "broadcast", True)
758
if len(mul_op.input) == 1:
760
if scale_param.num_axes != 1:
761
raise RuntimeError("This path has not been verified yet.")
763
output = mul_op.output[0]
764
mul_op_param = output + 'scale_w'
765
mul_op.input.append(mul_op_param)
767
weights.append(utils.NumpyArrayToCaffe2Tensor(
768
pretrained_blobs[0].flatten(), mul_op_param))
771
if len(pretrained_blobs) == 1:
774
elif len(pretrained_blobs) == 2:
778
add_op = copy.deepcopy(mul_op)
780
add_op_param = output + 'scale_b'
781
internal_blob = output + "_internal"
783
mul_op.output.append(internal_blob)
785
add_op.input.append(internal_blob)
786
add_op.input.append(add_op_param)
787
weights.append(utils.NumpyArrayToCaffe2Tensor(
788
pretrained_blobs[1].flatten(), add_op_param))
790
raise RuntimeError("Unexpected number of pretrained blobs in Scale")
794
caffe_ops.append(add_op)
795
assert len(caffe_ops) == len(weights)
796
return caffe_ops, weights
797
elif len(mul_op.input) == 2:
799
raise RuntimeError("This path has not been verified yet.")
801
raise RuntimeError("Unexpected number of inputs.")
804
@TranslatorRegistry.Register("Reshape")
805
def TranslateReshape(layer, pretrained_blobs, is_test, **kwargs):
806
caffe_op = BaseTranslate(layer, "Reshape")
807
caffe_op.output.append("_" + caffe_op.input[0] + "_dims")
808
reshape_param = layer.reshape_param
809
AddArgument(caffe_op, 'shape', reshape_param.shape.dim)
813
@TranslatorRegistry.Register("Flatten")
814
def TranslateFlatten(layer, pretrained_blobs, is_test, **kwargs):
815
param = layer.flatten_param
816
if param.end_axis != -1:
817
raise NotImplementedError("flatten_param.end_axis not supported yet.")
820
caffe_op = BaseTranslate(layer, "FlattenToVec")
821
elif param.axis == 1:
822
caffe_op = BaseTranslate(layer, "Flatten")
825
raise NotImplementedError(
826
"Not supported yet for flatten_param.axis {}.".format(param.axis))
831
@TranslatorRegistry.Register("Sigmoid")
832
def TranslateSigmoid(layer, pretrained_blobs, is_test, **kwargs):
833
caffe_op = BaseTranslate(layer, "Sigmoid")
837
@TranslatorRegistry.Register("ROIPooling")
838
def TranslateROIPooling(layer, pretrained_blobs, is_test, **kwargs):
839
caffe_op = BaseTranslate(layer, "RoIPool")
840
AddArgument(caffe_op, "order", "NCHW")
843
AddArgument(caffe_op, "is_test", is_test)
846
caffe_op.output.append(caffe_op.output[0] + '_argmaxes')
848
param = layer.roi_pooling_param
849
if param.HasField('pooled_h'):
850
AddArgument(caffe_op, 'pooled_h', param.pooled_h)
851
if param.HasField('pooled_w'):
852
AddArgument(caffe_op, 'pooled_w', param.pooled_w)
853
if param.HasField('spatial_scale'):
854
AddArgument(caffe_op, 'spatial_scale', param.spatial_scale)
859
@TranslatorRegistry.Register("PReLU")
860
def TranslatePRelu(layer, pretrained_blobs, is_test, **kwargs):
861
caffe_op = BaseTranslate(layer, "PRelu")
862
output = caffe_op.output[0]
863
caffe_op.input.extend([output + '_Slope'])
864
slope = utils.NumpyArrayToCaffe2Tensor(pretrained_blobs[0], output + '_Slope')
866
return caffe_op, [slope]
869
@TranslatorRegistry.Register("Reduction")
870
def TranslateReduction(layer, pretrained_blobs, is_test, **kwargs):
871
param = layer.reduction_param
872
if param.operation == caffe_pb2.ReductionParameter.SUM:
873
caffe_op = BaseTranslate(layer, "ReduceBackSum")
874
elif param.operation == caffe_pb2.ReductionParameter.MEAN:
875
caffe_op = BaseTranslate(layer, "ReduceBackMean")
877
raise NotImplementedError("Not yet supported")
882
raise NotImplementedError("Not yet supported")
883
num_reduce_dim = -param.axis
884
AddArgument(caffe_op, "num_reduce_dim", num_reduce_dim)
889
if __name__ == '__main__':
890
parser = argparse.ArgumentParser(
891
description="Utilitity to convert pretrained caffe models to Caffe2 models.")
892
parser.add_argument("prototext", help="Caffe prototext.")
893
parser.add_argument("caffemodel", help="Caffe trained model.")
894
parser.add_argument("--init_net", help="Caffe2 initialization net.",
895
default="init_net.pb")
896
parser.add_argument("--predict_net", help="Caffe2 prediction net.",
897
default="predict_net.pb")
898
parser.add_argument("--remove_legacy_pad", help="Remove legacy pad \
899
(Only works for nets with one input blob)",
902
parser.add_argument("--input_dims", help="Dimension of input blob", nargs='+',
903
type=int, default=[])
904
args = parser.parse_args()
906
caffenet = caffe_pb2.NetParameter()
907
caffenet_pretrained = caffe_pb2.NetParameter()
908
input_proto = args.prototext
909
input_caffemodel = args.caffemodel
910
output_init_net = args.init_net
911
output_predict_net = args.predict_net
913
with open(input_proto) as f:
914
text_format.Merge(f.read(), caffenet)
915
with open(input_caffemodel, 'rb') as f:
916
caffenet_pretrained.ParseFromString(f.read())
917
net, pretrained_params = TranslateModel(
918
caffenet, caffenet_pretrained, is_test=True,
919
remove_legacy_pad=args.remove_legacy_pad,
920
input_dims=args.input_dims
924
external_input = net.op[0].input[0]
925
external_output = net.op[-1].output[0]
927
net.external_input.extend([external_input])
928
net.external_input.extend([param.name for param in pretrained_params.protos])
929
net.external_output.extend([external_output])
930
init_net = ConvertTensorProtosToInitNet(pretrained_params, external_input)
932
with open(output_predict_net, 'wb') as f:
933
f.write(net.SerializeToString())
934
with open(output_predict_net + 'txt', 'w') as f:
936
with open(output_init_net, 'wb') as f:
937
f.write(init_net.SerializeToString())