续caffe初探1大笑大笑大笑有兴趣的朋友可以关注笔者的博客,笔者作为一个初涉深度学习领域和caffe的rookie,很高兴同大家一起学习和探讨,对于本人博客中的谬误与疏漏,笔者诚恳地期待各位读者朋友们的指点与建议。
在caffe初探1中,笔者提到了如何制作自己的数据集,那么在此文中,笔者将讲解如何撰写网络架构。众所周知,在卷及神经网络中,网络架构是最重要的一部分,网络的构造与模型效果的好与坏是息息相关的,因此,要学习如何构造符合各位读者朋友分类目的网络,请各位读者朋友们阅读有关机器学习与神经网络相关的文献。本文只是讲解如何在caffe框架下面如何设计网络架构。好了,废话不多说,下面具体讲解如何生成网络架构。
在撰写网络架构之前,我们需要一个meanfile文件,这个文件是训练模型的时候需要的,具体作用是什么呢?这里笔者以一张参与训练的图片举个栗子,均值文件会对这一张图片分通道(如B,G,R通道)求像素均值,然后求各通道像素值与均值的差值并以此作为输入。那么为什么需要求差值呢?其实是因为差值计算可以使结果像素均匀地分布在均值的周围。
和caffe初探1一样,我们生成的数据集在./caffe/forkrecognition/目录下面,有两个文件夹,名称分别为test_lmdb和train_lmdb,现在我们在./caffe/forkrecognition/目录下建立脚本文件make_imagenet_mean.sh,内容如下所示。
#!/usr/bin/env sh
# Compute the mean image from the imagenet training lmdb
EXAMPLE=forkrecognition #存放训练集的根目录
DATA=forkrecognition #存放均值计算结果的根目录
TOOLS=build/tools #计算均值文件的目录
$TOOLS/compute_image_mean $EXAMPLE/train_lmdb \
$DATA/imagenet_mean.binaryproto
echo "Done."
在caffe目录下执行./forkrecognition/make_imagenet_mean.sh命令,可以看到在./caffe/forkrecognition/目录下面生成了image_mean.binaryproto文件,这是我们在编写网络架构中需要用到的。接下来,我们就可以开始撰写网络结构了。
在此笔者想说的是,有关网络的设计规范官方文档,当我们在Ubuntu上配置caffe的时候就已经自动生成了,在caffe目录下面的src/caffe/proto/文件路径下面的caffe.proto文件中,建议各位读者朋友们对该文档建立大概的了解。在此文件中我们可以浏览深度神经网络设计规范,在此笔者想提醒大家的是,此规范使用的语法格式是google protobuf规范,因此,建议各位读者在阅读caffe.proto之前对google protobuf做一定的了解。在此笔者参照的是AlexNet经典网络,在./caffe/forkrecognition/路径下面建立一个train_val.prototxt文件并在其中撰写深度神经网络架构,内容如下所示。
name: "AlexNet" #网络名称
layer { #定义数据层
name: "forkdata" #层名称
type: "Data" #层类型
top: "data" #层输出数据1:图像数据
top: "label" #层输出数据2:图像标签,规定岔路口还是非岔路口
include {
phase: TRAIN #规定此数据层只有在训练的时候调用
}
transform_param {
mirror: true #制作镜像
crop_size: 227 #裁减了图像,请注意是裁剪不是缩放,如果需要缩放图像可以使用scale参数
mean_file: "forkrecognition/imagenet_mean.binaryproto" #均值文件路径
}
data_param {
source: "forkrecognition/train_lmdb" #训练集路径
batch_size: 5 #训练批次大小,就是训练一次送进网络多少图片,因为笔者数据集较少,因此批次容量也较小
backend: LMDB #指定训练集的格式
}
}
layer {
name: "forkdata" #此层数据用于训练模型时的测试数据
type: "Data"
top: "data"
top: "label"
include {
phase: TEST #规定此数据只有在测试训练成果的时候调用
}
transform_param {
mirror: false
crop_size: 227
mean_file: "forkrecognition/imagenet_mean.binaryproto"
}
data_param {
source: "forkrecognition/test_lmdb" #制定测试集路径
batch_size: 5
backend: LMDB
}
}
layer { #定义卷积层
name: "conv1"
type: "Convolution"
bottom: "data" #该层的输入为数据层的输出
top: "conv1" #该层的输出
param {
lr_mult: 1 #规定学习率乘子
decay_mult: 1 #规定权重衰减乘子
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 96 #规定一层输出元素的个数
kernel_size: 11 #规定卷积核的大小
stride: 4 #规定卷积核操作步长
weight_filler {
type: "gaussian" #指定权重过滤器类型为高斯滤波
std: 0.01 #在高斯滤波中需要用到的标准差大小
}
bias_filler {
type: "constant" #指定便宜滤波器类型为常值滤波
value: 0
}
}
}
layer {
name: "relu1" #激励层
type: "ReLU"
bottom: "conv1"
top: "conv1"
}
layer { #局部相应归一化层
name: "norm1"
type: "LRN"
bottom: "conv1"
top: "norm1"
lrn_param {
local_size: 5 #表征通道内归一化时求和区间的边长
alpha: 0.0001 #缩放因子
beta: 0.75 #指数项
}
}
layer { #定义池化层
name: "pool1"
type: "Pooling"
bottom: "norm1"
top: "pool1"
pooling_param {
pool: MAX #池化操作类型
kernel_size: 3 #进行池化操作的核的大小
stride: 2 #进行池化操作时的步长
}
}
layer {
name: "conv2"
type: "Convolution"
bottom: "pool1"
top: "conv2"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 256
pad: 2
kernel_size: 5
group: 2
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0.1
}
}
}
layer {
name: "relu2"
type: "ReLU"
bottom: "conv2"
top: "conv2"
}
layer {
name: "norm2"
type: "LRN"
bottom: "conv2"
top: "norm2"
lrn_param {
local_size: 5
alpha: 0.0001
beta: 0.75
}
}
layer {
name: "pool2"
type: "Pooling"
bottom: "norm2"
top: "pool2"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer {
name: "conv3"
type: "Convolution"
bottom: "pool2"
top: "conv3"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 384
pad: 1
kernel_size: 3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
name: "relu3"
type: "ReLU"
bottom: "conv3"
top: "conv3"
}
layer {
name: "conv4"
type: "Convolution"
bottom: "conv3"
top: "conv4"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 384
pad: 1
kernel_size: 3
group: 2
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0.1
}
}
}
layer {
name: "relu4"
type: "ReLU"
bottom: "conv4"
top: "conv4"
}
layer {
name: "conv5"
type: "Convolution"
bottom: "conv4"
top: "conv5"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 256
pad: 1
kernel_size: 3
group: 2
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0.1
}
}
}
layer {
name: "relu5"
type: "ReLU"
bottom: "conv5"
top: "conv5"
}
layer {
name: "pool5"
type: "Pooling"
bottom: "conv5"
top: "pool5"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer { #定义全连接层
name: "fc6"
type: "InnerProduct"
bottom: "pool5"
top: "fc6"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 4096
weight_filler {
type: "gaussian"
std: 0.005
}
bias_filler {
type: "constant"
value: 0.1
}
}
}
layer {
name: "relu6"
type: "ReLU"
bottom: "fc6"
top: "fc6"
}
layer {
name: "drop6"
type: "Dropout"
bottom: "fc6"
top: "fc6"
dropout_param {
dropout_ratio: 0.5
}
}
layer {
name: "fc7"
type: "InnerProduct"
bottom: "fc6"
top: "fc7"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 4096
weight_filler {
type: "gaussian"
std: 0.005
}
bias_filler {
type: "constant"
value: 0.1
}
}
}
layer {
name: "relu7"
type: "ReLU"
bottom: "fc7"
top: "fc7"
}
layer { #定义dropout层,目的是防止cnn过拟合,在模型训练时随机让网络某些隐含层节点的权重不工作,不工作的那些节点可以暂时认为不是网络结构的 #一部分,但是它的权重得保留下来(只是暂时不更新而已)
name: "drop7"
type: "Dropout"
bottom: "fc7"
top: "fc7"
dropout_param {
dropout_ratio: 0.5 #定义选择节点的概率
}
}
layer {
name: "forkfc8"
type: "InnerProduct"
bottom: "fc7"
top: "fc8"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 2
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer { #定义准确率层
name: "accuracy"
type: "Accuracy"
bottom: "fc8"
bottom: "label"
top: "accuracy"
include {
phase: TEST
}
}
layer { #定义损失层
name: "loss"
type: "SoftmaxWithLoss"
bottom: "fc8"
bottom: "label"
top: "loss"
}
值得注意的是,请各位读者朋友们观察定义网络时文档最开始两个名为"forkdata"的层和文档末尾名为"accuracy"和"loss"的层,这些层限定了训练时网络的输入与输出,两个"forkdata"层分别指定了训练网络时训练集和测试集的数据来源和均值文件来源,还规定了训练图像尺寸与批次;而通过"accuracy"层我们可以的得到训练出网络的准确率以评估网络的优劣,通过"loss"层我们可以得到训练网络时的损失;看到这里读者朋友可能已经看出了端倪,就是这四个层都和“训练网络”有关!请读者朋友切记,这四个层就是我们在训练网络和使用训练完毕的网络时最大的不同,这四个层是仅仅用于训练阶段的。