前回TF-Slim画像識別ライブラリを使って、転移学習を試してみました。その際使用したデータセットは、用意されていた花画像データセットを使用しましたが、自前で用意した画像も使える様にしたかったので、今回はTF-Slim画像識別ライブラリで使用できる形式のデータセットを作成するスクリプトを作ってみました。
環境
・TensorFlow r0.12
・TensorFlow-Slim image classification library
・自前で用意した画像は、imagesフォルダに下記の様に保存されているとします
images
+- tulip00.jpg
+- Lexus03.jpg
+- …
+- list.csv #ラベル一覧
ラベルデータを含んだCSVファイルは、[ファイル名,ラベル]というフォーマットで保存しておきます。
flower00.jpg, flower
Lexus03.jpg, car
…
データセットの作り方
元々TF-Slim画像識別ライブラリには、MNISTやCIFAR10といったデータセットを変換するプログラムが用意されているので、それらを参考にすれば作れそうです。
有名データセット変換プログラム
一通りソースコードを眺めてみると、どうやら下記データ項目を持ったTFRecordを作成すれば良いことがわかります。
datasets/dataset_utils.py
1 | def image_to_tfexample(image_data, image_format, height, width, class_id): |
2 | return tf.train.Example(features = tf.train.Features(feature = { |
3 | 'image/encoded' : bytes_feature(image_data), |
4 | 'image/format' : bytes_feature(image_format), |
5 | 'image/class/label' : int64_feature(class_id), |
6 | 'image/height' : int64_feature(height), |
7 | 'image/width' : int64_feature(width), |
ということで、画像ファイルパスから画像データを読み込み、TFRecordファイルに変換するプログラムの下記の様に作成してみました。
filepath_listsは、shape=[出力ファイル数, (1ファイル当たりの)画像数]の2次元配列になっています。label_listsも同じです。
1 | def write_tfrecord(split_name, filepath_lists, label_lists): |
2 | jpeg_path = tf.placeholder(dtype = tf.string) |
3 | jpeg_data = tf.read_file(jpeg_path) |
4 | decode_jpeg = tf.image.decode_jpeg(jpeg_data, channels = 3 ) |
6 | with tf.Session() as sess: |
7 | for i, filepath_list in enumerate (filepath_lists): |
8 | output_filename = '%s_%s_%05d-of-%05d.tfrecord' % (FLAGS.dataset_name, split_name, i, len (filepath_lists)) |
9 | with tf.python_io.TFRecordWriter(output_filename) as writer: |
10 | for j,filepath in enumerate (filepath_list): |
11 | sys.stdout.write( '\r>> Converting image %d/%d' % (j + 1 , len (filepath_list))) |
13 | image_data, image = sess.run([jpeg_data, decode_jpeg], feed_dict = {jpeg_path:filepath}) |
14 | example = image_to_tfexample(image_data, 'jpg' , image.shape[ 0 ], image.shape[ 1 ], label_lists[i][j]) |
15 | writer.write(example.SerializeToString()) |
16 | print ( ' Finished: %s' % (output_filename)) |
※なんか分かりづらいコードになってしまいました…
TFRecordファイルの作成は、tf.python_io.TFRecordWriterクラスを使うとできます。
ソースコード全体は下記に置いてあります。
https://github.com/workpiles/convert_TFRecord
作ったデータセットの読み込み
train_image_classifier.pyやeval_image_classifier.pyで作成したデータセットを読み込むためには、下記の簡単な変更を加えればできます。
datasets/dataset_factory.pyの変更
2 | from datasets import ccb |
ccb.pyの作成
あとは、追加したデータセット用のget_splitを実装すれば完了です。
これは、flowers.pyやmnist.pyなど、他のデータセットを元に作成すれば問題ないと思います。
きゅうり画像で転移学習
早速、きゅうり画像でVGG16で転移学習を試してみました。fc8,fc7を学習対象としています。
(fc6を含めるとHDDの空きがなくなってしまった…)
学習に使用したのは解像度80×80、3チャンネルの画像を6500枚です。
後のパラメータは適当に。
python train_image_classifier.py \
--train_dir=data \
--dataset_name=ccb \
--dataset_split_name=train \
--dataset_dir=/tmp/ccb\
--model_name=vgg_16 \
--checkpoint_path=/tmp/ckpt/vgg_16.ckpt \
--checkpoint_exclude_scopes=vgg_16/fc8,vgg_16/fc7 \
--trainable_scopes=vgg_16/fc8,vgg_16/fc7 \
--max_number_of_steps=1000 \
--batch_size=32 \
--learning_rate=0.01 \
--learning_rate_decay_type=fixed \
--save_interval_secs=300 \
--save_summaries_secs=300 \
--log_every_n_steps=100 \
--optimizer=rmsprop \
--weight_decay=0.00004

損失はsoftmax_cross_entropyで、200ステップ当たりで収束している感じです。
結果

テスト画像2500枚に対して、10クラスに識別した場合の結果は、下記の通りでした。
正答率:19.3%
予測上位5クラスに正解が含まれる確率:62.7%
うーん。いまいち。
あとがき
なんか転移学習のやり方が間違ってる気がしてしょうがない…。
Thanks for the great post! I just started working on TF-slim, but I’m in trouble with custom image data. I put my data like below, but it keeps making errors like “Invalid JPEG data” when I run download_and_convert_data.py. (Of course, I modified this file for my custom images)
starbucks
– star1
– 1.jpg
– 2.jpg
…
– star2
– 1.jpg
– 2.jpg
…
– star3
– 1.jpg
– 2.jpg
…
Can you see if I am doing this wrong?
Do I have to put list.csv file?
Can you please let me know your custom image data directory structure?
Please try my convert script.
< https://github.com/workpiles/convert_TFRecord>