利用caffe.proto自定义自己的网络
- 利用caffeproto自定义自己的网络
- 先不急让我们来看一下caffeproto里面都是些什么
- 让我们把caffeproto用起
- 后记
caffe.proto存放在src/caffe/proto/目录下,建议读者能够打开它,跟着这篇教程,学会如何把它用起来(很多人都说要看,反正才一千三百多行是吧…小编我看了两百行就想睡觉了,关键是看了还容易忘,所以小编的建议是在需要用它的时候再去看它)。
先不急,让我们来看一下caffe.proto里面都是些什么
对于懒得在翻目录的读者,可以点击这个链接,不过还是建议读者都看本地的,毕竟以后会经常用到的
https://github.com/BVLC/caffe/blob/master/src/caffe/proto/caffe.proto
下面列出文件里面的前十几行= =
message BlobShape {
repeated int64 dim = 1 [packed = true];
}
message BlobProto {
optional BlobShape shape = 7;
repeated float data = 5 [packed = true];
repeated float diff = 6 [packed = true];
repeated double double_data = 8 [packed = true];
repeated double double_diff = 9 [packed = true];
// 4D dimensions -- deprecated. Use "shape" instead.
optional int32 num = 1 [default = 0];
optional int32 channels = 2 [default = 0];
optional int32 height = 3 [default = 0];
optional int32 width = 4 [default = 0];
}
// The BlobProtoVector is simply a way to pass multiple blobproto instances
// around.
message BlobProtoVector {
repeated BlobProto blobs = 1;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
相信读者看到上面的内容会感到很熟眼,这不就是Blob吗?对,这就是Blob的细节,对于一直搞不懂Blob里面放的是什么东西的读者,现在可以深入到内部去理解Blob了。
读者在看.proto是会经常遇到deprecated这样的注释,建议读者跳过跟这个注释有关的代码,因为这些代码都是过时的,很有可能会在某一天就被删掉。
注意到这些定义有两种前缀,一个是optional,一个是repeated。repeated是数组,而optional就是一个数据。因此BlobShape里面包含了一个shape的数组。然后BlobProto里面就包含了5个数据(不考虑那些被标了deprecated的),分别是BlobShape,float和double类型的data和diff。因此,一个Blob里面的数据就是他自身的尺寸,以及用于前向的data和用于后向的diff,这样看够直接了吧?
然后我们再来看看Layer有关的,为了节约篇幅,下面只截取一部分
message LayerParameter {
optional string name = 1; // the layer name
optional string type = 2; // the layer type
repeated string bottom = 3; // the name of each bottom blob
repeated string top = 4; // the name of each top blob
optional Phase phase = 10;
repeated float loss_weight = 5;
repeated ParamSpec param = 6;
repeated BlobProto blobs = 7;
repeated bool propagate_down = 11;
repeated NetStateRule include = 8;
repeated NetStateRule exclude = 9;
optional TransformationParameter transform_param = 100;
optional LossParameter loss_param = 101;
...
optional ConvolutionParameter convolution_param = 106;
...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
可以看到LayerParameter包含很东西,比如什么name啊,type啊,blabla。记性好的读者应该发现在我们的Caffe学习2那部分的Nets的layer的protobuf代码有的,LayerParameter也都有。因此不难猜测,如果想要使用Caffe的Layer,那么,caffe.proto的内容将会提供很大的帮助。
让我们把caffe.proto用起!
下面我们先给出一个caffe的net的定义的结构,方便我们往里面加东西。
from caffe import layers as L, params as P
def mynet(lmdb, batch_size):
n = caffe.NetSpec()
return n.to_proto()
- 1
- 2
- 3
- 4
- 5
- 6
上面的代码,我们一开始就import了两个包,一个是layers,另一个是params。layers里面包含了Caffe所以内置的层(比如卷积,ReLU等),而params则包含了各种枚举值。方法的参数中的lmdb是指Caffe支持的数据库的一种,叫lmdb,我们传入数据库的路径即可。而n=caffe.NetSpec()是获取Caffe的一个Net,我们只需不断的填充这个n,最后面把n输出到文件就会使我们在Caffe学习2里面看到的Net的protobuf的定义。
接下来,我们想要通过lmdb读取数据。那代码该怎么写呢?谷歌一下,或者是偷看caffe的示例程序,我们可以知道应该如下
n.data, n.label = L.Data(batch_size=batch_size, backend=P.Data.LMDB, source=lmdb, transform_param=dict(scale=1./255), ntop=2)
- 1
由于lmdb里面存放了两个数据,一个是data,一个是label,因此这个层上面会有2个blobs,故ntop=2(小编试过把它删了,结果报错,看来这个还是少不了的)。
好,然后我们想添加一层Convolution layer,我们这次不谷歌也不看示例,看看怎么用caffe.proto写代码
- 首先,我们看下LayerParameter里面有没Convolution的身影,然后发现有ConvolutionParameter,这个就说明Caffe内置有Convolution Layer。然后在LayerParameter里面找到我们需要设置的参数,这里我们可以不用设置任何参数,python的接口会帮我们自动生成。因此我们有了我们的第一步代码
n.conv1 = L.Convolution(n.data, ...)
- 1
- 然后我们搜ConvolutionParameter,找到改名下我们需要用到的一下参数,比如num_output(过滤器的数目),kernel_size(过滤器的展度),pad(填充数),stride(步长),其中必不可少的num_output和kernel_size,这两个没有default值,因此如果少了的话Caffe会出错。
然后我们有了我们的完整代码
n.conv1 = L.Convolution(n.data, kernel_size=3, num_output=36, pad=1)
- 1
- 最后我们还想给这一层的权重一个比较科学的初始化,比如xavier。找到ConvolutionParameter的weight_filler,发现是一个FillerParameter,同理,找到FillerParameter下。我们只要把type设置正确即可。最终我们可以有如下的代码
n.conv1 = L.Convolution(n.data, kernel_size=3, num_output=36, pad=1, weight_filler=({type:"xavier"}))
- 1
- 同样地,我们还可以定义其他层(至于为什么就交给读者自己验证啦,有问题可以留言),比如
n.score = L.InnerProduct(n.conv1, num_output=10, weight_filler=dict(type='xavier'))
n.loss = L.SoftmaxWithLoss(n.score, n.label)
- 1
- 2
-最终我们用python定义了一个完整的Net
from caffe import layers as L, params as P
def mynet(lmdb, batch_size):
n = caffe.NetSpec()
n.data, n.label = L.Data(batch_size=batch_size, backend=P.Data.LMDB, source=lmdb, transform_param=dict(scale=1./255), ntop=2)
n.conv1 = L.Convolution(n.data, kernel_size=3, num_output=36, pad=1, weight_filler=({type:"xavier"}))
n.score = L.InnerProduct(n.conv1, num_output=10, weight_filler=dict(type='xavier'))
n.loss = L.SoftmaxWithLoss(n.score, n.label)
return n.to_proto()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
后记
学会了怎么使用caffe.proto和Python定义Caffe的Net之后,下一节小编会介绍如何用定义Caffe的solver~。