Edited at

Caffeで回帰をしてみる

More than 1 year has passed since last update.


この記事で述べること

画像認識向け深層学習ライブラリCaffeで回帰をしてみます.

Caffeには何故か回帰のチュートリアルが載っていませんが,大して難しくないです.

将来的には肌を映した写真で年齢予測したりできそうですが,exampleは追い追い.


prototxtを変更

分類で使っているtrain_val.prototxtやdeploy.prototxtを変更します.


train_val.prototxt

InnerProductの最終レイヤのnum_outputを1にします

SoftmaxWithLossをEuclideanLossに変更します

Accuracyも出力しない.

また,DataLayerでは

  transform_param {

scale: 0.00390625
mean_file: "train_regression_mean.binaryproto"
}

とscaleとmean_file(正規化)が設定されていることを確認


layer {
name: "prob"
type: "InnerProduct"
bottom: "conv4"
top: "prob"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 1
weight_filler {
type: "gaussian"
std: 0.1
}
bias_filler {
type: "constant"
}
}
}
# layer {
# name: "accuracy"
# type: "EuclideanLoss"
# bottom: "prob"
# bottom: "label"
# top: "accuracy"
# include {
# phase: TEST
# }
# }
layer {
name: "loss"
type: "EuclideanLoss"
bottom: "prob"
bottom: "label"
top: "loss"
}
# layer {
# name: "loss"
# type: "SoftmaxWithLoss"
# bottom: "ip1"
# bottom: "label"
# top: "loss"
# }


deploy.prototxt

train_val.prototxtをコピーしてdeploy.prototxtに改名

DataLayerの箇所を消して,以下に置き換える.

input: "data"

input_shape { # ここは各々で設定
dim: 256
dim: 3
dim: 200
dim: 250
}

InnerProductの最終レイヤのnum_output:1のやつより下=EuclideanLossやAccuracy を出力しない


学習する

いつもと同じ感じで学習します.学習済モデルをここではregression.caffemodelとします.


予測する

2018/1/8追記 pythonベースの回帰deployは出力値が何故か上手くいかない場合があります.

trainコマンドベースのこちらを使ってください.

画像分類のpythonコードを参考にしました.


regression.py

# coding:'utf-8'

import numpy as np
import caffe

# deploy.prototxtのinput_shapeと同じ
num = 256
channel = 3
height = 250
width = 200

caffe.set_mode_cpu()
# caffe.set_mode_gpu()
#caffe.set_device(0)

model_def = 'deploy.prototxt'
model_weights = 'regression.caffemodel'
net = caffe.Net(model_def, model_weights, caffe.TEST)

mu = np.load('train_regression_mean.npy') # 平均画像の読込
mu = mu.mean(1).mean(1) * 0.00390625 # 1/256スケールに変換
print 'mean-subtracted values:', zip('BGR', mu)

transformer = caffe.io.Transformer({'data': net.blobs['data'].data
.shape})
transformer.set_mean('data', mu)
# transformer.set_raw_scale('data', 255) # 1/256スケール(0~1)の間のままでよいので,0~255への変換はしない
transformer.set_transpose('data', (2, 0, 1))
transformer.set_channel_swap('data', (2, 1, 0))

net.blobs['data'].reshape(num, channel, width, height) # deploy.prototxtのinput_shapeと同じにする.

# 予測値の知りたい256個のデータ
for n in range(0, num):
image = caffe.io.load_image('valid_data/' + str(n) + '.jpg')
transformed_image = transformer.preprocess('data', image)
net.blobs['data'].data[n] = transformed_image

# 256個と違う数にしたいなら,input_shapeのnumをdeploy.prototxtに記載のものと一緒に変更.
# 1枚だけ結果が知りたいなら,num=1とかにする.当然ながら速度は256倍.断然速い

# 或いは,以下のように入力.低速ですが
# image = caffe.io.load_image('valid_data/' + str(n) + '.jpg')
# transformed_image = transformer.preprocess('data', image)
# net.blobs['data'].data[...] = transformed_image

# net forwardingする
output = net.forward()
output_prob = output['prob'] # []内はdeploy.prototxtのInnerProduct最終レイヤの名前と同じ
for n in range(0, num):
print 'valid_data/' + str(n) + '.jpg predicted value is ' + output_prob[n][0]

# 面倒ならこれでもOK
# print output


元ページではset_raw_scaleで0~1→0~255に変換していますが,これはmatplotlibで表示するために整数値にしているだけです.

通常,train_test.prototxtのDataLayer transform_param scaleで画素0~255を0~1に変換しているため,こっちのままにしないと結果がおかしくなります.

平均画像train_regression_mean.npyは,以下のスクリプトによって

./build/tools/compute_image_mean で作成していた.binaryproto ファイルから変換できます.

【Caffe】meanファイルをbinaryproto形式からnpy形式に変換する

python convert_protomean.py **.binaryproto ***.npy


convert_protomean.py

import caffe

import numpy as np
import sys

if len(sys.argv) != 3:
print "Usage: python convert_protomean.py proto.mean out.npy"
sys.exit()

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


後出しとなってしまいますが,input_shapeの入力データ数(regression.pyのnum)は,

予測値の知りたいデータの数に合わせるとよいです.

net.blobs['data'].data[...] = transformed_imageでもよいですが,同じデータに対して確保した入力データ数分の回数予測処理を繰り返すだけとなり,低速です.