CNN2 画像認識の応用

[Aidemy](https://aidemy.net/)　2020/10/3
#はじめに
　こんにちは、んがょぺです！バリバリの文系ですが、AIの可能性に興味を持ったのがきっかけで、AI特化型スクール「Aidemy」に通い、勉強しています。ここで得られた知識を皆さんと共有したいと思い、Qiitaでまとめています。以前のまとめ記事も多くの方に読んでいただけてとても嬉しいです。ありがとうございます！
　今回は、CNNの二つ目の投稿になります。どうぞよろしくお願いします。

＊本記事は「Aidemy」での学習内容を「自分の言葉で」まとめたものになります。表現の間違いや勘違いを含む可能性があります。ご了承ください。

今回学ぶこと
・データの水増しについて(ImageDataGenerator)
・正規化について
・転移学習について

#データの水増し
・画像認識では、画像データとそのラベル（教師データ）の組み合わせが大量に必要となるが、大量のデータを集めることはコストと時間がかかる。そこで、データの個数を十分量に増やすために__画像の水増し__を行う。
・水増しは、画像を反転させるなどして行う。（→データクレンジングの総合演習問題）
・今回は、KerasのImageDataGeneratorを使う。これの引数に値を適切に設定することで、簡単にデータを加工できる。

##ImageDataGeneratorでよく使われる引数
・__rotation_range__:ランダムに__回転する__ときの範囲（x°）
・__width_shift_range__:ランダムに__水平方向に平行移動する__ときの画像に対する割合（x％を小数で）
・__height_shift_range__:ランダムに__垂直方向に平行移動する__ときの画像に対する割合（x％を小数で）
・__shear_range__:__せん断の度合い__、値が大きいほど斜め方向に引っ張った画像になる（0~0.3925）
・__zoom_range__:ランダムに__画像を圧縮、拡大させる__割合。(下限1-x、上限1+x)
・__channel_shift_range__:RGB3チャンネルなら、R,G,Bそれぞれの値が0~255の範囲でランダムで変わる（色が変わる）(0~255)
・__horizontal_flip__:Trueとすると、ランダムに__水平方向に反転__
・__vertial_flip__:Trueとすると、ランダムに__垂直方向に反転__

###・flow
・__flow__は、データとラベルを受け取り、後述する拡張/正規化したデータのバッチを生成するために使う。具体的な動作についてもその項で取り扱う。
・__flow(データ,引数)__のように使い、その引数については以下で解説する。
　・x:画像データ（四次元）
　・y:ラベル
　・batch_size:データのバッチサイズ（モデルに一度に入力するデータの数→ディープラーニング）
　・shuffle:データをシャッフルするか（TrueorFalse）
　・save_to_dir:生成された拡張/正規化画像を保存するディレクトリを指定（可視化に有効）
　・save_prefix:保存の際のファイル名
　・save_format:保存のフォーマット（"png"または"jpeg"）

#正規化
##正規化とは
・正規化とは、データをある決まりにしたがって処理を行い、__使いやすくすること__である。
・正規化の方法は様々であり、代表的なものには「バッチ正規化（BN）」などがある。このような正規化の方法は、大きく__「標準化」__と__「白色化」__に分けられる。詳しくは次項以降で見る。

##標準化
・標準化とは、__特徴の平均を0、分散を1にする__ことで、特徴毎のデータの分布を近づける正規化の方法である。
・標準化を行った画像は平均化されるので灰色がかった色になるが、それによってそれまで目立っていなかった色が他の色と同じように重要視されるようになるので、__隠れていた特徴を見つけやすくなる__。

・標準化の方法は、平均を0、分散を1にすれば良いので、ImageDataGenerator（）の引数に、それぞれ__samplewise_center=True__と__samplewise_std_normalization=True__を設定すれば良い。
・また、先述の__flow__を使ってデータのバッチを生成する必要がある。これを行わないと、標準化されない。
・以下はCIFER-10(乗り物と動物の画像)について、標準化を行っている。

```python
from keras.datasets import cifar10
from keras.preprocessing.image import ImageDataGenerator
#CIFER-10からデータセットの取得
(X_train,y_train),(X_test,y_test)=cifar10.load_data()
#標準化ジェネレーターの生成
datagen= ImageDataGenerator(samplewise_center=True,samplewise_std_normalization=True)
#flowで標準化
g=datagen.flow(X_train, y_train, shuffle=False)
X_batch,y_batch=g.next()
```

##白色化
・白色化とは、特徴間の相関をなくす正規化の方法である。
・白色化を行うと、データが暗くなり、輪郭が強調されるが、それによって情報量の少ない背景などに割く学習時間を減らし、__情報量の多い（物体の）輪郭に割く学習時間が増える__ので、学習効率が良くなる。

・白色化の方法は、ImageDataGeneratorに__featurewise_center=True__と__zca_whitening=True __を指定すれば良い。また、標準化と同じで、flow()でバッチを作成する。

```python

#白色化ジェネレーターの生成
datagen = ImageDataGenerator(featurewise_center=True,zca_whitening=True)
#白色化
g = datagen.flow(X_train, y_train, shuffle=False)
X_batch, y_batch = g.next()
```

##バッチ正規化
・バッチ正規化とは、__バッチごとに正規化を行うこと__である。これにより、中間層（隠れ層）でも正規化を行うことができるようになる。特に、__ReLU関数を使っているとき__にバッチ正規化を使うことで学習がスムーズになる。
・方法は、__model.add(BatchNormalization())__で行う。

・以下はバッチ正規化の例である。

```python
model.add(BatchNormalization())
model.add(Dense(128))
model.add(Activation('relu'))
```

#転移学習
##転移学習とは
・転移学習とは、__すでに学習されているモデルを使って新たなモデルの学習を行うこと__である。これにより、大規模なデータを扱う場合でも、スムーズに新モデルが作成できるようになる。
・転移学習に使われるモデルの一例として、__VGG__というものがある。VGGモデルは、ImageNetという1200万枚の画像データを1000クラスに分けたデータセットで学習したモデルである。
・VGGはクラスが1000個あるので最後の全結合層も1000個あるが、__転移学習においては途中までの層を使うだけで良い__。

##VGGモデルを転移学習してCIFER-10を分類する
・この場合の学習の流れは
①VGGモデルをインポートして定義する
②VGGモデルの層以降に追加する新規層を定義する
③VGGモデルの層のうち何層までを使うかを設定する
④コンパイルして終了

```python
#①VGGモデルをインポートして定義する
from keras.applications.vgg16 import VGG16
input_tensor=Input(shape=(32,32,3))
vgg16=VGG16(include_top=False,weights='imagenet',input_tensor=input_tensor)
#②VGGモデルの層以降に追加する新規層を定義する
top_model=Sequential()
top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))
top_model.add(Dense(256,activation='sigmoid'))
top_model.add(Dropout(0.5))
top_model.add(Dense(10,activation='softmax'))
model = Model(inputs=vgg16.input,outputs=top_model(vgg16.output))
#③VGGモデルの層のうち何層までを使うかを設定する
for layer in model.layers[:19]:
    layer.trainable=False
#④コンパイルして終了
model.compile(loss='categorical_crossentropy',
              optimizer=optimizers.SGD(lr=1e-4, momentum=0.9),
              metrics=['accuracy'])
```

・①について、__Input(shape=(32,32,3))__は入力画像のインプット、__include_top=False__はVGGモデルの最後の全結合層を使うかどうかの設定、__weights='imagenet'__は重みはImageNetで学習したものを使用する、という意味である。
・②について、__Model(inputs=vgg16.input,outputs=top_model(vgg16.output))__はVGGモデルと新モデルを結合するという意味である。
・③について、__for layer in model.layers[:19]:__はVGGモデルの20層目までを使うという意味であり、__layer.trainable = False__はVGGモデルは学習しないように設定している。
・④について、コンパイルは普通だが、__転移学習をするときは、最適化関数はSGDにするのが良い__。

#まとめ
・データの水増しは、__ImageDataGenerator（）__に引数を与えて行う。具体的には、画像の反転などを行う。
・__正規化__とは、データをある決まりにしたがって処理を行い、使いやすくすることである。正規化の方法としては__「標準化」__や__「白色化」__がある。__正規化によって特徴の抽出の精度が上がり、学習効率が良くなる__。
・転移学習とは、__すでに学習されているモデルを使って新たなモデルの学習を行うこと__である。今回は、画像のデータセットVGGを使った。

今回は以上です。最後まで読んでいただき、ありがとうございました。