版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1437721
在 https://github.com/BVLC/caffe/blob/master/examples/mnist 是Caffe关于LetNet-5的相关文件,
这里面有几个后缀是 .prototxt 的文件,它是用 protocol buffer 这个工具生成的文件。
百度百科对 protocol buffer 描述如下:
protocol buffer(以下简称PB)是google 的一种数据交换的格式,它独立于语言,独立于平台。Google 提供了多种语言的实现:java、c#、c++、go 和 python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。
Caffe 源码中大量使用 protocol buffer 作为权值和模型参数的载体。对于参数管理,我们拥有多种选择,有人喜欢 TXT 的易于修改,有人喜欢 BIN 的读写高效,也有人喜欢图形化配置的直观形象。不一致的参数管理带来很多问题。例如,一个项目组内不同成员必须约定一套统一的参数方案,或者称为通信协议,以便于模块集成。protocol buffer 工具完美的解决了这个问题,用户只需要建立统一的参数描述文件(proto),然后利用 protoc 编译就能让协议细节等关键部分代码自动生成,节省了大量的开发、调试时间。使用 protocol buffer 还可以跨语言(java、c#、c++、go 和 python)传递相同的数据结构,让团队协作更有效率。
下面我们来看看 Caffe 是如何定义和描述 LetNet-5 网络模型:
对于 LetNet-5 广义模型描述文件是 lenet.prototxt
下面我们来详细看看 lenet.prototxt :
// 输入层的定义:
name: "LeNet" (网络的名字)
layer { (定义一个网络层)
name: "data" (网络层的名字为 data)
type: "Input" (网络层的类型,输入)
top: "data" (该网络层的输出叫 data )
input_param { shape: { dim: 64 dim: 1 dim: 28 dim: 28 } } }(64张图像为一批,28*28大小)
}
(读取这批数据维度:64 1 28 28)
// 第一卷积层的定义:
layer {
name: "conv1" (网络层的名字为 conv1)
type: "Convolution" (网络层类型是 卷积层)
bottom: "data" (该层的输入层是 data 层)
top: "conv1" (该层的输出层叫 conv1 feature maps)
param {
lr_mult: 1 (weights的学习率与全局相同)
}
param {
lr_mult: 2 (biases的学习率是全局的2倍)
}
convolution_param { {(卷积操作参数设置)
num_output: 20 (卷积输出数量20,由20个特征图Feature Map构成)
kernel_size: 5 (卷积核的大小是5*5)
stride: 1 (卷积操作步长)
weight_filler {
type: "xavier" (卷积滤波器的参数使用 xavier 方法来初始化)
}
bias_filler {
type: "constant" (bias使用0初始化)
}
}
}
卷积之后这批数据维度:64 20 24 24
// 第一池化层定义:
layer {
name: "pool1" (网络层的名字是 pool1 )
type: "Pooling" (网络层的类型是 池化操作)
bottom: "conv1" (网络层的输入是 conv1 feature maps)
top: "pool1" (网络层的输出是 pool1)
pooling_param { (池化参数设置)
pool: MAX (最大池化操作)
kernel_size: 2 (池化核尺寸,2*2 区域池化)
stride: 2 (池化步长)
}
}
池化之后这批数据维度:64 20 12 12
// 第二卷积层的定义:
layer {
name: "conv2" (该网络层的名字)
type: "Convolution" (该网络层的类型,卷积)
bottom: "pool1" (该网络层的输入是 pool1)
top: "conv2" (该网络层的输出是 conv2, feature maps)
param {
lr_mult: 1 (weights的学习率与全局相同)
}
param {
lr_mult: 2 (biases的学习率是全局的2倍)
}
convolution_param { (卷积参数设置)
num_output: 50 (卷积的输出个数,由50个特征图Feature Map构成)
kernel_size: 5 (卷积核尺寸 5*5)
stride: 1 (卷积步长)
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
卷积之后这批数据维度:64 50 8 8
// 第二池化层的定义:
layer {
name: "pool2"
type: "Pooling"
bottom: "conv2"
top: "pool2"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
池化之后这批数据维度:64 50 4 4
// 第一层全链接层的定义:
layer {
name: "ip1" (该网络层的名字 ip1)
type: "InnerProduct" (该网络层的类型是 全链接层)
bottom: "pool2" (该层的输入是 pool2)
top: "ip1" (该层的输出是 ip1)
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param { (全链接层的 参数设置)
num_output: 500 (500个输出神经元)
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
500 个神经元
// 激活函数层的定义:
layer {
name: "relu1" (该网络层的名字 relu1)
type: "ReLU" (该网络层的类型, ReLU 激活函数)
bottom: "ip1" (该层的输入是 ip1)
top: "ip1" (该层的输出还是 ip1,底层与顶层相同是为了减少开支)
}
// 第二全链接层的定义:(数据的分类判断在这一层中完成)
layer {
name: "ip2"
type: "InnerProduct"
bottom: "ip1"
top: "ip2"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 10 (直接输出结果,0-9,十个数字所以维度是10)
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
// 输出层的定义:
layer {
name: "prob"
type: "Softmax" (损失函数)
bottom: "ip2"
top: "prob"
}