keras(tensorflow)で花の画像から名前を特定


Convolutional Neural Network (CNN)畳み込みニューラルネットで、
花の画像から名前を特定する実験をしてみる！

# はじめに
今回はdocker環境上にプログラムをおいて実施します。
　１．環境準備
　２．プログラム
　３．実験
　
プログラムは[Git](https://github.com/tsunaki00/flower/tree/master/python)に置いておきます。

# 環境準備

## 開発環境
 Macbook pro 13 2016

## dockerのインストール

dockerは以下のサイトからバイナリで簡単にインストールできます。

https://docs.docker.com/docker-for-mac/

## Minicondaイメージから機械学習環境を構築

機械学習系のパッケージを詰め込んだ、コンパイル済みバイナリをインストールできる
conda(Miniconda)での環境構築をします。

### ① テンプレートのダウンロード
```bash
$ docker pull continuumio/miniconda3
```

### ② Dockerfileの作成
```bash
$ vi Dockerfile
　　:
$ cat Dockerfile
FROM continuumio/miniconda3
  RUN apt-get update
  RUN pip install --upgrade pip
  RUN pip install tensorflow \
                  pandas \
                  scipy \
                  Pillow \
                  keras
  RUN pip install -U scikit-learn
  RUN mkdir /home/src/
```

### ③ dockerのbuild(イメージの作成)

```bash
$ docker build -t conda:init .
```

### ④ 確認

```bash
$ docker images
REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
conda                    init                daf923e29da8        2 hours ago         1.55 GB

```

### ⑤ dockerの起動
今回はdockerに開発するフォルダをマウントします。

```bash
# [mount dir]を書き換えて使ってください。
$ docker run -i -t -v [mount dir]:/home/src conda:init /bin/bash

```

「Cntl + p」、「Cntl + q」で抜けれます。

 再度、コンテナにアクセスする場合
 
```bash
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
745860b72c67        conda:init         "/usr/bin/tini -- ..."   11 hours ago        Up 11 hours                             friendly_galileo

# CONTAINER ID を指定
$ docker attach 745860b72c67
```

これで環境準備は完了

## ここから花画像分類のプログラム

### ① トレーニングデータの準備

インターネットから適当に花の画像を集めます!
以下のキャプチャの様にディレクトリ名に花の名前をつけ、その中に花の画像を配置。

![キャプチャ](https://qiita-image-store.s3.amazonaws.com/0/52867/1d00fd43-9d54-ef0d-6f94-3eb1a30e5ba1.png "Screen Shot 0029-02-02 at 6.43.08 PM.png")


※ この辺は画像の権利等あるかと思いますのでご自身にて集めてください。

### ② 学習モデルの作成

画像データとカテゴリの配列を保存プログラムを準備します。

```python:data_create.py
from PIL import Image
import sys
from sklearn import cross_validation
import os, glob
import numpy as np
import random, math

#
# トレーニングデータを生成
#
class DataCreate : 
  def __init__(self, script_name):
    Image.LOAD_TRUNCATED_IMAGES = True
  
  def create(self) :
    input_dir = "images"
    categorys = []
    
    dir_list = os.listdir(input_dir)
    for index, dir_name in enumerate(dir_list):
      if dir_name == '.DS_Store' :
        continue
      categorys.append(dir_name)
    image_size = 50
    train_data = [] # 画像データ, ラベルデータ
    for idx, category in enumerate(categorys): 
      try :
        print("---", category)
        image_dir = input_dir + "/" + category
        files = glob.glob(image_dir + "/*.jpg")
        for i, f in enumerate(files):
          img = Image.open(f)
          img = img.convert("RGB")
          img = img.resize((image_size, image_size))
          data = np.asarray(img)
          train_data.append([data, idx])

      except:
        print("SKIP : " + category)

    # データをshuffle
    random.shuffle(train_data)
    X, Y = [],[]
    for data in train_data: 
      X.append(data[0])
      Y.append(data[1])

    test_idx = math.floor(len(X) * 0.8)
    xy = (np.array(X[0:test_idx]), np.array(X[test_idx:]), 
          np.array(Y[0:test_idx]), np.array(Y[test_idx:]))
    np.save("./npy/flower", xy)
  

if __name__ == "__main__":
  args = sys.argv
  datacreate = DataCreate(args[0])
  datacreate.create()


```

次に学習モデルを作るプログラムを準備します。

```python:train.py
import sys
import os
import numpy as np
import pandas as pd
import gc
from keras.models import Sequential
from keras.layers import Convolution2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.utils import np_utils

#
# モデルを生成
#
class TrainModel : 
  def __init__(self):
    input_dir = 'images'
    self.nb_classes = len([name for name in os.listdir(input_dir) if name != ".DS_Store"])
    x_train, x_test, y_train, y_test = np.load("./npy/flower.npy")
    # データを正規化する
    self.x_train = x_train.astype("float") / 256
    self.x_test = x_test.astype("float") / 256
    self.y_train = np_utils.to_categorical(y_train, self.nb_classes)
    self.y_test = np_utils.to_categorical(y_test, self.nb_classes)

  def train(self, input=None) :
    model = Sequential()
    # K=32, M=3, H=3
    if input == None :
      model.add(Convolution2D(32, 3, 3, border_mode='same', input_shape=self.x_train.shape[1:]))
    else :
      model.add(Convolution2D(32, 3, 3, border_mode='same', input_shape=input))

    # K=64, M=3, H=3（調整）
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(0.25))
    model.add(Convolution2D(64, 3, 3, border_mode='same'))
    model.add(Activation('relu'))
    # K=64, M=3, H=3（調整）
    model.add(Convolution2D(64, 3, 3))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(0.25))
    model.add(Flatten()) 
    model.add(Dense(512))
    # biases  nb_classes
    model.add(Activation('relu'))
    model.add(Dropout(0.5))
    model.add(Dense(self.nb_classes))
    model.add(Activation('softmax'))

    model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy'])
    if input == None :
      # 学習してモデルを保存
      model.fit(self.x_train, self.y_train, batch_size=32, nb_epoch=10)
      hdf5_file = "./model/flower-model.hdf5"
      model.save_weights(hdf5_file)

      # modelのテスト
      score = model.evaluate(self.x_test, self.y_test)
      print('loss=', score[0])
      print('accuracy=', score[1])
    return model

if __name__ == "__main__":
  args = sys.argv
  train = TrainModel()
  train.train()
  gc.collect()

```

これで学習は完了。

続いて実行してみます。
 ※ スペックにもよりますが、少し時間がかかります。

```bash
# 画像を配列化
$ python data_create.py
--- アイ（タデアイ） 
--- アオギリ 
--- アオモリアザミ（オキノアザミ） 
--- アガパンサス（ムラサキクンシラン）
--- アカザ 
--- アカショウマ 
--- アカバナソバ 
--- アカバナミツマタ 
--- アカヤジオウ 
--- アカンサス(ハアザミ)
--- アギナシ 
--- アキザキスノーフレーク 
--- アキノウナギツカミ 
--- アキノタムラソウ（サルビア・ヤポニカ） 
--- アキノノゲシ 
--- アキノワスレグサ 
   ：
   ：

# 学習
$ python train.py 
  :
loss= 0.0410282239616
accuracy= 0.992773946126

```
的中率 99%はバグってる（笑）

### ③ とりあえずモデルのテスト

モデルテスト用に簡単なプログラムを用意します。

```python:check.py
import train as train
import sys, os
from PIL import Image
import numpy as np
import pandas as pd

if len(sys.argv) <= 1:
  quit()

image_size = 50
input_dir = 'images'
categories = [name for name in os.listdir(input_dir) if name != ".DS_Store"]

X = []
for file_name in sys.argv[1:]:
  img = Image.open(file_name)
  img = img.convert("RGB")
  img = img.resize((image_size, image_size))
  in_data = np.asarray(img)
  X.append(in_data)

X = np.array(X)

model = train.TrainModel().train(X.shape[1:])
model.load_weights("./model/flower-model.hdf5")

predict = model.predict(X)

for pre in predict:
  y = pre.argmax()
  print("花の名前 : ", categories[y])

```

#### テスト実行
きゅうりの花でテスト。

![flower.jpg](https://qiita-image-store.s3.amazonaws.com/0/52867/25467655-8ada-d68d-b453-22b6665d75b6.jpeg "flower.jpg")


```bash
$ python check.py test/flower.jpg
Using TensorFlow backend.
花の名前 :  キュウリ 

```

きゅうりは成功！でも他の写真だとうまくいかないときもあった。
もう少し勉強して、またあげます。

## 最後に

時間あるときに学習付きのチャットボットの作り方もあげます。