当前位置 : 主页 > 大数据 > 区块链 >

Caffe学习4-利用caffe.proto自定义自己的网络

来源:互联网 收集:自由互联 发布时间:2021-06-22
利用caffe.proto自定义自己的网络 利用caffeproto自定义自己的网络 先不急让我们来看一下caffeproto里面都是些什么 让我们把caffeproto用起 后记 caffe.proto存放在src/caffe/proto/目录下,建议读者能

利用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~。

网友评论