From 129d222fbd885c1a9e727e6134d2387676fc3dac Mon Sep 17 00:00:00 2001 From: Jonathan L Long Date: Thu, 23 Jul 2015 20:23:42 -0700 Subject: [PATCH 1/6] [pycaffe] remove dead code --- python/caffe/net_spec.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/python/caffe/net_spec.py b/python/caffe/net_spec.py index 1b4814a45c6..95c03bcb9c5 100644 --- a/python/caffe/net_spec.py +++ b/python/caffe/net_spec.py @@ -44,8 +44,6 @@ def to_proto(*tops): """Generate a NetParameter that contains all layers needed to compute all arguments.""" - if not isinstance(tops, tuple): - tops = (tops,) layers = OrderedDict() autonames = {} for top in tops: From 7488163bf4b3d44be5fbc931ba58ccde07098818 Mon Sep 17 00:00:00 2001 From: Jonathan L Long Date: Thu, 23 Jul 2015 20:26:46 -0700 Subject: [PATCH 2/6] [pycaffe] use a Counter instead of a dict for counting net spec names --- python/caffe/net_spec.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/python/caffe/net_spec.py b/python/caffe/net_spec.py index 95c03bcb9c5..37e333d7cc5 100644 --- a/python/caffe/net_spec.py +++ b/python/caffe/net_spec.py @@ -18,7 +18,7 @@ class -- assign to its attributes directly to name layers, and call are not guaranteed to be forward-compatible. """ -from collections import OrderedDict +from collections import OrderedDict, Counter from .proto import caffe_pb2 from google import protobuf @@ -45,7 +45,7 @@ def to_proto(*tops): all arguments.""" layers = OrderedDict() - autonames = {} + autonames = Counter() for top in tops: top.fn._to_proto(layers, {}, autonames) net = caffe_pb2.NetParameter() @@ -107,9 +107,8 @@ def __init__(self, type_name, inputs, params): def _get_name(self, top, names, autonames): if top not in names: - n = autonames.setdefault(top.fn.type_name, 1) autonames[top.fn.type_name] += 1 - names[top] = top.fn.type_name + str(n) + names[top] = top.fn.type_name + str(autonames[top.fn.type_name]) return names[top] def _to_proto(self, layers, names, autonames): @@ -161,7 +160,7 @@ def __getattr__(self, name): def to_proto(self): names = {v: k for k, v in six.iteritems(self.tops)} - autonames = {} + autonames = Counter() layers = OrderedDict() for name, top in six.iteritems(self.tops): top.fn._to_proto(layers, names, autonames) From f16195aa8b1569e0260c48d4159b7d2ce0ea2fab Mon Sep 17 00:00:00 2001 From: Jonathan L Long Date: Thu, 23 Jul 2015 20:32:04 -0700 Subject: [PATCH 3/6] [pycaffe] add Top._to_proto convenience function This makes it possible to serialize Functions or Tops with a uniform interface. --- python/caffe/net_spec.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/python/caffe/net_spec.py b/python/caffe/net_spec.py index 37e333d7cc5..5fb26ed45ba 100644 --- a/python/caffe/net_spec.py +++ b/python/caffe/net_spec.py @@ -87,6 +87,9 @@ def to_proto(self): return to_proto(self) + def _to_proto(self, layers, names, autonames): + return self.fn._to_proto(layers, names, autonames) + class Function(object): """A Function specifies a layer, its parameters, and its inputs (which @@ -116,7 +119,7 @@ def _to_proto(self, layers, names, autonames): return bottom_names = [] for inp in self.inputs: - inp.fn._to_proto(layers, names, autonames) + inp._to_proto(layers, names, autonames) bottom_names.append(layers[inp.fn].top[inp.n]) layer = caffe_pb2.LayerParameter() layer.type = self.type_name @@ -163,7 +166,7 @@ def to_proto(self): autonames = Counter() layers = OrderedDict() for name, top in six.iteritems(self.tops): - top.fn._to_proto(layers, names, autonames) + top._to_proto(layers, names, autonames) net = caffe_pb2.NetParameter() net.layer.extend(layers.values()) return net From 96c2fe1de80c9752b992c4578a3ce46028d21fc5 Mon Sep 17 00:00:00 2001 From: Jonathan L Long Date: Thu, 23 Jul 2015 20:35:42 -0700 Subject: [PATCH 4/6] [pycaffe] allow layers to have names different from their first tops Previously, net spec only allowed names to be assigned to Tops, giving layers the names of their first tops. Now, names can be assigned to Functions, which become layer names in serialization. Unnamed Functions still get named after their first top, if present, or autogenerated, if not. (This will allow top-less layers in a natural way.) --- python/caffe/net_spec.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/python/caffe/net_spec.py b/python/caffe/net_spec.py index 5fb26ed45ba..16f30008579 100644 --- a/python/caffe/net_spec.py +++ b/python/caffe/net_spec.py @@ -108,7 +108,15 @@ def __init__(self, type_name, inputs, params): del self.params['in_place'] self.tops = tuple(Top(self, n) for n in range(self.ntop)) - def _get_name(self, top, names, autonames): + def _get_name(self, names, autonames): + if self not in names and self.ntop > 0: + names[self] = self._get_top_name(self.tops[0], names, autonames) + elif self not in names: + autonames[self.type_name] += 1 + names[self] = self.type_name + str(autonames[self.type_name]) + return names[self] + + def _get_top_name(self, top, names, autonames): if top not in names: autonames[top.fn.type_name] += 1 names[top] = top.fn.type_name + str(autonames[top.fn.type_name]) @@ -129,8 +137,8 @@ def _to_proto(self, layers, names, autonames): layer.top.extend(layer.bottom) else: for top in self.tops: - layer.top.append(self._get_name(top, names, autonames)) - layer.name = self._get_name(self.tops[0], names, autonames) + layer.top.append(self._get_top_name(top, names, autonames)) + layer.name = self._get_name(names, autonames) for k, v in six.iteritems(self.params): # special case to handle generic *params From b464a6e5d2e381185bd8aa0b9cbcba53c9819585 Mon Sep 17 00:00:00 2001 From: Jonathan L Long Date: Thu, 23 Jul 2015 20:40:02 -0700 Subject: [PATCH 5/6] [pycaffe] net spec layers can have ntop=0 In this case, the Function is returned instead of a Top, which can be assigned a name if desired. Thanks @philkr for an earlier implementation of this. --- python/caffe/net_spec.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/caffe/net_spec.py b/python/caffe/net_spec.py index 16f30008579..31cde7ad946 100644 --- a/python/caffe/net_spec.py +++ b/python/caffe/net_spec.py @@ -188,7 +188,9 @@ class Layers(object): def __getattr__(self, name): def layer_fn(*args, **kwargs): fn = Function(name, args, kwargs) - if fn.ntop == 1: + if fn.ntop == 0: + return fn + elif fn.ntop == 1: return fn.tops[0] else: return fn.tops From c80f6f585549334022996395015cd490cc2644c8 Mon Sep 17 00:00:00 2001 From: Jonathan L Long Date: Thu, 23 Jul 2015 20:41:40 -0700 Subject: [PATCH 6/6] [pytest] simple test of top-less layers --- python/caffe/test/test_net_spec.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/python/caffe/test/test_net_spec.py b/python/caffe/test/test_net_spec.py index 65b73b96f73..b344946932a 100644 --- a/python/caffe/test/test_net_spec.py +++ b/python/caffe/test/test_net_spec.py @@ -41,6 +41,14 @@ def anon_lenet(batch_size): loss = L.SoftmaxWithLoss(ip2, label) return loss.to_proto() +def silent_net(): + n = caffe.NetSpec() + n.data, n.data2 = L.DummyData(shape=[dict(dim=[3]), dict(dim=[4, 2])], + ntop=2) + n.silence_data = L.Silence(n.data, ntop=0) + n.silence_data2 = L.Silence(n.data2, ntop=0) + return n.to_proto() + class TestNetSpec(unittest.TestCase): def load_net(self, net_proto): f = tempfile.NamedTemporaryFile(delete=False) @@ -65,3 +73,10 @@ def test_lenet(self): net_proto.layer[6].top) net = self.load_net(net_proto) self.assertEqual(len(net.layers), 9) + + def test_zero_tops(self): + """Test net construction for top-less layers.""" + + net_proto = silent_net() + net = self.load_net(net_proto) + self.assertEqual(len(net.forward()), 0)