Python
DeepLearning
Caffe

caffeのprototxtを1つにしたった

caffeでやっかいなのはネットワークを記述するprototxtが学習用とテスト用に分かれていること。
これはけっこう厄介なので、1つのファイルに統一するまでの流れを書いていきます。

概要

要はtrain.ptとtest.ptの2つに分かれていて、これを1つのprototxtにしたいわけです。
要は include{ phase: TRAIN }include { phase: TEST } を追加して学習とテストで使うlayerをコントロールすればいいだけの話です。

include { phase: TRAIN } は学習時だけ使う、
include { phase: TEST } はテスト時だけ使う、という意味です。

caffeでsolverを使う時、
net = caffe.Net('@@@.prototxt', '@@@.caffemodel', caffe.TEST)
でprototxtを呼び出すわけですが、第3引数が
caffe.TRAIN だと「学習用」と「テスト用」の2つが呼ばれ、
caffe.TEST だと「テスト用」だけが呼ばれるわけです。

なので、include{phase}で使うlayerを制御しなければなりません。

問題

test.ptの入力層は最近はこういう書き方がほとんどです。

input: "data"
input_dim: 1 input_dim: 3 input_dim: 224 input_dim: 224

ここで厄介なのが、これだと include { phase: TEST } が使えない、つまり、テスト時だけにすることができないのです。
そこで、昔使われていたinput_layerを使ってコントロールしていきます。input_layerだとこんな書き方ができます。

layer {
  name: "data" type: "Input"  top: "data"
  input_param { shape: { dim: 1 dim: 3 dim: 227 dim: 227 } }
  include { phase: TEST }
}

input_layerの導入

最近の新しいcaffeだとこれが消されていることがほとんどです。
そこで、昔のcaffeからinput_layerのコードを引っ張ってきます。

input_layer.cpp

これは $CAFFE-HOME/src/caffe/layers/input_layer.cpp にあります。これを自分が使っているcaffeの同じ場所においてください。

input_layer.hpp

これは $CAFFE-HOME/include/caffe/layers/input_layer.hpp にあります。
これも自分が使っているcaffeの同じ場所においてください。

caffe.proto

次にlayerで使うパラメータの定義をします。
$CAFFE-HOME/src/caffe/proto/caffe.proto のファイルに追加します。

この中で、この部分を探します。

message LayerParameter {
  optional string name = 1; // the layer name
  optional string type = 2; // the layer type

これはパラメータを定義する部分です。この中の一番最後に次のコードを追加します。
144という数値はパラメータのIDのようなものです。他のものと被らないようにして下さい。(crop_param = 143は私が追加したものなので、気にしないで下さい。)

optional WindowDataParameter window_data_param = 129;
  optional CropParameter crop_param = 143;
  optional InputParameter input_param = 144;
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}

次にこれを追加します。

message InputParameter {
  // This layer produces N >= 1 top blob(s) to be assigned manually.
  // Define N shapes to set a shape for each top.
  // Define 1 shape to set the same shape for every top.
  // Define no shape to defer to reshaping manually.
  repeated BlobShape shape = 1;
}

make

あとはmakeし直して下さい。これで完了です。

prototxtの書き方

ここからprototxtを変えていきます。最初の方はこんな感じになります。
学習用の入力データとテスト用の入力データのlayerをそれぞれ定義することで、コントロールできます。(学習用の入力layerは私が独自で開発したlayerなので気にしないて下さい。)

#-----------  train data layer  --------------
layer {
  name: "data" type: "Python"
  top: "data"  top: "gt_prob"  top: "gt_posi"
  python_param {
    module: "layers.inputDataQuad"
    layer: "InputDataLayer"
    param_str: "{'img_list': '/home/nagayosi/Dataset/Incidental_Scene_Text/ImageSets/train_img.txt', 'dataset_path': '/home/nagayosi/Dataset/Incidental_Scene_Text/Images/', 'gt_path': '/home/nagayosi/Dataset/Incidental_Scene_Text/Annotations/', 'max_size': 1000 }"
  }
  include { phase: TRAIN }
}

#----------  test data layer  ------------
layer {
  name: "data" type: "Input"  top: "data"
  input_param { shape: { dim: 1 dim: 3 dim: 227 dim: 227 } }
  include { phase: TEST }
}

#--------------------- Conv1 -------------------
layer {
  name: "conv1_1" type: "Convolution"
  bottom: "data" top: "conv1_1"
  param { lr_mult: 1}  param { lr_mult: 2}
  convolution_param { num_output: 64 pad: 1 kernel_size: 3
    weight_filler { type: "xavier" }
    bias_filler {type: "constant" }
  }
}

出力部分に関しても次の通りに書くことで、SigmoidかSigmoidCrossEntropyLossかをコントロールできます。

layer {
  name: "out_prob" type: "Convolution" bottom: "deconv3" top: "out_prob"
  param { lr_mult: 1}#  param { lr_mult: 2}
  convolution_param { num_output: 1  pad: 0  kernel_size: 1
    weight_filler { type: "gaussian" std: 0.01 }
    bias_filler { type: "constant" value: 0 }
  }
}

#-----------  Output  -------------------
layer {
  name: "prob_out" type: "Sigmoid" bottom: "out_prob" top: "out_prob"
  include { phase: TEST }
}

#------------  Loss  -------------------------------------
layer {
  name: "loss" type: "SigmoidCrossEntropyLoss"
  bottom: "out_prob" bottom: "gt_prob_stride16" top: "loss_prob"
  propagate_down: 1 propagate_down: 0 loss_weight: 1
  loss_param { normalization: FULL }
  include { phase: TRAIN }
}

以上です。こんな感じでprototxtを統一できます。