[Keras/TensorFlow] Kerasで自前のデータから学習と予測

# 目的
ゼロからKerasとTensorFlow(TF)を自由自在に動かせるようになる。
そのための、End to Endの作業ログ(備忘録)を残す。
※環境はMacだが、他のOSでの汎用性を保つように意識。
※アジャイルで執筆しており、精度を逐次高めていく予定。


# 環境
- Mac: 10.12.3
- Python: 3.6
- TensorFlow: 1.0.1
- Keras: 2.0.2


# To Do
### Keras導入編
[Keras(Tensorflow)の環境構築](http://qiita.com/agumon/items/4d7826c2c39d0af868bd)
[KerasでMINSTの学習と予測](http://qiita.com/agumon/items/4c6ff24bfd77a5c8101d)
[KerasでTensorBoardの利用](http://qiita.com/agumon/items/114da6921c5dc4f7d7f9) 
[Kerasで重みファイルの保存/読み込み](http://qiita.com/agumon/items/91f897b7f260f6aeca95)
[Kerasで自前データの学習と予測](http://qiita.com/agumon/items/ab2de98a3783e0a93e66) <---いまココ
[Kerasで転移学習](http://qiita.com/f2um2326/items/60f7bcc8ae26b5004a3a#_reference-5d15f52415912c9c2182)


### 学習のテクニック編
KerasでCV
Kerasでグリッドサーチ、ランダムサーチ
Kerasで最適化手法の調整
Kerasで画像の水増し
Kerasで学習過程の可視化
Kerasで事前学習



# Kerasのデータセット
Kerasには自前の画像データセットはある。
https://keras.io/ja/datasets/

- CIFAR10
- CIFAR100
- MINST

読み込みは簡単。

```py
from keras.datasets import cifar10

(X_train, y_train), (X_test, y_test) = cifar10.load_data()
```

中身は２つのタプルになっているそうだ。

- X_train, X_test: shape (nb_samples, 3, 32, 32)のRGB画像データのuint8配列．
- y_train, y_test: shape (nb_samples,)のカテゴリラベル(0-9の範囲のinteger)のuint8配列．


一方で、そこに存在しないデータセットを利用した学習の方法を考える。


# 参考
http://aidiary.hatenablog.com/entry/20170110/1484057655


# 必要なパーツ

クラス宣言部分

```py
train_datagen = ImageDataGenerator(
    rescale=1.0 / 255,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True)

test_datagen = ImageDataGenerator(rescale=1.0 / 255)
```

入力フォルダ指定部分

```py
train_generator = train_datagen.flow_from_directory(
    'data/train',
    target_size=(256, 256),
    batch_size=batch_size,
    class_mode='categorical')

validation_generator = test_datagen.flow_from_directory(
    'data/validation',
    target_size=(256, 256),
    batch_size=batch_size,
    class_mode='categorical')
```

カテゴリと割り当てられてクラス番号の対応の確認

```py
print(train_generator.class_indices)
```

予測の部分

```py
history = model.fit_generator(
    train_generator,
    samples_per_epoch=10,
    nb_epoch=nb_epoch,
    validation_data=validation_generator,
    nb_val_samples=10)
```

※単品の画像の場合は`load_img('image.jpg',target(256,256))`でロードできる。


# 学習用データ
kaggleより、犬、猫の画像をダウンロード。学習データ：犬200枚/猫200枚、テストデータ：犬200枚/猫200枚、とした。
https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition


フォルダ構造

```
$ find ./data -type d  | sed -e "s/[^-][^\/]*\// |/g" -e "s/|\([^ ]\)/|-\1/"
 |-data
 | |-train
 | | |-cats
 | | |-dogs
 | |-validation
 | | |-cats
 | | |-dogs
```



# 実行
```py:cat_dog.py
from keras.models import Sequential
from keras.layers import Convolution2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.preprocessing.image import ImageDataGenerator


batch_size = 32
epochs = 1


model = Sequential()
model.add(Convolution2D(32, 3, 3, input_shape=(128, 128, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Convolution2D(64, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Flatten())
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(2))
model.add(Activation('softmax'))

model.summary()

model.compile(loss='categorical_crossentropy',
               optimizer='adam',
               metrics=['accuracy'])


train_datagen = ImageDataGenerator(
    rescale=1.0 / 255,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True)

test_datagen = ImageDataGenerator(rescale=1.0 / 255)


train_generator = train_datagen.flow_from_directory(
    'data/train',
    target_size=(128, 128),
    batch_size=batch_size,
    class_mode='categorical')

validation_generator = test_datagen.flow_from_directory(
    'data/validation',
    target_size=(128, 128),
    batch_size=batch_size,
    class_mode='categorical')


history = model.fit_generator(
    train_generator,
    samples_per_epoch=200,
    nb_epoch=epochs,
    validation_data=validation_generator,
    nb_val_samples=200)

from keras.utils import plot_model
plot_model(model, to_file="model.png", show_shapes=True)
```

以下、実行結果。

```
Epoch 1/1
6/6 [==============================] - 87s - loss: 1.4899 - acc: 0.5625 - val_loss: 0.7940 - val_acc: 0.5014
```

# モデルの可視化
https://keras.io/visualization/
Keras2になったため、以前と少し変わっている。

```py
from keras.utils import plot_model
plot_model(model, to_file="model.png", show_shapes=True)
```

![model.png](https://qiita-image-store.s3.amazonaws.com/0/175092/d45aecf1-6226-973b-966f-b294974c44c6.png)



# スクリプトの説明
#### model.add(Convolution2D(32, 3, 3, input_shape=(128, 128, 3)))
https://keras.io/ja/layers/convolutional/#convolution2d
(128, 128, 3)は入力画像のサイズを表す。128*128 pixlでRGBの3種類があるため3である。
32, 3, 3は、32枚のフィルタ 、サイズは3*3を表す。
(128, 128, 3)の画像がこのレイヤーを通ると、(126, 126, 32)になる。

#### model.add(Activation('relu'))
https://keras.io/ja/layers/core/#activation
活性化関数。画像ではreluがよく使われる。

#### model.add(MaxPooling2D(pool_size=(2, 2)))
入力のサイズを半分にする。
入力が(126, 126, 32)で、このレイヤーを通ると約半分の(61, 61, 32)になる。

#### model.add(Flatten())
https://keras.io/ja/layers/core/#flatten
平坦化。
入力(30, 30, 64)に対して、出力は(*, 57600)となる。
57600 =30x30x64

#### model.add(Dense(64))
https://keras.io/ja/layers/core/#dense
通常の全結合。
(*, 57600)->(*,64)となる。

#### model.add(Dropout(0.5))
https://keras.io/ja/layers/core/#dropout
50%の確率で各ユニットの出力を利用。

#### ImageDataGenerator
https://keras.io/ja/preprocessing/image/
rescale=1.0 / 255 # デフォルトはNone．Noneか0ならば，適用しない．それ以外であれば，(他の変換を行う前に) 与えられた値をデータに積算する
shear_range=0.2 # 浮動小数点数．シアー強度（反時計回りのシアー角度（ラジアン））．
zoom_range=0.2 # 浮動小数点数または[lower，upper]．ランダムにズームする範囲．
horizontal_flip=True # 真理値．水平方向に入力をランダムに反転します

#### fit_generator
https://keras.io/ja/models/model/
samples_per_epoch=200 # 
nb_val_samples=200 # 

# End