6

I want to set up a caffe CNN with python, using caffe.NetSpec() interface. Although I saw we can put test net in solver.prototxt, I would like to write it in model.prototxt with different phase. For example, caffe model prototxt implement two data layer with different phases:

layer {
  name: "data"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
....
}
layer {
  name: "data"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TEST
  }
....
}

How should I do in python to get such implementation?

Shai
  • 111,146
  • 38
  • 238
  • 371
user3162707
  • 721
  • 1
  • 7
  • 7
  • there is an open [bvlc/caffe github issue](https://github.com/BVLC/caffe/issues/4044). – Shai May 31 '16 at 09:51

5 Answers5

5

I assume you mean how to define phase when writing a prototxt using caffe.NetSpec?

from caffe import layers as L, params as P, to_proto
import caffe

ns = caffe.NetSpec()
ns.data = L.Data(name="data", 
                 data_param={'source':'/path/to/lmdb','batch_size':32},
                 include={'phase':caffe.TEST})

If you want to have BOTH train and test layers in the same prototxt, what I usually do is making one ns for train with ALL layers and another ns_test with only the test version of the duplicate layers only. Then, when writing the actual prototxt file:

with open('model.prototxt', 'w') as W:
  W.write('%s\n' % ns_test.to_proto())
  W.write('%s\n' % ns.to_proto())

This way you'll have BOTH phases in the same prototxt. A bit hacky, I know.

Shai
  • 111,146
  • 38
  • 238
  • 371
  • yes, I use caffe.NetSpec(), but the question is that I want to define two layers with same name but different phases, something like ns.data = L.Data(name="data", data_param={'source':'/path/to/lmdb','batch_size':32}, include={'phase':caffe.TEST}) ns.data = L.Data(name="data", data_param={'source':'/path/to/lmdb','batch_size':32}, include={'phase':caffe.TRAIN}) – user3162707 Apr 25 '16 at 17:03
  • But this only makes ns.data for TEST phase – user3162707 Apr 25 '16 at 17:05
  • thanks, it solves my problem. However, doing in this way, there is no data in solver.test_net. Does it mean that if I want to test the prediction after certain training process, I have to manually set the phase to test? What I want to do is to use solver.solve() and let the solver train and test network automatically. – user3162707 Apr 26 '16 at 06:48
  • 1
    @user3162707 I didn't completely understand your comment. If you set your test data source accordingly, and set the test interval in your solver prototxt, the solver should automatically use the testing data to test the network at the specified interval. In any case, if this solves your problem (as it did mine, albeit a bit hacky), it should be marked as the correct answer. – Ray May 31 '16 at 10:51
3

I find an useful method.

You can add a key named name for your test phase layer, and modify the keys ntop and top just like this:

net.data = L.Data(name='data', 
                include=dict(phase=caffe_pb2.Phase.Value('TRAIN')),
                ntop=1)
net.test_data = L.Data(name='data', 
                    include=dict(phase=caffe_pb2.Phase.Value('TEST')),
                    top='data',
                    ntop=0)
Rolrence
  • 31
  • 1
1

If your network is like:

layer {phase: TRAIN}
layer {phase: TEST}
layer {}
layer {phase: TRAIN}
layer {}
layer {phase: TEST}
layer {}
layer {}
layer {phase: TEST}

Create a train net ns, Create a test net ns_test
Now you basically have two strings str(ns.to_proto()) and str(ns_test.to_proto())
Merge those two using python regex taking into account the required layer order.

Gaurav Gupta
  • 1,929
  • 4
  • 21
  • 40
1

I found another way.
I could solve this problem returning the proto string.
Basically, you can add strings with the layers that are going to be replaced (in my case, the first layer).

def lenet(path_to_lmdb_train, path_to_lmdb_test,
          batch_size_train, batch_size_test ):
    n = caffe.NetSpec()
    n.data, n.label = L.Data(batch_size=batch_size_train, backend=P.Data.LMDB, source=path_to_lmdb_train,
                             include=dict(phase=caffe.TRAIN), transform_param=dict(scale=1./255), ntop=2)
    first_layer = str(n.to_proto())

    n.data, n.label = L.Data(batch_size=batch_size_test, backend=P.Data.LMDB, source=path_to_lmdb_test,
                             include=dict(phase=caffe.TEST), transform_param=dict(scale=1./255), ntop=2)
    n.conv1 = L.Convolution(n.data, kernel_size=5, num_output=20, weight_filler=dict(type='xavier'))
    n.pool1 = L.Pooling(n.conv1, kernel_size=2, stride=2, pool=P.Pooling.MAX)
    n.conv2 = L.Convolution(n.pool1, kernel_size=5, num_output=50, weight_filler=dict(type='xavier'))
    n.pool2 = L.Pooling(n.conv2, kernel_size=2, stride=2, pool=P.Pooling.MAX)
    n.ip1 = L.InnerProduct(n.pool2, num_output=500, weight_filler=dict(type='xavier'))
    n.relu1 = L.ReLU(n.ip1, in_place=True)
    n.ip2 = L.InnerProduct(n.relu1, num_output=10, weight_filler=dict(type='xavier'))
    n.loss = L.SoftmaxWithLoss( n.ip2, n.label )
    n.accuracy = L.Accuracy( n.ip2, n.label, include=dict(phase=caffe.TEST) )

    return first_layer + str(n.to_proto())
Thomio
  • 1,403
  • 15
  • 19
1

Although several answers have been given, none covers a more real-world scenario where you don't even know (at the moment of writing code) the names of your layers. For example when you're assembling a network from smaller blocks, you can't write:

n.data = L.Data(#...
n.test_data = L.Data(#...

Because every next instantiation of the block would overwrite data and test_data (or batchnorm, which is more likely to be put in blocks).

Fortunately, you can assign to the NetSpec object via __getitem__, like so:

layer_name = 'norm{}'.format(i) #for example
n[layer_name + '_train'] = L.Data(#...
n[layer_name + '_test']  = L.Data(#...
Przemek D
  • 654
  • 6
  • 26