個人メモ-画像処理①（Keras + VGG16）

今回読むのはこちら
https://www.kaggle.com/twhitehurst3/stanford-dogs-keras-vgg16

```python:
%matplotlib inline
import pandas as pd
import os,shutil,math,scipy,cv2
import numpy as np
import matplotlib.pyplot as plt
import random as rn


from sklearn.utils import shuffle
from sklearn.metrics import classification_report
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import confusion_matrix,roc_curve,auc

from PIL import Image
from PIL import Image as pil_image
from PIL import ImageDraw

from time import time
from glob import glob
from tqdm import tqdm
from skimage.io import imread
from IPython.display import SVG

from scipy import misc,ndimage
from scipy.ndimage.interpolation import zoom
from scipy.ndimage import imread


from keras import backend as K
from keras.utils.np_utils import to_categorical
from keras import layers
from keras.preprocessing.image import save_img
from keras.utils.vis_utils import model_to_dot
from keras.applications.vgg16 import VGG16,preprocess_input
from keras.applications.xception import Xception
from keras.applications.nasnet import NASNetMobile
from keras.models import Sequential,Input,Model
from keras.layers import Dense,Flatten,Dropout,Concatenate,GlobalAveragePooling2D,Lambda,ZeroPadding2D
from keras.layers import SeparableConv2D,BatchNormalization,MaxPooling2D,Conv2D
from keras.preprocessing.image import ImageDataGenerator
from keras.optimizers import Adam,SGD
from keras.utils.vis_utils import plot_model
from keras.callbacks import ModelCheckpoint,EarlyStopping,TensorBoard,CSVLogger,ReduceLROnPlateau,LearningRateScheduler
```

%matplotlib inline
Jupyter Notebookで、ノートブック上にグラフを描画する際に指定する記述

shutil
https://docs.python.org/ja/2.7/library/shutil.html
ファイルやファイルの集まりに対する高水準の操作方法を多数提供

cv2
https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_gui/py_image_display/py_image_display.html
画像操作用のライブラリ、OpenCVと呼ばれる。

PIL
https://note.nkmk.me/python-pillow-basic/
OpenCVに劣るけど、コードがシンプルでとっつきやすい。

glob
https://techacademy.jp/magazine/18928
引数に指定されたパターンにマッチするファイルパス名を取得するライブラリ

tqdm
https://blog.amedama.jp/entry/2018/07/23/080000
プログレスバー表示

from skimage.io import imread
https://qiita.com/supersaiakujin/items/fc54116df9ca6958a68d

from IPython.display import SVG
http://pynote.hatenablog.com/entry/jupyter-notebook-show-image-inline

from scipy import misc
https://docs.scipy.org/doc/scipy/reference/misc.html
PILで高度な操作を行うにはこいつが必要らしいことが書いてある。

from scipy import ndimage
https://docs.scipy.org/doc/scipy/reference/ndimage.html
多次元の画像処理に必要

from scipy.ndimage.interpolation import zoom
https://docs.scipy.org/doc/scipy-0.15.1/reference/generated/scipy.ndimage.interpolation.zoom.html

from scipy.ndimage import imread
https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.ndimage.imread.html


```python:
def show_final_history(history):
    fig, ax = plt.subplots(1, 2, figsize=(15,5))
    ax[0].set_title('loss')
    ax[0].plot(history.epoch, history.history["loss"], label="Train loss")
    ax[0].plot(history.epoch, history.history["val_loss"], label="Validation loss")
    ax[1].set_title('acc')
    ax[1].plot(history.epoch, history.history["acc"], label="Train acc")
    ax[1].plot(history.epoch, history.history["val_acc"], label="Validation acc")
    ax[0].legend()
    ax[1].legend()

```

学習結果を可視化するためのメソッド

```python:
def label_assignment(img,label):
    return label

def training_data(label,data_dir):
    for img in tqdm(os.listdir(data_dir)):
        label = label_assignment(img,label)
        path = os.path.join(data_dir,img)
        img = cv2.imread(path,cv2.IMREAD_COLOR)
        img = cv2.resize(img,(imgsize,imgsize))
        
        X.append(np.array(img))
        Z.append(str(label))
```
引数として渡されたディレクトリに含まれる画像ファイルでループを回して、
OpenCVで画像を読み込み、リサイズしたものをnumpyの配列としてXに格納。
答えのラベルはZに格納。

```python:
chihuahua_dir = '../input/images/Images/n02085620-Chihuahua'
japanese_spaniel_dir = '../input/images/Images/n02085782-Japanese_spaniel'
maltese_dir = '../input/images/Images/n02085936-Maltese_dog'
pekinese_dir = '../input/images/Images/n02086079-Pekinese'
shitzu_dir = '../input/images/Images/n02086240-Shih-Tzu'
blenheim_spaniel_dir = '../input/images/Images/n02086646-Blenheim_spaniel'
papillon_dir = '../input/images/Images/n02086910-papillon'
toy_terrier_dir = '../input/images/Images/n02087046-toy_terrier'
afghan_hound_dir = '../input/images/Images/n02088094-Afghan_hound'
basset_dir = '../input/images/Images/n02088238-basset'


X = []
Z = []
imgsize = 150
```
各画像格納のパスを設定する。

```python:
training_data('chihuahua',chihuahua_dir)
training_data('japanese_spaniel',japanese_spaniel_dir)
training_data('maltese',maltese_dir)
training_data('pekinese',pekinese_dir)
training_data('shitzu',shitzu_dir)
training_data('blenheim_spaniel',blenheim_spaniel_dir)
training_data('papillon',papillon_dir)
training_data('toy_terrier',toy_terrier_dir)
training_data('afghan_hound',afghan_hound_dir)
training_data('basset',basset_dir)
```
X,Zにそれぞれのデータとラベルを格納していく。

```python:
label_encoder= LabelEncoder()
Y = label_encoder.fit_transform(Z)
Y = to_categorical(Y,10)
X = np.array(X)
X=X/255

x_train,x_test,y_train,y_test = train_test_split(X,Y,test_size=0.3,random_state=69)
````
LabelEncoderは'No','Yes'といったカテゴリ変数を'0','1'といった離散値に変換する。
https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.LabelEncoder.html

to_categoricalは正解のラベルをone-hotに変換する際に使用する。
https://hironsan.hatenablog.com/entry/keras-to-categorical

値を0~1の範囲にするため255で割ります。

train_test_splitはデータを教師データとテストデータに分割する際に使用する。

```python:
augs_gen = ImageDataGenerator(
        featurewise_center=False,  
        samplewise_center=False, 
        featurewise_std_normalization=False,  
        samplewise_std_normalization=False,  
        zca_whitening=False,  
        rotation_range=10,  
        zoom_range = 0.1, 
        width_shift_range=0.2,  
        height_shift_range=0.2, 
        horizontal_flip=True,  
        vertical_flip=False) 

augs_gen.fit(x_train)
```
ImageDataGeneratorは画像を拡張して生成する。
https://keras.io/ja/preprocessing/image/

featurewise_center：データセット全体で，入力の平均を0にするかどうか。
samplewise_center：各サンプルの平均を0にするかどうか。
zca_whitening：ZCA白色化を適用するかどうか。
http://kikei.github.io/ai/2018/03/28/cifar10-whitening.html
rotation_range：画像をランダムに回転する回転範囲 
zoom_range：ランダムにズームする範囲
width_shift_range：ランダムに水平シフトする範囲
height_shift_range：ランダムに垂直シフトする範囲
horizontal_flip：水平方向に入力をランダムに反転します
vertical_flip：垂直方向に入力をランダムに反転します

```python:
fig,ax=plt.subplots(5,2)
fig.set_size_inches(15,15)
for i in range(5):
    for j in range (2):
        l=rn.randint(0,len(Z))
        ax[i,j].imshow(X[l])
        ax[i,j].set_title('Dog: '+Z[l])
        
plt.tight_layout()
```

5×2でランダムに画像を表示


```python:
base_model = VGG16(include_top=False,
                  input_shape = (imgsize,imgsize,3),
                  weights = 'imagenet')

for layer in base_model.layers:
    layer.trainable = False
    
for layer in base_model.layers:
    print(layer,layer.trainable)

model = Sequential()
model.add(base_model)
model.add(GlobalAveragePooling2D())
model.add(Dropout(0.5))
model.add(Dense(10,activation='softmax'))
model.summary()

SVG(model_to_dot(model).create(prog='dot', format='svg'))
plot_model(model, to_file='model_plot.png', show_shapes=True, show_layer_names=True, expand_nested=True)
```

VGG16は畳み込み13層とフル結合3層の計16層から成る畳み込みニューラルネットワーク。
http://aidiary.hatenablog.com/entry/20170104/1483535144
https://qiita.com/MuAuan/items/86a56637a1ebf455e180

include_top：VGG16のトップにある1000クラス分類するフル結合層（FC）を含むか含まないかを指定

レイヤーの重みの更新は行わない。

kerasのモデルを構築
Sequential()でインスタンスを生成し、レイヤーを追加していく。
https://keras.io/ja/models/sequential/
GlobalAveragePooling2Dは以下
https://ch.nicovideo.jp/akiba-cyberspacecowboys/blomaga/ar1425210
Dropout（引数は訓練時の更新においてランダムに入力ユニットを0とする割合）
https://keras.io/ja/layers/core/
Denseは通常の全結合ニューラルネットワークレイヤー
https://keras.io/ja/layers/core/

SVGでモデルのSVGファイルを出力

```python:
checkpoint = ModelCheckpoint(
    './base.model',
    monitor='val_loss',
    verbose=1,
    save_best_only=True,
    mode='min',
    save_weights_only=False,
    period=1
)
earlystop = EarlyStopping(
    monitor='val_loss',
    min_delta=0.001,
    patience=30,
    verbose=1,
    mode='auto'
)
tensorboard = TensorBoard(
    log_dir = './logs',
    histogram_freq=0,
    batch_size=16,
    write_graph=True,
    write_grads=True,
    write_images=False,
)

csvlogger = CSVLogger(
    filename= "training_csv.log",
    separator = ",",
    append = False
)

reduce = ReduceLROnPlateau(
    monitor='val_loss',
    factor=0.1,
    patience=3,
    verbose=1, 
    mode='auto'
)

callbacks = [checkpoint,tensorboard,csvlogger,reduce]
```
コールバックの設定。
https://keras.io/ja/callbacks/

checkpoint：各エポック終了後にモデルを保存
earlystop：監視する値の変化が停止した時に訓練を終了
tensorboard：Tensorboardによる基本的な可視化
https://www.tensorflow.org/tensorboard/r1/summaries
csvlogger：各エポックの結果をcsvファイルに保存する
ReduceLROnPlateau：評価値の改善が止まった時に学習率を減らす。

```python
#-----------Optimizers-----------#
opt = SGD(lr=1e-4,momentum=0.99)
opt1 = Adam(lr=1e-2)
#----------Compile---------------#
model.compile(
    loss='categorical_crossentropy',
    optimizer='adam',
    metrics=['accuracy']
)
#-----------Training------------#
history = model.fit_generator(
    augs_gen.flow(x_train,y_train,batch_size=16),
    validation_data  = (x_test,y_test),
    validation_steps = 1000,
    steps_per_epoch  = 1000,
    epochs = 20, 
    verbose = 1,
    callbacks=callbacks
)
```
https://keras.io/ja/optimizers/
SGD：確率的勾配降下法

modelのメソッドについて
https://keras.io/ja/models/model/

```python
show_final_history(history)
model.load_weights('./base.model')
model_score = model.evaluate(x_test,y_test)
print("Model Test Loss:",model_score[0])
print("Model Test Accuracy:",model_score[1])

model_json = model.to_json()
with open("model.json","w") as json_file:
    json_file.write(model_json)
    
model.save("model.h5")
print("Weights Saved")
```
結果の図示