美文网首页
Caffe | 你的第一个分类网络之Caffe训练

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

作者: yuanCruise | 来源:发表于2019-01-06 17:31 被阅读46次

    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."
    

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

    • 问题1:
      若是遇到 a total of 0 images,类似问题,基本就是图片路径的问题,尽量把sh文件中的路径换成绝对路径,所以我上述的例子中所有的路径均为绝对路径。
    • 问题2:
      若是遇到 Check failed: mkdir(source.c_str(), 0744) == 0 (-1 vs 0),类似问题,基本就是没有在sh文件中删掉上一次生成的lmdb文件,在之前sh文件中加入上述注释中的两句rm指令就可以解决这个问题了。
    • 问题3:
      有的时候会遇到permission denied,这个时候修改一下文件夹权限(利用chmod指令)

    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
    

    主要就是两个参数:

    • 第一个参数:examples/mnist/mnist_train_lmdb, 表示需要计算均值的数据,格式为lmdb的训练数据。
    • 第二个参数:examples/mnist/mean.binaryproto, 计算出来的结果保存文件。

    (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

    • test_iter:这个要与测试层中的batch_size结合起来理解。假设测试样本总数为10000,一次性执行全部数据效率很低,因此就需要测试数据分成几个批次来执行,每个批次的数量就是batch_size。假设我们设置batch_size为100,则需要迭代100次才能将这10000个数据全部执行完。因此test_iter设置为100。测试完这10000个数据才叫做一次测试完成。
    • test_interval:测试间隔。也就是每训练500次,才按照上述所说的过程进行一次完整的测试。

    (2)base_lr和lr_policy

    • base_lr:值得是初始化的学习率
    • lr_policy:lr_policy可以设置为下面这些值,相应的学习率的计算为(下图还有其中两个策略的可视化图):


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

    • Stochastic Gradient Descent (type: "SGD"),
    • AdaDelta (type: "AdaDelta"),
    • Adaptive Gradient (type: "AdaGrad"),
    • Adam (type: "Adam"),
    • Nesterov’s Accelerated Gradient (type: "Nesterov")
    • RMSprop (type: "RMSProp")

    (4)其他参数

    • momentum:上一次梯度更新的权重(所谓的惯性)。
    • weight_decay:权重衰减项,防止过拟合的一个参数。
    • display:每训练100次,在屏幕上显示一次。如果设置为0,则不显示。
    • max_iter:最大迭代次数。这个数设置太小,会导致没有收敛,精确度很低。设置太大,会导致震荡,浪费时间。
    • snapshot: 快照。将训练出来的model和solver状态进行保存,snapshot用于设置训练多少次后进行保存,默认为0,不保存。snapshot_prefix设置保存路径。还可以设置snapshot_diff,是否保存梯度值,默认为false,不保存。也可以设置snapshot_format,保存的类型。有两种选择:HDF5 和BINARYPROTO ,默认为BINARYPROTO

    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>有这样四种:

    • train:训练或finetune模型
    • test:测试模型
    • device_query:显示gpu信息
    • time:显示程序执行时间

    其中的<args>参数有:

    • solver:必选参数。一个protocol buffer类型的文件,即模型的配置文件。
    ./build/tools/caffe train -solver examples/mnist/lenet_solver.prototxt
    
    • gpu:可选参数。该参数用来指定用哪一块gpu运行,根据gpu的id进行选择,如果设置为'-gpu all'则使用所有的gpu运行。若要用第二个gpu如下:
    ./build/tools/caffe train -solver examples/mnist/lenet_solver.prototxt -gpu 2
    
    • snapshot:可选参数。该参数用来从快照(snapshot)中恢复训练。可以在solver配置文件设置快照,保存solverstate。如:
    ./build/tools/caffe train -solver examples/mnist/lenet_solver.prototxt 
    -snapshot examples/mnist/lenet_iter_5000.solverstate
    
    • weights:可选参数。用预先训练好的权重来fine-tuning模型,需要一个caffemodel,不能和-snapshot同时使用。如
    ./build/tools/caffe train -solver examples/finetuning_on_flickr_style/solver.prototxt 
    -weights models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel
    
    • iteration:可选参数,迭代次数,默认为50。 如果在配置文件文件中没有设定迭代次数,则默认迭代50次。
    • model:可选参数,就是train_test.prototxt的protocol buffer类型的文件。也可以在solver配置文件中指定。
    • sighup_effect:可选参数。用来设定当程序发生挂起事件时,执行的操作,可以设置为snapshot, stop或none, 默认为snapshot
    • sigint_effect:可选参数。用来设定当程序发生键盘中止事件时(ctrl+c), 执行的操作,可以设置为snapshot, stop或none, 默认为stop。

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

    相关文章

      网友评论

          本文标题:Caffe | 你的第一个分类网络之Caffe训练

          本文链接:https://www.haomeiwen.com/subject/yudsrqtx.html