最近一直在研究linux下的caffe,记录下自己在学习过程的一些步骤。
1、Solver文件分析
在caffe中,整个训练和加载开始是从solver.prototxt文件开始的,程序解析网络的solver文件开始,在solver.prototxt文件定义了网络的一些超参数,以及net网络模型的路径。lenet网络结构的solver文件(即./examples/mnist目录下的lenet_solver.prototxt文件)如下:
# The train/test net protocol buffer definition
#定义模型的文件
#定义网络结构的txt文件 -- 这里必须加载对
net: "examples/mnist/lenet_train_test.prototxt"
# test_iter specifies how many forward passes the test should carry out.
# In the case of MNIST, we have test batch size 100 and 100 test iterations,
# covering the full 10,000 testing images.
test_iter: 100 #指定测试应该执行多少次前传算法
# Carry out testing every 500 training iterations.
test_interval: 500 #多少次训练的迭代执行一次测试
# The base learning rate, momentum and the weight decay of the network.
base_lr: 0.01 # 基础的学习率
momentum: 0.9 # 动量值,使参数更新更稳健
weight_decay: 0.0005 #权重衰减率
# The learning rate policy
lr_policy: "inv" # 学习策略
gamma: 0.0001 #学习率变化的参数
power: 0.75
# Display every 100 iterations
display: 100 # 多少次迭代显示一次结果
#最大迭代次数
# The maximum number of iterations
max_iter: 10000
#输出中间结果snapshot的迭代次数,snapshot快照的思想
# snapshot intermediate results
snapshot: 5000
#保存中间结果存的路径
snapshot_prefix: "examples/mnist/lenet"
# 使用CPU还是GPU训练
solver mode:GPU
在解析solver文件的时候,最开始的是解析网络的模型,网络的模型也是由prototxt文件定义的,在这里是lenet_train_test.prototxt”文件。
2、Net 文件分析
Net文件主要是定义网络的模型结构,网络的结构,几个层每个层的结构等,以下是lenet的net文件,即examples/mnist/lenet_train_test.prototxt文件,这个文件路径要在solver.prototxt文件的第一行给出。
#定义模型prototxt文件
#训练和测试的时候也会用到
#这里layer的type类型时Data,所以读取的数据必须是转换后的lmdb或者leveb文件
name: "LeNet" #整个网络的名字
# 数据层--训练
layer {
name: "mnist"
type: "Data"
top: "data"
top: "label"
# 只在训练过程中有效
include
{
phase: TRAIN
}
# 预处理的操作
transform_param {
mean_file:"../mnist_mean/mean.binaryproto" # 均值文件地址
scale: 0.00390625 # 归一化的参数
}
# 指定训练用的lmdb文件的路径
data_param {
source: "../mnist_train_lmdb"
batch_size: 64 #指定batch_size批量的大小
backend: LMDB
}
}
# 数据层--测试
layer {
name: "mnist"
type: "Data"
top: "data"
top: "label"
# 只在测试过程中有效
include {
phase: TEST
}
transform_param {
mean_file:"../mnist_mean/mean.binaryproto"
scale: 0.00390625
}
# 指定测试用的lmdb文件的路径,注意这里和train使用的文件不同
data_param {
source: "../mnist_test_lmdb"
batch_size: 100 #指定batch_size批量的大小,和train的不同
backend: LMDB
}
}
# 卷积层1
layer {
name: "conv1"
type: "Convolution"
bottom: "data"
top: "conv1"
# 学习率
param {
lr_mult: 1 # 卷积层参数的学习率
}
param {
lr_mult: 2 # 偏置参数的学习率
}
# 卷积层的参数
convolution_param {
num_output: 20
kernel_size: 5
stride: 1
weight_filler {
type: "xavier" #卷积层参数的初始化的方式
}
bias_filler {
type: "constant" #偏置参数出初始化为常数
}
}
}
# 池化层1
layer {
name: "pool1"
type: "Pooling"
bottom: "conv1"
top: "pool1"
# 层的参数
pooling_param {
pool: MAX # 池化的方式
kernel_size: 2 # 池化的模板的大小
stride: 2
}
}
# 卷积层2
layer {
name: "conv2"
type: "Convolution"
bottom: "pool1"
top: "conv2"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
convolution_param {
num_output: 50
kernel_size: 5
stride: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
# 池化层2
layer {
name: "pool2"
type: "Pooling"
bottom: "conv2"
top: "pool2"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
# 全连接层1
layer {
name: "ip1"
type: "InnerProduct"
bottom: "pool2"
top: "ip1"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 500
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
# ReLU层
# 注意:一般激活层的bottom和top的名字一样(后面解释)
layer {
name: "relu1"
type: "ReLU"
bottom: "ip1"
top: "ip1"
}
# 全连接层2
layer {
name: "ip2"
type: "InnerProduct"
bottom: "ip1"
top: "ip2"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 10
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
# 精度层,只在test时有效
layer {
name: "accuracy"
type: "Accuracy"
bottom: "ip2"
bottom: "label"
top: "accuracy"
include {
phase: TEST
}
}
# softmaxloss层
layer {
name: "loss"
type: "SoftmaxWithLoss"
bottom: "ip2"
bottom: "label"
top: "loss"
}
*说明以下几点:
1、可以将全连接层看成是一种特殊的卷积层,所以一般全连接层的卷积层的参数形式是一样的。
2、除了数据输入层其他层都有top和bottom,输入数据层有top但是没有bottom层。
3、注意: 层的名字和top和bottom名字是没有关系的,一般默认的情况是top的名字和层的名字相同,但是也可以不同,只要下一层的bottom和上一层的top名字一致就可以了,层取什么名字不影响,这里需要注意的是下层的bottom其实对应的上层的top的名字而不是上层的层的名字。
4、注意:在激活层中,一般top和bottom层的名字一样,是因为在caffe中内存是与top和bottom的名字对应起来的,比如只要几个top或者bottom的名字相同,则其是相同的内存,因此在RELU层,经过RELU后相当于把上一层的Conv层的输出的数据改变了,这样可以节省空间。
以上路径都是自己为了测试改过的,实际都应该对应自己的对应文件的路径
3、网络结构
在caffe中根据定义的net文件如何显式显示网络的结构,这里推荐网站
(http://ethereon.github.io/netscope/#/editor),lenet文件可视化后的网络结构如下: