Caffe | 你的第一个分类网络之Caffe训练

2019-01-06  本文已影响46人  yuanCruise

1.生成lmdb

lmdb是caffe训练网络用的数据格式,因此我们需要将原始的图片数据转换成lmdb(当然caffe中也可以直接用jpg进行训练)。利用上文Caffe | 你的第一个分类网络之数据准备中得到的train.txt和test.txt结合GitHub上caffe自带的批处理文件create_imagenet.sh就可以生成lmdb文件,该批处理文件存在如下所示的路径中。


基于train.txt,test.txt以及原始的图片,并根据下面代码所示修改后在命令窗口使用sh create_imagenet.sh就可以生成赌对应的lmdb文件了(具体修改策略看下边代码中的中文注释)。
#!/usr/bin/env sh
# Create the imagenet lmdb inputs
# N.B. set the path to the imagenet train + val data dirs
set -e

EXAMPLE=/home/YL/DataSet        #该路径为lmdb存储路径
DATA=/home/YL/DataSet           #该路径为train.txt所在路径
TOOLS=/home/caffe/build/tools   #该路径为编译完caffe的路径(就是你安装的caffe路径)

TRAIN_DATA_ROOT=/home/YL/DataSet/
VAL_DATA_ROOT=/home/YL/DataSet/

# Set RESIZE=true to resize the images to 256x256. Leave as false if images have
# already been resized using another tool.
RESIZE=false                      #该参数表示是否要改变图片的大小
if $RESIZE; then
  RESIZE_HEIGHT=256
  RESIZE_WIDTH=256
else
  RESIZE_HEIGHT=0
  RESIZE_WIDTH=0
fi

if [ ! -d "$TRAIN_DATA_ROOT" ]; then
  echo "Error: TRAIN_DATA_ROOT is not a path to a directory: $TRAIN_DATA_ROOT"
  echo "Set the TRAIN_DATA_ROOT variable in create_imagenet.sh to the path" \
       "where the ImageNet training data is stored."
  exit 1
fi

if [ ! -d "$VAL_DATA_ROOT" ]; then
  echo "Error: VAL_DATA_ROOT is not a path to a directory: $VAL_DATA_ROOT"
  echo "Set the VAL_DATA_ROOT variable in create_imagenet.sh to the path" \
       "where the ImageNet validation data is stored."
  exit 1
fi

echo "Creating train lmdb..."
rm -rf $EXAMPLE/train_lmdb   #这两句表示在生成lmdb前先把老的删除
rm -rf $EXAMPLE/test_lmdb    #因为生成lmdb时,若同路径下有同名文件会出错
GLOG_logtostderr=1 $TOOLS/convert_imageset \
    --resize_height=$RESIZE_HEIGHT \
    --resize_width=$RESIZE_WIDTH \
    --shuffle \
    $TRAIN_DATA_ROOT \
    $DATA/train.txt \
    $EXAMPLE/train_lmdb

echo "Creating val lmdb..."

GLOG_logtostderr=1 $TOOLS/convert_imageset \
    --resize_height=$RESIZE_HEIGHT \
    --resize_width=$RESIZE_WIDTH \
    --shuffle \
    $VAL_DATA_ROOT \
    $DATA/test.txt \
    $EXAMPLE/test_lmdb

echo "Done."

该过程中可能会遇到的问题:

2.生成均值文件

图片减去均值后,归一化后,再进行训练和测试,会提高速度和精度。因此,一般在各种模型中都会有这个操作。那么这个均值怎么来的呢,主要有两种方式第一种就是直接将均值设置为128,但若遇到一些填充过的样本,那么均值就会和128相差较多,这种情况下就要用第二种方法。第二种方法:实际上就是计算所有训练样本的平均值,计算出来后,保存为一个均值文件,在以后的测试中,就可以直接使用这个均值来相减,而不需要对测试图片重新计算。而利用第二种方法时,可以用caffe自带的策略。当然某些场景下需要得到Python中可用的均值文件,那也可以用Python脚本自己计算。

(1)caffe计算均值文件
caffe中使用的均值数据格式是binaryproto, caffe的作者为我们提供了一个计算均值的文件compute_image_mean.cpp,放在caffe根目录下的tools文件夹里面。编译后的可执行体放在 build/tools/ 下面,安装如下mnist实例所示调用即可。

#注意下面3句话要写在一行上,用空格分开(此处便于展示,进行了分段)
sudo 
build/tools/compute_image_mean examples/mnist/mnist_train_lmdb 
examples/mnist/mean.binarypro

主要就是两个参数:

(2)Python计算均值文件
如果我们要进行特征可视化等操作,可能就会用到npy形式的文件。整体思路为:先用lmdb格式的数据,计算出对应的二进制格式的均值,最后再转换成npy格式的均值。首先先将下述代码保存为convert_mean.py。

#!/usr/bin/env python
import numpy as np
import sys,caffe

if len(sys.argv)!=3:
    print "Usage: python convert_mean.py mean.binaryproto mean.npy"
    sys.exit()

blob = caffe.proto.caffe_pb2.BlobProto()
bin_mean = open( sys.argv[1] , 'rb' ).read()
blob.ParseFromString(bin_mean)
arr = np.array( caffe.io.blobproto_to_array(blob) )
npy_mean = arr[0]
np.save( sys.argv[2] , npy_mean )

在得到convert_mean.py文件后,在命令行输入如下所示的指令即可生成对应的npy格式的均值文件了。

sudo python convert_mean.py mean.binaryproto mean.npy

3.构建train_test.prototxt文件

得到lmdb或者均值文件之后,就可以构建如下所示的train_test.prototxt文件了。该文件需要更改的就是lmdb文件,若要添加均值文件,就把下述的mean_file:中的128改成对应的均值文件。还需要修改的就是最后输出的类别个数,分几类就可写几类。该prototxt文件其实就是网络的整体结构,根据下面的prototxt文件就可以得到对应的网络(比如lenet,vgg,mobilenet等等),我们用不同的网络结构就会有不同的train_test.prototxt。下述的网络为lenet的网络结构。

name: "LeNet"
layer {
  name: "Input"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  transform_param {
    scale: 0.00390625
    mean_file:128
  }
  data_param {
    source: "/home/YL/DataSet/train_lmdb"
    batch_size: 64
    backend: LMDB
  }
}
layer {
  name: "Input"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TEST
  }
  transform_param {
    scale: 0.00390625
    mean_file:128
  }
  data_param {
    source: "/home/YL/DataSet/test_lmdb"
    batch_size: 100
    backend: LMDB
  }
}
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"
    }
  }
}
layer {
  name: "pool1"
  type: "Pooling"
  bottom: "conv1"
  top: "pool1"
  pooling_param {
    pool: MAX
    kernel_size: 2
    stride: 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"
    }
  }
}
layer {
  name: "pool2"
  type: "Pooling"
  bottom: "conv2"
  top: "pool2"
  pooling_param {
    pool: MAX
    kernel_size: 2
    stride: 2
  }
}
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"
    }
  }
}
layer {
  name: "relu1"
  type: "ReLU"
  bottom: "ip1"
  top: "ip1"
}
layer {
  name: "ip2"
  type: "InnerProduct"
  bottom: "ip1"
  top: "ip2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 4
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "accuracy"
  type: "Accuracy"
  bottom: "ip2"
  bottom: "label"
  top: "accuracy"
  include {
    phase: TEST
  }
}
layer {
  name: "loss"
  type: "SoftmaxWithLoss"
  bottom: "ip2"
  bottom: "label"
  top: "loss"
}

就想上面说到的,我们可以利用train_test.prototxt文件得到对应的网络结构。具体操作为我们可以打开链接:http://ethereon.github.io/netscope/#/editor
进入下述链接后,会展示出如下所示的界面:


得到该界面后,将上述的train_test.prototxt复制到界面左边黑色的部分。将鼠标的光标定位在黑色部分,并同时按下Enter和Shift就会显示出网络结构图了,如下图所示。

4.构建solver.prototxt文件

构建完train_test.prototxt,也就是网络输入和结构之后,就需要构建solver.prototxt文件。solver算是caffe的核心的核心,它协调着整个模型的运作。该文件主要包含的是一些深度网络训练的超参数。比如学习率,学习率下降规则,优化器,多少步训练后展示一次,多少步训练后进行一次测试。下面将详细解释各个参数的作用。

#train_test.prototxt的路径
net: "/home/YL/DataSet/train_test.prototxt"

#测试间隔和每batch图片数
test_iter: 100
test_interval: 500

# 基础学习率和学习率策略
base_lr: 0.01
lr_policy: "inv"
gamma: 0.0001
power: 0.75
base_lr: 0.01
momentum: 0.9
weight_decay: 0.0005

# 下面是multistep的示例
#lr_policy: "multistep"
#gamma: 0.9
#stepvalue: 5000
#stepvalue: 7000
#stepvalue: 8000
#stepvalue: 9000
#stepvalue: 9500

#优化器选择
type:"SGD"

# momentum and the weight decay of the network.
momentum: 0.9
weight_decay: 0.0005

# Display every 100 iterations
display: 100
# The maximum number of iterations
max_iter: 10000
# snapshot intermediate results
snapshot: 5000
snapshot_prefix: "/home/YL/DataSet"
# solver mode: CPU or GPU
solver_mode: GPU

(1)test_iter和test_interval

(2)base_lr和lr_policy

(3)type
优化器的选择。因为默认值就是SGD,所以可以不写,但选择其他优化器时就要写了。到目前的版本,caffe提供了六种优化算法来求解最优参数,在solver配置文件中,通过设置type类型来选择。

(4)其他参数

5.构建train.sh文件

构建完train_test.prototxt和solver.prototxt两个文件后,基本网络和解决策略就搭建完了。就可以利用如下代码进行训练了。

#!/usr/bin/env sh

./build/tools/caffe train --solver=examples/mnist/lenet_solver.prototxt

caffe的c++主程序(caffe.cpp)放在根目录下的tools文件夹内, 当然还有一些其它的功能文件,如:convert_imageset.cpp, train_net.cpp, test_net.cpp等也放在这个文件夹内。经过编译后,这些文件都被编译成了可执行文件,放在了 ./build/tools/ 文件夹内。因此我们要执行caffe程序,都需要加 ./build/tools/ 前缀。
caffe程序的命令行执行格式如下:

caffe <command> <args>

其中的<command>有这样四种:

其中的<args>参数有:

./build/tools/caffe train -solver examples/mnist/lenet_solver.prototxt
./build/tools/caffe train -solver examples/mnist/lenet_solver.prototxt -gpu 2
./build/tools/caffe train -solver examples/mnist/lenet_solver.prototxt 
-snapshot examples/mnist/lenet_iter_5000.solverstate
./build/tools/caffe train -solver examples/finetuning_on_flickr_style/solver.prototxt 
-weights models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel

发现一篇写caffe写的很好的博客,强烈推荐!:地址

上一篇下一篇

猜你喜欢

热点阅读