Caffe中mnist例子(一)自定义输入层

  1. 目标
    修改mnist的输入层,以图片《digits.png》作为数据源。
    数据源

2 操作步骤

2.1 创建新定义的头文件/opt/caffe-master/include/caffe/layers/my_data_layer.hpp
重新Layer名的方法:virtual inline const char* type() const {return “MyData”;}

#ifndef CAFFE_MY_DATA_LAYER_HPP_
#define CAFFE_MY_DATA_LAYER_HPP_

#include <string>
#include <utility>
#include <vector>
#include <algorithm>    // std::random_shuffle
#include <vector>       // std::vector
#include <ctime>        // std::time

#include "caffe/blob.hpp"
#include "caffe/data_transformer.hpp"
#include "caffe/internal_thread.hpp"
#include "caffe/layer.hpp"
#include "caffe/layers/base_data_layer.hpp"
#include "caffe/proto/caffe.pb.h"

namespace caffe {

template <typename Dtype>
class MyDataLayer : public BasePrefetchingDataLayer<Dtype> {
  public:
    explicit MyDataLayer(const LayerParameter& param)
    : BasePrefetchingDataLayer<Dtype>(param){}
    virtual ~MyDataLayer();
    virtual void DataLayerSetUp(const vector<Blob<Dtype>*>& bottom,
    const vector<Blob<Dtype>*>& top);

    virtual inline const char* type() const { return "MyData";}
    virtual inline int ExactNumBottomBlobs() const { return 0; }
    virtual inline int ExactNumTopBlobs() const { return 2; }

  protected:
    shared_ptr<Caffe::RNG> prefetch_rng_;
    virtual void ShuffleImages();
    virtual void load_batch(Batch<Dtype>* batch);

    inline cv::Mat get_one_sample(cv::Mat whole_img,int pos_x,int pos_y,int width,int height){
        return source_image_(cv::Rect(pos_x,pos_y,width,height)).clone();
    }

    vector<std::pair<cv::Mat, int> > samples_;
    int lines_id_;

    string image_address_;
    int start_col_;
    int end_col_;
    int sample_width_;
    int sample_height_;
    cv::Mat source_image_;

};


} // namespace caffe

#endif // CAFFE_MY_DATA_LAYER_HPP_

2.2 创建新定义数据层文件/opt/caffe-master/src/caffe/layers/my_data_layer.cpp
重写LayerSetUp,ShuffleImage,load_batch。

#ifdef USE_OPENCV
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

#include <fstream>  // NOLINT(readability/streams)
#include <iostream>  // NOLINT(readability/streams)
#include <string>
#include <utility>
#include <vector>
#include <stdio.h>

#include "caffe/data_transformer.hpp"
#include "caffe/layers/base_data_layer.hpp"
#include "caffe/layers/my_data_layer.hpp"
#include "caffe/util/benchmark.hpp"
#include "caffe/util/io.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/util/rng.hpp"

namespace caffe {

template <typename Dtype>
MyDataLayer<Dtype>::~MyDataLayer<Dtype>() {
  this->StopInternalThread();
}

template <typename Dtype>
void MyDataLayer<Dtype>::DataLayerSetUp(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top){
    // Init private variables.
    image_address_ = this->layer_param_.my_data_param().image_address();
    start_col_= this->layer_param_.my_data_param().start_col();
    end_col_  = this->layer_param_.my_data_param().end_col();
    sample_width_ = this->layer_param_.my_data_param().sample_width();
    sample_height_= this->layer_param_.my_data_param().sample_height();

    lines_id_ = 0;

    int label,x,y;
    cv::Mat image;
    source_image_ = cv::imread(image_address_);
    for(int i=0 ; i< 50 ; i++){
      label = (int)(i/5);
      for(int j=start_col_ ; j<end_col_ ; j++){
         y=20*i;
         x=20*j;
         image = get_one_sample(source_image_,x,y,20,20);
         samples_.push_back(std::make_pair(image,label));
      }
    }

    CHECK(!samples_.empty()) << "Data is empty";

    // Shuffle images
    if(this->layer_param_.my_data_param().shuffle()){
        LOG(INFO) << "Shuffling data";
        ShuffleImages();
    }


    // Save images
    if(this->layer_param_.my_data_param().is_save()){
    LOG(INFO) << "Saving images ";
        for(int i=0;i<samples_.size();i++){
        string save_folder = this->layer_param_.my_data_param().save_folder();
        char img_name[128];
        sprintf(img_name,"%s/%d.jpg",save_folder.c_str(),i);
        cv::imwrite(img_name,samples_[i].first);
        }
    }


    // Read an image, and use it to initialize the top blob
    cv::Mat cv_img = samples_[lines_id_].first;
    CHECK(cv_img.data) << "Could not load first image";

    // Use data_transformer to infer the expected blob shape from a cv_image
    vector<int> top_shape = this->data_transformer_->InferBlobShape(cv_img);
    this->transformed_data_.Reshape(top_shape);

    //Reshape prefetch_data and top[0] according to the batch_size.
    const int batch_size = this->layer_param_.my_data_param().batch_size();
    top_shape[0] = batch_size;
    for(int i=0;i<this->PREFETCH_COUNT;++i){
        this->prefetch_[i].data_.Reshape(top_shape);
    }
    top[0]->Reshape(top_shape);

    LOG(INFO) << "output data size: " << top[0]->num() << "," << top[0]->channels() << "," << top[0]->height() << "," << top[0]->width();

    // label
    vector<int> label_shape(1,batch_size);
    top[1]->Reshape(label_shape);
    for(int i=0;i<this->PREFETCH_COUNT;++i){
        this->prefetch_[i].label_.Reshape(label_shape);
    }


}


template <typename Dtype>
void MyDataLayer<Dtype>::ShuffleImages(){
    std::srand ( unsigned ( std::time(0) ) );
    std::random_shuffle(samples_.begin(),samples_.end());
}

template <typename Dtype>
void MyDataLayer<Dtype>::load_batch(Batch<Dtype>* batch){
    CPUTimer batch_timer;
    batch_timer.Start();
    double read_time = 0;
    double trans_time = 0;
    CPUTimer timer;
    CHECK(batch->data_.count());
    CHECK(this->transformed_data_.count());

    MyDataParameter my_data_param = this-> layer_param_.my_data_param();
    // Get batch size
    const int batch_size = my_data_param.batch_size();

    // Reshape according to the first image of each batch
    // on single input batches allows for inputs of varying dimension
    cv::Mat cv_img = samples_[lines_id_].first;
    CHECK(cv_img.data) << "Could not load "<<lines_id_<<" sample";
    // Use data_transformer to infer the expected blob shape from a cv_img
    vector<int> top_shape = this->data_transformer_->InferBlobShape(cv_img);
    this->transformed_data_.Reshape(top_shape);
    // Reshape batch according to the batch_size
    top_shape[0] = batch_size;
    batch->data_.Reshape(top_shape);

    Dtype* prefetch_data = batch->data_.mutable_cpu_data();
    Dtype* prefetch_label= batch->label_.mutable_cpu_data();

    // datum scales
    int samples_size = samples_.size();
    for(int item_id=0;item_id<batch_size;++item_id){
      // get a blob
      timer.Start();
      CHECK_GT(samples_size, lines_id_);
      cv::Mat sample = samples_[lines_id_].first;
      CHECK(sample.data) << "Could not load "<<lines_id_<<" sample";
      read_time += timer.MicroSeconds();
      timer.Start();
      // apply transformations to the image
      int offset = batch->data_.offset(item_id);
      this->transformed_data_.set_cpu_data(prefetch_data + offset);
      this->data_transformer_->Transform(sample,&(this->transformed_data_));
      trans_time += timer.MicroSeconds();

      prefetch_label[item_id] = samples_[lines_id_].second;
      // got the the next iter
      lines_id_++;
      if(lines_id_>=samples_size){
              // We have reached the end. restart from the first.
          DLOG(INFO) << "Restarting data prefetching from start.";
          lines_id_=0;
          if(my_data_param.shuffle()){
              ShuffleImages();
          }
      }
    }
    batch_timer.Stop();
    DLOG(INFO) << "Prefetch batch: " << batch_timer.MilliSeconds() << " ms.";
    DLOG(INFO) << "     Read time: " << read_time / 1000 << " ms.";
    DLOG(INFO) << "Transform time: " << trans_time / 1000 << " ms.";
}

INSTANTIATE_CLASS(MyDataLayer);
REGISTER_LAYER_CLASS(MyData);

} // namespaces caffe
#endif  // USE_OPENCV

2.3 在《caffe.porto》中注册数据层
备份文件《/opt/caffe-master/src/caffe/proto/caffe.porto》为《caffe_backup.proto》

在406行加入:
// 注册新的数据层,保险起见,设为150
optional MyDataParameter my_data_param = 150;

在《caffe.porto》中410行加入:
// 定义注册数据层的参数
// to infer image data
message MyDataParameter{
//数据源图片《digits.png》的地址
optional string image_address = 1;
// Start column for calculation
// 训练和测试用的起始列
optional int32 start_col = 2;
// 训练和测试用的结束列
optional int32 end_col = 3;
// Height of each image
optional int32 sample_height = 4 [default = 0];
// Width of each image
optional int32 sample_width = 5 [default = 0];
// Shuffle or not
optional bool shuffle = 6;
// Batch size for each computing
optional int32 batch_size = 7;
// Gray or color (equals to single channel or 3 channels)
optional bool is_color = 8 [default = false];
// Save the image seperated from the image address or not
optional bool is_save = 9 [default = false];
// If save, use this save folder
optional string save_folder = 10;
}

在1323行加入:
// My new data parameter
optional MyDataParameter my_data_param = 50;

2.4 测试
备份文件《/opt/caffe-master/examples/mnist/lenet_train_test.prototxt》为《lenet_train_test_backup.prototxt》

name: "LeNet"
layer {
  name: "mnist"
  type: "MyData"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  transform_param {
    scale: 0.00390625
  }
  my_data_param {
    image_address: "/opt/caffe-master/examples/mnist/Temp/digits.png"
    batch_size: 64
    start_col:0
    end_col:70
    shuffle:true
    sample_width:20
    sample_height:20
    is_save:false
    save_folder: "/opt/caffe-master/examples/mnist/Temp/img"
  }
}
layer {
  name: "mnist"
  type: "MyData"
  top: "data"
  top: "label"
  include {
    phase: TEST
  }
  transform_param {
    scale: 0.00390625
  }
  my_data_param {
    image_address: "/opt/caffe-master/examples/mnist/Temp/digits.png"
    batch_size:32
    start_col:70
    end_col:100
    sample_width:20
    sample_height:20
    shuffle:true
  }
}
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: 10
    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"
}

同样,备份《/opt/caffe-master/examples/mnist/lenet_solver.prototxt》为《lenet_solver_backup.prototxt》

# The train/test net protocol buffer definition
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: 50
# Carry out testing every 500 training iterations.
test_interval: 200
# 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: 200
# The maximum number of iterations
max_iter: 1000
# snapshot intermediate results
snapshot: 500
snapshot_prefix: "examples/mnist/lenet"
# solver mode: CPU or GPU
solver_mode: CPU

重新编译

$ cd /opt/caffe-master

$ cd build

/opt/caffemaster/build sudo rm -rf *

$ cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/usr/local \
-D INSTALL_C_EXAMPLES=ON \
-D INSTALL_PYTHON_EXAMPLES=ON \
-D WITH_TBB=ON \
-D WITH_V4L=ON \
-D WITH_QT=OFF \
-D WITH_OPENGL=ON \
-D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules \
-D CPU_ONLY=ON \
-D BUILD_EXAMPLES=ON ..

/opt/caffe-master/build$ sudo make all

新建文件mydata.sh

#!/usr/bin/env sh  
set -e  
./build/tools/caffe train --solver=examples/mnist/lenet_solver.prototxt $@

运行测试文件mydata.sh
/opt/caffe-master$ ./examples/mnist/mydata.sh

运行结果

参考资料:
caffe总结之自定义数据输入层
http://blog.csdn.net/u013289254/article/details/70432304

Caffe添加新层
http://blog.csdn.net/shuzfan/article/details/51322976

Caffe自定义数据读取
http://www.meltycriss.com/2016/07/04/caffe_1_layer/

Caffe数据层参数
http://www.cnblogs.com/denny402/p/5070928.html

Caffe实现多标签输入
http://www.tk4479.net/AMDS123/article/details/62423987

梳理caffe代码image_data_layer、data_layer、window_data_layer(七)
http://blog.csdn.net/langb2014/article/details/50998135

其他相关网站
http://www.miaoerduo.com/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值