2018年7月22日日曜日

画像を用意してTensorFlowで学習して、判別装置を作る方法


背景

TensorFlowとは、Googleが公開している機械学習プログラムです。
本家のサイトに参考になるページがあったので、それを参考にしながら、自分の用意した画像に対して学習する方法を共有します。

使ったもの

Linuxの動くPC

下記の環境で動くことを確認しました。
  • ノートPC(CPU: Intel Corei7-4600U、メモリ8GB)+ Ubuntu17.10
  • Raspberry Pi3B+ + Raspbian Stretch
自分の試した環境だと、Raspberry PiはノートPCの6倍くらいの実行時間がかかるようでした。
Raspberry Piを利用する場合、学習はノートPCで行い、結果のデータをRaspberry Piに移動して利用するなどをすると、学習時間を減らせると思います。

判別したい対象の画像

1種類に対して20以上の画像を用意します。(20未満だと学習時にエラーになることがあります。)
判別したい種別名をディレクトリ名として、それぞれのディレクトリ構造に画像を置いてください。

今回の記事で利用した画像データはこちらです。

asukiaaa/halake-chair-images

TensorFlowをインストール

参考にするページでPythonというプログラミング言語を通してTensorFlowの実行環境を構築していたので、それに倣って環境を構築します。
新しいバージョンのPythonが良いだろうと思うので、この記事ではPython3を利用します。

Python3とPython3のパッケージマネージャーであるpipをインストールします。
sudo apt install python3-pip

Python3のpipでTensorFlowをインストールします。
sudo pip3 install tensorflow tensorflow-hub

自分がRasbpianで試した時はlibatlasに関するエラーが出ましたが、下記のコマンドでlibatlasをインストールすると解決しました。
sudo apt install libatlas3-base

サンプルを実行

背景でも紹介した下記のページを参考に、テストデータに対して学習プログラムを実行します。

How to Retrain an Image Classifier for New Categories

作業用ディレクトリを作成

今回の記事のプログラムを動かすためのディレクトリを作って、その中に移動します。
mkdir ~/tensor-train
cd tensor-train

学習

学習対象データをダウンロードします。
curl -LO http://download.tensorflow.org/example_images/flower_photos.tgz
tar xzf flower_photos.tgz

このサンプルでは1種類に対して600個以上の画像が用意された5種類の花を学習するようです。(daisy: ヒナギク、dandelion: タンポポ、rose: バラ、sunflower: ひまわり、tulip: チューリップ)


学習を行うプログラムをダウンロードします。
wget https://raw.githubusercontent.com/tensorflow/hub/52d5066e925d345fbd54ddf98b7cadf027b69d99/examples/image_retraining/retrain.py

学習を行います。
ノートPCだと30分くらい、Raspberry Piだと3時間くらいかかりました。
python3 retrain.py --image_dir flower_photos

上記のプログラムを実行すると、90MBくらいのグラフデータ(学習結果)が/tmpディレクトリに出力されます。
下記のコマンドでサイズを確認できます。(# で始まる行はコメントなので、入力しなくてよいです。)
ls -la /tmp/output_graph.pb
# -rw-r--r-- 1 asuki asuki 87523054  7月 21 14:26 /tmp/output_graph.pb
# 87MB

学習結果を利用して判別

出力されたグラフデータ(/tmp/output_graph.pb)とラベルデータ(/tmp/output_labels.txt)を利用して、画像に写っている物体を判別します。

判別プログラムをダウンロードします。
wget https://raw.githubusercontent.com/tensorflow/tensorflow/master/tensorflow/examples/label_image/label_image.py

下記のようなコマンドで学習結果を利用して判別を行なえます。
python3 label_image.py \
  --graph=/tmp/output_graph.pb --labels=/tmp/output_labels.txt \
  --input_layer=Placeholder \
  --output_layer=final_result \
  --image=flower_photos/daisy/21652746_cc379e0eea_m.jpg

99%の確率でヒナギクだという結果が出力されました。
daisy 0.99792874
sunflowers 0.0012147657
dandelion 0.00043442898
tulips 0.00033576202
roses 8.6313376e-05

学習結果を保存

/tmpディレクトリに置いたままだとOSによって消されることがあるので、学習結果(フラフとラベルのデータ)を保存したい場合は、出力されたファイルを移動させます。
mv /tmp/output_graph.pb ./
mv /tmp/output_labels.txt ./

移動させたファイルを利用して判別を行う場合は、コマンドに渡すグラフとラベルのパスを変えてプログラムを実行します。
python3 label_image.py \
  --graph=output_graph.pb --labels=output_labels.txt \
  --input_layer=Placeholder \
  --output_layer=final_result \
  --image=flower_photos/daisy/21652746_cc379e0eea_m.jpg

mobilenetの学習結果を出力

mobilenetとは、機械学習の結果を携帯端末で利用するのを目的として作られた、比較的軽量なデータ形式です。
「90MB近くの学習結果を扱うのは難しい」という場合は、下記のコマンドでmobilenetの形式でグラフデータを出力できます。
python3 retrain.py \
  --image_dir flower_photos \
  --tfhub_module https://tfhub.dev/google/imagenet/mobilenet_v2_100_224/feature_vector/1

上記のコマンドで出力したグラフデータは9MBくらいでした。
ls -la /tmp/output_graph.pb
# -rw-r--r-- 1 asuki asuki 9204105  7月 21 15:17 /tmp/output_graph.pb
# 9.2MB

mobilenetの学習結果で判別

下記のコマンドでmobilenetのグラフデータを用いた判別を行なえます。
ヒナギクの画像を--imageに渡しています。
python3 label_image.py \
  --graph=/tmp/output_graph.pb --labels=/tmp/output_labels.txt \
  --input_layer=Placeholder \
  --output_layer=final_result \
  --input_height=224 --input_width=224 \
  --image=`pwd`/flower_photos/daisy/21652746_cc379e0eea_m.jpg

99%の確率でヒナギクだという結果が出力されました。
daisy 0.99240065
sunflowers 0.0067252107
dandelion 0.0007642565
tulips 6.780581e-05
roses 4.2129705e-05

mobilenetの学習結果を保存

90MBのグラフデータと同様に、学習結果を/tmpに置いたままだとOSによって消されてしまうことがあるので、結果を保存したい場合はファイルを移動させます。
下記のコマンドでは、90MBのファイルと名前が被るのを避けるために、「mobilenet」を含むファイル名に変更しています。
mv /tmp/output_graph.pb ./mobilenet_graph.pb
mv /tmp/output_labels.txt ./mobilenet_labels.txt

グラフとラベルのファイルパスを変えれば、保存したファイルを利用して画像の判別を行なえます。
python3 label_image.py \
  --graph=mobilenet_graph.pb --labels=mobilenet_labels.txt \
  --input_layer=Placeholder \
  --output_layer=final_result \
  --input_height=224 --input_width=224 \
  --image=flower_photos/daisy/21652746_cc379e0eea_m.jpg

用意したデータに対する学習と判別

画像を用意

「flower_photos」のディレクトリ構造を参考に、ラベル名をディレクトリ名として、画像ファイルを配置します。
コワーキングスペース HaLakeに置いてある椅子を分類するためのデータをこのように用意してみました。

asukiaaa/halake-chair-images

halake-chair-images
  |- train
  |  |- balance
  |  |  |- バランスボールが載った椅子の画像 20個以上
  |  |- round
  |  |  |- 丸っこい椅子の画像 20個以上
  |  |- sofa
  |     |- ソファーの画像 20個以上
  |- test
     |- balance
     |  |- バランスボールが載った椅子の画像
     |- round
     |  |- 丸っこい椅子の画像
     |- sofa
        |- ソファーの画像

一つの種類の画像を10個くらいで学習しようとしたところ、下記のような警告が出た後にエラーが出て終了しました。
そのため、一つの種類に対して最低20個は画像データを用意しておくのが良さそうです。
INFO:tensorflow:Looking for images in 'balance'
WARNING:tensorflow:WARNING: Folder has less than 20 images, which may cause issues.
INFO:tensorflow:Looking for images in 'round'
WARNING:tensorflow:WARNING: Folder has less than 20 images, which may cause issues.
INFO:tensorflow:Looking for images in 'sofa'
WARNING:tensorflow:WARNING: Folder has less than 20 images, which may cause issues.

このデータを利用して学習と判別を試してみたい場合は、下記のようなコマンドでデータをダウンロードできます。
sudo apt install git
git clone https://github.com/asukiaaa/halake-chair-images.git

TensorFlow標準の形式で学習・判別

halake-chair-images/testを入力として、学習します。
python3 retrain.py --image_dir halake-chair-images/train

結果が消えるのを防ぐために、学習結果を/tmpディレクトリから移動させます。
mv /tmp/output_graph.pb ./halake-chair-graph.pb
mv /tmp/output_labels.txt ./halake-chair-labels.txt

作成されたデータは、花の判別のときと同じく、90MBくらいでした。
学習に利用する画像の量に関係なく、出力される結果のデータサイズは同じようです。
ls -la halake-chair-graph.pb
# -rw-r--r-- 1 asuki asuki 87506662  7月 22 13:44 halake-chair-graph.pb
# 87MB

下記のようなコマンドで判別できます。
このコマンドは、テスト用のバランスボールが載った椅子の画像を「--image」として渡して判別しようとしています。
python3 label_image.py \
  --graph=halake-chair-graph.pb --labels=halake-chair-labels.txt \
  --input_layer=Placeholder \
  --output_layer=final_result \
  --image=halake-chair-images/test/balance/IMG_20180722_155644.jpg

上記のコマンドを実行すると、99%の確率でバランスボールの載った椅子であるという下記のような結果が得られました。
balance 0.9997631
sofa 0.00016037884
round 7.6473036e-05

mobilenet形式の出力で学習・判別

halake-chari-imagesを入力として、mobilenet形式の結果を出力する学習を行います。
python3 retrain.py \
  --image_dir halake-chair-images/train \
  --tfhub_module https://tfhub.dev/google/imagenet/mobilenet_v2_100_224/feature_vector/1

結果が消えるのを防ぐために、学習結果を/tmpディレクトリから移動させます。
mv /tmp/output_graph.pb ./halake-chair-mobilenet-graph.pb
mv /tmp/output_labels.txt ./halake-chair-mobilenet-labels.txt

tenstoflow形式のときと同じように、学習データの大きさが違っても同じくらいの大きさのデータが出力されるようです。
ls -la halake-chair-mobilenet-graph.pb
# -rw-r--r-- 1 asuki asuki 9193852  7月 22 13:51 halake-chair-mobilenet-graph.pb
# 9.2MB

下記のようなコマンドで判別できます。
TensorFlow標準の形式のときと同じように、バランスボールの載った椅子の判別をしようとしています。
python3 label_image.py \
  --graph=halake-chair-mobilenet-graph.pb --labels=halake-chair-mobilenet-labels.txt \
  --input_layer=Placeholder \
  --output_layer=final_result \
  --input_height=224 --input_width=224 \
  --image=halake-chair-images/test/balance/IMG_20180722_155644.jpg

上記のコマンドを実行すると、99%の確率でバランスボールの載った椅子であるという下記のような結果が得られました。
balance 0.9996779
sofa 0.0002461057
round 7.606004e-05

まとめ

自分で用意した画像をTensorFlowで学習して、判別装置を作れました。

何かの参考になれば嬉しいです。

0 件のコメント :