はじめに
一般的にCNN(Convolutional Neural Network、畳み込みニューラルネットワーク)は主に画像等の2次元配列データを入力とするが、これを3次元配列データにしたものが3D-CNN。
Caffeでは3D-CNNも使うことができるが、学習の方法についての資料が少なかったので、ダミーデータを使った学習方法をメモ。応用すれば一般のND-CNNにも適用できるとは思う。
環境
- OS:SUSE Linux Enterprise Server 11 SP3
- Python:2.7.10(Anaconda 2.3.0)
- Caffe:0.13.0(本家)
ダミーデータ
今回は横2×縦3×奥行4の3次元配列データを3D-CNNの入力とし、チャネル数が5の4次元配列のデータを使う。
データの個数は学習用に6個、テスト用に6個、計12個とする。
今回は2クラス分類問題とする。どのデータも、全ての要素が1であるか、全ての要素が0であるかのどちらかとなるようにし、全ての要素が1であるデータは正例(ラベル1)、全ての要素が0であるデータは負例(ラベル0)、というごく単純なデータセットを構築する。
このページによれば、3D-CNNを使う際はデータセットの形式としてLEVELDBやLMDBは使えず、HDF5を用いる必要があるらしい(?)。HDF5形式の学習用データセットファイルの、"data"データセットにCNNへの入力を、"label"データセットにラベルを記録することにする。テスト用データセットファイルに対しても同様に記録を行うことにする。
以上のようなデータは以下のスクリプトで作れる。このスクリプトをmake_hdf5.py
とする。
# -*- coding: utf-8 -*-
import h5py
import numpy as np
import random
import sys
def save_hdf5(filename, inputs, labels):
with h5py.File(filename, 'w') as f:
f['data'] = inputs
f['label'] = labels
def main():
train_file = sys.argv[1]
test_file = sys.argv[2]
channels = 5
width = 2
height = 3
depth = 4
n_train = 6
n_test = 6
labels0 = np.zeros((n_train+n_test)/2)
labels1 = np.zeros((n_train+n_test)/2) + 1
labels = np.r_[labels0, labels1]
neg = np.zeros(channels * width * height * depth) + 1
neg = neg.reshape([channels, width, height, depth])
pos = neg + 1
negs = []
poss = []
for i in range((n_train+n_test)/2):
negs.append(neg)
poss.append(pos)
negs = np.array(negs,dtype=np.float32)
poss = np.array(poss,dtype=np.float32)
inputs = np.r_[negs, poss]
perm = np.random.permutation(len(inputs))
train_inputs = inputs[perm[:n_train]]
train_labels = labels[perm[:n_train]]
save_hdf5(train_file, train_inputs, train_labels)
test_inputs = inputs[perm[n_train:]]
test_labels = labels[perm[n_train:]]
save_hdf5(test_file, test_inputs, test_labels)
if __name__ == "__main__":
main()
make_hdf5.py
の使い方は、まずh5py
、numpy
パッケージを以下のようにインストールしてから、
$ pip install h5py
$ pip install numpy
make_hdf5.py
を実行する。学習用データセットファイルをfoo_train.h5
、テスト用データセットファイルをfoo_test.h5
とすると、実行すべきコマンドは以下のようになる。
$ make_hdf5.py foo_train.h5 foo_test.h5
実行するとfoo_train.h5
とfoo_test.h5
が生成される。
学習の準備
foo_train_list.txt
、foo_test_list.txt
、foo_solver.prototxt
、foo_train_test.prototxt
という4つのファイルを作成する必要がある。
それぞれ、学習用データセットファイルへのパスのリスト、テスト用データセットファイルへのパスのリスト、Solverのパラメータの定義、学習・テストに用いるネットワークの定義を記述する。
foo_train_list.txt
、foo_test_list.txt
の作成
foo_train_list.txt
にはfoo_train.h5
へのパスを、foo_test_list.txt
にはfoo_test.h5
へのパスを書いておく。
foo_solver.prototxt
の作成
foo_solver.prototxt
を以下のように記述する。
このページに従って、「net:
」の部分に学習用ネットのprototxtファイル名を記述する。また、「snapshot prefix:
」の部分には、スナップショットファイル(後述) のプレフィックスを指定する。
# reduce the learning rate after 8 epochs (4000 iters) by a factor of 10
# The train/test net protocol buffer definition
net: "foo_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: 100
# Carry out testing every 500 training iterations.
test_interval: 500
# The base learning rate, momentum and the weight decay of the network.
base_lr: 0.001
momentum: 0.9
weight_decay: 0.004
# The learning rate policy
lr_policy: "fixed"
# Display every 100 iterations
display: 100
# The maximum number of iterations
max_iter: 4000
# snapshot intermediate results
snapshot: 4000
snapshot_prefix: "foo"
# solver mode: CPU or GPU
solver_mode: GPU
#solver_mode: CPU
foo_train_test.prototxt
の作成
foo_train_test.prototxt
に以下のように記述する。
基本的なフォーマットの説明はCaffe公式のLayer Catalogueに譲るが、3D-CNNを用いる場合、40行目や66行目のように畳み込み層のパラメータ(「convolution_param
」)の記述に「engine:CAFFE
」の記述が必要。また、畳み込み層のカーネル(フィルタ)の大きさの指定には42-44行目、68-70行目のように「kernel_size
」を3回書くことで指定できる。
また3D-CNNに限らないが、ソフトマックス層への入力となる層の出力フィルタ数(「num_output
」)は判定するクラス数と同じにする必要があることに注意する。今回の場合は2クラス判定問題のため、114行目のようにソフトマックス層への入力となるip2
層の出力フィルタ数を2とする。
name: "foo"
layer {
name: "foo"
type: "HDF5Data"
top: "data"
top: "label"
include {
phase: TRAIN
}
hdf5_data_param {
source: "foo_train_list.txt"
batch_size: 2
}
}
layer {
name: "foo"
type: "HDF5Data"
top: "data"
top: "label"
include {
phase: TEST
}
hdf5_data_param {
source: "foo_test_list.txt"
batch_size: 2
}
}
layer {
name: "conv1"
type: "Convolution"
bottom: "data"
top: "conv1"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
convolution_param {
engine: CAFFE
num_output: 48
kernel_size: 1
kernel_size: 1
kernel_size: 1
stride: 2
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "conv2"
type: "Convolution"
bottom: "conv1"
top: "conv2"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
convolution_param {
engine: CAFFE
num_output: 160
kernel_size: 1
kernel_size: 1
kernel_size: 1
stride: 2
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "ip1"
type: "InnerProduct"
bottom: "conv2"
top: "ip1"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 64
weight_filler {
type: "gaussian"
std: 0.1
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "ip2"
type: "InnerProduct"
bottom: "ip1"
top: "ip2"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 2
weight_filler {
type: "gaussian"
std: 0.1
}
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"
}
学習
以下のコマンドを入力して学習を行う。
$ caffe train --solver foo_solver.prototxt
学習が正常に終了するとfoo_iter_4000.caffemodel
、foo_iter_4000.solverstate
という名前の2つのファイルが生成される。
テスト
以下のコマンドを入力してテストを行う。
$ caffe test -weights=foo_iter_4000.caffemodel -model=foo_train_test.prototxt