3 Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
9 http://www.apache.org/licenses/LICENSE-2.0
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
22 from itertools import chain
23 from caffe import layers as L
26 from collections import Counter, OrderedDict
28 if (len(sys.argv) < 2):
30 print('Using current directory as destination folder')
32 dest_folder = sys.argv[1] + '/'
40 def __init__(self, type, param):
47 # bynaryProto file for Infogain
48 H = np.eye(3, dtype='f4')
49 blob = caffe.io.array_to_blobproto(H.reshape((1, 1, 3, 3)))
50 with open(dest_folder + 'infogainH.binaryproto', 'wb+') as f:
51 f.write(blob.SerializeToString())
54 with open(dest_folder + "in", 'w+') as f:
58 with open(dest_folder + "in_winds", 'w+') as f:
77 # HDF5 file for HDF5DataSet
78 h5f = h5py.File(dest_folder + "in.hdf5", "w")
79 h5f.create_dataset("data", data=np.random.rand(1, 3, LS, LS))
83 env = lmdb.open(dest_folder + 'test-lmdb')
84 with env.begin(write=True) as txn:
85 img_data = np.random.rand(3, LS, LS)
86 datum = caffe.io.array_to_datum(img_data, label=1)
87 txn.put('{:0>10d}'.format(1).encode('ascii'), datum.SerializeToString())
90 # recurring parameters
91 losspara = {'ignore_label': True, 'normalization': 1, 'normalize': True}
92 softmaxpara = {'engine': 0, 'axis': 1}
93 gdfil = {'type': 'gaussian', 'std': 0.001}
94 cofil = {'type': 'constant', 'value': 0}
97 'weight_filler': gdfil,
109 'sparse': -1, # -1 means no sparsification
111 } # 0 = FAN_IN, 1 = FAN_OUT, 2 = AVERAGE
123 'source': 'test-lmdb', # FIXME: unknown DB backend
126 'backend': 1, # 0 = LEVELDB, 1 = LMDB
127 'scale': 1.0, # deprecated in favor of TransformationParameter
128 'mean_file': 'wtf.is_that',
131 'force_encoded_color': False,
138 'data_filler': cofil, # ok
139 #'num' : [1,1,1], # deprecated shape specification
140 #'channels' : [2,2,2],
144 'dim': [1, 3, LS, LS]
151 'source': 'in_imgs', # file with list of imgs
160 'scale': 1.0, # deprecated in favor of TransformationParameter
167 'source': 'in_winds',
170 'mean_file': 'in.jpg',
175 #'fg_treshold' : 0.5,
176 #'bg_treshold' : 0.5,
177 #'fg_fraction' : 0.25,
181 'cache_images': True,
188 'source': 'in', # This is the name of the file WITH HDF5 FILENAMES 0_0
189 # Top should have the same name as the dataset in the hdf5 file
190 # FIXME Requires Caffegen to be built with Caffe that supports LMDB
204 'batch_size': 1, # ok
216 'num_output': 64, # ok
220 'weight_filler': gdfil,
233 'num_output': 12, # ok
239 'weight_filler': gdfil,
250 'convolution_param': # ok
256 'weight_filler': gdfil,
264 'convolution_param': # ok
272 'weight_filler': gdfil,
280 'moving_average_fraction': 0.999
292 # local_size[default 5]: the number of channels to sum over
293 # alpha[default 1]: the scaling paramete
294 # beta[default5]: the exponent
295 # norm_region[default ACROSS_CHANNLS]: whether to sum over adjacent channels(ACROSS_CHANNLS) or nearby
296 # spatial locations(WITHIN_CHANNLS)
297 # `input / (1 + (\alpha/n) \sum_i x_i^2)^\beta`
301 'normalize_variance': True, # ok
302 'across_channels': False,
308 'convolution_param': # ok
314 'weight_filler': gdfil,
315 # 'param' : [{'lr_mult':1},{'lr_mult':0.1}],
346 # reshapes only [axis, axis + num_axes] if those aren't 0 and -1; axis can be negative
347 # 0 in shape means retaining dim size, -1 means auto size
357 'pool': 0, # ok # pool: 0 = MAX, 1 = AVE, 2 = STOCHASTIC
358 'pad': 0, # can be replaced with pad_w, pad_h
359 'kernel_size': 3, # can be replaced with kernel_w, kernel_h
360 'stride': 1, # can be replaced with stride_w, stride_h
362 'global_pooling': False
364 # 'round_mode' : 0}), # 0 = CELS, 1 = FLOOR
368 'operation': 1, # ok # 1 = SUM, 2 = ASUM, 3 = SUMSQ, 4 = MEAN # ok
375 'pyramid_height': 1, # ok
382 'num_output': 2, # ok
384 'weight_filler': filler_par,
385 'bias_filler': filler_par,
392 'num_output': 2, # ok
395 'weight_filler': filler_par,
396 'bias_filler': filler_par
401 'out_max_val': False, # ok # if True, outputs pairs (argmax, maxval) # ok
414 'negative_slope': 0, # ok
420 'filler': filler_par, # ok
421 'channel_shared': False
448 'filler': filler_par,
450 'bias_filler': filler_par
458 'shift': PH(float, (2.0, 10.0)),
460 }), # y = ln(shift + scale * x) (log_base() for base > 0)
467 }), # y = (shift + scale * x) ^ power
483 }), # if one offset - for all dims, more - specifies
489 'stable_prod_grad': True,
498 }), # L1 = 1; L2 = 2; # ok
499 ("SigmoidCrossEntropyLoss", {
500 'loss_param': losspara,
504 ## TWO Inputs, special shape
508 'top_k': 1, # FIXME: different bottom shapes needed
512 "special_shape": [1, 3, 1, 1]
517 'loss_param': losspara, # FIXME: different bottom shapes needed
518 'softmax_param': softmaxpara,
520 "special_shape": [1, 1, 1, 1]
522 ("MultinomialLogisticLoss", {
523 'loss_param': losspara,
525 "special_shape": [1, 1, 1, 1]
526 }), # FIXME: different bottom shapes needed
529 "special_shape": [1, 1, 1, 1]
530 }), # FIXME: different bottom shapes needed
534 }), # takes indices as second blob
536 'source': 'infogainH.binaryproto',
539 "special_shape": [1, 1, 1, 1]
544 'python_param': # Custom Loss layer
546 'module': 'Pyloss', # the module name -- usually the filename -- that needs to be in $PYTHONPATH
547 'layer': 'EuclideanLossLayer', # the layer name -- the class name in the module
548 'share_in_parallel': False
550 # set loss weight so Caffe knows this is a loss layer.
551 # since PythonLayer inherits directly from Layer, this isn't automatically
555 "special_shape": [1, 3, 1, 1]
561 'file_name': 'out.hdf5',
568 }), # ok, need to remove tops
572 'recurrent_param': rp,
577 'recurrent_param': rp,
584 'recurrent_param': rp,
585 'top': ["out2", "out3"],
589 ## Handled explicitly (special case)
590 ("ContrastiveLoss", {
592 'legacy_version': False
599 def traverse(obj, callback=None):
601 walks a nested dict/list recursively
606 if isinstance(obj, dict):
607 value = {k: traverse(v, callback) for k, v in obj.items()}
608 elif isinstance(obj, list):
609 value = [traverse(elem, callback) for elem in obj]
616 return callback(value)
620 if not (isinstance(inp, PH)): return inp
622 return random.randint(*inp.param)
623 if inp.type == float:
624 return random.uniform(*inp.param)
628 [(), # alredy defined
636 Represents a caffe layer
639 def __init__(self, name, params):
642 if self.args == None: self.args = dict()
643 self.num_inp = self.args.pop("inputs", 1)
644 self.num_out = self.args.pop("outputs", 1)
645 self.special_shape = self.args.pop("special_shape",
646 False) # 2nd input has special shape
647 self.is_data = self.args.pop("is_data", False)
648 self.is_notop = self.args.pop("is_notop", False)
652 Creates a protobuf network
655 net = caffe.NetSpec()
658 net.data = getattr(L, self.name)(**self.args)
661 elif self.name == "ContrastiveLoss":
662 net.data = L.Input(shape={'dim': [1, 4]})
663 net.data1 = L.DummyData(data_filler=cofil, shape={'dim': [1, 4]})
664 net.data2 = L.DummyData(data_filler=cofil, shape={'dim': [1, 1]})
666 net.op = getattr(L, self.name)(net.data, net.data1, net.data2, **self.args)
668 # this covers most cases
670 net.data = L.Input(shape={'dim': [1, 3, LS, LS]})
671 if self.num_inp == 2:
672 net.data1 = L.DummyData(data_filler=cofil, shape={'dim': [1, 3, LS, LS]})
673 elif self.num_inp > 2:
674 for i in range(1, self.num_inp):
676 net, "data" + str(i),
677 L.DummyData(data_filler=cofil, shape={'dim': EXTRA_SHAPES[i]}))
678 if self.special_shape:
679 net.data = L.Input(shape={'dim': [1, 3, 1, 1]})
680 net.data1 = L.DummyData(
681 data_filler=cofil, shape={'dim': self.special_shape})
683 net.op = getattr(L, self.name)(
685 *[getattr(net, "data" + str(i))
686 for i in range(1, self.num_inp)], **self.args)
689 net.op.fn.tops = OrderedDict()
690 net.op.fn.ntop = 0 # the messing about in question
697 Factory class for Layer
700 def __init__(self, params):
701 self.name, self.args = params
702 self.how_many = self.args.pop("how_many", 1)
705 return [Layer(self.name, traverse(self.args, mock)) for i in range(self.how_many)]
708 layer_gen = chain(*map(lambda para: LayerMaker(para).make(), OPS))
710 filename = dest_folder + '{}_{}.prototxt'
713 for layer in layer_gen:
715 counter[layer.name] += 1
717 with open(filename.format(layer.name, counter[layer.name] - 1), 'w+') as ptxt_file:
718 print(n.to_proto(), file=ptxt_file)
720 if layer.name == "Python": # Special case for python layer
721 with open("Python_0.caffemodel", 'wb+') as caffemodelFile:
722 caffemodelFile.write(n.to_proto().SerializeToString())